網絡編程3

管道的性質

  1. 讀緩沖區為空,read阻塞
  2. 寫緩沖區為空,write阻塞
  3. 一端先close,另一端繼續read,read不阻塞,立刻返回0
  4. 一端先close,另一端繼續write,write會觸發SIGPIPE信號,進程異常終止

socket和管道性質基本一致

socket的性質幾乎和管道是一樣的,唯一的區別是socket是全雙工的

寫端先close,讀端繼續recv:recv不阻塞,返回0。

讀端先close,寫端后send:讀端會接接收到第一次的send,隨后關閉。

使用gdb

使用命令行:gdb --args? <./xxxx>? <ip>? ?<port>

會開啟gdb并且執行命令。

close

關閉文件描述符。具體類似于關閉管道。

基于tcp socket的即時聊天

實現的大致方式為:

?若使用:

while(1){read(stdin);……recv(通信socket);
}

會導致串聯式問題

需要改成并聯式解決問題

使用select IO 多路復用。(也可以使用多線程解決,但是IO多路復用比多線程好的多)

select IO多路復用

同時等待多個資源就緒

  1. 設計一個數據結構 fd_set 用來接收監聽集合
  2. FD_ZERO(&set) 初始化數據結構,同時將關注的資源加入 fd_set ——FD_SET()
  3. 操作系統內核做輪詢,進程在等待 select()
  4. 有任何一個資源就緒了,輪詢結束,返回一個就緒集合
  5. 進程根據就緒集合去做IO操作 FD_ISSET()

代碼實現:

使用bzero()初始化buf,需要加入頭文件#include <strings.h>

sever.c

#include <my_header.h>/* Usage:  */
int main(int argc, char *argv[]){                                  ARGS_CHECK(argc,3);int sockfd = socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));addr.sin_addr.s_addr = inet_addr(argv[1]);int ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));ERROR_CHECK(ret,-1,"bind");listen(sockfd,50);int netfd = accept(sockfd,NULL,NULL);char buf[4096];fd_set readset;//讀集合while(1){FD_ZERO(&readset);//初始化FD_SET(STDIN_FILENO,&readset);//把stdin加入監聽FD_SET(netfd,&readset);//把通信socket加入監聽select(netfd+1,&readset,NULL,NULL,NULL);//內核輪詢,進程等待//有至少一個就緒//此時readset變成就緒隊列if(FD_ISSET(STDIN_FILENO,&readset)){bzero(buf,sizeof(buf));ssize_t sret = read(STDIN_FILENO,buf,sizeof(buf));if(sret == 0){send(sockfd,"serve closed\n",12,0);printf("I will close connect\n");break;}send(netfd,buf,strlen(buf),0);}  if(FD_ISSET(netfd,&readset)){//對面來消息了或者對面斷開了bzero(buf,sizeof(buf));ssize_t sret = recv(netfd,buf,sizeof(buf),0);if(sret == 0){printf("close connect\n");break;}printf("buf = %s\n",buf);}}close(sockfd);close(netfd);return 0;
}

client.c

#include <my_header.h>/* Usage:  */
int main(int argc, char *argv[]){                                  ARGS_CHECK(argc,3);int sockfd = socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));addr.sin_addr.s_addr = inet_addr(argv[1]);int ret = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));ERROR_CHECK(ret,-1,"connect");char buf[4096];fd_set readset;//讀集合while(1){FD_ZERO(&readset);//初始化FD_SET(STDIN_FILENO,&readset);//把stdin加入監聽FD_SET(sockfd,&readset);//把通信socket加入監聽select(sockfd+1,&readset,NULL,NULL,NULL);//內核輪詢,進程等待//有至少一個就緒//此時readset變成就緒隊列if(FD_ISSET(STDIN_FILENO,&readset)){bzero(buf,sizeof(buf));ssize_t sret = read(STDIN_FILENO,buf,sizeof(buf));if(sret == 0){send(sockfd,"client closed\n",13,0);printf("I will close connect\n");break;}send(sockfd,buf,strlen(buf),0);}if(FD_ISSET(sockfd,&readset)){bzero(buf,sizeof(buf));ssize_t sret = recv(sockfd,buf,sizeof(buf),0);if(sret ==0){printf("close connect\n");break;}printf("buf =  %s\n",buf);}}close(sockfd);return 0;
}

