socket编程(3-2)
在上一节中,我们使用了REUSEADDR,解决了服务器重启不能立即使用的问题。这一节,我们来解决多个客户端连接服务器的问题。

首先分析一下,之前的服务器为什么不可以接收多个客户端的连接请求。
原因是:
已有一个客户端连接后 ,服务器处于while中,没有机会来处理accept
解决方法:fork
思路:子进程处理一个连接,父进程继续回去监听,处理accept
对于父进程来说,他不需要处理已连接进程conn,这时候我们要想到父子进程共享文件描述符的。子进程继承了父进程的文件描述符,对于父进程来说他不需要处理连接,将连接套接口关闭掉close(conn);
对于子进程来说,他不需要处理监听,将监听套接字关闭掉close(listenfd)
来看一下服务器代码:
#include<stdio.h> #include <sys/socket.h> #include<sys/types.h> #include<unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include<stdlib.h> #include<errno.h> #include<string.h> void do_service(int conn) { /*处理通信的细节*/ char recvbuf[1024]; while(1) { memset(recvbuf,0,sizeof(recvbuf)); int ret = read(conn,recvbuf,sizeof(recvbuf)); if(ret == 0) { printf("client close"); break; } fputs(recvbuf,stdout); write(conn,recvbuf,ret); } } int main() { /*create a socket*/ int listenfd; if((listenfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) printf("socket err"); /*init addr*/ struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/ /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/ int on = 1;/*开启地址重复利用*/ if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0) printf("setsockopt err"); /*将创建的套接字与本地地址进行绑定*/ if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0) printf("blind err"); /*监听套接字*/ if (listen(listenfd,SOMAXCONN) < 0 ) printf("listen err:"); struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); int conn; pid_t pid; while(1) { /*主动套接字*/ if ((conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0 ) printf("accept err"); /*当客户端断开时,服务器可以捕捉到*/ printf("ip = %s,port = %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port)); pid = fork(); if(pid == -1) printf("fork err"); else if(pid == 0) { close(listenfd); do_service(conn); exit(EXIT_SUCCESS); } else { close(conn); } } return 0; }
客户端代码没有修改:
#include<stdio.h> #include <sys/socket.h> #include<sys/types.h> #include<unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include<stdlib.h> #include<errno.h> #include<string.h> int main() { /*创建套接字*/ int sockfd; if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) printf("socket err"); /*serv addr and port*/ struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); /*连接,一旦连接成功,sockfd套接字就处于已连接状态,从逻辑上 是与服务器端的conn套接字连接*/ if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0) printf("connect err"); char sendbuf[1024]={0}; char recvbuf[1024]={0}; while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL) { write(sockfd,sendbuf,strlen(sendbuf)); read(sockfd,recvbuf,sizeof(recvbuf)); fputs(recvbuf,stdout); memset(sendbuf,0,sizeof(sendbuf)); memset(recvbuf,0,sizeof(recvbuf)); } close(sockfd); return 0; }
现在,我们已经可以处理多个客户端的连接了。并且,当客户端关闭时,服务器可以捕捉到。
一点总结:
服务器端需要两个套接字 监听套接字和已连接套接字两种类型套接字
监听套接字:主要用来接受三次握手数据,一旦三次握手完成,就将他放到已连接队列,accept就可以从队列中返回一个连接。返回的连接,我们称之为已连接套接字,他主要是用 来通信的,并不能接受连接。他是一个主动套接字。
客户端只有一种套接字 已连接套接字

浙公网安备 33010602011771号