I/O多路复用(select,poll)函数及示例程序

I/O多路复用

在网络编程中,不得不提的一个概念就是I/O多路复用机制,采用I/O多路复用机制,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,能够同时处理多个描述符。
目前Linux上的I/O多路复用机制主要有三种select,poll,epoll,因为select与poll比较相似,因此这篇文章先讲select和poll函数。
接下来分别从函数、结构、注意点分别进行说明:
参考:APUE,https://juejin.im/post/59f9c6d66fb9a0450e75713f

select函数

在这里插入图片描述

select的异步阻塞I/O模型

  • 应用进程向内核进行系统调用,由于内核中接受缓冲区没有可读内容,因此返回EAGEIN错误;
  • 应用进程阻塞与select调用,等到多个套接字中的任意一个变为可读;
  • 当内核的接收缓冲区中由数据读入时,内核向进程返回可读条件,应用程序再次进行系统调用,内核收到调用后,将内核接受缓冲区的数据复制到应用缓冲区
  • 内核向应用进程返回数据复制成功消息。

select的函数及结构

函数结构
#include <sys/select.h>
#include <sys/time.h>

int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
返回:若有就绪描述符则为其数目,超时则为0,若出错则为-1
函数参数及含义:

1.maxfdp1参数指定待测试的描述符个数,它的值是带测试的最大描述符加1。(在<sys/select.h>中限定了select的最大描述符个数,一般为1024,poll和epoll可以说是没有文件描述符的限制)
2.readfds,writefds,exceptfds指定我们要让内核测试读、写、异常的文件描述符;(fd_set是select特有的的数据结构,可以简单的理解为按位标记具柄的队列),select在处理fd_set是常用的四个宏如下所示

FD_ZERO(int fd, fd_set* fds)             /*clear all bits in fdset*/
FD_SET(int fd, fd_set* fds)              /*turn on the bit for fd in fdset*/
FD_ISSET(int fd, fd_set* fds)            /*turn off the bit for fd in fdset*/ 
FD_CLR(int fd, fd_set* fds)              /*is the bit for fd in fdset?*/

3.timeout参数内核等待的时间,timeval的结构如下,这个参数可以有以下三种情况

struct timeval{
   
   long tv_sec;         /*seconds*/
   long tv_usec;        /*microseconds*/
}
  • 永远等待(阻塞):当timeout为空指针时,仅当文件描述符准备好I/O才返回,否则将一直阻塞;
  • 等待固定时间:当timeout设置为具体的时间,且不为0,当文件描述符准备好I/O,且不超过设定的时间;
  • 不等待(非阻塞):timeout为0,检查文件描述符后理解返回,非阻塞轮询模式。
select的tcp回射服务器程序
/**server端
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define IPADDRESS   "127.0.0.1"
#define PORT        8787
#define MAXLINE     1024
#define LISTENQ     5
#define OPEN_MAX    1000
#define INFTIM      -1

//函数声明
//创建套接字并进行绑定
static int socket_bind(const char* ip,int port);
//IO多路复用select
static void do_select(int listenfd);
//处理多个连接
static void handle_connection(int* connfds, fd_set &allset, fd_set &rset, int num);

int main(int argc,char *argv[])
{
   
    int listenfd,connfd,sockfd;
    struct sockaddr_in cliaddr;
    socklen_t cliaddrlen;
    listenfd = socket_bind(IPADDRESS,PORT);
    listen(listenfd,LISTENQ);
    do_select(listenfd);
    return 0;
}

static int socket_bind(const char* ip,int port)
{
   
    int  listenfd;
    struct sockaddr_in servaddr;
    listenfd = socket(AF_INET,SOCK_STREAM,0);
    if (listenfd == -1){
   
        perror("socket error:");
        exit(1);
    }
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET,ip,&servaddr.sin_addr);
    servaddr.sin_port = htons(port);
    if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1){
   
        perror("bind error: ");
        exit(1);
    }
    return listenfd;
}

static void do_select(int listenfd)
{
   
    int  connfd,sockfd;
    struct sockaddr_in cliaddr;
    socklen_t cliaddrlen;
    int maxfd, maxi, i, nready, client[FD_SETSIZE];
    fd_set rset, allset;
    //添加监听描述符
    //初始化客户连接描述符
    maxfd = listenfd;
    maxi = -1;
    for(int i=0;i<FD_SETSIZE;++i)
        client[i] = -1;
    FD_ZERO(&allset);
    FD_SET(listenfd, 
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值