TCP服務器實現—多進程版,多線程版,線程池版

目錄

前言

1.存在的問題

2.多進程版

3.多線程版

4.線程池版

總結


前言

? ? ? ? 在上一篇文章中使用TCP協議實現了一個簡單的服務器,可以用來服務端和客戶端通信,但是之前的服務器存在一個問題,就是當有多個客戶端連接服務器的時候,服務器只能和一個客戶端通信,其它的客戶端是無法通信的,這是為什么呢?有該如何解決呢?將在這篇文章中為大家介紹

1.存在的問題

如圖所示:

?為什么會存在這樣的問題呢?

如上圖所示,之前我們實現的服務器是單進程版的,所以當第一次和客戶端建立連接完成之后,就死循環處理讀取信息的邏輯了,所以就無法再和新的客戶端建立連接了 。找到問題之后,很明顯解決方式就是將建立連接和通信分開執行,此時我們就可以使用多進程和多線程來解決了,下面我們就具體來實現以下如何使用多進程和多線程。

2.多進程版

實現思路:與客戶端建立連接完成,fork創建子進程,讓父進程繼續建立連接,讓子進程實現后續的數據通信。

這樣實現存在的問題:當父進程創建完子進程之后,需要使用waitpid回收子進程的資源,否則子進程就會變為僵尸進程,導致資源泄漏,但是使用waitpid回收子進程資源,程序變為串行化執行了,就無法實現之前的需求,讓父進程負責建立連接,子進程負責數據通信了。

解決方式有兩種:

1.fork創建子進程,在子進程內部再fork創建子進程,然后讓父進程直接退出,此時之前的子進程作為父進程退出,新創建的子進程就變為孤兒進程被操作系統領養,并不會造成資源泄漏,并且讓該子進程負責通信

2.因為子進程退出之后操作系統會發送一個SIGCHLD信號,所以可以使用signal函數捕捉SIGCHLD信號,將默認處理動作設置為SIG_IGN,在這個默認動作里會回收子進程的資源,并不會造成資源泄漏,并且讓該子進程負責通信

