Linux网络编程---I/O复用模型之select
生活随笔
收集整理的這篇文章主要介紹了
Linux网络编程---I/O复用模型之select
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
https://blog.csdn.net/men_wen/article/details/53456435
Linux網(wǎng)絡(luò)編程—I/O復(fù)用模型之select
1. IO復(fù)用模型
- IO復(fù)用能夠預(yù)先告知內(nèi)核,一旦發(fā)現(xiàn)進(jìn)程指定的一個(gè)或者多個(gè)IO條件就緒,它就通知進(jìn)程。
- IO復(fù)用阻塞在select或poll系統(tǒng)調(diào)用上,而不是阻塞在真正的IO系統(tǒng)調(diào)用上。
2. 函數(shù)select
select函數(shù)能夠告知內(nèi)核對(duì)哪些描述符(不局限于套接字)感興趣以及等待多長事件
#include <sys/time.h> #include <sys/types.h> #include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); //返回值:返回就緒描述符數(shù)目,若超時(shí)則為0, 若出錯(cuò)則為-1- timeout用來指定內(nèi)核等待所指定描述符的任何一個(gè)就緒花多長事件。timeval結(jié)構(gòu)用于指定這段事件的秒數(shù)和微妙數(shù)
- 中間的三個(gè)參數(shù)readfds、writefds、exceptfds指定內(nèi)核測試讀、寫、異常條件的描述符,這三個(gè)參數(shù)都是fd_set結(jié)構(gòu)的指針類型,fd_set結(jié)構(gòu)實(shí)現(xiàn)如下:
select函數(shù)使用描述符集,通常是一個(gè)整數(shù)數(shù)組,其中每一個(gè)整數(shù)中的每一位對(duì)應(yīng)一個(gè)描述符。如何操作這些描述符則系統(tǒng)提供了四個(gè)宏
void FD_CLR(int fd, fd_set *set); //把文件描述符集合里fd清零 int FD_ISSET(int fd, fd_set *set); //測試文件描述符集合里fd是否置1 void FD_SET(int fd, fd_set *set); //把文件描述符集合里fd位置1 void FD_ZERO(fd_set *set); //把文件描述符集合里所有位清0- 如果對(duì)應(yīng)哪一個(gè)條件不感興趣,則可以將它設(shè)置為空指針。
- nfds參數(shù)指定待測試的描述符個(gè)數(shù),它的值是待測試的最大描述符加1,描述符0到nfds-1都會(huì)被測試。
- select能監(jiān)聽的文件描述符個(gè)數(shù)受限于FD_SETSIZE,一般為1024,單純改變進(jìn)程打開的文件描述符個(gè)數(shù)并不能改變select監(jiān)聽文件個(gè)數(shù)。解決1024以下客戶端時(shí)使用select是很合適的,但如果鏈接客戶端過多,select采用的是輪詢模型,會(huì)大大降低服務(wù)器響應(yīng)效率。
- select函數(shù)修改由指針readfds、writefds、exceptfds指向的描述符集,調(diào)用函數(shù)時(shí),我們指定所關(guān)心的描述符的值,函數(shù)返回時(shí),結(jié)果將指示哪些描述符已就緒,函數(shù)返回后,使用FD_ISSET宏來測試fd_set數(shù)據(jù)類型中的描述符,描述符集內(nèi)任何與未就緒描述符對(duì)應(yīng)的位均清成0.為此,每次重新調(diào)用select函數(shù)時(shí)都要將描述符集內(nèi)所關(guān)心的位置為1。
3. select模型實(shí)現(xiàn)
3.1 服務(wù)器端
#include "wrap.h"#define MAXLINE 1024int start_ser(char *ipaddr, char *port) {int sock = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serveraddr;bzero(&serveraddr, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(port));inet_pton(AF_INET, ipaddr, &serveraddr.sin_addr);Bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));Listen(sock, 128);return sock; }int main(int argc, char *argv[]) {int i, maxi, maxfd, listenfd, connfd, sockfd;int nready, client[FD_SETSIZE];ssize_t n;fd_set readset, allset;char buf[MAXLINE];socklen_t clilen;struct sockaddr_in clientaddr;listenfd = start_ser(argv[1], argv[2]); //監(jiān)聽文件描述符maxfd = listenfd;//最大的文件描述符maxi = -1;//數(shù)組中最大文件描述符下標(biāo)for(i = 0; i < FD_SETSIZE; i++){client[i] = -1;}FD_ZERO(&allset);//初始化allset集合FD_SET(listenfd, &allset);//添加監(jiān)聽文件描述符到集合allset中while(1){readset = allset;//每次select都要初始化集合,結(jié)構(gòu)體可以直接賦值nready = select(maxfd+1, &readset, NULL, NULL, NULL);//組攝等待連接或請(qǐng)求if(FD_ISSET(listenfd, &readset)){//當(dāng)監(jiān)聽文件描述符相應(yīng)有新的客戶端連接時(shí)clilen = sizeof(clientaddr);connfd = Accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);//接收該客戶端if(connfd < 0){perr_exit("accept err");break;}for(i = 0; i < FD_SETSIZE; i++){if(client[i] < 0){client[i] = connfd; //保存文件描述符到數(shù)組break;}}if(i == FD_SETSIZE){ //連接個(gè)數(shù)不能大于內(nèi)核規(guī)定的FD_SETSIZEperr_exit("too many clients");}FD_SET(connfd, &allset);//添加新描述符到allset集合中if(connfd > maxfd){maxfd = connfd; //更新最大文件描述符}if(i > maxi){maxi = i; //更新最大文件描述符下標(biāo)}if(--nready == 0){ //只有一個(gè)listenfd響應(yīng)則直接跳過下面的語句continue;}}for(i = 0; i <= maxi; i++){ //處理已連接客戶端的請(qǐng)求if((sockfd = client[i]) < 0){continue;}if(FD_ISSET(sockfd, &readset)){ //sockfd是否在readset集合中memset(buf, '\0', MAXLINE);if((n = Read(sockfd, buf, MAXLINE-1)) == 0){Close(sockfd);FD_CLR(sockfd, &allset);client[i] = -1;}else{printf("client:%s\n", buf);}if(--nready == 0){//判斷是否查找完break;}}}}return 0; }3.2 客戶端
#include "wrap.h"int main(int argc, char *argv[]) {int connfd;struct sockaddr_in serveraddr;char buf[1024];connfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&serveraddr, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));inet_pton(AF_INET, argv[1], &serveraddr.sin_addr);Connect(connfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));while(fgets(buf, 1024, stdin) != NULL){Write(connfd, buf, strlen(buf));}Close(connfd);return 0; }3.3運(yùn)行結(jié)果
總結(jié)
以上是生活随笔為你收集整理的Linux网络编程---I/O复用模型之select的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 站点做动静分离,如何处理用户上传文件呢?
- 下一篇: 和同学solo,我盲僧他狗头,三头一塔,