day33:零基礎學嵌入式之網絡——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)清楚集合表

? ? ? ? ?void FD_CLR(int fd, fd_set *set);//把fd清掉
int ?FD_ISSET(int fd, fd_set *set);//查看fd在這個表中有沒有
void FD_SET(int fd, fd_set *set);//把fd放進集合表中
void FD_ZERO(fd_set *set);//把集合表整體清空

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

? ? ? ? 4)select:

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

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

? ? ? ? 參數:

? ? ? ? ? ? ? ? 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)創建文件描述符集合表;

??????????????int epoll_create(int size);

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

? ? ? ? ? ? ? ? 參數

? ? ? ? ? ? ? ? ? ? ? ? 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?? ??? ??? ?水平觸發

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返回檢測到的事件結果;

3.在數據量比較小的時候,select的比epoll的性能差不多,甚至更好,更小的時候,優勢體現不明顯

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



并發服務器的性能對比

線程池+epoll?

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

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

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

相關文章

VR全景制作的流程?VR全景制作可以用在哪些領域?

VR全景制作的流程&#xff1f;VR全景制作可以用在哪些領域&#xff1f;VR全景制作&#xff1a;流程、應用與未來虛擬現實&#xff08;VR&#xff09;全景制作正迅速改變我們的感官體驗&#xff0c;使我們能夠身臨其境地探索虛擬世界&#xff0c;享受沉浸式的奇妙感受。那么&…

用LangChain重構客服系統:騰訊云向量數據庫+GPT-4o實戰

人們眼中的天才之所以卓越非凡&#xff0c;并非天資超人一等而是付出了持續不斷的努力。1萬小時的錘煉是任何人從平凡變成超凡的必要條件。———— 馬爾科姆格拉德威爾 目錄 一、傳統客服系統痛點與重構價值 1.1 傳統方案瓶頸分析 1.2 新方案技術突破點 二、系統架構設計&…

主要分布在腹側海馬體(vHPC)CA1區域(vCA1)的混合調諧細胞(mixed-tuning cells)對NLP中的深層語義分析的積極影響和啟示

腹側海馬體CA1區&#xff08;vCA1&#xff09;的混合調諧細胞&#xff08;mixed-tuning cells&#xff09;通過整合情感、社會關系、空間概念等多模態信息&#xff0c;形成動態的情景化語義表征&#xff0c;為自然語言處理&#xff08;NLP&#xff09;的深層語義分析提供了重要…

ESP32的ADF詳解:6. Audio Processing的API

一、Downmix 1. 核心功能 將基礎音頻流和新加入音頻流混合為單一輸出流&#xff0c;支持動態增益控制和狀態轉換。輸出聲道數與基礎音頻一致&#xff0c;新加入音頻自動轉換聲道匹配。2. 關鍵特性聲道處理 輸出聲道數 基礎音頻聲道數新加入音頻自動轉換聲道&#xff08;如立體…

Qt(基本組件和基本窗口類)

一、基本組件1. Designer設計師為什么要上來先將這個東西呢&#xff0c;這個是QT外置的設計界面的工具&#xff0c;沒啥用&#xff0c;所以了解一下。我們用的多的是QT內置的界面設計&#xff0c;只需要我們雙擊我們創建的項目的.ui文件就可以進入這個界面&#xff0c;你對界面…

docker與k8s的容器數據卷

Docker容器數據卷 特性 docker鏡像由多個只讀層疊加而成&#xff0c;啟動容器時&#xff0c;Docker會加載只讀鏡像層并在鏡像棧頂部添加一個讀寫層。如果運行中的容器修改了現有的一個已經存在的文件&#xff0c;那么該文件將會從讀寫層下面的只讀層復制到讀寫層&#xff0c;該…

自然語言處理技術應用領域深度解析:從理論到實踐的全面探索

1. 引言:自然語言處理的技術革命與應用前景 自然語言處理(Natural Language Processing,NLP)作為人工智能領域的核心分支,正在以前所未有的速度改變著我們的數字化生活。從最初的規則基礎系統到如今基于深度學習的大語言模型,NLP技術經歷了從理論探索到實際應用的深刻變…

OpenGLRender開發記錄(二): 陰影(shadowMap,PCF,PCSS)

目錄已實現功能陰影shadowMapPCFPCSS實現shadowMapPCFPCSS陰影GitHub主頁&#xff1a;https://github.com/sdpyy1 OpenGLRender:https://github.com/sdpyy1/CppLearn/tree/main/OpenGL 已實現功能 除了上次實現IBL之外&#xff0c;項目目前新增了imGUI的渲染&#xff0c;更方便…

Linux:日志亂碼

