目录
服务器代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- int main()
- {
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
- assert(sockfd!=-1);
-
- struct sockaddr_in saddr,caddr;
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//主机,网络大小端转换
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//IP地址转换
-
- int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- assert(res!=-1);
-
- res=listen(sockfd,5);
- assert(res!=-1);
-
- while(1)
- {
- int len=sizeof(saddr);
- printf("accept wait...\n");
- int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字
- if(c<0)
- {
- continue;
- }
- printf("accept c=%d\n",c);
- printf("accept client ip:%s ,port=%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
- while(1)
- {
- char buff[128]={0};
- int n=recv(c,buff,127,0);//返回值为0说明断开连接
- if(n<=0)
- {
- break;
- }
- printf("buff=%s\n",buff);
-
- send(c,"ok",2,0);
- }
- close(c);
- }
- close(sockfd);
- exit(0);
- }
客户端代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- int main()
- {
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
- assert(sockfd!=-1);
-
- struct sockaddr_in saddr;
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
-
- int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- assert(res!=-1);
-
- while(1)
- {
- printf("input:\n");
- char buff[128]={0};
- fgets(buff,127,stdin);
-
- if(strncmp(buff,"end",3)==0)
- {
- break;
- }
- send(sockfd,buff,strlen(buff),0);
- memset(buff,0,128);
- recv(sockfd,buff,127,0);
- printf("read:%s\n",buff);
- }
- close(sockfd);
- exit(0);
- }
运行结果:
将代码从单词发送数据改为while(1)循环发送数据后,我们发现每次只能处理一个客户端,其它客户端消息无法发送给服务器。
原因:
这个问题可以通过引入多线程和多进程来解决。
服务端接收一个客户端的连接后(accept之后),创建一个线程或者进程,然后在新创建的线程或进程中循环处理数据。
主线程(父进程)只负责监听客户端的连接,并使用 accept()接受连接,不进行数据的处理。如下图所示:
客户端代码不变,服务器端代码做如下更改:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- void* work_pthread(void*arg)
- {
- int c=*(int*)arg;
- while(1)
- {
- char buff[128]={0};
- int n=recv(c,buff,127,0);//返回值为0说明断开连接
- if(n<=0)
- {
- break;
- }
- printf("recv(%d)=%s\n",c,buff);
-
- send(c,"ok",2,0);
- }
- printf("one clinet over!\n");
- close(c);
- }
-
- int main()
- {
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
- assert(sockfd!=-1);
-
- struct sockaddr_in saddr,caddr;
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//主机,网络大小端转换
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//IP地址转换
-
- int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- assert(res!=-1);
-
- res=listen(sockfd,5);
- assert(res!=-1);
-
- while(1)
- {
- int len=sizeof(saddr);
- printf("accept wait...\n");
- int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字
- if(c<0)
- {
- continue;
- }
- printf("accept c=%d\n",c);
- printf("accept client ip:%s ,port=%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
-
- pthread_t id;
- pthread_create(&id,NULL,work_pthread,(void*)&c);
-
- }
- close(sockfd);
- exit(0);
- }
netstat -natp连接成功之后发现有两个./ser
客户端代码不变,服务器代码如下:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- void DealClientLink(int c,struct sockaddr_in caddr)
- {
- while(1)
- {
- char buff[128]={0};
- int n=recv(c,buff,127,0);//返回值为0说明断开连接
- if(n<=0)
- {
- break;
- }
- printf("%s:%d:buff=%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);
-
- send(c,"ok",2,0);
- }
- printf("one clinet unlike!\n");
- close(c);
- }
-
- int main()
- {
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
- assert(sockfd!=-1);
-
- struct sockaddr_in saddr,caddr;
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//主机,网络大小端转换
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//IP地址转换
-
- int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- assert(res!=-1);
-
- res=listen(sockfd,5);
- assert(res!=-1);
-
- printf("%s:%d link success!\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
- while(1)
- {
- int len=sizeof(saddr);
- int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字
- if(c<0)
- {
- continue;
- }
- pid_t pid=fork();
- assert(pid!=-1);
-
- if(pid==0)
- {
- DealClientLink(c,caddr);
- exit(0);
- }
- }
- close(sockfd);
- exit(0);
- }
运行结果:
子进程为客户端,父进程为服务器端,子进程先于父进程结束,父进程没有获取到子进程的退出码,子进程就会变成僵死进程,占用内存,影响执行速度。
客户端代码运行前:
关闭客户端(子进程结束)后:
如下图,产生了两个僵死进程。
修改一下代码,让父进程调用wait()方法获取子进程的退出码,并结合信号使用,让它不再阻塞。
- #include
-
- void fun(int sig)
- {
- wait(&sig);
- }
-
- signal(SIGCHLD,fun);//在主进程中添加
完整代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- void DealClientLink(int c,struct sockaddr_in caddr)
- {
- while(1)
- {
- char buff[128]={0};
- int n=recv(c,buff,127,0);//返回值为0说明断开连接
- if(n<=0)
- {
- break;
- }
- printf("%s:%d:buff=%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);
-
- send(c,"ok",2,0);
- }
- printf("one clinet unlike!\n");
- close(c);
- }
-
- void fun(int sign)
- {
- wait(&sign);
- }
-
- int main()
- {
- signal(SIGCHLD,fun);
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
- assert(sockfd!=-1);
-
- struct sockaddr_in saddr,caddr;
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//主机,网络大小端转换
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//IP地址转换
-
- int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- assert(res!=-1);
-
- res=listen(sockfd,5);
- assert(res!=-1);
-
- printf("%s:%d link success!\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
- while(1)
- {
- int len=sizeof(saddr);
- int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字
- if(c<0)
- {
- continue;
- }
- pid_t pid=fork();
- assert(pid!=-1);
-
- if(pid==0)
- {
- DealClientLink(c,caddr);
- exit(0);
- }
- }
- close(sockfd);
- exit(0);
- }
使用ps -f命令查看进程信息,可以看到子进程退出后,没有僵死进程。