C++網絡編程快速入門(二):Linux下使用select演示簡單服務端程序

目錄

  • select參數解釋
  • select使用規范
  • select使用缺點
  • 基本流程
  • 實例代碼
  • 通信效果演示
  • 往期文章

select參數解釋

extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout);

__nfds:一般設置為所有需要使用select函數檢測時間的fd中的最大值+1
__readfds:需要監聽可讀事件的fd集合
__writefds:需要監聽可寫事件的fd集合
__exceptfds:需要監聽可寫事件的fd集合
__timeout:超時時間,在這個設定的時間內檢測這些fd事件,超過這個超時時間,select將立即返回
fd_set這個結構體信息如下:

/* fd_set for select and pselect.  */
typedef struct{/* XPG4.2 requires this member name.  Otherwise avoid the namefrom the global namespace.  */
#ifdef __USE_XOPEN__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif} fd_set;

可以簡化看做:

typedef struct {long int __fds_bits[16];	
} fd_set;

long int 占8字節,也就是8 * 8 = 64個bit,所以__fds_bits數組總共就占64 * 16 = 1024個fd狀態。每個bit位0表示無事件,1表示有事件。

select使用規范

1、select在調用前后可能會修改readfdswritefdsexceptfds中的內容,所以如果在下次調用時復用這些fd_set,則要在下次調用前使用FD_ZEROfd_set清空,然后調用FD_SET將要檢測的fd添加到fd_set中
2、linux系統下select函數也會修改timeval結構體的值,所以想要復用也必須給其重新設置值
3、select函數的timeval結構體中的tv_sectv_usec如果都被設置為0,代表著檢測事件的總時間被設置為0,行為就變成了select檢測相關集合中的fd,如果沒有需要的事件,則立即返回
4、如果select函數的timeval參數設置為NULL,則select會一直阻塞下去,直到我們需要的事件被觸發
5、Linux下,select函數第一個參數必須設置為需要檢測事件fd中最大值+1,所以每次產生一個新fd都需要和maxfd作比較

select使用缺點

1、select函數需要將fd集合從用戶態拷貝到內核態,在fd較多時開銷較大。并且每次檢測時也是在內核中遍歷這個fd_set。
2、單個進程能夠監視的文件描述符數量上存在最大限制,linux上我們算過了,只有1024個
3、select函數每次調用之前都要對傳入的參數重新設定,比較麻煩
4、在linux上select函數的實現原理是底層的poll函數,所以select和poll本質上沒有區別

基本流程

在這里插入圖片描述

實例代碼

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <sys/time.h>
#include <vector>
#include <errno.h>
#include <stdio.h>
#include <string.h>
using namespace std;
int main() {// 創建一個監聽socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1) {cout << "create listen error" << endl;return -1;}// 初始化服務器地址struct sockaddr_in bindaddr;bindaddr.sin_family = AF_INET;bindaddr.sin_addr.s_add當斷開各個客戶端時,服務端的select函數對各個客戶端fd進行檢測時,仍然會觸發可讀事件r = htonl(INADDR_ANY);bindaddr.sin_port = htons(3000);if (bind(listenfd, (struct sockaddr *)& bindaddr, sizeof(bindaddr)) == -1) {cout << "bind listen socket error" << endl;close(listenfd);return -1;}// 啟動監聽if (listen(listenfd,SOMAXCONN) == -1) {cout << "listen error" << endl;close(listenfd);return -1;}// 存儲客戶端socket的數組vector<int> clientfds;int maxfd;while (true) {fd_set readset;// 對標志位清零FD_ZERO(&readset);// 將監聽的socket加入到待檢測的可讀事件中// 第listenfd位被置為1FD_SET(listenfd, &readset);maxfd = listenfd;// 將客戶端socket加入到待檢測的可讀事件中for (int i = 0; i < clientfds.size(); i++) {if (clientfds[i] != -1) {FD_SET(clientfds[i], &readset);maxfd = max(maxfd, clientfds[i]);}}// 設置超時時間為1stimeval time;time.tv_sec = 1;time.tv_usec = 0;// 暫且只檢測可讀事件,不檢測可寫和異常事件int ret = select(maxfd + 1, &readset, NULL, NULL, &time);if (ret == -1) {// 出錯if (errno != EINTR)break;} else if (ret == 0) {// select函數超時continue;} else {// 檢測到某個socket有事件// 是否是監聽socket的可讀事件if (FD_ISSET(listenfd, &readset)) {// 如果是監聽socket的可讀事件,表示現在有新的連接到來struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);// 接受客戶端連接int clientfd = accept(listenfd, (struct sockaddr *)& clientaddr, &clientaddrlen);if (clientfd == -1) {cout << "client socket error" << endl;break;} else {cout << "accept a client connection , fd:" << clientfd << endl;clientfds.push_back(clientfd);}} else {// 從client socket上接受數據// 假設對端發送過來的數據長度不超過63個字符char recvbuf[64];for (int i = 0; i < clientfds.size(); i++) {if (clientfds[i] != -1 && FD_ISSET(clientfds[i], &readset)) {memset(recvbuf, 0, sizeof(recvbuf));// 接受數據int length = recv(clientfds[i], recvbuf, 64, 0);if (length <= 0) {cout << "recv data error, clientfd:" << clientfds[i] << endl;close(clientfds[i]);// 不直接刪除該元素而是將位置元素標記clientfds[i] = -1;continue;} else {cout << "clientfd:" << clientfds[i] << "recv data: " << recvbuf << endl;}}}}}}// 處理之后,關閉所有客戶端for (int i = 0; i < clientfds.size(); i++) {if (clientfds[i] != -1)close(clientfds[i]);}// 關閉監聽close(listenfd);return 0;
}

通信效果演示

這里不需要寫客戶端程序,直接用nc命令模擬,指定一下服務端的ip地址和端口號就可以通信了,127.0.0.1就是系統的回環地址,直接是用于本機內的socket的通信。這里我們開了兩個客戶端,分別發送hello和hello world。
由于nc命令發送的數據是按換行符區分的,所以數據包最后一個都是以\n結束。
當斷開各個客戶端時,服務端的select函數對各個客戶端fd進行檢測時,仍然會觸發可讀事件
不過此時對這些fd進行調用recv函數的話會返回0,表示對端關閉了連接。然后服務端這邊將fd進行置-1,然后關閉連接即可。
在這里插入圖片描述

往期文章

C++網絡編程快速入門(一):TCP網絡通信基本流程以及基礎函數使用

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

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

相關文章

Android轉載一:Android文件命名規范

REF&#xff1a;http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1&#xff0e;contentview命名&#xff1a;activity_功能模塊.xml 例如&#xff1a;activity_main.xml、activity_more.xml 2&#xff0e;Dialog命名&#xff1a;dialog_描述.xml …

[轉]XBRL應用軟件分類

1) 分類標準編輯軟件(Taxonomy editor)&#xff1a; 分類標準是XBRL技術的應用基礎&#xff0c;每一個采用XBRL技術的國家都必須先按各國的GAAP制訂XBRL分類標準&#xff0c;上市公司才能據以編制實例文件。由于一套XBRL 2.0或2.1版分類標準必須包含至少一份XML Schema文…

