云浮新興哪有做網(wǎng)站的企業(yè)網(wǎng)站推廣的方法
引言:網(wǎng)絡(luò)數(shù)據(jù)能夠正常到達(dá)用戶并且被接收是進(jìn)行網(wǎng)絡(luò)傳輸?shù)母灸康?#xff0c;網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)發(fā)送和接收有多種方案,本文章就對通過向量接收和發(fā)送等數(shù)據(jù)傳輸方式,并且對多種I/O模型進(jìn)詳細(xì)分析介紹。
目錄
一.I/O函數(shù)
1.1 recv和send
recv函數(shù)
send函數(shù)
1.2 readv和writev函數(shù)
readv函數(shù)
writev函數(shù)
1.3?recvmsg和recvmsg函數(shù)?
recvmsg函數(shù)
sendmsg函數(shù)
1.4 客戶—服務(wù)器實例
服務(wù)器端代碼:
客戶端代碼:
二.I/O模型介紹
2.1 阻塞I/O(Blocking I/O)
2.2非阻塞I/O(Non-blocking I/O)
2.3I/O多路復(fù)用(I/O Multiplexing)
2.4信號驅(qū)動I/O(Signal-driven I/O)
2.5異步I/O(Asynchronous I/O)
三.監(jiān)視函數(shù)?
3.1select和pselect
select函數(shù)
pselect函數(shù)
3.2poll函數(shù)和ppoll函數(shù)
poll函數(shù)
ppoll函數(shù)
四.總結(jié)
一.I/O函數(shù)
1.1 recv和send
在網(wǎng)絡(luò)編程中,recv
?和?send
?函數(shù)是用于在套接字上進(jìn)行數(shù)據(jù)傳輸?shù)膬蓚€基本函數(shù)。它們是在Unix-like系統(tǒng)中的Berkeley套接字 API(也稱為BSD套接字API)的一部分,在Windows系統(tǒng)中也有對應(yīng)的實現(xiàn)。
recv函數(shù)
recv
?函數(shù)用于從連接的套接字接收數(shù)據(jù)。函數(shù)原型如下:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
-
sockfd
:指定接收數(shù)據(jù)的套接字文件描述符。 -
buf
:指向緩沖區(qū)的指針,用于存放接收到的數(shù)據(jù)。 -
len
:指定緩沖區(qū)的大小,即最多接收的數(shù)據(jù)量。 -
flags
:指定接收數(shù)據(jù)的操作方式,可以是0或者以下一個或多個值的邏輯或:MSG_OOB
:接收帶外數(shù)據(jù)(Out-of-Band Data)。MSG_PEEK
:查看數(shù)據(jù)但不從接收隊列中移除數(shù)據(jù)。MSG_WAITALL
:等待所有請求的數(shù)據(jù),直到請求的數(shù)量被接收為止。- 等等。
返回值:
- 成功時,返回接收到的字節(jié)數(shù)。
- 如果連接被對方關(guān)閉,返回0。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
send函數(shù)
send
?函數(shù)用于向連接的套接字發(fā)送數(shù)據(jù)。函數(shù)原型如下:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
參數(shù)說明:
-
sockfd
:指定發(fā)送數(shù)據(jù)的套接字文件描述符。 -
buf
:指向要發(fā)送數(shù)據(jù)的指針。 -
len
:指定要發(fā)送數(shù)據(jù)的長度。 -
flags
:指定發(fā)送數(shù)據(jù)的操作方式,可以是0或者以下一個或多個值的邏輯或:MSG_OOB
:發(fā)送帶外數(shù)據(jù)。MSG_DONTROUTE
:發(fā)送數(shù)據(jù)時不通過網(wǎng)關(guān)。- 等等。
返回值:
- 成功時,返回發(fā)送的字節(jié)數(shù)。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
1.2 readv和writev函數(shù)
readv
?和?writev
?函數(shù)是 Unix 和類 Unix 操作系統(tǒng)中的系統(tǒng)調(diào)用,它們允許程序在一次操作中從多個緩沖區(qū)讀取數(shù)據(jù)或?qū)?shù)據(jù)寫入多個緩沖區(qū)。這些函數(shù)對于分散讀(scatter read)和集中寫(gather write)操作非常有用。
readv函數(shù)
readv
?函數(shù)用于從文件描述符讀取數(shù)據(jù)到多個緩沖區(qū)中。函數(shù)原型如下:
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
參數(shù)說明:
fd
:指定要讀取數(shù)據(jù)的文件描述符。iov
:指向?iovec
?結(jié)構(gòu)數(shù)組的指針,每個?iovec
?結(jié)構(gòu)指定一個緩沖區(qū)。iovcnt
:指定?iov
?數(shù)組中?iovec
?結(jié)構(gòu)的數(shù)量。
iovec
?結(jié)構(gòu)定義如下:
struct iovec {void *iov_base; /* Starting address */size_t iov_len; /* Number of bytes to transfer */
};
iov_base
:指向數(shù)據(jù)緩沖區(qū)的指針。iov_len
:指定緩沖區(qū)的長度。
返回值:
- 成功時,返回讀取的總字節(jié)數(shù)。
- 如果遇到文件結(jié)束,返回0。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
writev函數(shù)
writev
?函數(shù)用于將多個緩沖區(qū)的數(shù)據(jù)寫入到文件描述符。函數(shù)原型如下:
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
參數(shù)說明:
fd
:指定要寫入數(shù)據(jù)的文件描述符。iov
:指向?iovec
?結(jié)構(gòu)數(shù)組的指針,每個?iovec
?結(jié)構(gòu)指定一個數(shù)據(jù)源緩沖區(qū)。iovcnt
:指定?iov
?數(shù)組中?iovec
?結(jié)構(gòu)的數(shù)量。
返回值:
- 成功時,返回寫入的總字節(jié)數(shù)。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
1.3?recvmsg和recvmsg函數(shù)?
recvmsg
?和?sendmsg
?函數(shù)是 Unix 和類 Unix 操作系統(tǒng)中的系統(tǒng)調(diào)用,它們提供了比?recv
?和?send
?更高級的接口,用于在套接字上發(fā)送和接收數(shù)據(jù)。這些函數(shù)支持更多的功能,如scatter/gather I/O、控制消息的發(fā)送和接收,以及帶外數(shù)據(jù)。
recvmsg函數(shù)
recvmsg
?函數(shù)用于從套接字接收數(shù)據(jù),并可以接收輔助數(shù)據(jù)(ancillary data)。函數(shù)原型如下:
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
參數(shù)說明:
-
sockfd
:指定接收數(shù)據(jù)的套接字文件描述符。 -
msg
:指向?msghdr
?結(jié)構(gòu)的指針,該結(jié)構(gòu)包含了接收操作的所有參數(shù)。 -
flags
:指定接收數(shù)據(jù)的操作方式,可以是0或者以下一個或多個值的邏輯或:MSG_OOB
:接收帶外數(shù)據(jù)。MSG_PEEK
:查看數(shù)據(jù)但不從接收隊列中移除數(shù)據(jù)。MSG_WAITALL
:等待所有請求的數(shù)據(jù),直到請求的數(shù)量被接收為止。- 等等。
msghdr
?結(jié)構(gòu)定義如下:
struct msghdr {void *msg_name; /* Optional address */socklen_t msg_namelen; /* Size of address */struct iovec *msg_iov; /* Scatter/gather array */int msg_iovlen; /* # elements in msg_iov */void *msg_control; /* Ancillary data, see below */socklen_t msg_controllen; /* Ancillary data buffer len */int msg_flags; /* Flags on received message */
};
msg_name
?和?msg_namelen
:用于接收對方地址信息(僅用于面向連接的套接字)。msg_iov
?和?msg_iovlen
:指定分散讀的緩沖區(qū)數(shù)組?iovec
?和其長度。msg_control
?和?msg_controllen
:用于接收輔助數(shù)據(jù)和控制信息。msg_flags
:接收消息的標(biāo)志,由系統(tǒng)填充。
返回值:
- 成功時,返回接收到的字節(jié)數(shù)。
- 如果連接被對方關(guān)閉,返回0。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
sendmsg函數(shù)
sendmsg
?函數(shù)用于向套接字發(fā)送數(shù)據(jù),并可以發(fā)送輔助數(shù)據(jù)。函數(shù)原型如下:
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
參數(shù)說明:
-
sockfd
:指定發(fā)送數(shù)據(jù)的套接字文件描述符。 -
msg
:指向?msghdr
?結(jié)構(gòu)的指針,該結(jié)構(gòu)包含了發(fā)送操作的所有參數(shù)。 -
flags
:指定發(fā)送數(shù)據(jù)的操作方式,可以是0或者以下一個或多個值的邏輯或:MSG_OOB
:發(fā)送帶外數(shù)據(jù)。MSG_DONTROUTE
:發(fā)送數(shù)據(jù)時不通過網(wǎng)關(guān)。- 等等。
返回值:
- 成功時,返回發(fā)送的字節(jié)數(shù)。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
1.4 客戶—服務(wù)器實例
這個服務(wù)器的功能主要是返回客戶端輸入的數(shù)據(jù)長度,其中使用了I/O函數(shù)中的readv和writev。
服務(wù)器端代碼:
(我這個代碼涉及到許多前面的基礎(chǔ)知識,需要了解的就可以看我上一篇博客:
寫文章-CSDN創(chuàng)作中心socket套接字函數(shù)-CSDN博客寫文章-CSDN創(chuàng)作中心)
#include<t_stdio.h>
#include<t_file.h>
#include<stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include<unistd.h>
static struct iovec*vs=NULL;
void sig_process(int signo){//信號處理函數(shù)printf("catch a exit signal..\n");free(vs);_exit(0);
}#define port 8888//端口號
#define backlog 2//最大監(jiān)聽數(shù)量//業(yè)務(wù)處理邏輯
void process_conn_server(int s){char buffer [30];ssize_t size=0; //向量的緩沖區(qū)//申請3個向量空間struct iovec * v = (struct iovec *)malloc(3*sizeof(struct iovec));if(!v) E_MSG("malloc",-1);vs = v;//掛接全局變量,易于釋放管理v[0].iov_base= buffer;//每個向量十個地址空間v[1].iov_base= buffer+10;v[2].iov_base= buffer+20;v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;//初始化長度為10for(;;){size = readv(s, v , 3);// 從套接字中讀取數(shù)據(jù)放入數(shù)據(jù)緩沖區(qū)if(size == 0){return ;}//構(gòu)建響應(yīng)字符sprintf(v[0].iov_base,"%d",size);sprintf(v[1].iov_base,"bytes alter");sprintf(v[2].iov_base,"ogether\n");//設(shè)置寫入數(shù)據(jù)的長度v[0].iov_len = strlen(v[0].iov_base);v[1].iov_len = strlen(v[1].iov_base);v[2].iov_len = strlen(v[2].iov_base);writev(s , v , 3);//發(fā)送給客戶端}}
int main(int argc ,char * argv[])
{int ss ,sc;//服務(wù)器和客戶端的套接字文件描述符struct sockaddr_in server_addr;//服務(wù)器地址結(jié)構(gòu)struct sockaddr_in client_addr;//客戶端地址結(jié)構(gòu)pid_t pid;//創(chuàng)建子進(jìn)程進(jìn)行分叉進(jìn)行signal(SIGINT,sig_process);//添加sigint信號到信號掩碼signal(SIGPIPE,sig_process);ss = socket(AF_INET,SOCK_STREAM,0);// 創(chuàng)建服務(wù)器套接字if(ss < 0) E_MSG("socket",-1);bzero(&server_addr,sizeof(server_addr));//將地址清零server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htons(INADDR_ANY);//本地地址server_addr.sin_port=htons(port);//設(shè)置端口//綁定本地到套接字符int err = bind(ss,(struct sockaddr * )&server_addr,sizeof(server_addr));if(err < 0)E_MSG("bind",-1);err = listen(ss,backlog);//創(chuàng)建監(jiān)聽隊列for(;;){int addrlen = sizeof(struct sockaddr);sc = accept(ss,(struct sockaddr * )&client_addr,&addrlen);//獲取用戶套接字if(sc < 0)continue;//客戶端出錯,結(jié)束這次循環(huán),繼續(xù)監(jiān)聽客戶;pid = fork();//創(chuàng)建新的子進(jìn)程來處理當(dāng)前連接客戶端;if(pid==0){close(ss);// 子進(jìn)程中關(guān)閉服務(wù)端process_conn_server(sc);}else {close(sc);//父進(jìn)程關(guān)閉客戶端連接,繼續(xù)監(jiān)聽;}}return 0;
}
客戶端代碼:
#include<t_stdio.h>
#include<t_file.h>
#include<stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include<unistd.h>
#define port 8888
static int s;
static struct iovec*vc=NULL;
void sig_proccess(int signo){printf("catch a exit signal..\n");free(vc);_exit(0);
}void sig_pipe(int signo){printf("catch a sigpipe signal..\n");free(vc);_exit(0);}//業(yè)務(wù)處理函數(shù)
void process_conn_client(int s){char buffer [30];ssize_t size=0; //向量的緩沖區(qū)//申請3個向量空間struct iovec * v = (struct iovec *)malloc(3*sizeof(struct iovec));if(!v) E_MSG("malloc",-1);vc = v;//掛接全局變量,易于釋放管理v[0].iov_base= buffer;//每個向量十個地址空間v[1].iov_base= buffer+10;v[2].iov_base= buffer+20;v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;//初始化長度為10int i=0;for(;;){//從標(biāo)準(zhǔn)輸入中讀取數(shù)據(jù)放入緩沖區(qū)buffer中size = read(0,v[0].iov_base,10);//只有十個字節(jié)if(size>0){v[0].iov_len = size;writev(s,v,1);//發(fā)送給服務(wù)器v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;size=readv(s,v,3);//從服務(wù)器讀取數(shù)據(jù)for(i = 0;i<3;i++){if(v[i].iov_len > 0){write(1,v[i].iov_base,v[i].iov_len);//將服務(wù)區(qū)處理后數(shù)據(jù)輸出}} }}}
int main(int argc ,char * argv[])
{struct sockaddr_in server_addr;//服務(wù)器結(jié)構(gòu)地址int err;if(argc == 1){printf("pls input server addr\n");return 0;}//沒有輸入指令參數(shù)signal(SIGINT,sig_proccess);signal(SIGPIPE,sig_pipe);//設(shè)置信號掩碼s = socket(AF_INET,SOCK_STREAM,0);//創(chuàng)建客戶端套接字if(s < 0)E_MSG("socket",-1);//設(shè)置服務(wù)器地址bzero(&server_addr,sizeof(server_addr));//將地址清0server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htons(INADDR_ANY);//本地地址server_addr.sin_port=htons(port);//設(shè)置端口inet_pton(AF_INET,argv[1],&server_addr.sin_addr);//將用戶輸入的字符串ip轉(zhuǎn)化為二進(jìn)制ipconnect(s,(struct sockaddr *)&server_addr,sizeof(server_addr));//連接服務(wù)器process_conn_client(s);return 0;
}
?對于這兩個代碼,大家可以仔細(xì)閱讀,我寫了非常詳細(xì)的注釋,包括信號的處理,業(yè)務(wù)邏輯的處理等等,可以將我們整個基礎(chǔ)網(wǎng)絡(luò)知識串聯(lián)起來!
二.I/O模型介紹
網(wǎng)絡(luò)編程中的I/O模型主要影響程序如何處理網(wǎng)絡(luò)套接字的讀寫操作。以下是幾種常見的I/O模型:
2.1 阻塞I/O(Blocking I/O)
?阻塞I/O模型是最簡單的I/O模型。當(dāng)發(fā)起一個I/O操作(如讀或?qū)?#xff09;時,如果數(shù)據(jù)未準(zhǔn)備好,程序會阻塞直到操作完成。在這段時間內(nèi),程序無法執(zhí)行其他任務(wù)。
// 阻塞讀
char buffer[1024];
read(socket, buffer, sizeof(buffer));
2.2非阻塞I/O(Non-blocking I/O)
在非阻塞I/O模型中,I/O操作不會阻塞程序。如果數(shù)據(jù)未準(zhǔn)備好,I/O操作會立即返回一個錯誤碼(通常是EAGAIN
或EWOULDBLOCK
)。程序需要定期輪詢檢查數(shù)據(jù)是否準(zhǔn)備好.
// 設(shè)置套接字為非阻塞模式
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);// 非阻塞讀
char buffer[1024];
while (read(socket, buffer, sizeof(buffer)) == -1 && errno == EAGAIN) {// 數(shù)據(jù)未準(zhǔn)備好,繼續(xù)做其他事情
}
2.3I/O多路復(fù)用(I/O Multiplexing)
?I/O多路復(fù)用允許程序同時監(jiān)視多個文件描述符,等待一個或多個變得“就緒”。這可以通過select
、poll
、epoll
(Linux特有)或kqueue
(BSD特有)系統(tǒng)調(diào)用來實現(xiàn)。
// 使用select進(jìn)行I/O多路復(fù)用
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(socket, &readfds);select(socket + 1, &readfds, NULL, NULL, NULL);if (FD_ISSET(socket, &readfds)) {char buffer[1024];read(socket, buffer, sizeof(buffer));
}
2.4信號驅(qū)動I/O(Signal-driven I/O)
信號驅(qū)動I/O模型中,程序通過sigaction
系統(tǒng)調(diào)用請求內(nèi)核在文件描述符就緒時發(fā)送一個信號。當(dāng)數(shù)據(jù)準(zhǔn)備好時,內(nèi)核會發(fā)送一個SIGIO信號,程序可以在信號處理函數(shù)中處理數(shù)據(jù)。?
// 設(shè)置信號處理函數(shù)
struct sigaction sa;
sa.sa_handler = io_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGIO, &sa, NULL);// 設(shè)置套接字的所有者
fcntl(socket, F_SETOWN, getpid());// 開啟信號驅(qū)動I/O
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_ASYNC);
2.5異步I/O(Asynchronous I/O)
?異步I/O模型中,程序發(fā)起I/O操作后,立即返回,無需等待數(shù)據(jù)準(zhǔn)備好。當(dāng)操作完成時,程序會收到通知。這種模型通常通過aio_read
、aio_write
等系統(tǒng)調(diào)用來實現(xiàn)。
// 異步讀
aiocb aio;
memset(&aio, 0, sizeof(aio));
aio.aio_fildes = socket;
aio.aio_buf = buffer;
aio.aio_nbytes = sizeof(buffer);
aio.aio_offset = 0;aio_read(&aio);
每種I/O模型都有其適用的場景。例如,阻塞I/O適用于簡單應(yīng)用,非阻塞I/O和I/O多路復(fù)用適用于需要處理多個套接字的應(yīng)用,而異步I/O適用于需要高性能和低延遲的應(yīng)用。在選擇合適的I/O模型時,需要考慮應(yīng)用的特定需求和性能。
三.監(jiān)視函數(shù)?
3.1select和pselect
在網(wǎng)絡(luò)編程中,select
?和?pselect
?函數(shù)是用于多路復(fù)用(I/O multiplexing)的系統(tǒng)調(diào)用,它們允許程序同時監(jiān)視多個文件描述符,以判斷它們是否準(zhǔn)備好進(jìn)行讀、寫或異常(錯誤)操作。
select函數(shù)
select
?函數(shù)的原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
參數(shù)說明:
nfds
:需要檢查的最高文件描述符加1。通常設(shè)置為最大的文件描述符加上1。readfds
:指向一組文件描述符的指針,這些文件描述符需要檢查是否準(zhǔn)備好讀取數(shù)據(jù)。writefds
:指向一組文件描述符的指針,這些文件描述符需要檢查是否準(zhǔn)備好寫入數(shù)據(jù)。exceptfds
:指向一組文件描述符的指針,這些文件描述符需要檢查是否發(fā)生異常。timeout
:指向?timeval
?結(jié)構(gòu)的指針,用于指定?select
?應(yīng)該阻塞等待的最大時間。如果設(shè)置為?NULL
,select
?將無限期地等待;如果設(shè)置為0,select
?將立即返回。
timeval
?結(jié)構(gòu)定義如下:
struct timeval {long tv_sec; /* seconds */long tv_usec; /* microseconds */
};
返回值:
- 成功時,返回準(zhǔn)備好的文件描述符的數(shù)量。
- 如果超時,返回0。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
使用?select
?時,需要使用?FD_ZERO
、FD_SET
?和?FD_CLR
?宏來操作?fd_set
?集合。
pselect函數(shù)
pselect
?函數(shù)與?select
?類似,但它提供了一些額外的功能,特別是在處理信號方面。pselect
?的原型如下:
int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);
參數(shù)說明:
- 前四個參數(shù)與?
select
?函數(shù)相同。 timeout
:指向?timespec
?結(jié)構(gòu)的指針,用于指定?pselect
?應(yīng)該阻塞等待的最大時間。timespec
?結(jié)構(gòu)與?timeval
?類似,但它使用秒和納秒來表示時間。sigmask
:指向?sigset_t
?類型的一個信號集的指針,用于在?pselect
?調(diào)用期間臨時替換進(jìn)程的信號掩碼。這意味著?pselect
?在等待文件描述符就緒時會阻塞指定的信號。
timespec
?結(jié)構(gòu)定義如下:
struct timespec {time_t tv_sec; /* seconds */long tv_nsec; /* nanoseconds */
};
返回值:
- 成功時,返回準(zhǔn)備好的文件描述符的數(shù)量。
- 如果超時,返回0。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>int main() {fd_set readfds;struct timeval timeout;int result;FD_ZERO(&readfds);FD_SET(0, &readfds); // 監(jiān)視標(biāo)準(zhǔn)輸入(文件描述符0)timeout.tv_sec = 5; // 設(shè)置超時時間為5秒timeout.tv_usec = 0;result = select(1, &readfds, NULL, NULL, &timeout);if (result == -1) {perror("select");exit(EXIT_FAILURE);} else if (result > 0) {printf("Data is available to read.\n");} else {printf("Timeout occurred.\n");}return 0;
}
在這個例子中,我們使用?select
?來監(jiān)視標(biāo)準(zhǔn)輸入(文件描述符0),設(shè)置超時時間為5秒。如果在這段時間內(nèi)有數(shù)據(jù)可讀,select
?將返回大于0的值;如果超時,將返回0;如果發(fā)生錯誤,將返回-1。
3.2poll函數(shù)和ppoll函數(shù)
?poll
?和?ppoll
?函數(shù)是 Unix 和類 Unix 操作系統(tǒng)中的系統(tǒng)調(diào)用,用于多路復(fù)用(I/O multiplexing),允許程序同時監(jiān)視多個文件描述符的讀寫狀態(tài)。與?select
?和?pselect
?函數(shù)相比,poll
?和?ppoll
?提供了更豐富的接口和更好的性能。
poll函數(shù)
poll
?函數(shù)的原型如下:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
參數(shù)說明:
fds
:指向一個?pollfd
?結(jié)構(gòu)數(shù)組的指針,每個結(jié)構(gòu)描述了一個要監(jiān)視的文件描述符。nfds
:指定?fds
?數(shù)組中?pollfd
?結(jié)構(gòu)的數(shù)量。timeout
:指定?poll
?應(yīng)該阻塞等待的最大時間(毫秒)。如果設(shè)置為-1,poll
?將無限期地等待;如果設(shè)置為0,poll
?將立即返回。
pollfd
?結(jié)構(gòu)定義如下:
struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */
};
fd
:要監(jiān)視的文件描述符。events
:請求監(jiān)視的 events(通過位掩碼指定)。revents
:返回時,表示實際發(fā)生的事件(通過位掩碼指定)。
events
?和?revents
?可以是以下標(biāo)志的組合(定義在?<poll.h>
?頭文件中):
POLLIN
:有數(shù)據(jù)可讀。POLLOUT
:可以寫數(shù)據(jù)。POLLERR
:發(fā)生錯誤。POLLHUP
:掛起。POLLNVAL
:無效的請求。
返回值:
- 成功時,返回準(zhǔn)備好的文件描述符的數(shù)量。
- 如果超時,返回0。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
ppoll函數(shù)
ppoll
?函數(shù)與?poll
?類似,但它提供了一些額外的功能,特別是在處理信號方面。ppoll
?的原型如下:
int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask);
參數(shù)說明:
- 前三個參數(shù)與?
poll
?函數(shù)相同。 timeout
:指向?timespec
?結(jié)構(gòu)的指針,用于指定?ppoll
?應(yīng)該阻塞等待的最大時間。timespec
?結(jié)構(gòu)與?timeval
?類似,但它使用秒和納秒來表示時間。sigmask
:指向?sigset_t
?類型的一個信號集的指針,用于在?ppoll
?調(diào)用期間臨時替換進(jìn)程的信號掩碼。這意味著?ppoll
?在等待文件描述符就緒時會阻塞指定的信號。
返回值:
- 成功時,返回準(zhǔn)備好的文件描述符的數(shù)量。
- 如果超時,返回0。
- 出錯時,返回-1,并設(shè)置errno來指示錯誤。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>int main() {struct pollfd fds[1];int result;fds[0].fd = 0; // 監(jiān)視標(biāo)準(zhǔn)輸入(文件描述符0)fds[0].events = POLLIN; // 監(jiān)視可讀事件result = poll(fds, 1, 5000); // 設(shè)置超時時間為5000毫秒if (result == -1) {perror("poll");exit(EXIT_FAILURE);} else if (result > 0) {if (fds[0].revents & POLLIN) {printf("Data is available to read.\n");}} else {printf("Timeout occurred.\n");}return 0;
}
?在這個例子中,我們使用?poll
?來監(jiān)視標(biāo)準(zhǔn)輸入(文件描述符0),設(shè)置超時時間為5000毫秒。如果在這段時間內(nèi)有數(shù)據(jù)可讀,poll
?將返回大于0的值;如果超時,將返回0;如果發(fā)生錯誤,將返回-1。
四.總結(jié)
這篇文章對數(shù)據(jù)I/O進(jìn)行了整體的介紹,包括recv ,send, readv , writev,recmsg , sendmsg等I/O函數(shù),并且給了很多例子,來介紹I/O的各種模型及其使用,相信經(jīng)過這篇文章的學(xué)習(xí),大家對網(wǎng)絡(luò)編程中的I/O操作能有個整體認(rèn)知!!!