嵌入式LINUX——————TCP并發服務器

一、服務器

1.服務器分類

單循環服務器:只能處理一個客戶端任務的服務器
并發服務器:可同時處理多個客戶端任務的服務器

二、TCP并發服務器的構建

1.如何構建??? ??

(1)多進程(每一次創建都非常耗時耗空間,但是安全)

#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
void do_wait(int signo)
{wait(NULL);
}
int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50000);if (sockfd < 0){return 1;}signal(SIGCHLD, do_wait);char buf[1024] = {0};struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);while (1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if (connfd < 0){perror("connect fail");return 1;}pid_t pid = fork();if (pid > 0){}else if (0 == pid){while (1){memset(buf, 0, sizeof(buf));ssize_t size = recv(connfd, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");break;}else if (0 == size){printf("client connet offline");break;}printf("[%s] [%d] ?: %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(connfd, buf, sizeof(buf), 0);if (size < 0){perror("fail send");break;}}close(connfd);exit(1);}else{perror("fork fail");return 1;}}close(sockfd);return 0;
}

(2)多線程(并發程度高、不太安全)

#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
typedef struct
{int connfd;struct sockaddr_in cliaddr;
} XIN;void do_thurance(void *arg)
{XIN xi = *(XIN *)arg;char buf[1024] = {0};while (1){memset(buf, 0, sizeof(buf));ssize_t size = recv(xi.connfd, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");break;}else if (0 == size){printf("client connet offline");break;}printf("[%s] [%d] ?: %s\n", inet_ntoa(xi.cliaddr.sin_addr), ntohs(xi.cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(xi.connfd, buf, sizeof(buf), 0);if (size < 0){perror("fail send");break;}}close(xi.connfd);pthread_exit(NULL);
}int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50000);if (sockfd < 0){return 1;}char buf[1024] = {0};pthread_t tid;struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);while (1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if (connfd < 0){perror("connect fail");return 1;}printf("client getline\n");XIN xi;xi.connfd = connfd;xi.cliaddr = cliaddr;pthread_create(&tid, NULL, do_thurance, &xi);pthread_detach(tid); ? ? ? ? ? ? ? ? ? ?//設置分離屬性,線程結束,操作系統自動會回收;}close(sockfd);return 0;
}

(3)線程池

????????主要解決:程序運行過程中,線程被反復創建和銷毀帶來的耗時問題;
(4)IO多路復用

? ? ? ? 理解:不創建進程和線程的情況下,對多個文件描述符監測復用一個進程;

二、IO多路復用

1.阻塞IO方式:

(1)多個IO之間是同步關系;

(2)多個IO之間相互影響;

2.IO多路復用

(1)步驟
1)創建文件描述符集合(數組、鏈表、樹形結構.......);

? ? ? ? 2)添加關注的文件描述符帶集合中;

? ? ? ? 3)通過函數接口,把集合傳遞給內核,并開始檢測IO事件(輸入輸出、讀寫事件);

? ? ? ? 4)當內核檢測到事件時,通過相關函數返回,做具體的相關操作;

(2)select
1)創建文件描述符集合表:fd_set

? ? ? ? 2)清楚集合表? ? ?

? ? ? ? 3)把文件描述符加入到集合表中

? ? ? ? 4)select:? ?

? ? ? ? 功能:通知內核檢測的集合表并開始檢測

? ? ? ? 參數:

? ? ? ? ? ? ? ? nfds:關注的最大描述符+1

? ? ? ? ? ? ? ? readfds:關注的讀事件的我文件描述符的地址

? ? ? ? ? ? ? ? writefds:關注的寫事件的我文件描述符的地址

? ? ? ? ? ? ? ? exceptfds:其他事件

? ? ? ? ? ? ? ? timeout:超時事件的地址;設置一個時間結點,如果都沒有事件來,就直接返回;????????

????????????????NULL:不設置超時時間

? ? ? ? 返回值:

? ? ? ? ? ? ? ? 成功:返回到達事件的個數

? ? ? ? ? ? ? ? 失敗:-1

? ? ? ? ? ? ? ? 超時時間到達沒有事件時:0

位圖在內核中,保持最小未被使用原則

