Linux--->網絡編程(TCP并發服務器構建:[ 多進程、多線程、select ])

TCP并發服務器構建

一、服務器

? ??單循環服務器:服務端同一時刻只能處理一個客戶端的任務(TCP)
并發服務器:服務端同一時刻可以處理多個客戶端的任務(UDP)

二、TCP服務端并發模型

1、多進程

? ? ?進程資源開銷大,安全性高。

? ? ?以下是一個實現多進程TCP服務端代碼:

#define SER_PORT 62300
#define SER_IP "192.168.0.165"int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");return 0;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(SER_PORT);seraddr.sin_addr.s_addr = inet_addr(SER_IP);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){printf("bind error");return -1;}int cnt = listen(sockfd, 100);if(cnt < 0){perror("listen error");return -1;}return sockfd;
}void wait_handler(int signo)
{wait(NULL);
}int main(int argc, char *argv[])
{signal(SIGCHLD, wait_handler);struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if(sockfd < 0){perror("init_tcp_ser error");return -1;}while(1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen); if(connfd < 0){perror("accept error");return -1;}pid_t pid = fork();if(pid > 0){}else if(pid == 0){char buff[1024] = {0};while(1){memset(buff, 0, sizeof(buff));ssize_t ret = recv(connfd, buff, sizeof(buff), 0);if(ret < 0){perror("send error");return -1;}else if(ret == 0){printf("[%s : %d]: offline!\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));break;}printf("[%s : %d]: %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buff);strcat(buff, "----OK");ssize_t cnt = send(connfd, buff, strlen(buff), 0);if(cnt < 0){perror("send error");break;}}   close(connfd);}}close(sockfd);return 0;
}

2、多線程

? ? ?線程相對與進程資源開銷小,相同資源環境下,并發量比進程大。

? ? ?以下是一個實現多線程TCP服務端代碼:

#include "head.h"#define SER_PORT 62300
#define SER_IP "192.168.0.165"int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");return 0;}//允許綁定處于TIME_WAIT狀態的地址,避免端口占用問題:int optval = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(SER_PORT);seraddr.sin_addr.s_addr = inet_addr(SER_IP);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){printf("bind error");return -1;}int cnt = listen(sockfd, 100);if(cnt < 0){perror("listen error");return -1;}return sockfd;
}void *task(void *sug)
{int connfd = *(int *)sug;char buff[1024] = {0};while(1){memset(buff, 0, sizeof(buff));ssize_t ret = recv(connfd, buff, sizeof(buff), 0);if(ret < 0){perror("send error");break;}else if(ret == 0){printf("[%s : %d]: offline!\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));break;}printf("[%s : %d]: %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buff);strcat(buff, "----OK");ssize_t cnt = send(connfd, buff, strlen(buff), 0);if(cnt < 0){perror("send error");break;}}   close(connfd);
}int main(int argc, char *argv[])
{struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if(sockfd < 0){perror("init_tcp_ser error");return -1;}while(1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen); if(connfd < 0){perror("accept error");return -1;}pthread_t tid;int fd = pthread_create(&tid, NULL, task, &connfd);if(fd < 0){perror("thread create error");return -1;}  pthread_detach(tid);}close(sockfd);return 0;
}

3、線程池

? ? ?為了解決多線程或者多進程模型,在服務器運行過程,頻繁創建和銷毀線程(進程)帶來的時間消耗問題。
基于生產者和消費者編程模型,以及任務隊列等,實現的一套多線程框架。

4. ?IO多路復用

? ? 1)概念

? ? ?對多個文件描述符的讀寫可以復用一個進程。
在不創建新的進程和線程的前提下,使用一個進程實現對多個文件讀寫的同時監測。
阻塞IO模式:

? ? 2)方式

? ? ? ? 實現方式有 select、poll、epoll 三種。

5、select 實現IO多路復用

? ? 1)實現步驟

? ??(1)創建文件描述符集合? ? ?fd_set
(2)添加關注的文件描述符到集合? ? ?FD_SET
(3)使用 select 傳遞集合表給內核,內核開始監測事件? ? select()
(4)當內核監測到事件時,應用層select將解除阻塞,并獲得相關的事件結果
(5)根據 select 返回的結果做不同的任務處理

? ? 2)select 的宏

