目錄
前言
1.存在的問題
2.多進(jìn)程版
3.多線程版
4.線程池版
總結(jié)
前言
? ? ? ? 在上一篇文章中使用TCP協(xié)議實現(xiàn)了一個簡單的服務(wù)器,可以用來服務(wù)端和客戶端通信,但是之前的服務(wù)器存在一個問題,就是當(dāng)有多個客戶端連接服務(wù)器的時候,服務(wù)器只能和一個客戶端通信,其它的客戶端是無法通信的,這是為什么呢?有該如何解決呢?將在這篇文章中為大家介紹
1.存在的問題
如圖所示:
?為什么會存在這樣的問題呢?
如上圖所示,之前我們實現(xiàn)的服務(wù)器是單進(jìn)程版的,所以當(dāng)?shù)谝淮魏涂蛻舳私⑦B接完成之后,就死循環(huán)處理讀取信息的邏輯了,所以就無法再和新的客戶端建立連接了 。找到問題之后,很明顯解決方式就是將建立連接和通信分開執(zhí)行,此時我們就可以使用多進(jìn)程和多線程來解決了,下面我們就具體來實現(xiàn)以下如何使用多進(jìn)程和多線程。
2.多進(jìn)程版
實現(xiàn)思路:與客戶端建立連接完成,fork創(chuàng)建子進(jìn)程,讓父進(jìn)程繼續(xù)建立連接,讓子進(jìn)程實現(xiàn)后續(xù)的數(shù)據(jù)通信。
這樣實現(xiàn)存在的問題:當(dāng)父進(jìn)程創(chuàng)建完子進(jìn)程之后,需要使用waitpid回收子進(jìn)程的資源,否則子進(jìn)程就會變?yōu)榻┦M(jìn)程,導(dǎo)致資源泄漏,但是使用waitpid回收子進(jìn)程資源,程序變?yōu)榇谢瘓?zhí)行了,就無法實現(xiàn)之前的需求,讓父進(jìn)程負(fù)責(zé)建立連接,子進(jìn)程負(fù)責(zé)數(shù)據(jù)通信了。
解決方式有兩種:
1.fork創(chuàng)建子進(jìn)程,在子進(jìn)程內(nèi)部再fork創(chuàng)建子進(jìn)程,然后讓父進(jìn)程直接退出,此時之前的子進(jìn)程作為父進(jìn)程退出,新創(chuàng)建的子進(jìn)程就變?yōu)楣聝哼M(jìn)程被操作系統(tǒng)領(lǐng)養(yǎng),并不會造成資源泄漏,并且讓該子進(jìn)程負(fù)責(zé)通信
2.因為子進(jìn)程退出之后操作系統(tǒng)會發(fā)送一個SIGCHLD信號,所以可以使用signal函數(shù)捕捉SIGCHLD信號,將默認(rèn)處理動作設(shè)置為SIG_IGN,在這個默認(rèn)動作里會回收子進(jìn)程的資源,并不會造成資源泄漏,并且讓該子進(jìn)程負(fù)責(zé)通信
思路1代碼:
pid_t id = fork();
if(id == 0)//child
{
close(_sock);
if(fork() > 0)
exit(0);
serviceIO(sock);
close(sock);
exit(0);
}
close(sock);
waitpid(id,nullptr,0);
思路2代碼:
signal(SIGCHLD,SIG_IGN);
if(id == 0)//child
{
// 子進(jìn)程會繼承父進(jìn)程的文件描述符表,當(dāng)子進(jìn)程不需要時進(jìn)行關(guān)閉,
// 防止子進(jìn)程文件描述符資源泄露
close(_sock);
serviceIO(sock);
close(sock);
exit(0);
}
運行截圖:
[myl@VM-8-12-centos tcp]$ ./tcpServer 8080
create socket success
bind socket success
listen socket success
accept a new link success
sock: 4
accept a new link success
sock: 4
recvice message: 你好,我是客戶端1
recvice message: 你好,我是客戶端2
此時就實現(xiàn)了一個客戶端可以被多個服務(wù)端連接并且實現(xiàn)通信。
3.多線程版
說明:相比于多進(jìn)程,多線程的創(chuàng)建和銷毀對操作系統(tǒng)是更輕量的,消耗的資源也是更少的,所以實現(xiàn)數(shù)據(jù)通信可以采用多線程的方式,讓主線程負(fù)責(zé)建立,讓從線程負(fù)責(zé)數(shù)據(jù)通信
代碼實現(xiàn):
class TcpServerData
{
public:
TcpServerData(TcpServer* self,int sock)
:_self(self),_sock(sock) {}
public:
TcpServer* _self;
int _sock;
};
cout << "我是主線程" << endl;
pthread_t tid;
TcpServerData* tsd = new TcpServerData(this,sock);
pthread_create(&tid,nullptr,start_routine,tsd);
//因為是類內(nèi)成員函數(shù),必須包含this指針,但是start_routine作為參數(shù)是沒有this指針的
//所以start_routine函數(shù)必須加上static,靜態(tài)成員方法是不能訪問類內(nèi)成員的,所以參數(shù)傳遞this
//調(diào)用serviceIO,但是serviceIO函數(shù)需要傳遞參數(shù)sock,所以可以封裝一個結(jié)構(gòu)體,在結(jié)構(gòu)體中包含成員
//屬性sock和this
static void* start_routine(void* args) {
//設(shè)置與主線程分離,此時主線程不需要等待從線程退出了,而是繼續(xù)建立連接
cout << "我是從線程" << endl;
pthread_detach(pthread_self());
TcpServerData* t = static_cast<TcpServerData*>(args);
t->_self->serviceIO(t->_sock);
close(t->_sock);
delete t;
return nullptr;
運行截圖:
[myl@VM-8-12-centos tcp]$ ./tcpServer 8080
create socket success
bind socket success
listen socket success
accept a new link success
sock: 4
我是主線程
我是從線程
recvice message: 你好,我是客戶端1
accept a new link success
sock: 5
我是主線程
我是從線程
recvice message: 你好,我是客戶端2
4.線程池版
說明:線程池版的實現(xiàn)思路是基于多線程,雖然多線程創(chuàng)建和銷毀的消耗比多進(jìn)程的低,但是為了更進(jìn)一步提升效率,可以預(yù)先創(chuàng)建好一批線程,主線程負(fù)責(zé)建立連接獲取任務(wù),然后將任務(wù)加入到隊列中,讓預(yù)先創(chuàng)建好的線程從隊列中獲取任務(wù),然后處理獲取到的任務(wù)。
代碼實現(xiàn):
void start()
{
//線程池初始化:預(yù)先創(chuàng)建好一批線程:
ThreadPool<Task>::getInstance()->run();
for (;;)
{
// 建立連接:
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sock = accept(_sock, (struct sockaddr *)&peer, &len);
if (sock < 0)
{
logMessage(ERROR, "accept error, next");
continue;
}
logMessage(NORMAL, "accept a new link success");
std::cout << "sock: " << sock << std::endl;
//未來通信全部用sock,面向字節(jié)流的,后續(xù)全部都是文件操作:
ThreadPool<Task>::getInstance()->push(Task(sock,serviceIO));
}
}
運行截圖:
[myl@VM-8-12-centos tcp]$ ./tcpServer 8080
create socket success
bind socket success
listen socket success
thread-1 start ...
thread-2 start ...
thread-3 start ...
thread-4 start ...
thread-5 start ...
thread-6 start ...
thread-7 start ...
thread-8 start ...
thread-9 start ...
thread-10 start ...
accept a new link success
sock: 4
accept a new link success
sock: 5
recv message: 你好,我是客戶端1
recv message: 你好,我是客戶端2
注:關(guān)于線程池詳細(xì)的設(shè)計與實現(xiàn)可以觀看線程池這篇文章,里面有相信的代碼實現(xiàn)文章來源:http://www.zghlxwxcb.cn/news/detail-659748.html
總結(jié)
? ? ? ? 以上就是關(guān)于TCP服務(wù)器實現(xiàn)多進(jìn)程版,多線程版,線程池版的詳細(xì)介紹,可以通過這篇文章發(fā)現(xiàn)之前在系統(tǒng)中學(xué)習(xí)的知識在網(wǎng)絡(luò)中全部結(jié)合起來了,今天的介紹就到這里了,感謝大家的閱讀,我們下次再見!文章來源地址http://www.zghlxwxcb.cn/news/detail-659748.html
到了這里,關(guān)于TCP服務(wù)器實現(xiàn)—多進(jìn)程版,多線程版,線程池版的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!