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來實現。



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384396.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384396.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384396.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux 進程學習(四)------ sigaction 函數

轉自&#xff1a;http://www.cnblogs.com/wblyuyang/archive/2012/11/13/2768923.html 使用 sigaction 函數&#xff1a; signal 函數的使用方法簡單&#xff0c;但并不屬于 POSIX 標準&#xff0c;在各類 UNIX 平臺上的實現不盡相同&#xff0c;因此其用途受 到了一定的限制…

linux網絡編程(二)高并發服務器

linux網絡編程&#xff08;二&#xff09;高并發服務器錯誤處理高并發服務器多進程并發服務器客戶端錯誤處理 #include "wrap.h"int Bind(int fd, const struct sockaddr* sa, socklen_t salen) {int ret;if ((ret bind(fd, sa, salen)) < 0){perror("bind…

linux知識(一) 程序、進程與線程

linux知識&#xff08;一&#xff09; 程序、進程與線程程序進程程序如何變成進程&#xff1f;線程線程與進程fork和創建新線程的區別優點程序 程序&#xff1a;程序是已編譯好的二進制文件&#xff0c;存儲在磁盤中&#xff0c;不占用系統資源 程序包括&#xff1a; RO段&am…

linux 信號signal和sigaction理解

轉載&#xff1a;http://blog.csdn.net/beginning1126/article/details/8680757 今天看到unp時發現之前對signal到理解實在淺顯&#xff0c;今天拿來單獨學習討論下。 signal&#xff0c;此函數相對簡單一些&#xff0c;給定一個信號&#xff0c;給出信號處理函數則可&#xff…

linux知識(二)互斥量、信號量和生產者消費者模型

linux知識&#xff08;二&#xff09;互斥量、信號量和生產者消費者模型一、互斥量產生原因二、信號量生產者消費者模型一、互斥量 產生原因 使用多線程常常會碰到數據混亂的問題&#xff0c;那么使用互斥量&#xff0c;相當于“加鎖”的操作&#xff0c;將有助于解決數據混亂…

基于Linux的Socket編程之TCP全雙工Server-Client聊天程序

轉載&#xff1a;http://blog.csdn.net/apollon_krj/article/details/53437764#0-tsina-1-58570-397232819ff9a47a7b7e80a40613cfe1 一、引言&#xff1a; 由于accept函數、read、write、recv、send等函數都是是阻塞式的&#xff0c;在同一個進程之中&#xff0c;只要有任何一個…

數據結構(一)線性表

數據結構&#xff08;一&#xff09;線性表一、線性表定義二、順序表定義動態數組三、單鏈表定義不帶頭結點帶頭結點頭結點與不帶頭結點的區別頭插法與尾插法雙鏈表循環鏈表循環單鏈表循環雙鏈表靜態鏈表一、線性表定義 線性表是具有相同數據類型的n個數據元素的有限序列 特點…

linux網絡編程(二)TCP通訊狀態

linux網絡編程&#xff08;二&#xff09;TCP通訊狀態TCP狀態轉換為什么需要等待2MSL&#xff1f;端口復用TCP狀態轉換 tcp協議連接開始會經過三次握手&#xff0c;客戶端和服務器開始都會處于CLOSED狀態 第一次握手&#xff1a;客戶端會先發送SYN請求給服務器&#xff0c;客戶…

gethostbyname() 函數說明

轉載&#xff1a;http://www.cnblogs.com/cxz2009/archive/2010/11/19/1881611.html gethostbyname()函數說明——用域名或主機名獲取IP地址 包含頭文件 #include <netdb.h> #include <sys/socket.h> 函數原型 struct hostent *gethostbyna…

linux網絡編程(三)select、poll和epoll

linux網絡編程&#xff08;三&#xff09;select、poll和epoll一、為什么會有多路I/O轉接服務器&#xff1f;二、select三、poll三、epoll一、為什么會有多路I/O轉接服務器&#xff1f; 為什么會有多路I/O轉接服務器呢&#xff1f;在學這個之前&#xff0c;我們同使用的是多線…