1、Linux日志亂碼可能是XShell客戶端編碼沒設置為UTF-8引起的&#xff0c;按照以下步驟&#xff0c;設置終端格式&#xff1a;中文版&#xff1a;打開Xshell會話屬性&#xff08;文件→屬性→終端→編碼&#xff09;&#xff0c;選擇與服務器一致的編碼格式&#xff08;如UTF-8…

Rouge:面向摘要自動評估的召回導向型指標——原理、演進與應用全景

“以n-gram重疊量化文本生成質量&#xff0c;為摘要評估提供可計算標尺” Rouge&#xff08;Recall-Oriented Understudy for Gisting Evaluation&#xff09; 是由 南加州大學信息科學研究所&#xff08;ISI&#xff09;的Chin-Yew Lin 于2004年提出的自動文本摘要評估指標&am…

[STM32][HAL]stm32wbxx 超聲波測距模塊實現(HY-SRF05)

前言 在電子技術應用中,距離測量是一個常見且重要的需求。超聲波模塊因其測量精度較高、成本較低、易于使用等優點,被廣泛應用于機器人避障、液位檢測、智能停車系統等領域。該文主要講解以stm32wb芯片為主控,用HAL庫來對HY-SRF05超聲波模塊進行代碼編寫,實現基本的驅動和測…

MySQL 性能調優實戰指南:從診斷到優化全解析

引言在日常的數據庫運維工作中&#xff0c;我們經常需要對 MySQL 數據庫進行診斷和性能分析。本文將介紹一套全面的 MySQL 診斷腳本&#xff0c;適用于 MySQL 8.0&#xff08;兼容 8.0.15 及以上版本&#xff09;&#xff0c;涵蓋事務鎖分析、性能瓶頸定位、配置檢查、連接狀態…

8. 狀態模式

目錄一、應用背景二、狀態模式2.1 解決的問題2.2 角色2.3 實現步驟三、通用設計類圖四、實現4.1 設計類圖4.2 狀態轉換圖4.3 代碼實現一、應用背景 某對象發生變化時&#xff0c;其所能做的操作也隨之變化。應用程序的可維護性和重用性差代碼的邏輯較復雜 二、狀態模式 2.1 …

php語法--foreach和in_array的使用

文章目錄foreach基礎語法&#xff1a;案例1&#xff1a;引用傳遞模式&#xff1a;嵌套數組處理&#xff1a;避免在循環中計算數組長度&#xff1a;使用引用減少內存拷貝&#xff1a;打印數組in_array基礎使用嚴格使用foreach 基礎語法&#xff1a; foreach ($iterable as $va…

ES6模塊詳解:核心語法與最佳實踐

以下是 EMAScript 6&#xff08;ES6&#xff09;模塊規范的核心要點及細節解析&#xff1a; &#x1f4e6; 一、核心語法導出&#xff08;export&#xff09; 命名導出&#xff1a;支持導出多個具名成員。export const a 1; export function b() { /* ... */ } // 或集中導出 …

Python day25

浙大疏錦行 Python day25. 內容&#xff1a; 異常處理&#xff0c;在日常的編碼工作過程中&#xff0c;為了避免由于各種bug導致的異常情況&#xff0c;我們需要引入異常處理機制&#xff0c;它的工作場景是當程序運行出現意外時&#xff0c;可以根據編碼規則處理響應的錯誤。…

mac llama_index agent算術式子計算示例

本文通過簡單數學計算&#xff0c;示例llama_index使用agent解決復雜任務過程。 假設mac本地llama_index環境已安裝&#xff0c;過程參考 mac測試ollama llamaindex-CSDN博客 測試mac筆記本內存8G&#xff0c;所以使用較小LLM完成示例。 ollama pull qwen3:1.7b qwen3:1.7b能…

uni-app小程序云效持續集成

創建項目 必須是 cli 命令行創建的 uni-app 小程序項目參考uni-app官方構建命令&#xff1a; npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project生成小程序代碼上傳密鑰 管理-開發設置-小程序代碼上傳生成的文件放在根目錄即可 安裝持續集成插件 pnpm install uni-mi…

uniapp+高德地圖實現打卡簽到、打卡日歷

一、注冊高德地圖。應用管理創建應用&#xff0c;分別添加Andriod平臺、Web服務、Web端、微信小程序四種類型的key。二、考勤規則打卡地點選擇位置代碼&#xff1a;<script setup lang"ts"> import { onMounted, onUnmounted, reactive, ref, watchEffect } fr…

CentOS 7.9 + GCC9 離線安裝 IWYU(Include What You Use)

本教程適用于 離線環境下在 CentOS 7.9 系統中使用 GCC 9 離線安裝 IWYU 的完整步驟&#xff0c;涵蓋 Clang 11.1.0 編譯、IWYU 構建以及頭文件自動優化流程。&#x1f4e5; 一、準備安裝包請提前下載以下源碼包&#xff08;可通過在線機器提前下載&#xff0c;再傳輸到離線環境…