????????將集合清0:void FD_ZERO(fd_set *set);
向集合中添加文件描述符:void FD_SET(int fd, fd_set *set);
從集合中移除文件描述符:void FD_CLR(int fd, fd_set *set);
檢查文件描述符是否在集合中:int ?FD_ISSET(int fd, fd_set *set);

? ? 3)select() 函數接口

?int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

功能:傳遞文件描述符結合表給內核并等待獲取事件結果
參數:
nfds : 關注的最大文件描述符+1
readfds:讀事件的文件描述符集合
writefds:寫事件的文件描述符集合
exceptfds:其他事件的文件描述符集合
timeout:設置select監測時的超時時間
若為NULL : 不設置超時時間(select一直阻塞等待)

返回值:
成功:返回內核監測的到達事件的個數
失敗:-1
0 : 超時時間到達,但沒有事件發生,則返回0

? ? 4)應用示例

? ? ? ? (1)通過select函數構建多路復用,實現管道和終端的讀

????????管道向服務端寫的代碼:

int main(void)
{mkfifo("./myfifo", 0664);int fd = open("./myfifo", O_WRONLY);if(fd < 0){perror("open error");return -1;}while(1){write(fd, "hello world", 11);sleep(1);}close(fd);return 0;
}

? ? ? ? 讀的代碼:

int main(void)
{char buff[1024] = {0};mkfifo("./myfifo", 0664);int fifofd = open("./myfifo", O_RDONLY);if(fifofd < 0){perror("open error");return -1;}//1、創建文件描述符集合fd_set rdfds;fd_set rdfdstmp;//2、清空文件描述符集合表FD_ZERO(&rdfds);//初始化...集//3、添加關注的文件描述符到集合中FD_SET(0, &rdfds);//添加int maxfd = 0;FD_SET(fifofd, &rdfds);maxfd = maxfd > fifofd ? maxfd : fifofd;while(1){rdfdstmp = rdfds;//4、傳遞集合表給內核并等待返回到達事件的結果int cnt = select(maxfd+1, &rdfdstmp, NULL, NULL, NULL);if(cnt < 0){perror("select error");return -1;}if(FD_ISSET(0, &rdfdstmp))//檢查文件描述符0是否在集合中{fgets(buff, sizeof(buff), stdin);//0printf("STDIN: %s\n", buff);}if(FD_ISSET(fifofd, &rdfdstmp)){memset(buff, 0, sizeof(buff));read(fifofd, buff, sizeof(buff));printf("FIFO: %s\n", buff);}}close(fifofd);return 0;
}

? ? ? ? (2)使用select實現TCP服務端IO多路復用代碼

#define SER_PORT  50000
#define SER_IP    "192.168.0.165"int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(SER_PORT);seraddr.sin_addr.s_addr = inet_addr(SER_IP);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind error");return -1;}ret = listen(sockfd, 100);if (ret < 0){perror("listen error");return -1;}return sockfd;
}int main(int argc, const char *argv[])
{char buff[1024] = {0};struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if (sockfd < 0){return -1;}//1. 創建文件描述符集合fd_set rdfds;fd_set rdfdstmp;FD_ZERO(&rdfds);//2. 添加關注的文件描述符到集合FD_SET(sockfd, &rdfds);int maxfd = sockfd;while (1){rdfdstmp = rdfds;//3. 傳遞集合到內核,并等待返回監測結果int cnt = select(maxfd+1, &rdfdstmp, NULL, NULL, NULL);if (cnt < 0){perror("select error");return -1;}//4. 是否有監聽套接字事件到達 ----》三次握手已完成,可以acceptif (FD_ISSET(sockfd, &rdfdstmp)){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if (connfd < 0){perror("accept error");return -1;}FD_SET(connfd, &rdfds);maxfd = maxfd > connfd ? maxfd : connfd;}//5. 是否有通訊套接字事件到達for (int i = sockfd+1; i <= maxfd; i++){if (FD_ISSET(i, &rdfdstmp)){memset(buff, 0, sizeof(buff));ssize_t cnt = recv(i, buff, sizeof(buff), 0);if (cnt < 0){perror("recv error");FD_CLR(i, &rdfds);close(i);continue;}else if (0 == cnt){FD_CLR(i, &rdfds);close(i);continue;}printf("%s\n", buff);strcat(buff, "--->ok");cnt = send(i, buff, strlen(buff), 0);if (cnt < 0){perror("send error");FD_CLR(i, &rdfds);close(i);continue;}}}	}close(sockfd);return 0;
}

