12_TCP和UDP实现服务端和客户端的通信

一、TCP实现多进程通信

server_process.c 服务端代码:

#define _XOPEN_SOURCE
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>
void recyleChild(int arg)
{
    while(1)
    {
        int ret=waitpid(-1,NULL,WNOHANG);
        if(ret==-1)
        {
            //所有的子进程都被回收了;
            break;
        }
        else if(ret==0)
        {
            //还有子进程活着;
            break;
        }
        else if(ret>0)
        {
            //被回收了
            printf("子进程%d被回收了\n",ret);
        }
    }
 
}
 
int main()
{
    //注册信号捕捉
    struct sigaction act;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    act.sa_handler=recyleChild;
    sigaction(SIGCHLD,&act,NULL);
 
    //创建socket
    int lfd =socket(AF_INET,SOCK_STREAM,0);
    if(lfd==-1)
    {
        perror("socket");
        exit(-1);
    }
    //绑定
    struct sockaddr_in saddr;
    saddr.sin_addr.s_addr=0;
    saddr.sin_port=htons(9999);
    saddr.sin_family=AF_INET;
    int ret = bind(lfd,(const struct sockaddr*)&saddr,sizeof(saddr));
     if(ret==-1)
    {
        perror("bind");
        exit(-1);
    }
    //监听
    ret=listen(lfd,8);
    if(ret==-1)
    {
        perror("listen");
        exit(-1);
    }
 
    //不断循环,接收客户端连接;
    while(1)
    {
        struct sockaddr_in cliaddr;
        int len =sizeof(cliaddr);
 
        //接收连接
        int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
        if(cfd==-1)
        {
            if(errno==EINTR)
            {
                continue;
            }
            perror("accept");
            exit(-1);
        }
 
        //每一个连接进来就创建一个子进程进行客户端通信;
            pid_t pid=fork();
            if(pid==0)
            {
                //子进程
                //获取客户端信息
                    char cliIP[16];
                    inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,cliIP,sizeof(cliIP));
                    unsigned short cliPort=ntohs(cliaddr.sin_port);
                    printf("cliIp is :%s,port is %d\n",cliIP,cliPort);
 
                    //接收客户端发来的数据
                    char recvBuff[1024]={0};
                    while(1)
                    {
                        int len1=read(cfd,&(recvBuff),sizeof(recvBuff)+1);
                        if(len1==-1)
                        {
                            perror("read");
                            exit(-1);
                        }
                        else if(len1>0)
                        {
                            printf("recive client data:%s\n",recvBuff);
                        }
                        else if(len1==0)
                        {
                            printf("client close\n");
                        }
                    write(cfd,recvBuff,strlen(recvBuff)+1);
 
                    }
                  close(cfd);
                 exit(0);
            }
    }
    close(lfd);
    return 0;
}

分析:

  1. 首先创建socket函数,在进行绑定,封装服务器的IP、端口号等相关信息;
  2. 然后进行监听,等待客户端的连接,客户端的连接是在while循环中进行的,因为需要不断接收客户端的连接;
  3. 连接成功一个客户端后,创建一个进程,在子进程完成服务器与客户端的通信;
  4. 关于子进程的回收是利用信号进行回收,因为如果利用wait函数进行回收,会造成程序在对应位置进行阻塞。

client.c客户端代码 :

//TCP通信客户端
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
    //1、创建套接字
  int fd = socket(AF_INET,SOCK_STREAM,0);
  if(fd==-1)
  {
    perror("socket");
    exit(-1);
  }
    //2、连接服务器端
    struct sockaddr_in serve_addr;
    serve_addr.sin_family=AF_INET;
   inet_pton(AF_INET,"192.168.254.171",&serve_addr.sin_addr.s_addr);
   serve_addr.sin_port=htons(9999);
   int ret= connect(fd,(const struct sockaddr *)&serve_addr,sizeof(serve_addr));
    if(ret==-1)
    {
        perror("connect");
        exit(-1);
    }
    //3、进行通信
    char readbuff[1024]={0};
   // char writebuff[1024]={0};
    int i=0;
    while(1)
    {
     // memset(writebuff,0,sizeof(writebuff));
      //printf("请输入内容:\n");
      //scanf("%s",writebuff);
       //char * data="I am client";
       sprintf(readbuff,"data:%d\n",i++);
       //给服务端发送数据
       write(fd, &readbuff,strlen(readbuff));
     
      sleep(1);
 
    int len =read(fd,readbuff,sizeof(readbuff));
      if(len==-1)
    {
        perror("read");
       exit(-1);
    }else if(len>0)
    {
     printf("recive client data:%s\n",readbuff);
    }
  else if(len==0)
  {
    //表示服务器端断开连接。
    printf("serve closed...\n");
    break;
  }
    }
 
//关闭文件描述符;
  close(fd);
    return 0;
}

 分析:

  1. 首先创建socket函数,然后连接服务器;
  2. 链接成功后进行通信,进行数据的收发;
  3. 最后回收文件描述符。
  4. 一个客户端在一个子进程中通信,这也是TCP实现多进程通信的方法原理。

 二、TCP实现多线程通信 

利用TCP实现多线程通信与多进程通信的区别在于,当服务端连接到客户端后,与每一个客户端进行通信时,是创建进程进行通信函数还是线程进行通信函数,也就是说客户端的代码是相同的,区别在于服务器端的代码。

server_process.c 服务端代码:

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
 
struct sockinfo//该结构体用于存放服务端发送给子进程关于客户端的信息,因为进程只能传递一个参数。
{
    int fd;//通信文件描述符;
    struct sockaddr_in addr;
    pthread_t tid;//线程号;
};
 