思路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
{// 子進程會繼承父進程的文件描述符表,當子進程不需要時進行關閉,// 防止子進程文件描述符資源泄露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

此時就實現了一個客戶端可以被多個服務端連接并且實現通信。

3.多線程版

說明:相比于多進程,多線程的創建和銷毀對操作系統是更輕量的,消耗的資源也是更少的,所以實現數據通信可以采用多線程的方式,讓主線程負責建立,讓從線程負責數據通信

代碼實現:

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);//因為是類內成員函數,必須包含this指針,但是start_routine作為參數是沒有this指針的
//所以start_routine函數必須加上static,靜態成員方法是不能訪問類內成員的,所以參數傳遞this
//調用serviceIO,但是serviceIO函數需要傳遞參數sock,所以可以封裝一個結構體,在結構體中包含成員
//屬性sock和this
static void* start_routine(void* args) {
//設置與主線程分離,此時主線程不需要等待從線程退出了,而是繼續建立連接
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.線程池版

說明:線程池版的實現思路是基于多線程,雖然多線程創建和銷毀的消耗比多進程的低,但是為了更進一步提升效率,可以預先創建好一批線程,主線程負責建立連接獲取任務,然后將任務加入到隊列中,讓預先創建好的線程從隊列中獲取任務,然后處理獲取到的任務。

代碼實現:

void start()
{//線程池初始化:預先創建好一批線程: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,面向字節流的,后續全部都是文件操作: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
注:關于線程池詳細的設計與實現可以觀看線程池這篇文章,里面有相信的代碼實現

總結

? ? ? ? 以上就是關于TCP服務器實現多進程版,多線程版,線程池版的詳細介紹,可以通過這篇文章發現之前在系統中學習的知識在網絡中全部結合起來了,今天的介紹就到這里了,感謝大家的閱讀,我們下次再見!

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

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

相關文章

002-Spring boot 自動配置相關分析

目錄 自動配置 EnableAutoConfiguration開啟自動配置讀取配置提前過濾自動配置配置包 AutoConfigurationPackage 自動配置 EnableAutoConfiguration 開啟自動配置 在Spring 啟動類上的 SpringBootApplication 中有 EnableAutoConfiguration 讀取配置 Import(AutoConfigurat…

后端返回圖片,前端接收并顯示的解決方案

后端圖片數據返回 后端通過二進制流的形式&#xff0c;寫入response中 controller層 /*** 獲取簽到二維碼*/GetMapping("/sign-up-pict")public void signUpPict(Long id, Long semId, HttpServletResponse response) throws NoSuchAlgorithmException {signUpServ…

musl libc ldso 動態加載研究筆記:01

前言 musl 是一個輕量級的標準C庫&#xff0c;建立在系統調用之上&#xff0c;可以認為是【用戶態】的C 庫&#xff0c;與 glibc 或者 uClibc 屬于同一類。 基于 musl 的 gcc 工具鏈包括交叉編譯工具鏈&#xff0c;可以用于編譯 Linux 或者其他的操作系統&#xff0c;如當前 L…

深入解析 MyBatis 中的 <foreach> 標簽:優雅處理批量操作與動態 SQL

在當今的Java應用程序開發中&#xff0c;數據庫操作是一個不可或缺的部分。MyBatis作為一款頗受歡迎的持久層框架&#xff0c;為我們提供了一種優雅而高效的方式來管理數據庫操作。在MyBatis的眾多特性中&#xff0c;<foreach>標簽無疑是一個強大的工具&#xff0c;它使得…

構建可遠程訪問的企業內部論壇

文章目錄 前言1.cpolar、PHPStudy2.Discuz3.打開PHPStudy&#xff0c;安裝網頁論壇所需軟件4.進行網頁運行環境的構建5.運行Discuz網頁程序6.使用cpolar建立穿透內網的數據隧道&#xff0c;發布到公網7.對云端保留的空白數據隧道進行配置8.Discuz論壇搭建完畢 前言 企業在發展…

Python中import模塊導入的實現原理

歡迎關注博主 Mindtechnist 或加入【Linux C/C/Python社區】一起探討和分享Linux C/C/Python/Shell編程、機器人技術、機器學習、機器視覺、嵌入式AI相關領域的知識和技術。 Python中import模塊導入的實現原理 什么是模塊import搜索路徑import導入模塊的原理圖書推薦 專欄&…

京東門詳一碼多端探索與實踐 | 京東云技術團隊

本文主要講述京東門詳業務在支撐過程中遇到的困境&#xff0c;面對問題我們在效率提升、質量保障等方向的探索和實踐&#xff0c;在此將實踐過程中問題解決的思路和方案與大家一起分享&#xff0c;也希望能給大家帶來一些新的啟發 一、背景 1.1、京東門詳介紹 1.1.1、京東門…

VB+SQL上機考試系統設計與實現

摘 要 隨著計算機技術的迅猛發展,學校教學和管理的信息化發展也有長足的進步,這就要求各個環節都均衡發展,從軟硬件雙方面把學校建設成一流的信息管理、教育教學的平臺。本文設計開發的考試管理系統也是其中重要的一個方面。該系統本著減輕教師工作負擔、提高工作效率、優…

六、分組背包

六、分組背包 題記算法題目代碼 題記 一個旅行者有一個最多能裝V公斤的背包和有N件物品&#xff0c;它們的重量分別是W[1]&#xff0c;W[2]&#xff0c;…,W[n]&#xff0c;它們的價值分別為C[1],C[2],…,C[n]。這些物品被劃分為若干組&#xff0c;每組中的物品互相沖突&#…

【es6】函數參數設置默認值

1、es6之前的函數參數默認值寫法 1.1、使用短路或||的寫法 當y為空時&#xff0c;y判斷為false &#xff0c;走||右邊的&#xff0c;所以y world;當y不為空時&#xff0c;y判斷為true&#xff0c;不需要再運行||右邊的&#xff0c;所以 y y function log(x, y) {y y || W…

數據的深海潛行:數據湖、數據倉庫與數據湖庫之間的微妙關系

導言&#xff1a;數據的重要性與存儲挑戰 在這個信息爆炸的時代&#xff0c;數據已經成為企業的核心資產&#xff0c;而如何高效、安全、便捷地存儲這些數據&#xff0c;更是每個組織面臨的重大挑戰。 數據作為組織的核心資產 數據在過去的幾十年里從一個輔助工具演變成企業的…

Ubuntu 20.04(服務器版)安裝 Anaconda

0、Anaconda介紹 Anaconda是一個開源的Python發行版本&#xff0c;包含了包括Python、Conda、科學計算庫等180多個科學包及其依賴項。因此&#xff0c;安裝了Anaconda就不用再單獨安裝CUDA、Python等。 CUDA&#xff0c;在進行深度學習的時候&#xff0c;需要用到GPU&#xf…

操作符詳解上(非常詳細)

目錄 二進制介紹二進制2進制轉10進制10進制轉2進制數字2進制轉8進制和16進制2進制轉8進制2進制轉16進制 原碼、反碼、補碼移位操作符左移操作符右移操作符 位操作符&#xff1a;&、|、^逗號表達式 二進制介紹 在初學計算機時我們常常會聽到2進制、8進制、10進制、16進制……

C++中String的語法及常用接口用法

在C語言中&#xff0c;string是一個標準庫類&#xff08;class&#xff09;&#xff0c;用于處理字符串&#xff0c;它提供了一種更高級、更便捷的字符串操作方式&#xff0c;string 類提供了一系列成員函數和重載運算符&#xff0c;以便于對字符串進行操作和處理。 一、string…

scala TraversableOnce

scala TraversableOnce 1. 由來 TraversableOnce是Scala中的一個特質&#xff08;trait&#xff09;&#xff0c;它定義了一組操作&#xff0c;用于遍歷和處理集合類型的元素。它是Scala集合層次結構中的基本概念之一。 2. 示例 以下是使用TraversableOnce的簡單示例&#…

Redis高可用:主從復制詳解

目錄 1.什么是主從復制&#xff1f; 2.優勢 3.主從復制的原理 4.全量復制和增量復制 4.1 全量復制 4.2 增量復制 5.相關問題總結 5.1 當主服務器不進行持久化時復制的安全性 5.2 為什么主從全量復制使用RDB而不使用AOF&#xff1f; 5.3 為什么還有無磁盤復制模式&#xff…

C# 一種求平方根的方法 立方根也可以 極大 極小都可以

不知道研究這些干啥&#xff0c;純純的浪費時間。。。 public static double TQSquare(double number){Random random1 new Random(DateTime.Now.Millisecond);double x1 0, resultX1 0, diff 9999999999, diffTemporary 0;for (int i 0; i < 654321; i){if (random1…

怎么做Tik Tok海外娛樂公會呢?新加坡市場怎么樣?

一、為什么選擇TikTok直播 1. 海外市場潛力巨大 ? 自2016年始&#xff0c;多家直播平臺陸續拓展至東南亞、中東、俄羅斯、日韓、歐美、拉美等地區。 ? 海外市場作為直播發展新藍海&#xff0c;2021年直播行業整申請cmxyci體規模達百億美元&#xff0c;并維持高速增長。 &a…

C++初階語法——內部類

前言&#xff1a;內部類&#xff0c;顧名思義是定義在類中的類&#xff0c;許多人會以為它屬于外部的類&#xff0c;實際上并不是&#xff0c;它們是兩個獨立的類&#xff0c;但是內部類受外部類類域的限制。 目錄 一.概念二.特性1.內部類和外部類相互獨立2.內部類是外部類的友…

10,遍歷任意參

遍歷可變參數 遍歷可變參數獲取可變參數大小通過遞歸方式遍歷可變參數通過可變參數特性來求和 遍歷可變參數 #pragma oncetemplate<class ... ParamTypes> void Func(paramTypes &... param) {}可以看作是有一個結構體里面裝滿了參數&#xff0c;把結構體放到…中。…