博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux网络编程---I/O复用模型之select
阅读量:4095 次
发布时间:2019-05-25

本文共 5621 字,大约阅读时间需要 18 分钟。

https://blog.csdn.net/men_wen/article/details/53456435

Linux网络编程—I/O复用模型之select

1. IO复用模型

  • IO复用能够预先告知内核,一旦发现进程指定的一个或者多个IO条件就绪,它就通知进程。
  • IO复用阻塞在select或poll系统调用上,而不是阻塞在真正的IO系统调用上。

这里写图片描述

2. 函数select

select函数能够告知内核对哪些描述符(不局限于套接字)感兴趣以及等待多长事件

#include 
#include
#include
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);//返回值:返回就绪描述符数目,若超时则为0, 若出错则为-1
  • timeout用来指定内核等待所指定描述符的任何一个就绪花多长事件。timeval结构用于指定这段事件的秒数和微妙数
struct timeval {    long    tv_sec;         /* seconds */    long    tv_usec;        /* microseconds */};//当timeout为NULL,则永远等待。//当timeout为timeval,等待固定时间。//当timeout为timeval,但timeval时间设置为0,则检查描述符字立即返回,称为轮询。
  • 中间的三个参数readfds、writefds、exceptfds指定内核测试读、写、异常条件的描述符,这三个参数都是fd_set结构的指针类型,fd_set结构实现如下:
#define __FD_SETSIZE    1024typedef struct {        unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];} __kernel_fd_set;

select函数使用描述符集,通常是一个整数数组,其中每一个整数中的每一位对应一个描述符。如何操作这些描述符则系统提供了四个宏

void FD_CLR(int fd, fd_set *set);//把文件描述符集合里fd清零int  FD_ISSET(int fd, fd_set *set);//测试文件描述符集合里fd是否置1void FD_SET(int fd, fd_set *set);//把文件描述符集合里fd位置1void FD_ZERO(fd_set *set);//把文件描述符集合里所有位清0
  • 如果对应哪一个条件不感兴趣,则可以将它设置为空指针。
  • nfds参数指定待测试的描述符个数,它的值是待测试的最大描述符加1,描述符0到nfds-1都会被测试。
  • select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数。解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型,会大大降低服务器响应效率。
  • select函数修改由指针readfds、writefds、exceptfds指向的描述符集,调用函数时,我们指定所关心的描述符的值,函数返回时,结果将指示哪些描述符已就绪,函数返回后,使用FD_ISSET宏来测试fd_set数据类型中的描述符,描述符集内任何与未就绪描述符对应的位均清成0.为此,每次重新调用select函数时都要将描述符集内所关心的位置为1。

3. select模型实现

3.1 服务器端

#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]); //监听文件描述符        maxfd = listenfd;//最大的文件描述符        maxi = -1;//数组中最大文件描述符下标        for(i = 0; i < FD_SETSIZE; i++){                client[i] = -1;        }        FD_ZERO(&allset);//初始化allset集合        FD_SET(listenfd, &allset);//添加监听文件描述符到集合allset中        while(1){                readset = allset;//每次select都要初始化集合,结构体可以直接赋值                nready = select(maxfd+1, &readset, NULL, NULL, NULL);//组摄等待连接或请求                if(FD_ISSET(listenfd, &readset)){
//当监听文件描述符相应有新的客户端连接时 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; //保存文件描述符到数组 break; } } if(i == FD_SETSIZE){ //连接个数不能大于内核规定的FD_SETSIZE perr_exit("too many clients"); } FD_SET(connfd, &allset);//添加新描述符到allset集合中 if(connfd > maxfd){ maxfd = connfd; //更新最大文件描述符 } if(i > maxi){ maxi = i; //更新最大文件描述符下标 } if(--nready == 0){ //只有一个listenfd响应则直接跳过下面的语句 continue; } } for(i = 0; i <= maxi; i++){ //处理已连接客户端的请求 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运行结果

这里写图片描述

这里写图片描述

这里写图片描述

你可能感兴趣的文章
shell 快捷键
查看>>
MODULE_DEVICE_TABLE的理解
查看>>
No devices detected. Fatal server error: no screens found
查看>>
db db2_monitorTool IBM Rational Performace Tester
查看>>
postgresql监控工具pgstatspack的安装及使用
查看>>
【JAVA数据结构】双向链表
查看>>
【JAVA数据结构】先进先出队列
查看>>
谈谈加密和混淆吧[转]
查看>>
乘法逆元
查看>>
Objective-C 基础入门(一)
查看>>
找工作准备的方向(4月22日写的)
查看>>
关于fwrite写入文件后打开查看是乱码的问题
查看>>
用结构体指针前必须要用malloc,不然会出现段错误
查看>>
Linux系统中的美
查看>>
一些实战项目(linux应用层编程,多线程编程,网络编程)
查看>>
STM32CubeMX 真的不要太好用
查看>>
不要买铝合金机架的无人机,不耐摔,易变形弯曲。
查看>>
ACfly也是基于FreeRTOS的
查看>>
电机堵转
查看>>
carzepony也在想往FreeRTOS上迁移
查看>>