一、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);//创建socketint 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;
}
分析:
- 首先创建socket函数,在进行绑定,封装服务器的IP、端口号等相关信息;
- 然后进行监听,等待客户端的连接,客户端的连接是在while循环中进行的,因为需要不断接收客户端的连接;
- 连接成功一个客户端后,创建一个进程,在子进程完成服务器与客户端的通信;
- 关于子进程的回收是利用信号进行回收,因为如果利用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;
}
分析:
- 首先创建socket函数,然后连接服务器;
- 链接成功后进行通信,进行数据的收发;
- 最后回收文件描述符。
- 一个客户端在一个子进程中通信,这也是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()
{//创建socketint 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 9999int 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 9999int 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.cgcc -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