自动搜索IP建立TCP连接

在这里插入代码片# client
使用UDP广播和TCP通信的网络程序,主要功能是搜索局域网内的服务器并与之建立TCP连接进行通信。
程序首先创建一个UDP套接字,设置多播TTL参数和接收超时时间。然后将目标地址设置为多播地址和指定的端口,发送UDP广播消息。
在接收到服务器的响应后,将服务器的IP地址保存在kSrvInfos数组中,并创建一个新的线程来处理与该服务器的TCP通信。线程中通过创建TCP套接字,连接到服务器,并循环发送数据和接收数据。
主函数中调用search_son()函数来执行搜索服务器的操作。搜索过程中,每接收到一个服务器的响应就会创建一个新的线程来处理与该服务器的TCP通信。
最后程序进入休眠状态,等待一段时间后退出。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUF_SIZE 		88
#define UDP_PORT     	12345
#define TCP_PORT     	10000
#define MUNICASTADDR    "239.0.0.99"   //组播地址

struct server_info{
    char ip[16];
};
struct server_info kSrvInfos[20];

void work_thread(void *arg){
    int index = (int)arg;
    // 1. 创建通信的套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }

    // 2. 连接服务器
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(TCP_PORT);
    inet_pton(AF_INET, kSrvInfos[index].ip, &addr.sin_addr.s_addr);

    int ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1) {
        perror("connect");
        return -2;
    }

    // 3. 和服务器端通信
    int number = 0;
    for(int i = 0; i < 20; i++) {
        // 发送数据
        char buf[1024];
        sprintf(buf, "hello world...%d\n", number++);
        write(fd, buf, strlen(buf)+1);
        
        // 接收数据
        memset(buf, 0, sizeof(buf));
        int len = read(fd, buf, sizeof(buf));
        if(len > 0) {
            printf("服务器say: %s\n", buf);
        }
        else if(len  == 0) {
            printf("服务器断开了连接...\n");
            break;
        }
        else {
            perror("read");
            break;
        }
        sleep(1);   // 每隔1s发送一条数据
    }
    close(fd);
}