#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50002);if (sockfd < 0){return 1;}struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);int maxs;fd_set rdfds;fd_set tmprdfds;FD_ZERO(&rdfds);FD_SET(sockfd, &rdfds);int i = 0;maxs = sockfd;char buf[1024]={0};while (1){tmprdfds = rdfds;int cnt = select(maxs + 1, &tmprdfds, NULL, NULL, NULL);if (cnt < 0){perror("fail select");return 1;}if (FD_ISSET(sockfd, &tmprdfds)){int connfd = accept(sockfd,(struct sockaddr*)&cliaddr, &clilen);if (connfd < 0){perror("fail accept");return 1;}FD_SET(connfd, &rdfds);maxs = maxs > connfd ? maxs : connfd;}// for(i=sockfd;i<maxs+1;++i)// {// ? ? printf("%d\n",i);// }// sleep(3);for (i = sockfd + 1; i < maxs + 1; ++i){if (FD_ISSET(i, &tmprdfds)){memset(buf, 0, sizeof(buf));ssize_t size = recv(i, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");continue;}if(0==size){printf("client offlink\n");return 1;}printf("[%s] [%d] ?: %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(i, buf, sizeof(buf), 0);if (size < 0){perror("fail send");continue;}} ?}}close(sockfd);return 0;
}

??缺陷:

????????限制了最多只能檢測1024個文件描述符(底層使用數組的機制儲存);
在應用層每次需要遍歷才可找到到達的事件的文件描述符,效率不高,還耗時;
集合表存在于應用層,內核存在應用層和內核層的數據表的反復拷貝,耗時;
select只能工作在水平觸發模式(低速模式),不能工作在邊沿觸發模式(高速模式);

????????邊沿觸發:數據從無變有,從低電平到高電平,觸發一次,稱讀數據的上升沿觸發;??數據一次收不完,但是下一次繼續讀

? ? ??? ? ? ? 水平觸發:數據從無到有,先觸發一次讀,沒讀完,再觸發讀,一直到讀完了,才不觸發;優勢在反復把數據讀完;缺點:耗時,低俗模式

? ?(3)poll
1)解決的問題:檢測的文件描述符個數不受1024限制;底層對于集合表的方式改變,變成了鏈表,時間復雜度O(n),其他問題未被改善,仍然需要反復拷貝、遍歷、只可工作在水平觸發模式;

(4)epoll
1)解決的問題:檢測的文件描述符是樹形結構;時間復雜度是O(log(n)【紅黑樹】,也不受1024限制;將檢測的文件描述符集合創建在內核,解決了內核和用戶層的數據拷貝;直接返回到達事件的文件描述符集合,不需要遍歷尋找;epoll可以工作在水平觸摸式,也可工作在邊沿觸發模式;

? ? ? ? 2)步驟

? ? ? ? ? ? ? ? a)創建文件描述符集合表;? ? ? ?

? ? ? ? ? ? ? ? 功能:創建文件描述符集合表到內核

? ? ? ? ? ? ? ? 參數:

? ? ? ? ? ? ? ? ? ? ? ? size:最多監測的文件描述符的個數

? ? ? ? ? ? ? ? 返回值:

? ? ? ? ? ? ? ? ? ? ? ? 成功返回非負的文件描述符,代表了內核的集合;

? ? ? ? ? ? ? ? ? ? ? ? 失敗返回-1

? ? ? ? ? ? ? ? b)添加關注的文件描述符到集合;

? ? ? ? ? ? ? ? int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

? ? ? ? ? ? ? ? 功能:對文件描述符進行操作;

? ? ? ? ? ? ? ? 參數:

? ? ? ? ? ? ? ? ? ? ? ? epfd:文件描述符集合表的文件描述符

? ? ? ? ? ? ? ? ? ? ? ? op: ? ? ? ?
EPOLL_CTL_ADD ? ? ? ?新增事件
EPOLL_CTL_MOD ? ? ? ?修改事件?
EPOLL_CTL_DEL ? ? ? ?刪除事件
fd:要操作的文件描述符?
events:事件相關結構體

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?EPOLLIN ? ? ? ?讀事件
EPOLLOUT ? ?寫事件 ?
EPOLLET ? ? ? ?邊沿觸發 ? ?
LT ? ? ? ? ? ?水平觸發

epoll函數里面的data里存放很多相關內容,需將fd直接放入此類型結構體里進行調用

????????????????typedef union epoll_data {
void ? ? ? ?*ptr;
int ? ? ? ? ?fd;
uint32_t ? ? u32;
uint64_t ? ? u64;
} epoll_data_t;

? ? ? ? ????????struct epoll_event {
uint32_t ? ? events; ? ? ?/* Epoll events */
epoll_data_t data; ? ? ? ?/* User data variable */
};

? ? ? ? ? ? ? ? 返回值:

? ? ? ? ? ? ? ? ? ? ? ? 成功返回0;

? ? ? ? ? ? ? ? ? ? ? ? 失敗返回-1;

? ? ? ? ? ? ? ? c)epoll通知內核開始檢測;