C++網絡編程快速入門(三):阻塞與非阻塞式調用網絡通信函數

目錄阻塞與非阻塞定義send與recvconnect一些問題為什么要將監聽socket設置為非阻塞阻塞與非阻塞定義 阻塞模式指的是當前某個函數執行效果未達預期&#xff0c;該函數會阻塞當前的執行線程&#xff0c;程序執行流在超時時間到達或者執行成功后恢復原有流程。非阻塞模式相反&am…

css3實現頭像旋轉360度

css樣式: .div a img{ width: 88px; height: 88px; border-radius: 88px; transition: all 1.2s ease-out 0s;}.div a img:hover{ -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); -o-transform:rotate(360deg); -ms-transform:rotate(360deg); transform:r…

POJ 2488 深搜

POJ 2488 深搜 要求字典序的順序。 1 #include <iostream>2 #include <stdio.h>3 #include <string.h>4 using namespace std;5 int n,m,cnt;6 bool success;7 bool sign[30][30];8 int step[30][2];9 int dir[8][2]{ 10 -2,-1,-2,1, 11 …

socket 端口和地址復用

https://blog.csdn.net/weibo1230123/article/details/79978745 https://blog.csdn.net/weixin_42157432/article/details/115560824 在linux socket網絡編程中&#xff0c;大規模并發TCP或UDP連接時&#xff0c;經常會用到端口復用&#xff1a; int opt 1; if (setsockopt…

MyEclipse老是彈出problem occurred窗口

有的時候是因為jsp頁面中的java腳本有誤&#xff0c;比如說<% String name"";>就會出現錯誤&#xff0c;因為結束標簽少了一個百分號&#xff05;。轉載于:https://www.cnblogs.com/passer1991/archive/2013/03/15/2961624.html

C++網絡編程快速入門(四):EPOLL模型使用

目錄基本使用方法step1:創建epollfdstep2:將fd綁定到epollfdstep3:調用epoll_wait檢測事件epoll_wait與poll、select區別所在水平觸發與邊緣觸發基本使用方法 step1:創建epollfd 創建一個epollfd&#xff0c;若epoll_create調用成功&#xff0c;則返回一個非負值的epollfd&am…