Linux socket編程(一) 對套接字操作的封裝

轉載:http://www.cnblogs.com/-Lei/archive/2012/09/04/2670942.html 以前寫的&#xff0c;現在回顧一下&#xff1a; 下面是對socket操作的封裝&#xff0c;因為在Linux下寫中文到了windows里面會亂碼&#xff0c;所以注釋用英文來寫&#xff0c;有空再查下解決方法吧 socket.…

數據結構(二)棧

棧一、棧順序棧線性棧(不帶頭結點)線性棧(帶頭結點)共享棧一、棧 棧是只允許在一端進行插入或刪除操作的線性表。 棧頂&#xff1a;線性表允許進行插入刪除的那一端 棧底&#xff1a;固定的&#xff0c;不允許進行插入和刪除的那一端 空棧&#xff1a;不含如何元素的空表 順序…

如何在linux上安裝sqlite數據庫

如何在linux上安裝sqlite數據庫一、下載二、解壓三、配置&#xff08;configure&#xff09;四、編譯和安裝五、執行sqlite3程序六、測試代碼一、下載 首先要先下載sqlite3源碼包 鏈接&#xff1a;https://pan.baidu.com/s/1_70342ZLlPjLlqGzpy5IHw 提取碼&#xff1a;84ne …

數據結構(三)隊列

數據結構&#xff08;三&#xff09;隊列隊列隊列&#xff08;順序存儲&#xff09;循環隊列&#xff08;順序存儲&#xff09;隊列&#xff08;鏈式存儲&#xff09;隊列 隊列是一種受限制的線性表&#xff0c;只允許表的一端插入&#xff0c;在表的另一端刪除 隊列&#xf…

Linux fcntl函數詳解

轉載&#xff1a;http://www.cnblogs.com/xuyh/p/3273082.html 功能描述&#xff1a;根據文件描述詞來操作文件的特性。 文件控制函數 fcntl -- file control 頭文件&#xff1a; #include <unistd.h> #include <fcntl.h> 函數原型&#xff1a; …

vs2019使用sqlite數據庫遠程連接linux

vs2019使用sqlite數據庫遠程連接linux一、sqlite3添加到目錄二、添加依賴庫三、測試一、sqlite3添加到目錄 將兩個sqlite3頭文件放入目錄中 二、添加依賴庫 打開項目屬性 添加完成 三、測試 #include <stdio.h> #include <sqlite3.h>int main(int argc, cha…

linux網絡編程(四)線程池

linux網絡編程&#xff08;四&#xff09;線程池為什么會有線程池&#xff1f;實現簡單的線程池為什么會有線程池&#xff1f; 大多數的服務器可能都有這樣一種情況&#xff0c;就是會在單位時間內接收到大量客戶端請求&#xff0c;我們可以采取接受到客戶端請求創建一個線程的…

AIGC:大語言模型LLM的幻覺問題

引言 在使用ChatGPT或者其他大模型時&#xff0c;我們經常會遇到模型答非所問、知識錯誤、甚至自相矛盾的問題。 雖然大語言模型&#xff08;LLMs&#xff09;在各種下游任務中展示出了卓越的能力&#xff0c;在多個領域有廣泛應用&#xff0c;但存在著幻覺的問題&#xff1a…

關于C++子類父類成員函數的覆蓋和隱藏

轉載&#xff1a;http://blog.csdn.net/worldmakewayfordream/article/details/46827161 函數的覆蓋 覆蓋發生的條件&#xff1a; &#xff08;1&#xff09; 基類必須是虛函數&#xff08;使用virtual 關鍵字來進行聲明&#xff09; &#xff08;2&#xff09;發生覆蓋的兩個函…

數據結構(四)串的順序存儲

#include <stdio.h> #include <stdlib.h>#define MAXLEN 255 //定長順序存儲 typedef struct {char ch[MAXLEN]; //每個分量存儲一個字符int length; //串的實際長度 }SString;//串的初始化 bool StrAssign(SString& T, char* chars) {int i 0, len;char* …