?? ? ? int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);

? ? ? ? ? ? ? ? 功能:監聽事件表中的事件,并將產生的事件存放到結構體數組中
參數:
epfd:事件表文件描述符
events:存放結果事件結構體數組空間首地址?
maxevents:最多存放事件個數
timeout:超時時間
-1:阻塞等待直到有事件發生?
返回值:
成功返回產生事件個數
失敗返回-1?

? ? ? ? ? ? ? ? ? d)epoll返回檢測到的事件結果

步驟:????????1.創建文件描述符集合表;
2.添加關注的文件描述符到集合
3.epoll通知內核開始監測
4.epoll返回監測到的事件結果

3.在數據量比較小的時候,select的比epoll的性能差不多,甚至更好,更小的時候,優勢體現不明顯,對于IO:如果處理的任務有耗時任務,此時應該考慮增加進線程,把耗時的任務給進線程去做

并發函數對比

IO多路復用——并發服務器

并發服務器的性能對比

線程池+epoll?

對于IO:如果處理的任務有耗時任務,此時應該考慮增加進線程,把耗時的任務給進線程去做

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

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

相關文章

論文潤色不能降低文章的重復率

最近大家問到多的&#xff0c;你們潤色好了重復率會不會就降低了。這事兒啊&#xff0c;得從好幾個方面去剖析&#xff0c;今天咱們就一塊兒來探個究竟。咱們先得清楚&#xff0c;重復率檢測工具一般會把內容標記成兩類&#xff1a;一是那些和其他文獻在文字表達上高度相似的部…

Python爬蟲實戰:構建alltheplaces平臺地理信息數據采集系統

1. 引言 1.1 研究背景與意義 在大數據與智慧城市建設的推動下,地理位置信息(如餐館、景點、公共設施等 POI 數據)已成為商業分析、城市規劃、公共服務優化的核心基礎數據。alltheplaces 作為全球領先的開放場所數據平臺,整合了來自多個數據源的標準化信息,涵蓋場所名稱、…

HTML第三次作業

抽獎項目代碼<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>簡易抽獎轉盤</title><sty…

PyTorch 面試題及詳細答案120題(01-05)-- 基礎概念與安裝

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

云手機選哪個比較好用?

云手機作為基于云計算技術運行的一款虛擬手機&#xff0c;能夠幫助企業與個人用戶進行賬號多開和遠程訪問等多種功能&#xff0c;是手游玩家的首要選擇&#xff0c;能夠多開賬號掛機不卡頓&#xff0c;但是哪一款云手機更加流暢好用呢&#xff1f;對于熱衷于手游的玩家來說&…

[科研理論]無人機底層控制算法PID、LQR、MPC解析

文章目錄1. PX4飛控PID簡介1.1 位置控制器1.2 速度控制器1.3 加速度和yaw轉到姿態1.4 姿態控制器1.5 角速率控制器2. 線性二次型優化&#xff08;LQR&#xff09;控制3. 模型預測控制MPC/NMPC3.1 MPC3.2 NMPC1. PX4飛控PID簡介 相關鏈接&#xff1a;PX4官方中文文檔、PID概念(…

AI系統性思維復盤概述

核心價值&#xff1a;從“被動思考”到“主動進化”。 基于數據驅動、機器學習和知識圖譜的智能化組織學習系統&#xff0c;它將經驗積累從傳統的主觀性、碎片化模式轉變為客觀性、系統化的科學模式&#xff0c;最終實現從被動應對向主動預防、從經驗決策向數據決策、從個體智慧…

C++繼承(2)

2.基類和派生類間的轉換 ?public繼承的派?類對象可以賦值給基類的指針/基類的引?。這?有個形象的說法叫切?或者切 割。寓意把派?類中基類那部分切出來&#xff0c;基類指針或引?指向的是派?類中切出來的基類那部分。 ? 基類對象不能賦值給派?類對象。 ? 基類的指針或…

easya2a: 一鍵將 LangChain Agent 發布為 A2A 服務

easya2a: 一鍵將 LangChain Agent 發布為 A2A 服務 隨著 A2A (Agent-to-Agent) 協議的發布&#xff0c;相關的實踐項目也逐漸涌現。對于許多希望體驗 A2A 功能&#xff0c;但又擔心學習成本和開發時間的開發者來說&#xff0c;推薦使用 easya2a——一個可以快速、無縫地將現有 …

原學之設計模式- 設計模式來源