服務端與客戶端的大致代碼一樣,只需要做少量修改。可以復制一份在做更改即可。

vim文本更改技巧 :<n>,<m>s/<src>/<dec> 將第n行到第m行的src更改為dec

退出時,我們可以使用更加溫和的方式,用ctrl + D實現退出。

Linux上 ctrl + D 是讓recv就緒,并且返回值為0。

服務端主動退出

當退出后,短時間再次連接,主動退出的以訪會進入TIME_WAIT狀態,此狀態下,bind函數會報錯。socket庫這樣設計的原因是為了避免相同的四元組連接在短時間內斷開又重連。實際上該設計過分嚴格,僅針對于bind,而我們在實現時,客戶端并不使用bind,每次都是不同的ip與port,只有服務端使用了bind。

修改socket的屬性

setsockopt(sockfd,level,optname,*optval,optlen)

這是一個函數,可以修改很多種類屬性,不同種類的屬性是不一樣的

使用<man 7 socket>查詢手冊

在bind時無視TIME_WITE參數選項:(flag = 0/1,flag = 1允許重復使用本地址)?

optval 是參數的地址,optlen是參數的長度,用來支持多種類型。?

TIME_WITE狀態依舊存在,只是在bind時無視掉了。

setsockopt()使用在服務端socket之后,bind之前

    int flag = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));

?

斷線重連

應用層的斷線重連,A退出了,B不要立刻終止,而是等待A的重新連接。

accept的本質是一個讀全連接隊列的操作,accept的阻塞就緒可以由select監聽。select除了監聽數據,還可以用來監聽發有沒有新的連接到來。

幾種情況

A的狀態

  • 無連接狀態(單身狀態):等待B連接。此時,select監聽sockfd。
  • 通信狀態(戀愛狀態):等待stdin、B發送數據。此時select監聽stdin 和 netfd。

解除監聽集合和就緒集合之間的耦合

把兩個無關的東西扯在一起稱為耦合,現在需要解除耦合

    while(1){memcpy(&readset,&monitorset,sizeof(fd_set));select(nfds,&readset,NULL,NULL,NULL);}

若服務器A與B通信時,此時C與服務器A,請求建立連接,是可以成功的,服務器A將C放入全連接隊列但是不進行通信。這是因為建立連接是由操作系統內核完成的。等到客戶端B終止了,就可以從全連接隊列取出C進行通信。

當客戶端斷開連接

客戶端B斷開連接,netfd就緒,recv的返回值是0。

此時需要:

  • 重新監聽sockfd
  • 取消監聽netfd和stdin
  • 修改netfd == -1

代碼實現

select()第一個參數不知道寫什么可以寫1024。

serve.c

#include <my_header.h>/* Usage:  */
int main(int argc, char *argv[]){                                  ARGS_CHECK(argc,3);int sockfd = socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));addr.sin_addr.s_addr = inet_addr(argv[1]);int flag = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));int ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));ERROR_CHECK(ret,-1,"bind");listen(sockfd,50);char buf[4096];fd_set readyset;//就緒集合fd_set monitorset;//監聽集合FD_ZERO(&monitorset);FD_SET(sockfd,&monitorset);int netfd = -1;while(1){//用監聽集合覆蓋就緒集合memcpy(&readyset,&monitorset,sizeof(fd_set));select(1024,&readyset,NULL,NULL,NULL);//sockfd就緒,此時accept不會zuseif(FD_ISSET(sockfd,&readyset)){netfd = accept(sockfd,NULL,NULL);printf("connect\n");//移除對sockfd的監聽FD_CLR(sockfd,&monitorset);//增加對stdin以及netfd的監聽FD_SET(STDIN_FILENO,&monitorset);FD_SET(netfd,&monitorset);}//stdin狀態if(netfd != -1 && FD_ISSET(STDIN_FILENO,&readyset)){bzero(buf,sizeof(buf));read(STDIN_FILENO,buf,sizeof(buf));send(netfd,buf,strlen(buf),0);}//netfd就緒狀態if(netfd != -1 && FD_ISSET(netfd,&readyset)){bzero(buf,sizeof(buf));ssize_t sret = recv(netfd,buf,sizeof(buf),0);if(sret == 0){FD_CLR(netfd,&monitorset);FD_CLR(STDIN_FILENO,&monitorset);FD_SET(sockfd,&monitorset);close(netfd);netfd = -1;continue;}printf("%s\n",buf);}}close(sockfd);return 0;
}

