什么是并發(fā)服務(wù)器
當(dāng)涉及到構(gòu)建高性能的服務(wù)器應(yīng)用程序時(shí),我們通常會考慮使用并發(fā)服務(wù)器來處理多個(gè)客戶端請求。在并發(fā)服務(wù)器中,多進(jìn)程和多線程是兩種常見的并發(fā)模型,它們都有各自的優(yōu)點(diǎn)和適用場景。本文將介紹多進(jìn)程和多線程并發(fā)服務(wù)器的基礎(chǔ)知識。
多進(jìn)程并發(fā)服務(wù)器
多進(jìn)程并發(fā)服務(wù)器通過創(chuàng)建多個(gè)子進(jìn)程來處理客戶端請求。每個(gè)子進(jìn)程是操作系統(tǒng)中獨(dú)立運(yùn)行的單位,擁有自己的內(nèi)存空間和資源。當(dāng)有新的客戶端連接請求到達(dá)時(shí),服務(wù)器創(chuàng)建一個(gè)新的子進(jìn)程來處理該請求。子進(jìn)程負(fù)責(zé)與客戶端通信并提供所需的服務(wù)。
多進(jìn)程并發(fā)服務(wù)器的優(yōu)點(diǎn)是穩(wěn)定性高。由于每個(gè)子進(jìn)程都是相互獨(dú)立的,一個(gè)子進(jìn)程的崩潰或錯(cuò)誤不會影響其他子進(jìn)程的執(zhí)行。這種獨(dú)立性使得多進(jìn)程并發(fā)服務(wù)器能夠有效地隔離錯(cuò)誤,提高服務(wù)器的可靠性。
然而,多進(jìn)程并發(fā)服務(wù)器也有一些缺點(diǎn)。創(chuàng)建和管理多個(gè)進(jìn)程需要消耗更多的系統(tǒng)資源,包括內(nèi)存和CPU時(shí)間。進(jìn)程間的通信也需要特殊的機(jī)制,例如管道或共享內(nèi)存,以便在不同進(jìn)程之間傳遞數(shù)據(jù)。此外,由于每個(gè)進(jìn)程都有自己的內(nèi)存空間,進(jìn)程間的數(shù)據(jù)共享和同步可能會變得復(fù)雜。
多線程并發(fā)服務(wù)器
多線程并發(fā)服務(wù)器通過創(chuàng)建多個(gè)線程來處理客戶端請求。線程是在進(jìn)程內(nèi)部運(yùn)行的獨(dú)立執(zhí)行流,共享同一個(gè)進(jìn)程的內(nèi)存空間和資源。與多進(jìn)程不同,多線程服務(wù)器不需要?jiǎng)?chuàng)建新的進(jìn)程來處理請求,而是在同一個(gè)進(jìn)程中創(chuàng)建多個(gè)線程。
多線程并發(fā)服務(wù)器的優(yōu)點(diǎn)是資源消耗較少。與進(jìn)程相比,線程的創(chuàng)建和切換開銷更小,因?yàn)樗鼈児蚕磉M(jìn)程的資源。這使得多線程并發(fā)服務(wù)器更加輕量級,能夠更高效地利用系統(tǒng)資源。
然而,多線程并發(fā)服務(wù)器也存在一些問題。首先,線程共享進(jìn)程的內(nèi)存空間,因此在多線程環(huán)境中訪問共享數(shù)據(jù)需要特殊的同步機(jī)制,以避免競態(tài)條件和數(shù)據(jù)不一致。其次,由于線程共享相同的地址空間,一個(gè)線程的錯(cuò)誤可能會影響整個(gè)進(jìn)程,導(dǎo)致服務(wù)器崩潰或不穩(wěn)定。
選擇適合的并發(fā)模型
在選擇多進(jìn)程還是多線程并發(fā)服務(wù)器時(shí),需要根據(jù)具體的應(yīng)用需求和性能要求進(jìn)行權(quán)衡。以下是一些建議:
- 如果穩(wěn)定性和容錯(cuò)性是首要考慮因素,多進(jìn)程并發(fā)服務(wù)器可能是更好的選擇。每個(gè)子進(jìn)程的獨(dú)立性可以有效地隔離錯(cuò)誤,提高服務(wù)器的可靠性。
- 如果服務(wù)器需要處理大量的并發(fā)連接并需要更高的性能和資源利用率,多線程并發(fā)服務(wù)器可能更適合。線程的創(chuàng)建和切換開銷相對較小,可以更高效地處理并發(fā)請求。
- 如果同時(shí)需要穩(wěn)定性和性能,可以考慮使用混合模型,即在每個(gè)進(jìn)程中創(chuàng)建多個(gè)線程,以實(shí)現(xiàn)更好的負(fù)載平衡和資源利用率。
無論選擇多進(jìn)程還是多線程并發(fā)服務(wù)器,都需要注意正確處理并發(fā)訪問共享數(shù)據(jù)的問題,使用適當(dāng)?shù)耐綑C(jī)制(如鎖、信號量)來保證數(shù)據(jù)的一致性和正確性。
總結(jié)起來,多進(jìn)程和多線程并發(fā)服務(wù)器是實(shí)現(xiàn)高性能服務(wù)器的常見方式。它們各有優(yōu)劣,選擇合適的并發(fā)模型需要考慮應(yīng)用需求和性能要求,并注意處理并發(fā)訪問共享數(shù)據(jù)的問題。
多進(jìn)程并發(fā)服務(wù)器代碼實(shí)現(xiàn)
使用多進(jìn)程并發(fā)服務(wù)器時(shí)要考慮以下幾點(diǎn):
- 父進(jìn)程最大文件描述個(gè)數(shù)(父進(jìn)程中需要close關(guān)閉accept返回的新文件描述符);
- 系統(tǒng)內(nèi)創(chuàng)建進(jìn)程個(gè)數(shù)(與內(nèi)存大小相關(guān));
-進(jìn)程創(chuàng)建過多是否降低整體服務(wù)性能(進(jìn)程調(diào)度);
server
/* server.c */
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<sys/wait.h>
#include<sys/types.h>
#include"wrap.h"
#define?MAXLINE 80
#define?SERV_PORT 800
voiddo_sigchild(int?num)
{
while?(waitpid(0,?NULL, WNOHANG) >?0)
? ? ;
}
intmain(void)
{
structsockaddr_inservaddr, cliaddr;
socklen_t?cliaddr_len;
int?listenfd, connfd;
char?buf[MAXLINE];
char?str[INET_ADDRSTRLEN];
int?i, n;
pid_t?pid;
structsigactionnewact;
? newact.sa_handler = do_sigchild;
??sigemptyset(&newact.sa_mask);
? newact.sa_flags =?0;
??sigaction(SIGCHLD, &newact,?NULL);
? listenfd =?Socket(AF_INET, SOCK_STREAM,?0);
??bzero(&servaddr,?sizeof(servaddr));
? servaddr.sin_family = AF_INET;
? servaddr.sin_addr.s_addr =?htonl(INADDR_ANY);
? servaddr.sin_port =?htons(SERV_PORT);
??Bind(listenfd, (struct?sockaddr *)&servaddr,?sizeof(servaddr));
??Listen(listenfd,?20);
printf("Accepting connections ...n");
while?(1) {
? ? cliaddr_len =?sizeof(cliaddr);
? ? connfd =?Accept(listenfd, (struct?sockaddr *)&cliaddr, &cliaddr_len);
? ? pid = fork();
if?(pid ==?0) {
? ? ??Close(listenfd);
while?(1) {
? ? ? ? n =?Read(connfd, buf, MAXLINE);
if?(n ==?0) {
printf("the other side has been closed.n");
break;
? ? ? ? }
printf("received from %s at PORT %dn",
? ? ? ? ? ??inet_ntop(AF_INET, &cliaddr.sin_addr, str,?sizeof(str)),
? ? ? ? ? ??ntohs(cliaddr.sin_port));
for?(i =?0; i < n; i++)
? ? ? ? ? buf[i] =?toupper(buf[i]);
? ? ? ??Write(connfd, buf, n);
? ? ? }
? ? ??Close(connfd);
return0;
? ? }?elseif?(pid >?0) {
? ? ??Close(connfd);
? ? }?else
? ? ??perr_exit("fork");
? }
??Close(listenfd);
return0;
}
client:
/* client.c */
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include"wrap.h"
#define?MAXLINE 80
#define?SERV_PORT 6666
intmain(int?argc,?char?*argv[])
{
structsockaddr_inservaddr;
char?buf[MAXLINE];
int?sockfd, n;
? sockfd =?Socket(AF_INET, SOCK_STREAM,?0);
??bzero(&servaddr,?sizeof(servaddr));
? servaddr.sin_family = AF_INET;
??inet_pton(AF_INET,?"127.0.0.1", &servaddr.sin_addr);
? servaddr.sin_port =?htons(SERV_PORT);
??Connect(sockfd, (struct?sockaddr *)&servaddr,?sizeof(servaddr));
while?(fgets(buf, MAXLINE, stdin) !=?NULL) {
? ??Write(sockfd, buf,?strlen(buf));
? ? n =?Read(sockfd, buf, MAXLINE);
if?(n ==?0) {
printf("the other side has been closed.n");
break;
? ? }?else
? ? ??Write(STDOUT_FILENO, buf, n);
? }
??Close(sockfd);
return0;
}
多線程并發(fā)服務(wù)器代碼實(shí)現(xiàn)
在使用線程模型開發(fā)服務(wù)器時(shí)需考慮以下問題:
- 調(diào)整進(jìn)程內(nèi)最大文件描述符上限;
- 線程如有共享數(shù)據(jù),考慮線程同步;
- 服務(wù)于客戶端線程退出時(shí),退出處理(退出值,分離態(tài));
- 系統(tǒng)負(fù)載,隨著鏈接客戶端增加,導(dǎo)致其它線程不能及時(shí)得到CPU;
server:
/* server.c */
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include"wrap.h"
#define?MAXLINE 80
#define?SERV_PORT 6666
structs_info {
structsockaddr_incliaddr;
int?connfd;
};
void?*do_work(void?*arg)
{
int?n,i;
structs_info *ts = (structs_info*)arg;
char?buf[MAXLINE];
char?str[INET_ADDRSTRLEN];
/* 可以在創(chuàng)建線程前設(shè)置線程創(chuàng)建屬性,設(shè)為分離態(tài),哪種效率高內(nèi)?*/
??pthread_detach(pthread_self());
while?(1) {
? ? n =?Read(ts->connfd, buf, MAXLINE);
if?(n ==?0) {
printf("the other side has been closed.n");
break;
? ? }
printf("received from %s at PORT %dn",
? ? ? ??inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str,?sizeof(str)),
? ? ? ??ntohs((*ts).cliaddr.sin_port));
for?(i =?0; i < n; i++)
? ? ? buf[i] =?toupper(buf[i]);
? ??Write(ts->connfd, buf, n);
? }
??Close(ts->connfd);
}
intmain(void)
{
structsockaddr_inservaddr, cliaddr;
socklen_t?cliaddr_len;
int?listenfd, connfd;
int?i =?0;
pthread_t?tid;
structs_infots[256];
? listenfd =?Socket(AF_INET, SOCK_STREAM,?0);
??bzero(&servaddr,?sizeof(servaddr));
? servaddr.sin_family = AF_INET;
? servaddr.sin_addr.s_addr =?htonl(INADDR_ANY);
? servaddr.sin_port =?htons(SERV_PORT);
??Bind(listenfd, (struct?sockaddr *)&servaddr,?sizeof(servaddr));
??Listen(listenfd,?20);
printf("Accepting connections ...n");
while?(1) {
? ? cliaddr_len =?sizeof(cliaddr);
? ? connfd =?Accept(listenfd, (struct?sockaddr *)&cliaddr, &cliaddr_len);
? ? ts[i].cliaddr = cliaddr;
? ? ts[i].connfd = connfd;
/* 達(dá)到線程最大數(shù)時(shí),pthread_create出錯(cuò)處理, 增加服務(wù)器穩(wěn)定性 */
? ??pthread_create(&tid,?NULL, do_work, (void*)&ts[i]);
? ? i++;
? }
return0;
}
client:
/* client.c */
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include"wrap.h"
#define?MAXLINE 80
#define?SERV_PORT 6666
intmain(int?argc,?char?*argv[])
{
structsockaddr_inservaddr;
char?buf[MAXLINE];
int?sockfd, n;
? sockfd =?Socket(AF_INET, SOCK_STREAM,?0);
??bzero(&servaddr,?sizeof(servaddr));
? servaddr.sin_family = AF_INET;
??inet_pton(AF_INET,?"127.0.0.1", &servaddr.sin_addr);
? servaddr.sin_port =?htons(SERV_PORT);
??Connect(sockfd, (struct?sockaddr *)&servaddr,?sizeof(servaddr));
while?(fgets(buf, MAXLINE, stdin) !=?NULL) {
? ??Write(sockfd, buf,?strlen(buf));
? ? n =?Read(sockfd, buf, MAXLINE);
if?(n ==?0)
printf("the other side has been closed.n");
else
? ? ??Write(STDOUT_FILENO, buf, n);
? }
??Close(sockfd);
return0;
}