Socket编程实践(3) 多连接服务器实现与简单P2P聊天程序例程
SO_REUSEADDR選項
在上一篇文章的最后我們貼出了一個簡單的C/S通信的例程。在該例程序中,使用"Ctrl+c"結束通信后,服務器是無法立即重啟的,如果嘗試重啟服務器,將被告知:
bind: Address already in use
原因在于服務器重新啟動時需要綁定地址:
bind (listenfd , (struct sockaddr*)&servaddr, sizeof(servaddr));而這個時候網絡正處于TIME_WAIT的狀態,只有在TIME_WAIT狀態退出后,套接字被刪除,該地址才能被重新綁定。TIME_WAIT的時間是兩個MSL,大約是1~4分鐘。若每次服務器重啟都需要等待TIME_WAIT結束那就太不合理了,好在選項SO_REUSEADDR能夠解決這個問題。
服務器端盡可能使用REUSEADD,在bind()之前調用setsockopt來設置SO_REUSEADDR套接字選項,使用SO_REUSEADDR選項可以使不必等待TIME_WAIT狀態消失就可以重啟服務器。
處理多客戶的服務器
在上一篇文章例程中,服務器端只能夠連接一個客戶端,并不能處理多個客戶端的連接。原因在于服務器使用accept從已連接隊列中獲取一個連接后,便進入了對該連接的服務中,處于while循環狀態。當一個新的客戶端連接已經放入已連接隊列時,服務器并不能執行到accpet的代碼去獲取隊列中的連接。
為了解決這個問題,我們可以fork()一個子進程,讓子進程來處理一個客戶端的連接,而父進程循環執行accept的代碼,獲取新的連接:
啟動服務器端,使用多個客戶端進行連接,可以看到服務器能夠同時處理多個連接:
實現一個P2P簡單聊天程序
為了實現聊天的功能,客戶端與服務器端都需要有一個進程來讀取連接,另一個進程來處理鍵盤輸入。使用fork()來完成這個簡單的聊天程序。
客戶端程序:
服務器端程序:
// p2pser.c #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<signal.h> #include<arpa/inet.h> #include<netinet/in.h> #include<sys/types.h> #include<sys/socket.h> #include<string.h> #define ERR_EXIT(m)\do \{\perror(m);\exit(EXIT_FAILURE);\}while(0) /*信號處理函數*/ void handler(int sig) {exit(EXIT_SUCCESS); } int main() {/* 創建一個套接字*/int listenfd= socket(AF_INET ,SOCK_STREAM,0);if(listenfd==-1)ERR_EXIT("socket");/*定義一個地址結構并填充*/struct sockaddr_in addr;addr.sin_family = AF_INET; //協議族為ipv4addr.sin_port = htons(5888); //綁定端口號addr.sin_addr.s_addr = htonl(INADDR_ANY);//主機字節序轉為網絡字節序/*重復使用地址*/int on = 1;if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0){ERR_EXIT("setsockopt");}/*將套接字綁定到地址上*/if(bind(listenfd,(const struct sockaddr *)&addr ,sizeof(addr))==-1){ERR_EXIT("bind");}/*監聽套接字,成為被動套接字*/if(listen(listenfd,SOMAXCONN)<0){ERR_EXIT("Listen");}struct sockaddr_in peeraddr;socklen_t peerlen = sizeof(peeraddr);int conn ;conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen);if(conn <0)ERR_EXIT("accept error");elseprintf("連接到服務器的客戶端的IP地址是:%s,端口號是:%d\n",inet_ntoa(peeraddr.sin_addr),htons(peeraddr.sin_port));pid_t pid ;pid = fork();//創建一個新進程if(pid == -1){ERR_EXIT("fork");}if(pid == 0)//子進程{signal(SIGUSR1,handler);char sendbuf[1024] = {0};while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL){write(conn,sendbuf,sizeof(sendbuf));memset(sendbuf,0,sizeof(sendbuf));}exit(EXIT_SUCCESS);}else //父進程 用來獲取數據{char recvbuf [1024]={0};while(1){memset(recvbuf,0,sizeof(recvbuf));int ret = read(conn ,recvbuf,sizeof(recvbuf));if(ret == -1){ERR_EXIT("read");}if(ret == 0) //對方已關閉 {printf("對方關閉\n");break;}fputs(recvbuf,stdout);}kill(pid,SIGUSR1);exit(EXIT_SUCCESS);}/*關閉套接字*/close(listenfd);close(conn);return 0;轉載于:https://www.cnblogs.com/QG-whz/p/5435396.html
總結
以上是生活随笔為你收集整理的Socket编程实践(3) 多连接服务器实现与简单P2P聊天程序例程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bootstrap弹出的模态框水平垂直居
- 下一篇: 【转】软件需求分析方法