client.c

#include <my_header.h>/* Usage:  */
int main(int argc, char *argv[]){                                  ARGS_CHECK(argc,3);int sockfd = socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));addr.sin_addr.s_addr = inet_addr(argv[1]);int ret = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));ERROR_CHECK(ret,-1,"connect");char buf[4096];fd_set readset;//讀集合while(1){FD_ZERO(&readset);//初始化FD_SET(STDIN_FILENO,&readset);//把stdin加入監聽FD_SET(sockfd,&readset);//把通信socket加入監聽select(sockfd+1,&readset,NULL,NULL,NULL);//內核輪詢,進程等待//有至少一個就緒//此時readset變成就緒隊列if(FD_ISSET(STDIN_FILENO,&readset)){bzero(buf,sizeof(buf));ssize_t sret = read(STDIN_FILENO,buf,sizeof(buf));if(sret == 0){send(sockfd,"client closed\n",13,0);printf("I will close connect\n");break;}send(sockfd,buf,strlen(buf),0);}if(FD_ISSET(sockfd,&readset)){bzero(buf,sizeof(buf));ssize_t sret = recv(sockfd,buf,sizeof(buf),0);if(sret ==0){printf("close connect\n");break;}printf("buf =  %s\n",buf);}}close(sockfd);return 0;
}

?

群聊

模型

客戶端實現同上的斷線重連

服務端的幾種監聽狀態:

  • 沒有客戶端,監聽sockfd
  • 每連入一個客戶端,監聽對應的netfd,原來的sockfd依然保持監聽
  • 客戶端斷開連接時,要取消監聽相關的netfd

由于netfd將會由很多個,我們需要設計數據結構去管理它。

設計一個數組管理netfd

    int netfd[1024];//用netfd數組中的元素來存放所有的netfdint curidx = 0;//下一次進入netfd數組的元素的下標

寫代碼時,先考慮數據結構怎么處理、存儲,接著就知道如何去進行增刪改查了。?

        if(FD_ISSET(sockfd,&readyset)){netfd[curidx] = accept(sockfd,NULL,NULL);printf("curidx = %d, netfd = %d\n",curidx,netfd[curidx]);FD_SET(netfd[curidx],&monitorset);++curidx;}

在監聽到sockfd連接時,使用下一個數組元素accept。

代碼實現

sever.c

#include <my_header.h>/* Usage:  */
int main(int argc, char *argv[]){                                  ARGS_CHECK(argc,3);int sockfd = socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));addr.sin_addr.s_addr = inet_addr(argv[1]);int flag = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));int ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));ERROR_CHECK(ret,-1,"bind");listen(sockfd,50);char buf[4096];fd_set readyset;//就緒集合fd_set monitorset;//監聽集合//一直監聽sockfdFD_ZERO(&monitorset);FD_SET(sockfd,&monitorset);int netfd[1024];//用netfd數組中的元素來存放所有的netfdint curidx = 0;//下一次進入netfd數組的元素的下標for(int i=0;i<1024;i++){netfd[i]=-1;}while(1){memcpy(&readyset,&monitorset,sizeof(fd_set));select(1024,&readyset,NULL,NULL,NULL);if(FD_ISSET(sockfd,&readyset)){netfd[curidx] = accept(sockfd,NULL,NULL);printf("curidx = %d, netfd = %d\n",curidx,netfd[curidx]);FD_SET(netfd[curidx],&monitorset);++curidx;}//已連接的客戶端發消息for(int i=0;i<curidx;i++){//i號客戶端來消息if(netfd[i]!=-1&&FD_ISSET(netfd[i],&readyset)){bzero(buf,sizeof(buf));ssize_t sret = recv(netfd[i],buf,sizeof(buf),0);if(sret == 0){printf("client disconnected i = %d, netfd = %d\n",i,netfd[i]);FD_CLR(netfd[i],&monitorset);close(netfd[i]);netfd[i] = -1;continue;}for(int j =0 ;j<curidx;j++){if(j!=i&&netfd[i] != -1){send(netfd[j],buf,strlen(buf),0);}}}}}close(sockfd);return 0;
}