int search_son()
{
   // 创建套接字
    int search_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (search_sock < 0) {
        printf("Error creating socket\n");
        return -1;
    }

    // 设置多播TTL参数
    unsigned char ttl = 1;
    if (setsockopt(search_sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
        printf("Error setting multicast TTL\n");
        return -1;
    }

    struct timeval tv = {3, 0};
    if (setsockopt(search_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval)) < 0) {
        printf("Error setting multicast TTL\n");
        return -1;
    }
    

    // 设置目标地址
    struct sockaddr_in multicast_addr;
    memset(&multicast_addr, 0, sizeof(multicast_addr));
    multicast_addr.sin_family = AF_INET;
    multicast_addr.sin_addr.s_addr = inet_addr(MUNICASTADDR); // 多播地址
    multicast_addr.sin_port = htons(UDP_PORT); // 目标端口

    // 发送数据
    char * msg = "Hello, Multicast!";
    if (sendto(search_sock, msg, strlen(msg), 0, (struct sockaddr*)&multicast_addr, sizeof(multicast_addr)) < 0) {
        printf("Error sending data\n");
        return -1;
    }

    char *ip = NULL;
    char buf[BUF_SIZE] = {0};
    struct sockaddr_in cliaddr;
    socklen_t cliaddrlen = sizeof(cliaddr);
    
    int index = 0;
    while(1)
    {
        //接收数据
        memset(buf, 0, BUF_SIZE);
        bzero(&cliaddr, cliaddrlen);
        int recv_len = recvfrom(search_sock, buf, BUF_SIZE, 0, (struct sockaddr *)&cliaddr, &cliaddrlen);
        if (recv_len <= 0) {
            printf("search timeout\n");
            break;
        }
        printf("udp server from [%s - %hu]:%s\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port, buf);
        memcpy(kSrvInfos[index].ip, inet_ntoa(cliaddr.sin_addr), sizeof(kSrvInfos[index].ip));
        // ip = inet_ntoa(cliaddr.sin_addr);
        pthread_t tcp_thread_t;
        pthread_create(&tcp_thread_t, NULL, work_thread, (void*)index);
        pthread_detach(tcp_thread_t);
        index++;
    }
    //关闭套接字
    close(search_sock);
 
    return 0;
}


int main(){
    search_son();
    printf("search over, wait...\n");
    sleep(100);
}

server

基于TCP和UDP的服务器程序。它创建了一个TCP套接字,用于监听客户端连接,并创建了一个UDP套接字,用于接收和发送UDP数据报。
在主函数中,首先创建了一个线程tcp_listen_thread,用于监听TCP连接。在这个线程中,首先创建了一个监听套接字lfd,然后通过bind函数绑定本地IP和端口,之后通过listen函数开始监听客户端连接。接着进入一个循环,调用accept函数等待并接受客户端连接,当有客户端连接成功时,创建一个工作线程work_thread来处理与客户端的通信。在工作线程中,通过read函数接收客户端发送的数据,然后通过write函数将数据返回给客户端。
同时,在主函数中也创建了一个UDP套接字sock_fd,通过bind函数将其绑定到本地地址和端口,并通过setsockopt函数加入多播组。之后进入一个循环,调用recvfrom函数接收UDP数据报,并通过sendto函数将数据报返回给发送者。
最后,关闭套接字并等待tcp_listen_thread线程结束。

代码如下

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define BUF_SIZE 		88
#define PORT     		55555
#define ADDR     		"自定义ip地址"
#define MUNICASTADDR    "239.0.0.1"

void work_thread(void *arg) {
    int cfd = (int)arg;
    // 5. 和客户端通信
    while(1)
    {
        // 接收数据
        char buf[1024];
        memset(buf, 0, sizeof(buf));
        int len = read(cfd, buf, sizeof(buf));
        if(len > 0) {
            printf("客户端say: %s\n", buf);
            write(cfd, buf, len);
        }
        else if(len  == 0) {
            printf("客户端断开了连接...\n");
            break;
        }
        else {
            perror("read");
            break;
        }
    }
}

void tcp_listen_thread(void *arg){
    // 1. 创建监听的套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1) 
    {
        perror("socket");
        return;
    }

    // 2. 将socket()返回值和本地的IP端口绑定到一起
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10000);   // 大端端口
    addr.sin_addr.s_addr = INADDR_ANY;  // 这个宏的值为0 == 0.0.0.0  适配本机网卡
    int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret == -1) {
        perror("bind");
        return;
    }

    // 3. 设置监听
    ret = listen(lfd, 128);
    if(ret == -1) {
        perror("listen");
        return;
    }

    // 4. 阻塞等待并接受客户端连接
    struct sockaddr_in peer_addr;
    int clilen = sizeof(peer_addr);
    while(1) {

        int cfd = accept(lfd, (struct sockaddr*)&peer_addr, &clilen);
        if(cfd == -1) {
            perror("accept");
            return;
        }
        // 打印客户端的地址信息
        char ip[24] = {0};
        printf("客户端的IP地址: %s, 端口: %d\n",
            inet_ntop(AF_INET, &peer_addr.sin_addr.s_addr, ip, sizeof(ip)),
            ntohs(peer_addr.sin_port));

        pthread_t work_thread_t;
        pthread_create(&work_thread_t, NULL, work_thread, (void *)cfd);
        pthread_detach(work_thread_t);
    }

    close(lfd);

    return;
}

int main(int argc, char *argv[])
{
    pthread_t tcp_listen_t;
    pthread_create(&tcp_listen_t, NULL, tcp_listen_thread, NULL);
    // 创建套接字
    int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock_fd < 0) {
        printf("Error creating socket\n");
        return -1;
    }

    // 绑定到本地地址和端口
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // 任意本地地址
    addr.sin_port = htons(12345); // 目标端口

    if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        printf("Error binding socket\n");
        return -1;
    }

    // 加入多播组
    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr("239.0.0.99"); // 多播地址
    mreq.imr_interface.s_addr = htonl(INADDR_ANY); // 任意网络接口

    if (setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        printf("Error joining multicast group\n");
        return -1;
    }
 
    //接收数据
    char buf[BUF_SIZE] = { 0 };
    struct sockaddr_in peer_addr;
    socklen_t cliaddrlen = sizeof(peer_addr);
    while (1) {
        bzero(buf, BUF_SIZE);
        bzero(&peer_addr, cliaddrlen);
        int recv_len = recvfrom(sock_fd, buf, BUF_SIZE, 0, (struct sockaddr *)&peer_addr, &cliaddrlen);
        printf("udp server from [%s - %hu]:%s\n",inet_ntoa(peer_addr.sin_addr),peer_addr.sin_port,buf);
	    sendto(sock_fd, buf, recv_len, 0, (struct sockaddr*)&peer_addr, cliaddrlen);
		//printf("send clilen to %s\n",buf);
    }
 
    //关闭套接字
    close(sock_fd);
    pthread_join(tcp_listen_t);
 
    return 0;
}

你可能感兴趣的:(C语言,tcp/ip,网络协议)