【END】

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

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

相關文章

深入解析達夢數據庫:模式分類、狀態管理與實操指南

達夢數據庫&#xff08;DM Database&#xff09;作為國產數據庫的核心代表&#xff0c;其模式與狀態機制是保障數據高可用、實現主備同步的關鍵基礎。無論是日常運維中的數據庫配置&#xff0c;還是故障場景下的主備切換&#xff0c;都需要深入理解模式與狀態的特性及交互邏輯。…

如何選擇適合自己的PHP微服務框架?

在開始選擇之前&#xff0c;我們首先要明白&#xff1a;為什么需要微服務框架&#xff1f;傳統的單體應用&#xff08;Monolithic Application&#xff09;雖然開發簡單&#xff0c;但隨著業務復雜度的增加&#xff0c;會變得臃腫且難以維護。而微服務架構通過將應用拆分為一組…

ESP32使用場景及大規模物聯網IoT

最近用ESP32搭建了一個網絡,想知道搭建的網絡拓撲對不對。一、物聯網無線通信v.s通訊網絡無線通信我第一個好奇的問題就是&#xff0c;物聯網用ESP32的話&#xff0c;路由器用什么&#xff1f;物聯網也可以組WLAN&#xff0c;通訊網也可以組WLAN。把自己的Tenda AC1200路由器拆…

NSSCTF 4th WP

第一次打比賽AK了&#xff0c;雖然題比較簡單沒啥好說的&#xff0c;但還是想記錄一下 WEB ez_signin 源碼&#xff1a; from flask import Flask, request, render_template, jsonify from pymongo import MongoClient import reapp Flask(__name__)client MongoClient…

Paimon——官網閱讀:主鍵表

主鍵表(Table with PK)PK 是 Primary Key&#xff08;主鍵&#xff09;的縮寫。在數據庫中&#xff0c;主鍵是一個或多個列的組合&#xff0c;其值在表中是唯一的&#xff0c;并且不能為 NULL。主鍵的作用是確保每一行記錄的唯一性&#xff0c;便于數據的查找、管理和維護&…

【配置 PyCharm 連接遠程服務器進行開發和調試的完整流程】

前提條件&#xff1a; 1.PyCharm Professional&#xff08;社區版不支持遠程解釋器&#xff09; 2.代碼在本地目錄里面&#xff0c;可以同步上傳遠程服務器 3.宿主機上安裝了conda 環境 操作方法&#xff1a; 1、在本地使用PyCharm打開工程代碼&#xff1b; 2、然后Add New_in…

在壓力測試中如何確定合適的并發用戶數?

確定壓力測試中的合適并發用戶數 在進行壓力測試時&#xff0c;確定合適的并發用戶數是評估系統性能的關鍵步驟。并發用戶數是指同時向系統發送請求的用戶數量&#xff0c;它直接影響系統的負載水平和性能表現。以下是幾種常用的方法和考慮因素&#xff0c;用于確定合適的并發…

微算法科技(NASDAQ:MLGO)突破性FPGA仿真算法技術助力Grover搜索,顯著提升量子計算仿真效率

在量子計算迅猛發展的今天&#xff0c;量子算法尤其是在搜索和加密領域的應用&#xff0c;正逐步揭開了其顛覆性潛力。然而&#xff0c;量子計算機的實際實現仍是一項復雜且充滿挑戰的任務&#xff0c;因此&#xff0c;如何在經典計算平臺上高效建模和仿真量子算法成為了當前的…

TencentOS Server 4.4 下創建mysql容器無法正常運行的問題

環境 騰訊的 TencentOS Server 4.4 服務器系統 Linux app 6.6.92-34.1.tl4.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 25 14:33:47 CST 2025 x86_64 x86_64 x86_64 GNU/Linux docker使用的是yum安裝的版本 [rootapp ~]# docker version Client:Version: 28.0.1-202…

稀土:從“稀有”到“命脈”的科技核心

稀土&#xff0c;這個聽起來有些陌生的詞匯&#xff0c;其實早已悄然滲透進我們生活的方方面面。它并非真的“稀有”&#xff0c;而是指17種金屬元素的統稱&#xff0c;包括鑭、鈰、釹、銪等。這些元素在地殼中并不稀少&#xff0c;但因其獨特的物理和化學性質&#xff0c;使其…

