linux网络编程:使用多进程实现socket同时收发数据
轉載:http://blog.csdn.net/li_wen01/article/details/52685844
前面已講過使用一個進程實現服務端和客戶端P2P通信的實例,但是它只能同時處理一個客戶端的連接。如果要實現并發處理多個客戶端的連接并且實現P2P通信,可以使用多進程來處理。相比與多線程來說,多進程耗費的系統資源是比較多的,后續會介紹使用線程池實現簡單的數據收發。
? ? 使用多進程并發處理多個client請求以及實現P2P通信,父進程專門監聽端口,每監聽到一個連接就創建一個子進程處理這個客戶端,于此同時,在子進程中創建一個孫子進程來處理數據的讀取,在子進程實現數據的發送。如果客戶端斷開連接,recv函數會返回參數0,recv函數所在進程發送信號給send函數所在進程,然后退出recv進程,send函數所在進程接收到信號SIGUSR1就退出該進程。在多進程中,子進程退出時會產生僵尸進程,僵尸進程的處理有多種方法,最簡單的就是直接忽視SIGCHLD信號。
下面直接上代碼:
#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<stdlib.h> #include<errno.h> #include<arpa/inet.h> #include<netinet/in.h> #include<string.h> #include<signal.h>#define MAXLINE 256 #define PORT 6666/*進程退出函數*/ void process_out(int signo) {exit(EXIT_SUCCESS); }/*socket write 函數*/ void write_func(int pid, int fd) {char* write = "I am server";printf("write id = %d\n",pid);signal(SIGUSR1,process_out); /* 注冊信號SIGUSR1,該信號由read 進程發送過來。*/while(1){sleep(1);send(fd,write,strlen(write)+1,0);} }/*socket read 函數*/ void read_func(int pid, int fd) {char readbuff[MAXLINE];int n = 0;printf("read id = %d \n",pid);memset(&readbuff,0,sizeof(readbuff));while(1){n = recv(fd, readbuff, MAXLINE, 0); /*recv 在這里是阻塞運行*/if(n > 0) /*客戶端有數據發送過來*/{printf("server recv data: %s \n",readbuff);}else if(n == 0) /*客戶端斷開了連接*/{break;}};kill(pid, SIGUSR1); /*發送信號SIGUSR1 到write進程*/exit(EXIT_SUCCESS); /*進程退出*/ }int main(void) {int listenfd,connetfd;int on = 1;int addrlen = 0;pid_t pid, pid_child, pid_send;struct sockaddr_in server_addr;struct sockaddr_in client_addr;if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){printf("create socket err \n");}/*設置服務端地址*/addrlen = sizeof(struct sockaddr_in);memset(&server_addr, 0, addrlen);server_addr.sin_family = AF_INET; /*AF_INET表示 IPv4 Intern 協議*/server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*INADDR_ANY 可以監聽任意IP */server_addr.sin_port = htons(PORT); /*設置端口*//*對套接字進行設置*/if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);exit(0);}/*綁定地址結構到套接字描述符*/if( bind(listenfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);exit(0);}/*設置監聽隊列,這里設置為10,表示可以同時處理10個客戶端的連接*/if( listen(listenfd, 10) == -1){printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);exit(0);}printf("wait client accpt \n");while(1){/*接收客戶端的連接,這里會阻塞,直到有客戶端連接*/if( (connetfd = accept(listenfd, (struct sockaddr*)&client_addr, &addrlen)) == -1){printf("accept socket error: %s(errno: %d)",strerror(errno),errno);continue;}signal(SIGCHLD, SIG_IGN); /*忽略SIGCHLD,避免僵尸進程*/pid = fork();if(pid == -1){printf("fork err \n");}if(pid == 0) /* 子進程*/{pid_child = fork(); if(pid_child == 0) /*孫子進程*/{pid_send = getpid(); /*獲取孫子進程ID*/read_func(pid_send, connetfd);}else{pid_send = getpid(); /* 獲取子進程ID*/write_func(pid_send,connetfd);}}} }
? ? 測試程序這里不再實現,將上面代碼在PC機上編譯運行,在手機端使用網絡助手工具直接連接PC機的6666端口,可以看到如下運行結果:
root@ubuntu:/home/share/Socket/process# ./process_server wait client accpt write id = 3883 read id = 3884 server recv data: I am client 1 server recv data: I am client 1 server recv data: I am client 1 server recv data: I am client 1 write id = 3885 read id = 3886 server recv data: I am client 1 server recv data: I am client 2 server recv data: I am client 1 server recv data: I am client 2 server recv data: I am client 1 write id = 3887 read id = 3888 server recv data: I am client 2 server recv data: I am client 1 server recv data: I am client 2 server recv data: I am client 1 server recv data: I am client 2 server recv data: I am client 1 server recv data: I am client 1 server recv data: I am client 1 ^C root@ubuntu:/home/share/Socket/process# ? ? 先連接三個客戶端,然后再斷開連接,客戶端收到的數據不再貼出。于此同時我們使用PS查看進程狀態:
root@ubuntu:/home/share# ps aux |grep process_server root 3882 0.0 0.0 4164 348 pts/0 S+ 22:38 0:00 ./process_server root 3883 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3884 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3885 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3886 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3887 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3888 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3890 0.0 0.0 13588 940 pts/2 S+ 22:38 0:00 grep --color=auto process_server root@ubuntu:/home/share# ps aux |grep process_server root 3882 0.0 0.0 4164 348 pts/0 S+ 22:38 0:00 ./process_server root 3883 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3884 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3885 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3886 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3894 0.0 0.0 13588 940 pts/2 S+ 22:38 0:00 grep --color=auto process_server root@ubuntu:/home/share# ps aux |grep process_server root 3882 0.0 0.0 4164 348 pts/0 S+ 22:38 0:00 ./process_server root 3883 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3884 0.0 0.0 4164 88 pts/0 S+ 22:38 0:00 ./process_server root 3896 0.0 0.0 13588 940 pts/2 S+ 22:39 0:00 grep --color=auto process_server root@ubuntu:/home/share# ps aux |grep process_server root 3882 0.0 0.0 4164 348 pts/0 S+ 22:38 0:00 ./process_server root 3898 0.0 0.0 13588 944 pts/2 S+ 22:39 0:00 grep --color=auto process_server root@ubuntu:/home/share# ? ? 可以看到并未產生僵尸進程,但是如果把上面程序的signal(SIGCHLD, SIG_IGN);去掉,我們就可以看到產生了僵尸進程。
root@ubuntu:/home/ysj000# ps aux |grep process_server root 3812 0.0 0.0 4164 352 pts/0 S+ 22:24 0:00 ./process_server root 3814 0.0 0.0 4164 92 pts/0 S+ 22:25 0:00 ./process_server root 3815 0.0 0.0 4164 92 pts/0 S+ 22:25 0:00 ./process_server root 3816 0.0 0.0 4164 92 pts/0 S+ 22:25 0:00 ./process_server root 3817 0.0 0.0 4164 92 pts/0 S+ 22:25 0:00 ./process_server root 3819 0.0 0.0 13588 944 pts/2 S+ 22:25 0:00 grep --color=auto process_server root@ubuntu:/home/ysj000# ps aux |grep process_server root 3812 0.0 0.0 4164 352 pts/0 S+ 22:24 0:00 ./process_server root 3814 0.0 0.0 4164 92 pts/0 S+ 22:25 0:00 ./process_server root 3815 0.0 0.0 4164 92 pts/0 S+ 22:25 0:00 ./process_server root 3816 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3821 0.0 0.0 13588 944 pts/2 S+ 22:25 0:00 grep --color=auto process_server root@ubuntu:/home/ysj000# ps aux |grep process_server root 3812 0.0 0.0 4164 352 pts/0 S+ 22:24 0:00 ./process_server root 3814 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3816 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3823 0.0 0.0 13588 944 pts/2 S+ 22:26 0:00 grep --color=auto process_server root@ubuntu:/home/ysj000# ps aux |grep process_server root 3812 0.0 0.0 4164 352 pts/0 S+ 22:24 0:00 ./process_server root 3814 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3816 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3824 0.0 0.0 4164 92 pts/0 S+ 22:26 0:00 ./process_server root 3825 0.0 0.0 4164 92 pts/0 S+ 22:26 0:00 ./process_server root 3827 0.0 0.0 13588 944 pts/2 S+ 22:26 0:00 grep --color=auto process_server root@ubuntu:/home/ysj000# ps aux |grep process_server root 3812 0.0 0.0 4164 352 pts/0 S+ 22:24 0:00 ./process_server root 3814 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3816 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3824 0.0 0.0 4164 92 pts/0 S+ 22:26 0:00 ./process_server root 3825 0.0 0.0 4164 92 pts/0 S+ 22:26 0:00 ./process_server root 3830 0.0 0.0 4164 92 pts/0 S+ 22:27 0:00 ./process_server root 3831 0.0 0.0 4164 92 pts/0 S+ 22:27 0:00 ./process_server root 3833 0.0 0.0 13588 940 pts/2 S+ 22:27 0:00 grep --color=auto process_server root@ubuntu:/home/ysj000# ps aux |grep process_server root 3812 0.0 0.0 4164 352 pts/0 S+ 22:24 0:00 ./process_server root 3814 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3816 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3824 0.0 0.0 4164 92 pts/0 S+ 22:26 0:00 ./process_server root 3825 0.0 0.0 4164 92 pts/0 S+ 22:26 0:00 ./process_server root 3830 0.0 0.0 0 0 pts/0 Z+ 22:27 0:00 [process_server] <defunct> root 3835 0.0 0.0 13588 944 pts/2 S+ 22:27 0:00 grep --color=auto process_server root@ubuntu:/home/ysj000# ps aux |grep process_server root 3812 0.0 0.0 4164 352 pts/0 S+ 22:24 0:00 ./process_server root 3814 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3816 0.0 0.0 0 0 pts/0 Z+ 22:25 0:00 [process_server] <defunct> root 3824 0.0 0.0 0 0 pts/0 Z+ 22:26 0:00 [process_server] <defunct> root 3830 0.0 0.0 0 0 pts/0 Z+ 22:27 0:00 [process_server] <defunct> root 3839 0.0 0.0 13588 944 pts/2 S+ 22:27 0:00 grep --color=auto process_server
注意1:在上面的代碼中,我們直接忽視了SIGCHLD 信號來避免產生僵尸進程,在linux系統中是可以的,但是在其他的一些系統不一定都可以。另外,直接忽視SIGCHLD信號會造成一些其他的影響:會影響system函數的正常使用。system里面會將sigchld設置為阻塞,因為system里面會調用fork,然后執行命令,最后通過waitpid等待子進程的返回,不將sigchld設置為阻塞有可能信號被別人處理掉,system無法獲得到信號就會報錯,錯誤號echld,no child processes,這就是因為信號被別人處理了,其實命令是運行成功的。而忽略sigchld信號就會導致這一問題。所以正常使用的時候需要多加注意(可以使用wait 或waitpid 來避免僵尸進程)如下:
void sig_chld(int signo) {pid_t pid;int stat;while ((pid = waitpid(-1,stat,WNOHANG))>0){ //printf("child %d terminated \n",pid);}return ; }
信號注冊 signal(SIGCHLD, sig_chld); ?
注意2:如果我們不主動收發數據也想檢測到TCP連接的對方已經退出或是崩潰,我們可以使用套接字選項SO_KEEPALIVE來實現。
總結
以上是生活随笔為你收集整理的linux网络编程:使用多进程实现socket同时收发数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux socket编程,对套接字进
- 下一篇: Linux 进程学习(四)------