引言 各位旅行者們你們好&#xff0c;我是小森&#xff0c;首先我為啥是程序員。學了技術快六年了&#xff0c;但一直都是斷斷續續&#xff0c;本身自己的條件&#xff0c;從2021年11月份開始下載原神&#xff0c;總而言之不了解一些抽卡機制導致退了并且刪除了具體賬號打算重新…

有鹿機器人:AI技術如何重新定義「掃地」這件小事?

當掃地成為一門“技術活”掃地&#xff0c;可能是人類最古老的清潔行為之一。從掃帚到吸塵器&#xff0c;再到今天的無人駕駛清潔設備&#xff0c;我們一直在尋找更高效、更徹底的方式維護環境整潔。但有鹿機器人的出現&#xff0c;讓“掃地”這件事有了新的定義——它不再只是…

62.不同路徑

dp問題描述 62.不同路徑 確定本題的狀態表示 dp[i,j]表示的是從左上角走到這個位置的路徑條數 確定本題的狀態轉移方程 根據已知條件&#xff1a;dp[0,0]1&#xff0c;dp[0,1]1&#xff0c;dp[1,0]1 本題的狀態轉移方程是&#xff1a; dp[i,j]dp[i,j-1]dp[i-1,j] 填表求…

python---包

文章目錄包的基本概念創建包的基本結構__init__.py文件導入包和模塊相對導入&#xff08;在包內部使用&#xff09;導入包和導入模塊的區別包是Python中組織模塊的一種方式&#xff0c;它允許你將相關的模塊分組在一起&#xff0c;形成一個層次結構。包的主要目的是幫助避免命名…

超詳細yolov8/11-obb旋轉框全流程概述:配置環境、數據標注、訓練、驗證/預測、onnx部署(c++/python)詳解

因為yolo的檢測/分割/姿態/旋轉/分類模型的環境配置、訓練、推理預測等命令非常類似&#xff0c;這里不再詳細敘述環境配置&#xff0c;主要參考【超詳細yolo8/11-detect目標檢測全流程概述&#xff1a;配置環境、數據標注、訓練、驗證/預測、onnx部署(c/python)詳解】&#xf…

創世理論達成 全關聯的動態振動網:量子世界的“底層邏輯”

全關聯的動態振動網&#xff1a;量子世界的“底層邏輯”&#xff08;不帶公式&#xff0c;超級詳細&#xff09;要真正理解量子世界的本質&#xff0c;我們需要跳出“粒子”和“波”的傳統框架&#xff0c;從量子場論的核心邏輯出發&#xff0c;用最生活化的類比和日常經驗&…

銀行間交易IMIX協議加密相關

加密流程 字段篩選與序列化 提取業務報文中標記為敏感的字段&#xff0c;生成待加密的數據塊 <!-- 示例&#xff1a;原始交易指令 --> <Order><Symbol>ABC123</Symbol> <!-- 非敏感 --><Price>100.50</Price> …

ABM和強化學習-2015年全國大學生數學建模競賽B題

多智能體系統&#xff08;Agent-Based Model, ABM&#xff09;和強化學習&#xff08;Reinforcement Learning, RL&#xff09;是兩個不同但可結合的概念&#xff0c;尤其在復雜系統建模和人工智能領域有重要應用。下面分別解釋它們&#xff0c;并說明二者的關聯&#xff1a; …

ALBEF/BLIP/BLIP2/Instruct BLIP/X Instruct BLIP

ALBEF 研究動機 ALBEF之前的一些方式&#xff0c; 視覺分支基本都是基于 dector的方式&#xff08;檢出目標框&#xff09;&#xff0c;如下圖所示&#xff0c;由于大量的數據沒有標框&#xff0c;因此視覺預訓練的受限于dector的檢測方式。創新點 能不能不再采用dector的方式訓…

數據結構——排序算法(簡單篇:冒泡排序、選擇排序、插入排序)

1?? 冒泡排序&#xff08;Bubble Sort&#xff09; 基本思想 重復地比較相鄰的兩個元素&#xff0c;如果順序錯誤就交換它們。一趟冒泡結束后&#xff0c;最大&#xff08;或最小&#xff09;的元素會“浮”到末尾。下一趟時可以少比較一次&#xff0c;因為最后的元素已經排好…

配置 Docker 鏡像加速,解決 docker pull 拉取鏡像失敗、docker search 查詢鏡像失敗等問題

一、概述 記錄時間 [2025-08-16] 在 Docker 學習中&#xff0c;可能會遇到諸如 docker 遠程倉庫無法訪問、docker pull 拉取鏡像失敗、docker search 查詢鏡像失敗等問題。 這是由于國內網絡對 docker 遠程倉庫的訪問受到限制。 那么在國內如何獲取 docker 鏡像呢&#xff1f…