?當然也可以使用刪除,將退出netfd元素后所有元素前移。

產生錯誤處理方式

  1. 使用gdb復現bug? ?<gdb? --args? ?./xxxxx>
  2. 查看錯誤提示
  3. bt 從上往下找到自己寫的代碼,分析修改

擴展

服務端超時踢人

如果客戶端十秒沒有通信,服務器將他踢掉。

timeout是一個傳入傳出參數,如果是固定的超時時間,把設置timeout放在循環里面。

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

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

相關文章

influxdb時序數據庫

以下概念及操作均來自influxdb2 官方文檔 InfluxDB2 is the platform purpose-built to collect, store, process and visualize time series data. Time series data is a sequence of data points indexed in time order. Data points typically consist of successive meas…

洛谷 P3372 【模板】線段樹 1

【題目鏈接】 洛谷 P3372 【模板】線段樹 1 【題目考點】 1. 線段樹 2. 樹狀數組 【解題思路】 本題要求維護區間和&#xff0c;實現區間修改、區間查詢。 可以使用樹狀數組或線段樹完成該問題&#xff0c;本文僅介紹使用線段樹的解法。 解法1&#xff1a;線段樹 線段樹…

軟件更新 | TSMaster 202504 版本已上線!三大功能讓車載測試更智能

車載測試的智能化時代正在加速到來&#xff01;TSMaster 202504 版本正式發布&#xff0c;本次更新聚焦以太網通信與數據高效處理&#xff0c;帶來三大核心功能升級—以太網報文信息過濾、XCP on Ethernet支持、按時間范圍離線回放&#xff0c;助力工程師更精準、更靈活地完成測…

java-單列集合list與set。

集合定位&#xff1a;存儲數據的容器 與數組的區別&#xff1a; 數組只能存儲同種數據類型數據&#xff0c;集合可以存儲不同類型的數據。 數組的長度一旦創建長度不可變&#xff0c;集合的長度是可變的 數組的操作單一&#xff0c;集合的操作比較豐富&#xff08;增刪改查&…

ai之pdf解析工具 PPStructure 還是PaddleOCR

目錄 重點是四 先用 PPStructure 版面分析,分成不同的塊兒,再選用 PaddleOCR、或PPStructure基礎路徑OCR模型配置OCR模型配置GPU配置硬件配置性能配置一、框架選型對比分析1. **PaddleOCR核心能力**2. **PP-Structure核心能力**3. **選型結論**二、錯誤根因分析與修復方案1. …

Android計算機網絡學習總結

TCP vs UDP 核心區別?? ?題目?&#xff1a;TCP為什么稱為可靠傳輸協議&#xff1f;UDP在哪些場景下比TCP更具優勢&#xff1f; ?得分要點?&#xff1a; ?可靠性機制? 三握四揮建立可靠連接確認應答&#xff08;ACK&#xff09; 超時重傳滑動窗口流量控制擁塞控制&…

深入解析Java組合模式:構建靈活樹形結構的藝術

引言&#xff1a;當對象需要樹形組織時 在日常開發中&#xff0c;我們經常需要處理具有層次結構的對象集合。比如&#xff1a; 文件系統中的文件夾與文件GUI界面中的容器與控件企業組織架構中的部門與員工 這類場景中的對象呈現明顯的整體-部分層次結構&#xff0c;如何優雅…

mobaxterm通過ssh登錄docker無圖形界面

1. 流程 下面是使用Mobaxterm通過SSH登錄Docker無圖形界面的步驟&#xff1a; 步驟 操作 1 在本地安裝Mobaxterm 2 配置Mobaxterm連接SSH 3 啟動Docker容器 4 在Mobaxterm中通過SSH連接到Docker容器 2. 操作步驟 步驟1&#xff1a;安裝Mobaxterm 首先&#xff…

【趙渝強老師】HBase的體系架構

HBase是大表&#xff08;BigTable&#xff09;思想的一個具體實現。它是一個列式存儲的NoSQL數據庫&#xff0c;適合執行數據的分析和處理。簡單來說&#xff0c;就是適合執行查詢操作。從體系架構的角度看&#xff0c;HBase是一種主從架構&#xff0c;包含&#xff1a;HBase H…

linux 新增驅動宏config.in配置

