多并发原理如图,多个客户端连接一个服务器,无论哪个客户端发送数据给服务器,服务器都能把数据准确的返回给这个客户端。
在socket编程中,socket这种文件描述符被默认设置为阻塞,故而read函数和accept函数时阻塞函数,read函数只有从缓冲区读到数据了才返回,否则一直等待,程序一直卡在这个位置;accept函数只要有客户端连接到服务器就返回,否则一直等待,程序一直卡在这个位置。就很容易出现能发数据但连不上其他客户端或者只能连不能发数据的问题,为避免这种情况的发生,就需要程序同时检测有没有客户端连接和读取缓冲区数据,这里我们就以多进程的方式来实现多并发。父进程用于连接客户端,只要有客户端连接就创建一个进程,用于与服务器通信。
服务器
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>void do_work(int client_sock,char *ip_addr)
{char arr[100]={0};while(1){memset(arr,0,sizeof(arr));int ret=read(client_sock,arr,sizeof(arr));//当客户端关闭,read函数立即返回0if(ret==0){printf("%s 客户机已关闭\n",ip_addr);break;}printf("%s :",ip_addr);fputs(arr,stdout);write(client_sock,arr,ret);}
}int inter()
{int client_sock=0;struct sockaddr_in sock;memset(&sock,0,sizeof(sock));sock.sin_family=AF_INET;sock.sin_port=htons(5188);sock.sin_addr.s_addr=htonl(INADDR_ANY);int sockid=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);const int on=1;if(setsockopt(sockid,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)//设置端口可重复利用{printf("setsockopt\n");return 0;}if(bind(sockid,(struct sockaddr *)&sock,sizeof(sock))<0){printf("bind\n");return 0;}if(listen(sockid,SOMAXCONN)<0){printf("listen\n");return 0;}struct sockaddr_in other_sock;socklen_t other_socklen=sizeof(other_sock);pid_t pid=0,son_pid=0;while(1){client_sock=accept(sockid,(struct sockaddr *)&other_sock,&other_socklen);//父进程处理与客户端的连接if(client_sock<0){printf("accept\n");return 0;}char *ip_addr=inet_ntoa(other_sock.sin_addr);printf("ip=%s 已连接\n",ip_addr);pid=fork();//只要有客户端与服务器连接就创建一个进程if(pid==0)//子进程{close(sockid);//子进程不用sockid需要将其关闭do_work(client_sock,ip_addr);//子进程用于处理与客户端的收发数据操作break;}else//父进程{close(client_sock);//父进程不用client_sock需要将其关闭}}close(sockid);return 0;
}int main()
{inter();return 0;
}
客户端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>int main()
{struct sockaddr_in sock;memset(&sock,0,sizeof(sock));sock.sin_family=AF_INET;sock.sin_port=htons(5188);sock.sin_addr.s_addr=inet_addr("127.0.0.1");int sockid=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);socklen_t socklen=sizeof(sock);if(connect(sockid,(struct sockaddr *)&sock,socklen)<0){printf("connect\n");return 0;}char send[100]={0};char arr[100]={0};while(1) {fgets(send,sizeof(send),stdin);//从标准输入获取数据write(sockid,send,strlen(send));if(strcmp(send,"NULL\n")==0)//输入NULL退出客户端{break;}read(sockid,arr,sizeof(arr));fputs(arr,stdout);memset(arr,0,strlen(arr));memset(send,0,strlen(send));}close(sockid);return 0;
}
多线程方式请点此处