之前socket编程第二节中客户端和服务器链接有个问题,那就是一旦服务器断开链接客户端是无法再重新链接的。因为服务器是没办法绑定成功的。
所以需要REUSEADDR进行设置。
只要在上一节加上这几行代码就可以了。
如何处理多客户端并发
思想就是创建一个子进程,让子进程执行打印,父进程执行链接。
修改服务器端的代码:(代码注释可以参考上一节)客户端代码不需要动。执行两次就可以看到有连个客户端与一个服务器链接。
#include
#include
#include
#include
#include
#include
#include
#include
#include #define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \}while(0)void do_server(int conn);
int main()
{int listenfd;if((listenfd &#61; socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) <0){ERR_EXIT("socket");}struct sockaddr_in servaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family &#61; AF_INET;servaddr.sin_port &#61; htons(5188);servaddr.sin_addr.s_addr &#61; htonl(INADDR_ANY);int on &#61; 1;if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) <0){ERR_EXIT("setsockopt");}if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0){ERR_EXIT("bind");}if(listen(listenfd,SOMAXCONN) <0){ERR_EXIT("listen");}int conn; struct sockaddr_in peeraddr;socklen_t peerlen &#61; sizeof(peeraddr);while(1) //循环执行父进程执行链接套接字{if((conn &#61; accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) <0){ERR_EXIT("accept");}printf("ip &#61; %s port &#61; %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));pid_t pid &#61; fork();if(pid &#61;&#61; -1){ERR_EXIT("fork");}if(pid &#61;&#61; 0)//子进程执行操作{close(listenfd);do_server(conn);}else{close(conn);}}return 0;}void do_server(int conn)
{char recvbuf[1024];while(1){memset(recvbuf,0,sizeof(recvbuf));int ret &#61; read(conn,recvbuf,sizeof(recvbuf));fputs(recvbuf,stdout);write(conn,recvbuf,ret);}close(conn);
}
运行结果&#xff1a;
实现一个客户端关闭&#xff0c;服务器端捕捉到客户端关闭&#xff1a;
#include
#include
#include
#include
#include
#include
#include
#include
#include #define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \}while(0)void do_server(int conn);
int main()
{int listenfd;if((listenfd &#61; socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) <0){ERR_EXIT("socket");}struct sockaddr_in servaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family &#61; AF_INET;servaddr.sin_port &#61; htons(5188);servaddr.sin_addr.s_addr &#61; htonl(INADDR_ANY);int on &#61; 1;if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) <0){ERR_EXIT("setsockopt");}if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0){ERR_EXIT("bind");}if(listen(listenfd,SOMAXCONN) <0){ERR_EXIT("listen");}int conn; struct sockaddr_in peeraddr;socklen_t peerlen &#61; sizeof(peeraddr);while(1) //循环执行父进程执行链接套接字{if((conn &#61; accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) <0){ERR_EXIT("accept");}printf("ip &#61; %s port &#61; %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));pid_t pid &#61; fork();//只要有一个客户端链接就创建一个进程//假如有三个客户端链接其实是有4个进程的if(pid &#61;&#61; -1){ERR_EXIT("fork");}if(pid &#61;&#61; 0)//子进程执行操作{close(listenfd);do_server(conn);exit(EXIT_SUCCESS);//当客户端关闭了&#xff0c;子进程就应该销毁了&#xff0c;否则子进程也会执行父进程的代码}else{close(conn);}}return 0;}void do_server(int conn)
{char recvbuf[1024];while(1){memset(recvbuf,0,sizeof(recvbuf));int ret &#61; read(conn,recvbuf,sizeof(recvbuf));if(ret &#61;&#61; 0){printf("client close\n");break;}else if (ret &#61;&#61; -1){ERR_EXIT("read");}fputs(recvbuf,stdout);write(conn,recvbuf,ret);}close(conn);
}
执行结果&#xff1a;
实现一个点对点通信
实现的思路是服务器和客户端分辨创建一个子进程。子进程接收键盘的信息并且发送信息&#xff0c;父进程负责接收信息并且显示出来。
服务器代码&#xff1a;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \}while(0)void handler(int sig)
{printf("recv asig &#61; %d\n",sig);exit(EXIT_SUCCESS);
}
int main()
{int listenfd;if((listenfd &#61; socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) <0){ERR_EXIT("socket");}struct sockaddr_in servaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family &#61; AF_INET;servaddr.sin_port &#61; htons(5188);servaddr.sin_addr.s_addr &#61; htonl(INADDR_ANY);int on &#61; 1;if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) <0){ERR_EXIT("setsockopt");}if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0){ERR_EXIT("bind");}if(listen(listenfd,SOMAXCONN) <0){ERR_EXIT("listen");}int conn;struct sockaddr_in peeraddr;socklen_t peerlen &#61; sizeof(peeraddr);if((conn &#61; accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) <0){ERR_EXIT("accept");}printf("ip &#61; %s port &#61; %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));pid_t pid;pid &#61; fork();if(pid &#61;&#61; 0){signal(SIGUSR1,handler);char sendbuf[1024] &#61;{0};while(fgets(sendbuf,sizeof(sendbuf),stdin) !&#61; NULL){write(conn,sendbuf,strlen(sendbuf));memset(sendbuf,0,sizeof(sendbuf));}printf("child close\n");exit(EXIT_SUCCESS);} else {char recvbuf[1024];while(1){memset(recvbuf,0,sizeof(recvbuf));int ret &#61; read(conn,recvbuf,sizeof(recvbuf));if(ret &#61;&#61; -1){ERR_EXIT("read");}else if(ret &#61;&#61; 0){printf("peer close\n");break;}fputs(recvbuf,stdout); }printf("parent colse\n");kill(pid,SIGUSR1);exit(EXIT_SUCCESS);}return 0;}
客户端代码&#xff1a;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \}while(0)
void handler(int sig)
{printf("recv a sig &#61; %d\n",sig);exit(EXIT_SUCCESS);
}
int main()
{int sock;if((sock &#61; socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) <0){ERR_EXIT("socket");}struct sockaddr_in servaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family &#61; AF_INET;servaddr.sin_port &#61; htons( 5188);servaddr.sin_addr.s_addr &#61; inet_addr("127.0.0.1");if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0){ERR_EXIT("connect");}pid_t pid;pid &#61; fork();if(pid &#61;&#61; -1){ERR_EXIT("fork");}if(pid &#61;&#61; 0){ char recvbuf[1024] &#61; {0};while(1){memset(recvbuf,0,sizeof(recvbuf));int ret &#61; read(sock,recvbuf,sizeof(recvbuf));if(ret &#61;&#61; -1){ERR_EXIT("read");}else if(ret &#61;&#61; 0){printf("peer close \n");break;}fputs(recvbuf,stdout);}close(sock);kill(getppid(),SIGUSR1);}else {signal(SIGUSR1,handler);char sendbuf[1024] &#61; {0}; while(fgets(sendbuf,sizeof(sendbuf),stdin) !&#61; NULL){write(sock,sendbuf,strlen(sendbuf)); memset(sendbuf,0,sizeof(sendbuf)); }close(sock);}return 0;
}
执行结果