?1. 添加配置宏步驟? ?1.1 修改 Kconfig&#xff08;推薦方式&#xff09;? ?定位 Kconfig 文件? 內核各子目錄&#xff08;如 drivers/char/&#xff09;通常包含 Kconfig 文件&#xff0c;用于定義模塊配置選項7。?添加宏定義? 示例&#xff1a;在 drivers/char/Kc…

關于git的使用

下載git 可以去git的官網下載https://git-scm.com/downloads 也可以去找第三方的資源下載&#xff0c;下載后是一個exe應用程序&#xff0c;直接點開一直下一步就可以安裝了 右鍵任意位置顯示這兩個就代表成功&#xff0c;第一個是git官方的圖形化界面&#xff0c;第二個是用…

WPF【11_8】WPF實戰-重構與美化(UI 與視圖模型的聯動,實現INotifyPropertyChanged)

11-13 【重構】INotifyPropertyChanged 與 ObservableCollection 現在我們來完成新建客戶的功能。 當用戶點擊“客戶添加”按鈕以后系統會清空當前所選定的客戶&#xff0c;客戶的詳細信息以及客戶的預約記錄會從 UI 中被清除。然后我們就可以在輸入框中輸入新的客戶信息了&am…

ArkUI:鴻蒙應用響應式與組件化開發指南(一)

文章目錄 引言1.ArkUI核心能力概覽1.1狀態驅動視圖1.2組件化&#xff1a;構建可復用UI 2.狀態管理&#xff1a;從單一組件到全局共享2.1 狀態裝飾器2.2 狀態傳遞模式對比 引言 鴻蒙生態正催生應用開發的新范式。作為面向全場景的分布式操作系統&#xff0c;鴻蒙的北向應用開發…

List優雅分組

一、前言 最近小永哥發現&#xff0c;在開發過程中&#xff0c;經常會遇到需要對list進行分組&#xff0c;就是假如有一個RecordTest對象集合&#xff0c;RecordTest對象都有一個type的屬性&#xff0c;需要將這個集合按type屬性進行分組&#xff0c;轉換為一個以type為key&…

AI與.NET技術實操系列(八):使用Catalyst進行自然語言處理

引言 自然語言處理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能領域中最具活力和潛力的分支之一。從智能客服到機器翻譯&#xff0c;再到語音識別&#xff0c;NLP技術正以其強大的功能改變著我們的生活方式和工作模式。 Catalyst的推出極大降低了NLP…

MySQL 8.0 OCP 1Z0-908 題目解析(13)

題目49 Choose the best answer. t is a non - empty InnoDB table. Examine these statements, which are executed in one session: BEGIN; SELECT * FROM t FOR UPDATE;Which is true? ○ A) mysqlcheck --analyze --all - databases will execute normally on all ta…

Docker 一鍵部署倒計時頁面:Easy Countdown全設備通用

Easy Countdown 介紹 Easy countdown是一個易于設置的倒計時頁面。可以設置為倒計時或計時器。可用于個人生活、工作管理、教育、活動策劃等多個領域。 &#x1f6a2; 項目地址 Github&#xff1a;https://github.com/Yooooomi/easy-countdown &#x1f680;Easy Countdown …

Python訓練打卡Day35

模型可視化與推理 知識點回顧&#xff1a; 三種不同的模型可視化方法&#xff1a;推薦torchinfo打印summary權重分布可視化進度條功能&#xff1a;手動和自動寫法&#xff0c;讓打印結果更加美觀推理的寫法&#xff1a;評估模式 模型結構可視化 理解一個深度學習網絡最重要的2點…

四、生活常識

一、效應定律 效應 1、沉沒成本效應 投入的越多&#xff0c;退出的難度就越大&#xff0c;因為不甘心自己之前的所有付出都付之東流。 2、破窗效應 干凈的環境下&#xff0c;沒有人會第一個丟垃圾&#xff0c;但是當環境變得糟糕&#xff0c;人們就開始無所妒忌的丟垃圾。…

機器學習圣經PRML作者Bishop20年后新作中文版出版!

機器學習圣經PRML作者Bishop20年后新書《深度學習&#xff1a;基礎與概念》出版。作者克里斯托弗M. 畢曉普&#xff08;Christopher M. Bishop&#xff09;微軟公司技術研究員、微軟研究 院 科學智 能 中 心&#xff08;Microsoft Research AI4Science&#xff09;負責人。劍橋…