struct sockinfo sockinfos[128];//定义全局变量,用于存放客户端的信息;
 
void * working (void *arg)
{
//获取客户端信息
    struct sockinfo *pinfo=(struct sockinfo*)arg;
 
        char cliIP[16];
        inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,cliIP,sizeof(cliIP));
        unsigned short cliPort=ntohs(pinfo->addr.sin_port);
        printf("cliIp is :%s,port is %d\n",cliIP,cliPort);
 
        //接收客户端发来的数据
        char recvBuff[1024]={0};
        while(1)
        {
            int len1=read(pinfo->fd,&(recvBuff),sizeof(recvBuff)+1);
            if(len1==-1)
            {
                perror("read");
                exit(-1);
            }
            else if(len1>0)
            {
                printf("recive client data:%s\n",recvBuff);
            }
            else if(len1==0)
            {
                printf("client close\n");
            }
        write(pinfo->fd,recvBuff,strlen(recvBuff)+1);
 
        }
    close(pinfo->fd);
    exit(0);
//子线程与客户端进行通信; cfd; 客户端的信息;线程号;
    return NULL;
}
 
 
 
int main()
{
 
 
    //创建socket
    int lfd =socket(AF_INET,SOCK_STREAM,0);
    if(lfd==-1)
    {
        perror("socket");
        exit(-1);
    }
    //绑定
    struct sockaddr_in saddr;
    saddr.sin_addr.s_addr=0;
    saddr.sin_port=htons(9999);
    saddr.sin_family=AF_INET;
    int ret = bind(lfd,(const struct sockaddr*)&saddr,sizeof(saddr));
     if(ret==-1)
    {
        perror("bind");
        exit(-1);
    }
    //监听
    ret=listen(lfd,8);
    if(ret==-1)
    {
        perror("listen");
        exit(-1);
    }
    //  初始化数据
    int max = sizeof(sockinfos)/sizeof( sockinfos[0]);
    for(int i=0;i<max;i++)
    {
        bzero(&sockinfos[i],sizeof(sockinfos[i]));
        sockinfos[i].fd=-1;
        sockinfos[i].tid=-1;
    }
 
    while(1)
    {
        struct sockaddr_in cliaddr;
        int len =sizeof(cliaddr);
 
        //接收连接
        int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
       
        struct sockinfo *pinfo;
        for(int i=0;i<max;i++)
        {
            //从这个数组中可以找到一个可以用的sockInfo元素;
            if (sockinfos[i].fd==-1)
            {
                pinfo=&sockinfos[i];
                break;
            }
            if(i==max-1)
            {
                sleep(1);
                i--;
            }
 
        }
       pinfo->fd=cfd;
       memcpy(&pinfo->addr,&cliaddr,len);
       
        pthread_t tid;
    //  每一个连接进来,创建一个子线程与客户端进行通信
        pthread_create(&pinfo->tid,NULL,working,pinfo);
 
        pthread_detach(pinfo->tid);
 
    }
 
    close(lfd);
    return 0;
}

三、UDP实现通信

server_udp.c:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_PORT 9999

int server_udp()
{
    int ret = 0;
    int socket_fd = -1;
    int addr_len = 0;
    char buf[1024] = {0};
    struct sockaddr_in client_addr;
    struct sockaddr_in server_addr;

    socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd < 0) {
        printf("%s: socket failed\n", __FUNCTION__);
        return 0;
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    addr_len = sizeof(server_addr);
    ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);
    if (ret < 0) {
        printf("%s: bind failed\n", __FUNCTION__);
        close(socket_fd);
        return 0;
    }

    while (1) {
        memset(buf, 0, sizeof(buf));
        addr_len = sizeof(client_addr);
        ret = recvfrom(socket_fd, buf, sizeof(buf), 0,
                         (struct sockaddr *)&client_addr, &addr_len);
        if (ret > 0) {
            printf("recv len:%d, data:[%s] from %s:%d\n", ret, buf,
                    inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
        } else if (0 == ret) {
            printf("ret:%d\n", ret);
        } else {
            printf("ret:%d\n", ret);
        }
    }

    close(socket_fd);

    return 0;
}

int main(int argc, char *argv[])
{
    server_udp();

    return 0;
}

client_udp.c:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 9999

int client_udp(char *data)
{
    int ret = 0;
    int socket_fd = -1;
    int addr_len = 0;
    struct sockaddr_in client_addr;
    struct sockaddr_in server_addr;

    socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd < 0) {
        printf("%s: socket failed\n", __FUNCTION__);
        return 0;
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);

    addr_len = sizeof(server_addr);
    ret = sendto(socket_fd, data, strlen(data), 0,
                     (struct sockaddr *)&server_addr, addr_len);
    if (ret > 0) {
        printf("send data: [%s] to %s:%d\n", data, inet_ntoa(server_addr.sin_addr),
                        ntohs(server_addr.sin_port));
    } else {
        printf("ret:%d\n", ret);
    }

    close(socket_fd);

    return 0;
}

int main(int argc, char *argv[])
{
    if (argc < 2) {
        client_udp("hello world");
    } else {
        client_udp(argv[1]);
    }

    return 0;
}

Makefile:

all:
	gcc -o server server_udp.c
	gcc -o client client_udp.c
clean:
	-@rm server client

编译方式:make

测试运行:

$ ./client
send data: [hello world] to 127.0.0.1:9999
$ ./server
recv len:11, data:[hello world] from 127.0.0.1:44180

$ ./client "I can do it"
send data: [I can do it] to 127.0.0.1:9999
$ ./server
recv len:11, data:[I can do it] from 127.0.0.1:43607
posted @ 2025-09-26 11:07  碧蓝i之海  阅读(30)  评论(0)    收藏  举报