目录
- 多进程实现并发服务器
- 多线程实现并发服务器
- TCP状态转换
多进程实现并发服务器
要实现TCP服务器处理并发的任务,使用多线程或者多进程来解决
一个父进程,多个子进程
父进程负责等待并接受客户端的连接
子进程:完成通信,接收一个客户端连接,就创建一个子进程用于通信
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.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(PF_INET,SOCK_STREAM,0);if(lfd==-1){perror("socket");exit(-1);}struct sockaddr_in saddr;saddr.sin_family=AF_INET;saddr.sin_port=htons(9999);saddr.sin_addr.s_addr=INADDR_ANY;//绑定int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));if(ret==-1){perror("bind");exit(0);}//监听ret=listen(lfd,128);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("client ip is:%s,port is %d\n",cliIp,cliPort);//接收客户端发来的数据char recvBuf[1024]={0};while(1){int len=read(cfd,&recvBuf,sizeof(recvBuf));if(len==-1){perror("read");exit(-1);}else if(len>0){printf("recv client data:%s\n",recvBuf);}else {printf("client close...");break;}write(cfd,recvBuf,strlen(recvBuf));}close(cfd);exit(0);//退出当前子进程}}close(lfd);return 0;
}
#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 serveraddr;serveraddr.sin_family=AF_INET;inet_pton(AF_INET,"192.168.227.129",&serveraddr.sin_addr.s_addr);serveraddr.sin_port=htons(9999);int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));if(ret==-1){perror("connect");exit(-1);}char recvBuf[1024]={0};int i=0;//3.通信while(1){sprintf(recvBuf,"data:%d\n",i++);//给服务器发送数据write(fd,recvBuf,strlen(recvBuf));sleep(1);int len=read(fd,recvBuf,sizeof(recvBuf));if(len==-1){perror("read");exit(-1);}else if(len>0){printf("recv server data:%s\n",recvBuf);}else if(len==0){//表示客户端断开连接printf("server closed...");break;}}//关闭连接close(fd);return 0;
}
同时开启两个客户端,会交叉接收信息
这样就支持了并发,多个客户端
多线程实现并发服务器
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<errno.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("client ip is:%s,port is %d\n",cliIp,cliPort);//接收客户端发来的数据char recvBuf[1024];while(1){int len=read(pinfo->fd,&recvBuf,sizeof(recvBuf));if(len==-1){perror("read");exit(-1);}else if(len>0){printf("recv client data:%s\n",recvBuf);}else if(len==0) {printf("client closed...\n");break;}write(pinfo->fd,recvBuf,strlen(recvBuf)+1);}close(pinfo->fd);return NULL;
}
int main(){//创建socketint lfd=socket(PF_INET,SOCK_STREAM,0);if(lfd==-1){perror("socket");exit(-1);}struct sockaddr_in saddr;saddr.sin_family=AF_INET;saddr.sin_port=htons(9999);saddr.sin_addr.s_addr=INADDR_ANY;//绑定int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));if(ret==-1){perror("bind");exit(0);}//监听ret=listen(lfd,128);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_create(&pinfo->tid,NULL,working,pinfo);pthread_detach(pinfo->tid);}close(lfd);return 0;
}
#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 serveraddr;serveraddr.sin_family=AF_INET;inet_pton(AF_INET,"192.168.227.129",&serveraddr.sin_addr.s_addr);serveraddr.sin_port=htons(9999);int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));if(ret==-1){perror("connect");exit(-1);}char recvBuf[1024]={0};int i=0;//3.通信while(1){sprintf(recvBuf,"data:%d\n",i++);//给服务器发送数据write(fd,recvBuf,strlen(recvBuf));int len=read(fd,recvBuf,sizeof(recvBuf));if(len==-1){perror("read");exit(-1);}else if(len>0){printf("recv server data:%s\n",recvBuf);}else if(len==0){//表示客户端断开连接printf("server closed...\n");break;}sleep(1);}//关闭连接close(fd);return 0;
}