開發手札:UnrealEngine編輯器開發

以前在unity框架中開發了非常多實用且高頻使用的編輯器工具&#xff0c;現在準備把目前用得上工具移植到ue4中。下面說明一下ue4開發編輯器工具的流程。1.創建編輯器工具控件2.在控件中創建一個Button和一個EditableText&#xff0c;用于測試3.新建一個繼承UEditorUtilityWidge…

EXCEL開發之路(一)公式解析—仙盟創夢IDE

Excel 數據校驗&#xff1a;基于自定義格式的深度解析與開發實現引言在數據處理和管理領域&#xff0c;Excel 是一款廣泛應用的工具。確保 Excel 中數據的準確性和完整性至關重要&#xff0c;而數據校驗是達成這一目標的關鍵手段。本文將借助特定的代碼示例&#xff0c;深入探討…

Day14——JavaScript 核心知識全解析:變量、類型與操作符深度探秘

接續上文&#xff1a;《前端小白進階 Day13&#xff1a;JavaScript 基礎語法 交互技巧 知識圖譜&#xff0c;零基礎也能懂》-CSDN博客 點關注不迷路喲。你的點贊、收藏&#xff0c;一鍵三連&#xff0c;是我持續更新的動力喲&#xff01;&#xff01;&#xff01; 主頁:一位…

anaconda本身有一個python環境(base),想用別的環境就是用anaconda命令行往anaconda里創建虛擬環境

差不多是這個意思&#xff0c;但需要稍微澄清一下&#xff1a;Anaconda 可以管理任意版本的 Python你安裝了 Anaconda 后&#xff0c;默認有一個 base 環境自帶的 Python。如果你想用其他版本&#xff0c;比如 Python 3.9、3.10&#xff0c;可以用 conda create -n py39 python…

畢業項目推薦:28-基于yolov8/yolov5/yolo11的電塔危險物品檢測識別系統(Python+卷積神經網絡)

文章目錄 項目介紹大全&#xff08;可點擊查看&#xff0c;不定時更新中&#xff09;概要一、整體資源介紹技術要點功能展示&#xff1a;功能1 支持單張圖片識別功能2 支持遍歷文件夾識別功能3 支持識別視頻文件功能4 支持攝像頭識別功能5 支持結果文件導出&#xff08;xls格式…

字庫原理 GB2312-80

這篇文章介紹的是 在嵌入式開發中 常常會遇見的 中文字體點陣字庫&#xff08;如漢字庫&#xff09; 的核心編碼原理&#xff1a;區位碼 偏移計算 內存映射。我們將會介紹 GB2312-80 字庫的工作機制。 &#x1f4d8;什么是 GB2312-80&#xff1f; GB2312-80 是中國國家標準定…

【Golang】 項目啟動方法

Go 項目啟動方法 1. 常見啟動方式 使用 go run 命令 # 運行主程序文件 go run main.go# 運行多個文件 go run *.go# 運行整個模塊&#xff08;Go 1.11&#xff09; go run .# 運行指定包 go run github.com/yourusername/yourproject先構建再執行 # 構建可執行文件 go build# 運…

3459. 最長 V 形對角線段的長度

Problem: 3459. 最長 V 形對角線段的長度 文章目錄思路解題過程復雜度Code思路 深度優先搜索 記憶數組 解題過程 主函數和先遍歷從每一個1開始搜索&#xff0c;并枚舉每一個方向進入dfs&#xff0c;dfs先檢查是否遍歷過&#xff0c;然后枚舉下一個可以走的方向&#xff0c;最后…

Unity 串口通信

可以通過計算機管理->設備管理器&#xff0c;查看端口串口通訊&#xff0c;通常是指的通過計算機或其他設備上的串行端口實現數據傳輸的過程。 定義與特點&#xff1a;串口通訊是按位&#xff08;bit&#xff09;發送和接收字節的通信方式&#xff0c;它將數據一位一位地順序…

ArcGIS JSAPI 高級教程 - 創建漸變色材質的自定義幾何體

ArcGIS JSAPI 高級教程 - 創建漸變色材質的自定義幾何體核心代碼完整代碼在線示例工作中遇到一個比較復雜的功能&#xff0c;其中用到漸變色&#xff0c;于是研究了一下&#xff0c;發現雖然 JS API 不直接支持漸變色&#xff0c;但是也可以自定義創建漸變色&#xff0c;通過 M…