开源 多进程 框架 c++_linux fork多进程并发服务器模型之C/C++代码实战
今天我們一起來聊聊多進程實現與多個客戶端進行通信。
如果是在while中循環accept, 然后循環處理事情, 此時, 這種服務是迭代服務, 只能逐一處理客戶端的請求, 后一個請求必須等前一個請求處理完畢, 無法并發處理, 真是急死人。 要實現并發, 我們可以考慮多線程, 也可以考慮多進程, 本文來說說后者。 在我們的多進程服務器模型中, 我們用父進程來處理連接(監聽socket), 用fork子進程的方法來處理通信(通信socket), 各司其職, 美哉。
一旦涉及到fork, 就必須注意僵尸進程的處理, 所以, 我們要用waitpid進行收尸, 這一點, 我們已經說過了。 另外, 要注意, 父子進程共享socket句柄的文件表(如果不理解的話, 建議看看APUE), 所以close socket的時候, 只是使引用計數減1, 并不是真正地直接關閉socket(減為0才是真正的關閉)。
廢話少說, 直接上菜。
服務端程序為:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void sigChildFun(int sigNO)
{
pid_t pid;
int stat;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0) // 循環收尸(僵尸進程), 此時waitpid不會阻塞
{
NULL;
}
return;
}
int main()
{
sockaddr_in servAddr;
memset(&servAddr,0,sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = INADDR_ANY;
servAddr.sin_port = htons(8765);
int iListenSocket = socket(AF_INET, SOCK_STREAM, 0);
bind(iListenSocket, (sockaddr *)&servAddr, sizeof(servAddr));
listen(iListenSocket,5);
signal(SIGCHLD, sigChildFun);
while(1)
{
sockaddr_in clientAddr;
socklen_t iSize = sizeof(clientAddr);
memset(&clientAddr, 0, sizeof(clientAddr));
int iConnSocket = accept(iListenSocket,(sockaddr*)&clientAddr, &iSize);
if(iConnSocket < 0)
{
if(errno == EINTR || errno == ECONNABORTED)
{
continue;
}
else
{
printf("accept error, server");
return -1;
}
}
int tmpPid = fork();
if(tmpPid == 0)
{
close(iListenSocket); // 子進程讓監聽socket的計數減1, 并非直接關閉監聽socket
char szBuf[1024] = {0};
snprintf(szBuf, sizeof(szBuf), "server pid[%u], client ip[%s]", getpid(), inet_ntoa(clientAddr.sin_addr));
write(iConnSocket, szBuf, strlen(szBuf) + 1);
while(1)
{
if(read(iConnSocket, szBuf, 1) <= 0)
{
close(iConnSocket); // 子進程讓通信的socket計數減1
return -2; // 子進程退出
}
}
close(iConnSocket); // 子進程讓通信的socket計數減1
return 0; // 子進程退出
}
close(iConnSocket); // 父進程讓通信的socket計數減1
}
getchar();
close(iListenSocket); // 父進程讓監聽socket計數減1, 此時會關掉監聽socket(因為之前子進程已經有此操作)
return 0;
}
啟動它。
客戶端程序為:
我們開啟一個客戶端, 此時如下:
客戶端信息為:
服務端信息為:
可以看到, 服務端16402子進程是與客戶端通信的進程, 父進程16096是監聽的父進程(主進程)。
另外再開啟一個客戶端(不要關閉舊的客戶端), 此時如下:
新客戶端信息為:
服務端信息為:
可以看到, 父進程16096新開了一個子進程16497來與新的客戶端進行通信。
我們關閉第一個客戶端, 然后看到服務端為:
我們再關閉第二個客戶端, 然后看到服務端為:
顯然, 客戶端退出后, 發FIN包, 服務端子進程的recv函數就為0, 退出子進程的while循環了, 因此, 對應的子進程就over了, 而且不會留下僵尸進程(有waitpid)。 而且, 我們可以看到, 負責連接管理(accept)的父進程(主進程)依然安然無恙, 優哉游哉地等待下一個客戶端連接。
在這里, 我們可以看到, 這個服務器是并發的, 而不是迭代的。 什么意思呢? 你看, 即使子進程處理業務需要很久很久, 那么上述服務依然能并發地響應n個幾乎同時到達的客戶端, 此時,父進程開啟n個子進程, 并發地工作, 并發地與客戶端進行通信, 而且還互不干擾, 大大提升了服務滿意度。
注:需要C/C++ Linux服務器開發學習資料私信“資料”(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg等),免費分享
總結
以上是生活随笔為你收集整理的开源 多进程 框架 c++_linux fork多进程并发服务器模型之C/C++代码实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python爬取国内代理ip_Pytho
- 下一篇: linux 其他常用命令