Mysql中代替like模糊查詢的一種方法

使用Mysql的函數instr,可代替傳統的like方式查詢,并且速度更快。 instr函數&#xff0c;第一個參數是字段&#xff0c;第二個參數是要查詢的串&#xff0c;返回串的位置&#xff0c;第一個是1&#xff0c;如果沒找到就是0. 例如&#xff1a; select username from prefix_user …

兩種大小端判斷的方式

網絡通信是按照字節流進行數據交換的&#xff0c;主機根據不同的CPU型號可能是大段存儲&#xff0c;也可能是小端存儲。而網絡字節序在TCP/IP協議中已經規定好了&#xff0c;采用大端的排序方式。 所以網絡通信中一般將需要傳輸的整數型值轉換成網絡字節序。 從本機字節序轉換成…

把數據庫復制成腳本(包含遠程以及數據庫數據)

1.啟動VS 2.服務器資源管理器 3.連接需要的數據庫 4.右鍵數據庫 選擇publist to provider.... 5.剩下的 選擇數據庫 選擇存放地址 下一步 這方法應該是用在把08的數據還原到05上面 明天用這個方法去盜取哈公司的數據庫 看行不行轉載于:https://www.cnblogs.com/Rock-Lee/a…

代理模式用來初始化的延遲下載

package 設計模式; //代理模式實現延遲加載來減小啟動時間 //數據庫查詢接口 interface IDBQery{ public String request(); }class DBQuery implements IDBQery {//創建一個DBQery非常耗時的&#xff0c;這里面我可以在需要DBQuery的時候在創建public DBQuery(){try {Thread.s…

Linux網絡故障排查命令(ifconfig、ping、telnet、netstat、lsof、nc、curl、tcpdump)

目錄ifconfig-s&#xff0c;顯示網卡信息的精簡列表-a、up、down將IP地址綁定到某個網卡&#xff0c;以及解綁操作pingtelnetnetstatlsofnc模擬一個服務器程序和客戶端程序進行通信發送文件curltcpdump參數連接一個正常的監聽端口ifconfig 該命令用來查看當前系統的網卡和IP地…

My Oracle Support Metalink站點最近將放棄flash界面轉而使用ADF HTML

根據oracle官方博客的報道《The New My Oracle Support User Interface (HTML-based) 》&#xff0c; MY ORACLE SUPPORT開發team會在最近將support.oracle.com站點從原來的flash界面遷移到基于ADF HTML的用戶界面上。 實際上在2012年的 January 27&#xff0c; MOS開發team就…

BF算法

BF(Brute Force)算法是普通的模式匹配算法&#xff0c;BF算法的思想就是將目標串S的第一個字符與模式串T的第一個字符進行匹配&#xff0c;若相等&#xff0c;則繼續比較S的第二個字符和 T的第二個字符&#xff1b;若不相等&#xff0c;則比較S的第二個字符和T的第一個字符&…

心跳檢測以及應用層心跳包機制設計

博主聯系方式&#xff1a; QQ:1540984562 微信&#xff1a;wxid_nz49532kbh9u22 QQ交流群&#xff1a;750313950&#xff08;嵌入式方向&#xff09; QQ交流群&#xff1a;856398158&#xff08;后端方向&#xff09; 目錄心跳檢測應用場景死連接情況保活傳遞有效業務數據心跳包…

【APUE】孤兒進程與僵死進程

基本概念&#xff1a; 在unix/linux中&#xff0c;正常情況下&#xff0c;子進程是通過父進程創建的&#xff0c;子進程在創建新的進程。子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程 到底什么時候結束。 當一個 進程完成它的工作終止之后&#xff0c…

一個DBA的工作寫照

一個DBA的工作寫照&#xff0c; 一個DBA的內心 Know the DBA Mind! DBA也是 IT民工啊&#xff0c; 民工何苦為難民工&#xff01; 轉載于:https://www.cnblogs.com/macleanoracle/archive/2013/03/19/2968227.html

使用mutex和條件變量實現信號量

c提供了互斥量&#xff1a;mutex和條件變量&#xff1a;condition_variable&#xff0c;但是并沒有信號量&#xff1a;semaphore。而linux和windows系統庫會提供的。下面簡單介紹一下信號量的特性&#xff0c;然后給出一個簡單的demo&#xff0c;使用mutex condition_variable…

2014-07-28 使用Axure RP進行手機端BBS的原型設計

今天是在吾索實習的第14天。因本公司的微信公眾號需要有一個對外的技術交流平臺&#xff0c;所以我們小組打算設計一個手機端的BBS以滿足其要求。首先&#xff0c;我們需要做的是進行數據庫設計與原型設計&#xff0c;然后提交給經理驗收&#xff0c;看看是否合理&#xff0c;是…