????????經(jīng)過前面的鋪墊,我們已經(jīng)具備實(shí)現(xiàn)并發(fā)服務(wù)器的基礎(chǔ)了,接下來讓我們嘗試將之前的單任務(wù)回聲服務(wù)器改裝成多任務(wù)并發(fā)模式吧!
多任務(wù)回聲服務(wù)器模型
????????在編寫代碼前,先讓我們大致將多任務(wù)(回聲)服務(wù)器的模型抽象一下,如下圖所示:
? ? ? ? 當(dāng)客戶端請求服務(wù)(連接請求)時(shí),回聲服務(wù)器端便會創(chuàng)建子進(jìn)程以提供服務(wù)。為了完成這些任務(wù),需要經(jīng)過以下過程:
- 第一階段 : 回聲服務(wù)器端(父進(jìn)程)調(diào)用accept函數(shù)受理客戶端發(fā)來的連接請求。
- 第二階段 : 父進(jìn)程套接字的文件描述符被拷貝傳遞給子進(jìn)程 。
- 第三階段 : 子進(jìn)程利用傳遞來的文件描述符提供服務(wù)。
????????OK,讓我們開始試著用代碼去實(shí)現(xiàn)
multi_echoserver.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void Handler_childproc(int sig)
{
int status;
pid_t pid = waitpid(-1, &status, WNOHANG);
printf("Removed process id: %d \n", pid);
}
void Sender_message(char *message)
{
puts(message);
exit(1);
}
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
int str_len, state, port;
struct sockaddr_in serv_adr, clnt_adr;
struct sigaction act;
pid_t pid;
socklen_t adr_sz;
char buf[BUF_SIZE];
//防止僵尸進(jìn)程
act.sa_handler = Handler_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD, &act, 0);
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1)
{
Sender_message((char *)"Socket creation error");
}
printf("Please input the port of socket that you want to create:\n");
scanf("%d", &port);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(port);
if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
{
Sender_message((char *)"bind error");
}
if (listen(serv_sock, 5) == -1)
{
Sender_message((char *)"listen error");
}
while (1)
{
adr_sz = sizeof(clnt_adr);
//accept后(受理客戶端發(fā)來的請求)創(chuàng)建子進(jìn)程
clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);
if (clnt_sock == -1)
{
continue;
}
else
{
puts("New client was connected...");
}
pid = fork();
switch (pid)
{
case -1:
{
close(clnt_sock);
continue;
}
case 0: //子進(jìn)程
{
//關(guān)閉服務(wù)器端套接字的文件描述符(很重要,若不處理會使服務(wù)器端套接字最終無法正常關(guān)閉)
close(serv_sock);
while ((str_len = read(clnt_sock, buf, BUF_SIZE)) != 0)
{
write(clnt_sock, buf, str_len);
}
close(clnt_sock);
puts("Client was disconnected...");
return 0;
}
default: //父進(jìn)程
{
//關(guān)閉客戶端連接
close(clnt_sock);
break;
}
}
}
close(serv_sock);
return 0;
}
????????* 可以直接用之前編譯的回聲客戶端代碼,參見:?回聲客戶端缺陷的解決?這一篇文章
????????運(yùn)行結(jié)果:
????????啟動多個(gè)回聲客戶端后,沒啥問題,功能正常~
補(bǔ)充:
????????在上述代碼中(multi_echoserver),調(diào)用fork函數(shù)后,父進(jìn)程會復(fù)制2個(gè)套接字的文件描述符給子進(jìn)程(一個(gè)是服務(wù)器,一個(gè)是客戶端),過程如下圖所示:
文章來源:http://www.zghlxwxcb.cn/news/detail-480950.html
????????當(dāng)1個(gè)套接字中衍生出了2個(gè)文件描述符時(shí),只有這2個(gè)描述符都被銷毀后,套接字才能被完全銷毀。(無論是在客戶端還是服務(wù)器端,依此類推)因此,在調(diào)用fork函數(shù)后,一定要將無關(guān)的套接字的文件描述符處理好,否則將導(dǎo)致套接字無法正常關(guān)閉。?文章來源地址http://www.zghlxwxcb.cn/news/detail-480950.html
到了這里,關(guān)于【TCP/IP】多進(jìn)程服務(wù)器的實(shí)現(xiàn)(進(jìn)階) - 多進(jìn)程服務(wù)器模型及代碼實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!