傳統(tǒng)藝能??
小編是雙非本科大二菜鳥不贅述,歡迎米娜桑來指點江山哦
1319365055
????非科班轉(zhuǎn)碼社區(qū)誠邀您入駐????
小伙伴們,滿懷希望,所向披靡,打碼一路向北
一個人的單打獨斗不如一群人的砥礪前行
這是和夢想合伙人組建的社區(qū),誠邀各位有志之士的加入!!
社區(qū)用戶好文均加精(“標兵”文章字數(shù)2000+加精,“達人”文章字數(shù)1500+加精)
直達: 社區(qū)鏈接點我
select??
回顧一下上一篇,select 是系統(tǒng)提供的一個多路轉(zhuǎn)接接口
:
select 系統(tǒng)調(diào)用可以讓我們的程序同時監(jiān)視多個文件描述符的事件是否就緒。我們知道select 的核心工作就是等,當監(jiān)視的多個文件描述符中有一個或多個事件就緒時,select 才會成功返回并告知調(diào)用者
函數(shù)原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds
為需要監(jiān)視的文件描述符中最大的文件描述符值 +1;readfds
即輸入輸出型參數(shù),調(diào)用時用戶告知內(nèi)核需要監(jiān)視哪些文件描述符的讀事件是否就緒,返回時內(nèi)核告知用戶哪些事件已經(jīng)就緒;同理 writefds
為寫時間對應(yīng)的參數(shù);exceptfds
為用戶告知內(nèi)核需要監(jiān)視哪些文件描述符的異常事件是否就緒,返回時內(nèi)核告知用戶哪些事件已經(jīng)就緒;timeout
為輸入輸出型參數(shù),由用戶設(shè)置 select 等待時間,返回時表示 timeout 的剩余時間。
參數(shù) timeout 的取值:
- NULL/nullptr:select 調(diào)用后進行阻塞等待,直到被監(jiān)視的某個事件就緒。
- 0:selec 調(diào)用后進行非阻塞等待,無論被監(jiān)視的事件是否就緒,select 檢測后都會立即返回。
- 特定的時間值:select 在指定的時間內(nèi)進行阻塞等待,如果被監(jiān)視的文件描述符上一直沒有事件就緒,過時進行超時返回。
函數(shù)調(diào)用成功,則返回有事件就緒的文件描述符個數(shù);如果 timeout 時間耗盡,則返回 0。調(diào)用失敗則返回 -1,同時錯誤碼會被設(shè)置。
select 調(diào)用失敗時,錯誤碼可能被設(shè)置為
:
EBADF:文件描述符為無效的或該文件已關(guān)閉
EINTR:此調(diào)用被信號所中斷
EINVAL:nfds 參數(shù)為負值
ENOMEM:核心內(nèi)存不足
fd_set 結(jié)構(gòu)??
fd_set 結(jié)構(gòu)與 sigset_t
結(jié)構(gòu)類似,fd_set 本質(zhì)也是一個位圖,用位圖中對應(yīng)的位來表示要監(jiān)視的文件描述符:
調(diào)用 select 之前就需要用 fd_set 定義出對應(yīng)的文件描述符集,然后將需要監(jiān)視的文件描述符添加到文件描述符集當中,這個添加動作本質(zhì)是進行位操作,但這個位操作不需要用戶自己進行,系統(tǒng)提供了一組專門的接口,用于 fd_set 位圖進行各種操作:
void FD_CLR(int fd, fd_set *set); //清除描述詞組set中相關(guān)fd的位
int FD_ISSET(int fd, fd_set *set); //測試描述詞組set中相關(guān)fd的位是否為真
void FD_SET(int fd, fd_set *set); //設(shè)置描述詞組set中相關(guān)fd的位
void FD_ZERO(fd_set *set); //清除描述詞組set的全部位
timeval 結(jié)構(gòu)??
select 函數(shù)的最后一個參數(shù)是 timeout,這是一個指向 timeval 結(jié)構(gòu)的指針,timeval 用于描述一段時間長度,該結(jié)構(gòu)當中包含兩個成員,其中 tv_sec
表示的是秒,tv_usec
表示的是微秒:
socket 就緒條件??
讀條件??
- 接收緩沖區(qū)中的字節(jié)數(shù),≥ 水位標記
SO_RCVLOWAT
,此時可以無阻塞的讀取該文件描述符,并且返回值大于0。 - socket TCP通信中,對端關(guān)閉連接,此時對該 socket 讀,則返回 0。
- 監(jiān)聽的socket上有新的連接請求。
- socket 上有未處理的錯誤。
寫就緒??
- 發(fā)送緩沖區(qū)中的可用字節(jié)數(shù),大于等于低水位標記
SO_SNDLOWAT
,此時可以無阻塞的寫,并且返回值大于0。 - socket 的寫操作被關(guān)閉(close或者shutdown),對一個關(guān)閉寫操作的 socket 進行寫操作,會觸發(fā)
SIGPIPE
信號。 - socket 使用非阻塞connect連接成功或失敗之后。
- socket 上有未讀取的錯誤。
異常就緒??
socket 上收到帶外數(shù)據(jù)(帶外數(shù)據(jù)和 TCP 緊急模式相關(guān),TCP報頭當中 URG 標志位和 16位 緊急指針搭配使用,就能夠發(fā)送/接收帶外數(shù)據(jù))
select 工作流程??
如果想實現(xiàn)一個 select 服務(wù)器,那么我們可以大概知道他的工作流程:
- 先初始化服務(wù)器,創(chuàng)建套接字,綁定和監(jiān)聽套接字
- 定義一個 fd_array 數(shù)組來保存監(jiān)聽套接字以及已經(jīng)和客戶端建立鏈接的套接字,監(jiān)聽套接字一開始就要加入數(shù)組
- 服務(wù)器循環(huán)進行 select 監(jiān)測讀事件是否就緒,如果就緒就可以執(zhí)行對應(yīng)操作
- 在 select 之前還應(yīng)該創(chuàng)建一個 readfds 文件描述符集,將 fd_array 的文件描述符放入 readfds ,select 就會對這些文件描述符對應(yīng)的讀事件進行監(jiān)視,在最后就會看到 在 readfds 里面會有一條條的記錄
- select 檢測到讀事件就緒就會將其對應(yīng)的文件描述符放進 readfds ,我們就能知道哪些事件已經(jīng)就緒
- 如果是讀事件的監(jiān)聽套接字就緒了,就用 accept 從底層全連接隊列獲取已建立的連接,并將對應(yīng)連接的套接字添加到 fd_array 里
- 如果是讀事件和客戶端建立連接的套接字,就調(diào)用 read 將讀取到的信息打印出來
- 如果讀事件是和客戶端建立連接的套接字就緒,也可能是因為客戶端關(guān)閉了連接,此時服務(wù)器應(yīng)該調(diào)用 close 關(guān)閉套接字,并將該套接字從 fd_array 數(shù)組中清除,因為下一次不需要再監(jiān)視該讀事件了。
因為傳入 select 的 readfds、writefds 和 exceptfds都是輸入輸出型參數(shù),當 select 返回時這些參數(shù)中的值已經(jīng)修改了,因此每次調(diào)用 select 都需要重新設(shè)置,timeout 也是同理。
進行重新設(shè)置,就需要定義一個 fd_array 數(shù)組保存與客戶端已經(jīng)建立的若干連接和監(jiān)聽套接字,實際 fd_array 數(shù)組當中的文件描述符就是需要讓 select 監(jiān)視讀事件的文件描述符。select 服務(wù)器只是讀取客戶端發(fā)來的數(shù)據(jù),因此只需要讓 select 幫我們監(jiān)視特定文件描述符的讀事件,如果要讓 select 同時幫我們監(jiān)視讀事件和寫事件,則需要分別定義 readfds 和 writefds,并定義兩個數(shù)組分別保存需要被監(jiān)視的文件描述符,便于每次調(diào)用 select 前對readfds 和 writefds 進行重新設(shè)置。
服務(wù)器剛開始運行時,fd_array 數(shù)組當中只有監(jiān)聽套接字,因此 select 第一次只需要告知監(jiān)聽套接字的讀事件是否就緒,但每次調(diào)用accept 獲取到新連接后,都會將對應(yīng)的套接字添加到 fd_array 當中,后續(xù) select 就需要監(jiān)視監(jiān)聽套接字和連接套接字的讀事件是否就緒。
由于調(diào)用 select 時還需要傳入被監(jiān)視的文件描述符中最大文件描述符值+1,因此每次在遍歷 fd_array 對 readfds 進行重新設(shè)置時,還需要記錄最大文件描述符值。
select 服務(wù)器實現(xiàn)??
socket 類??
首先編寫一個 Socket 類,對套接字相關(guān)的接口進行一定程度的封裝,為了能夠直接調(diào)用 Socket 類中封裝的函數(shù),我們將這些函數(shù)定義成靜態(tài)成員函數(shù):
#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <cstring>
#include <cstdlib>
class Socket{
public:
//創(chuàng)建套接字
static int SocketCreate()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0){
std::cerr << "socket error" << std::endl;
exit(2);
}
//設(shè)置端口復(fù)用
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
return sock;
}
//綁定
static void SocketBind(int sock, int port)
{
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(local);
if (bind(sock, (struct sockaddr*)&local, len) < 0){
std::cerr << "bind error" << std::endl;
exit(3);
}
}
//監(jiān)聽
static void SocketListen(int sock, int backlog)
{
if (listen(sock, backlog) < 0){
std::cerr << "listen error" << std::endl;
exit(4);
}
}
};
SelectServer 類??
因為當前使用的是云服務(wù)器,所以編寫的 select 服務(wù)器在綁定時不需要顯示綁定IP地址,直接將IP地址設(shè)置為 INADDR_ANY
就行了,所以類當中只包含監(jiān)聽套接字和端口號兩個成員變量。
在構(gòu)造 SelectServer 對象時,需要指明 select 服務(wù)器的端口號,當然也可以在初始化 select 服務(wù)器的時候指明。初始化 select 服務(wù)器的時候調(diào)用 Socket 類當中的函數(shù),依次進行套接字的創(chuàng)建、綁定和監(jiān)聽即可。在析構(gòu)函數(shù)中可以選擇調(diào)用 close 函數(shù)將監(jiān)聽套接字進行關(guān)閉,但實際也可以不用,因為服務(wù)器運行后一般是不退出的:
#pragma once
#include "socket.hpp"
#include <sys/select.h>
#define BACK_LOG 5
class SelectServer{
private:
int _listen_sock; //監(jiān)聽套接字
int _port; //端口號
public:
SelectServer(int port)
: _port(port)
{}
void InitSelectServer()
{
_listen_sock = Socket::SocketCreate();
Socket::SocketBind(_listen_sock, _port);
Socket::SocketListen(_listen_sock, BACK_LOG);
}
~SelectServer()
{
if (_listen_sock >= 0){
close(_listen_sock);
}
}
};
運行服務(wù)器??
服務(wù)器初始化完畢就應(yīng)該周期性的執(zhí)行某種動作了,而服務(wù)器要做的就是不斷調(diào)用 select ,當事件就緒時執(zhí)行對應(yīng)某種動作即可。
首先,在 select 服務(wù)器開始死循環(huán)調(diào)用 select 函數(shù),需要先定義一個 fd_array
數(shù)組,先把所有位置初始化為無效,并將監(jiān)聽套接字添加到該數(shù)組當中,fd_array 數(shù)組中保存的就是需要被select監(jiān)視讀事件是否就緒的文件描述符。
此后,服務(wù)器就不斷調(diào)用 select 監(jiān)視讀事件是否就緒,每次調(diào)用 select 之前都需要重新設(shè)置 readfds
,具體設(shè)置過程就是遍歷 fd_array 數(shù)組的文件描述符添加到 readfds 中,并同時記錄最大的文件描述符值 maxfd
,因為后續(xù)調(diào)用 select 時需要將maxfd+1
作為第一個參數(shù)傳入。
當 select 函后,如果返回值為0,則說明 timeout 時間耗盡,直接進行下一次 select 調(diào)用即可;如果返回值為 -1,則說明 select 調(diào)用失敗,此時讓服務(wù)器準備進行下一次 select 調(diào)用,但實際應(yīng)該進一步判斷錯誤碼,根據(jù)錯誤碼來判斷是否應(yīng)該繼續(xù)調(diào)用 select 。如果 select 返回值大于 0,則說明 select 函數(shù)調(diào)用成功,此時已經(jīng)有文件描述符的讀事件就緒,接下來就應(yīng)該對就緒事件進行處理:
#pragma once
#include "socket.hpp"
#include <sys/select.h>
#define BACK_LOG 5
#define NUM 1024
#define DFL_FD - 1
class SelectServer{
private:
int _listen_sock; //監(jiān)聽套接字
int _port; //端口號
public:
void Run()
{
fd_set readfds; //讀文件描述符集
int fd_array[NUM]; //被監(jiān)視讀事件是否就緒的文件描述符
ClearFdArray(fd_array, NUM, DFL_FD); //將數(shù)組中的所有位置設(shè)置為無效
fd_array[0] = _listen_sock; //將監(jiān)聽套接字添加到fd_array數(shù)組中的第0個位置
for (;;){
FD_ZERO(&readfds); //清空readfds
//將fd_array數(shù)組當中的文件描述符添加到readfds當中,并記錄最大的文件描述符
int maxfd = DFL_FD;
for (int i = 0; i < NUM; i++){
if (fd_array[i] == DFL_FD) //跳過無效的位置
continue;
FD_SET(fd_array[i], &readfds); //將有效位置的文件描述符添加到readfds當中
if (fd_array[i] > maxfd) //更新最大文件描述符
maxfd = fd_array[i];
}
switch (select(maxfd + 1, &readfds, nullptr, nullptr, nullptr)){
case 0:
std::cout<<"timeout..."<<std::endl;
break;
case -1:
std::cerr << "select error" << std::endl;
break;
default:
//正常的事件處理
std::cout<<"有事件發(fā)生..."<<std::endl;
//HandlerEvent(readfds, fd_array, NUM);
break;
}//end switch
}//end for
}
private:
void ClearFdArray(int fd_array[], int num, int default_fd)
{
for (int i = 0; i < num; i++){
fd_array[i] = default_fd;
}
}
};
事件處理??
當 select 檢測到文件描述符的讀事件就緒并成功返回后,就應(yīng)該對事件進行處理了,這里編寫一個 HandlerEvent 函數(shù)
在處理時需要遍歷 fd_array 數(shù)組的文件描述符,依次判斷各個文件描述符對應(yīng)的讀事件是否就緒,如果就緒則需要進行事件處理。當一個讀事件就緒后,還需要進一步判斷該文件描述符是否是監(jiān)聽套接字,如果是監(jiān)聽套接字就應(yīng)該調(diào)用 accept 函數(shù)將底層的連接獲取上來。但是只調(diào)用 accept 將連接獲取上來還不夠,為了下一次調(diào)用 select 時能讓 select 幫我們監(jiān)視新連接的讀事件是否就緒,在連接獲取上來后還應(yīng)該將該連接對應(yīng)的文件描述符添加到 fd_array 數(shù)組當中,這樣在下一次調(diào)用 select 前對 readfds 重新設(shè)置時就能將該文件描述符添加進去了。
如果是與客戶端建立連接的讀事件就緒,那么就應(yīng)該調(diào)用 read 函數(shù)讀取客戶端發(fā)來的數(shù)據(jù),讀取成功則進行打印。如果讀取失敗或者客戶端關(guān)閉連接,那么 select 服務(wù)器應(yīng)該調(diào)用 close 函數(shù)關(guān)閉對應(yīng)連接,但此時只關(guān)閉連接也是不夠的,還應(yīng)該將該連接對應(yīng)的文件描述符從 fd_array 數(shù)組當中清除,否則后續(xù)調(diào)用的 select 還會繼續(xù)監(jiān)視該連接的讀事件是否就緒。
代碼如下:
#pragma once
#include "socket.hpp"
#include <sys/select.h>
#define BACK_LOG 5
#define NUM 1024
#define DFL_FD - 1
class SelectServer{
private:
int _listen_sock; //監(jiān)聽套接字
int _port; //端口號
public:
void HandlerEvent(const fd_set& readfds, int fd_array[], int num)
{
for (int i = 0; i < num; i++){
if (fd_array[i] == DFL_FD){ //跳過無效的位置
continue;
}
if (fd_array[i] == _listen_sock&&FD_ISSET(fd_array[i], &readfds)){ //連接事件就緒
//獲取連接
struct sockaddr_in peer;
memset(&peer, 0, sizeof(peer));
socklen_t len = sizeof(peer);
int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);
if (sock < 0){ //獲取連接失敗
std::cerr << "accept error" << std::endl;
continue;
}
std::string peer_ip = inet_ntoa(peer.sin_addr);
int peer_port = ntohs(peer.sin_port);
std::cout << "get a new link[" << peer_ip << ":" << peer_port << "]" << std::endl;
if (!SetFdArray(fd_array, num, sock)){ //將獲取到的套接字添加到fd_array當中
close(sock);
std::cout << "select server is full, close fd: " << sock << std::endl;
}
}
else if (FD_ISSET(fd_array[i], &readfds)){ //讀事件就緒
char buffer[1024];
ssize_t size = read(fd_array[i], buffer, sizeof(buffer)-1);
if (size > 0){ //讀取成功
buffer[size] = '\0';
std::cout << "echo# " << buffer << std::endl;
}
else if (size == 0){ //對端連接關(guān)閉
std::cout << "client quit" << std::endl;
close(fd_array[i]);
fd_array[i] = DFL_FD; //將該文件描述符從fd_array中清除
}
else{
std::cerr << "read error" << std::endl;
close(fd_array[i]);
fd_array[i] = DFL_FD;
}
}
}
}
private:
bool SetFdArray(int fd_array[], int num, int fd)
{
for (int i = 0; i <num; i++){
if (fd_array[i] == DFL_FD){ //該位置沒有被使用
fd_array[i] = fd;
return true;
}
}
return false; //fd_array數(shù)組已滿
}
};
當調(diào)用 accept 從底層獲取上來連接后,不能立即調(diào)用 read 讀取連接當中的數(shù)據(jù),因為此時新連接中的數(shù)據(jù)可能并沒有就緒,如果直接調(diào)用read 可能需要進行阻塞等待,我們應(yīng)該將等待過程交給 select 來完成,因此獲取完連接后應(yīng)該直接將該連接對應(yīng)的文件描述符添加到 fd_array 數(shù)組當中就行了,當該連接的讀事件就緒時 select 會告知我們,再進行數(shù)據(jù)讀取就不會被阻塞了。
添加文件描述符到 fd_array 數(shù)組當中,本質(zhì)就是遍歷 fd_array 數(shù)組,找到一個沒有被使用的位置將該文件描述符添加進去即可。但有可能 fd_array 數(shù)組中全部的位置都已經(jīng)被占用了,那么文件描述符就會添加失敗,此時就只能將剛剛獲取上來的連接對應(yīng)的套接字進行關(guān)閉,因為此時服務(wù)器沒有能力處理這個連接。
至此 select 服務(wù)器編寫完畢,用 telnet 工具連接我們的服務(wù)器,此時通過 telnet 向服務(wù)器發(fā)送的數(shù)據(jù)就能夠被服務(wù)器讀到并且打印輸出了。
此外,雖然當前 select 服務(wù)器是一個單進程的服務(wù)器,但它卻可以同時為多個客戶端提供服務(wù),根本原因就是因為 select 函數(shù)調(diào)用后會告知 select 服務(wù)器哪個客戶端對應(yīng)的連接事件就緒了,此時 select 服務(wù)器就可以讀取發(fā)來的數(shù)據(jù),讀取完后又會調(diào)用 select 等待某個客戶端連接的讀事件就緒。
一些問題??
當前的 select 服務(wù)器還存在一些問題:
select 服務(wù)器如果要向客戶端發(fā)送數(shù)據(jù),不能直接調(diào)用write函數(shù),因為調(diào)用write函數(shù)時實際也分為 “等” 和“拷貝” 兩步,我們也應(yīng)該將 “等” 的這個過程交給 select ,因此在每次調(diào)用 select 之前,除了需要重新設(shè)置 readfds
還需要重新設(shè)置writefds
,并且還需要一個數(shù)組來保存需要被監(jiān)視寫事件是否就緒的文件描述符,就緒時我們才能夠調(diào)用 write 向客戶端發(fā)送數(shù)據(jù)。
其次沒有定制協(xié)議。代碼中讀取數(shù)據(jù)時并沒有按照某種規(guī)則進行讀取,此時就可能造成粘包問題,根本原因就是因為我們沒有定制協(xié)議,比如HTTP協(xié)議規(guī)定在讀取底層數(shù)據(jù)時讀取到空行就表明讀完了一個HTTP報頭,此時再根據(jù)HTTP報頭當中的 Content-Length 屬性得知正文的長度,最終就能夠讀取到一個完整的HTTP報文。
沒有對應(yīng)的輸入輸出緩沖區(qū)。代碼中直接將讀取的數(shù)據(jù)存儲到了字符數(shù)組 buffer 當中,這是不嚴謹?shù)模驗楸敬螖?shù)據(jù)讀取可能并沒有讀取到一個完整的報文,此時服務(wù)器就不能進行數(shù)據(jù)的分析處理,應(yīng)該將讀取到的數(shù)據(jù)存儲到一個輸入緩沖區(qū)當中,當讀取到一個完整的報文后再讓服務(wù)器進行處理。此外服務(wù)器的響應(yīng)數(shù)據(jù)也不應(yīng)該直接調(diào)用 write 發(fā)送給客戶端,應(yīng)該先存儲到一個輸出緩沖區(qū)當中,因為數(shù)據(jù)可能很龐大,無法一次發(fā)送完畢,可能需要進行分批發(fā)送。
select 優(yōu)點??
當然,這也是所有多路轉(zhuǎn)接接口的優(yōu)點:
- 可以同時等待多個文件描述符,并且只負責(zé)等待,實際的IO操作由 accept、read、write 等接口來完成,這些接口在進行IO操作時不會被阻塞。
- select 可同時等待多個文件描述符,將“等”的時間重疊提高了IO的效率。
select 缺點??
- 每次調(diào)用 select 都需要手動設(shè)置 fd 集合,非常不便。
- 每次調(diào)用 select 都需要把 fd 集合從用戶態(tài)拷貝到內(nèi)核態(tài),這個開銷在 fd 很多時會很大。
- 每次調(diào)用 select 都需要遍歷傳遞進來的所有 fd,這個開銷在 fd 很多時也很大。
- select 可監(jiān)控的文件描述符數(shù)量太少。
這里我細嗦一下 select 的文件描述符數(shù)量,調(diào)用 select 時的 readfds、writefds以及exceptfds 都是fd_set
結(jié)構(gòu),fd_set 結(jié)構(gòu)本質(zhì)是一個位圖,它用每一個比特位來標記一個文件描述符,因此 select 可監(jiān)控的文件描述符個數(shù)是取決于 fd_set 的比特位個數(shù)。
我們可以計算一下 fd_size 的比特位大小:
#include <iostream>
#include <sys/types.h>
int main()
{
std::cout << sizeof(fd_set)* 8 << std::endl;
return 0;
}
結(jié)果很明顯,可監(jiān)控的文件描述符數(shù)量為 1024 個 \color{red} {結(jié)果很明顯,可監(jiān)控的文件描述符數(shù)量為 1024 個} 結(jié)果很明顯,可監(jiān)控的文件描述符數(shù)量為1024個:
適用場景??
多路轉(zhuǎn)接接口的 select,poll epoll 都有自己適用的場景,在不恰當?shù)膱龊鲜褂媚硞€接口只會適得其反。
多路轉(zhuǎn)接接口一般適用于多連接,且多連接中只有少部分連接比較活躍。因為少量連接比較活躍,也就意味著幾乎所有的連接在進行IO操作時,都需要花費大量時間來等待事件就緒,此時使用多路轉(zhuǎn)接接口就可以將這些等的事件進行重疊,提高IO效率。對于多連接中大部分連接都很活躍的場景,就不適合多路轉(zhuǎn)接了。因為每個連接都很活躍,也就意味著任何時刻事件基本都是就緒的,此時根本不需要動用多路轉(zhuǎn)接接口來幫我們進行等待,畢竟使用多路轉(zhuǎn)接接口需要花費系統(tǒng)時間和空間資源。
多連接中只有少量連接是比較活躍的,比如聊天工具,我們登錄QQ后大部分時間其實是沒有聊天的,此時服務(wù)端不可能調(diào)用一個 read 來阻塞等待讀事件就緒。文章來源:http://www.zghlxwxcb.cn/news/detail-482634.html
多連接中大部分連接都很活躍,比如企業(yè)當中進行數(shù)據(jù)備份時,兩臺服務(wù)器之間不斷在交互數(shù)據(jù),這時的連接是特別活躍的,幾乎不需要等的過程,也就沒必要使用多路轉(zhuǎn)接接口了。文章來源地址http://www.zghlxwxcb.cn/news/detail-482634.html
到了這里,關(guān)于Linux 多路轉(zhuǎn)接 —— select的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!