IO复用_select函数

select函数:

#include <sys/select.h>
#include <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);

参数含义:

  • nfds:一个整型变量,它比所有文件描述符集合中的文件描述符的最大值大1。使用select的时候,必须计算最大值的文件描述符的值,将值通过nfds传入。
  • readfds:这个文件描述符集合监视文件集中的任何文件是否有数据可读,当select()返回的时候,readfds将清除其中不可读的文件描述符,只留下可读的文件描述符,即可以被recv()、read()等进行读操作。
  • writefds:这个文件描述符集合监视文件集中的任何文件是否有数据可写,当select()返回的时候,readfds将清除其中不可写的文件描述符,只留下可写的文件描述符,即可以被send()、write()等进行读操作。
  • exceptfds:这个文件集合将监视文件中的任何文件是否发生错误,其实,它还能用于监视带外数据OOB,带外数据使用MSG_OOB标志发送到套接字上。当select()返回时,readfds将清除其中的其他文件描述符,只留下可读的OOB数据。
  • timeout:设置在select()所监视的文件集合中的事件没有发生时,最长的等待时间,当超过这个时间时,函数返回。当超时返回为NULL时,表示阻塞操作,会一直等待,直到某个监视的文件集中的某个文件描述符符合返回条件。当timeout的值为0时,select()会立即返回。
  • sigmask:信号

返回值:

当返回大于0的正值:监视的文件集合中的文件描述符符合上述要求。

当等于0时:超时。

当为-1时:发生错误

struct timeval结构:

struct timeval
{
       time_t tv_sec;                //秒
       long tv_usec;                 // 微秒,即1/1000000s
}

另外,还有4个宏操作文件描述符的集合:

FD_ZERO():清理文件描述符集合;

FD_SET():向某个文件描述符集合中加入文件描述符;

FD_CLR():从某个文件描述符集合中取某个文件描述符;

FD_ISSET():测试某个文件描述符是否为某个集合中的一员。

注:文件描述符的集合存在最大的限制,其最大值为FD_SETSIZE,当超出最大值时,发生不可预料的事。同时,可以修改这个值,但是监视集合的效率会降低,因为select()轮询的线性的,更加牛B的的函数,请查看epoll:epoll没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于2048, 一般来说这个数目和系统内存关系很大。最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远高于select和poll。在内存拷贝上,epoll在这点上使用了“共享内存”,这个内存拷贝也省略了----epoll的相关函数白天写了,现在睡觉。

附加:使用select()写的IO复用循环服务器模型的例子

  1 #include <sys/types.h>
  2 #include <sys/socket.h>
  3 #include <netinet/in.h>
  4 #include <time.h>
  5 #include <string.h>
  6 #include <stdio.h>
  7 #include <pthread.h>
  8  #include <sys/select.h>
  9 #define BUFFLEN 1024
 10 #define SERVER_PORT 8888
 11 #define BACKLOG 5
 12 #define CLIENTNUM 1024/*最大支持客户端数量*/
 13
 14 /*可连接客户端的文件描述符数组*/
 15 int connect_host[CLIENTNUM];
 16 int connect_number =  0;
 17
 18 //处理客户端请求函数
 19 static void *handle_request(void *argv)
 20 {
 21     time_t now;        /*时间*/
 22     char buff[BUFFLEN];/*收发数据缓冲区*/
 23     int n = 0;
 24
 25     int maxfd = -1;/*最大侦听文件描述符*/
 26     fd_set scanfd;    /*侦听描述符集合*/
 27     struct   timeval   timeout;     /*超时*/
 28     timeout.tv_sec     =   1; /*   阻塞1秒后超时返回   */
 29     timeout.tv_usec   =   0;
 30
 31     int i = 0;
 32     int err  = -1;
 33     for(;;)
 34     {
 35         /*最大文件描述符值初始化为-1*/
 36         maxfd = -1;
 37         FD_ZERO(&scanfd);/*清零文件描述符集合*/
 38         for(i=0;i<CLIENTNUM;i++)/*将文件描述符放入集合*/
 39         {
 40             if(connect_host[i] != -1)/*合法的文件描述符*/
 41             {
 42                 FD_SET(connect_host[i], &scanfd);/*放入集合*/
 43                 if(maxfd <     connect_host[i])/*更新最大文件描述符值*/
 44                 {
 45                     maxfd = connect_host[i];
 46                 }
 47             }
 48         }
 49         /*select等待*/
 50         err = select(maxfd + 1, &scanfd, NULL, NULL, &timeout) ;
 51         switch(err)
 52         {
 53             case 0:/*超时*/
 54                 break;
 55             case -1:/*错误发生*/
 56                 break;
 57             default:/*有可读套接字文件描述符*/
 58                 if(connect_number<=0)
 59                     break;
 60                 for(i = 0;i<CLIENTNUM;i++)
 61                 {
 62                     /*查找激活的文件描述符*/
 63                     if(connect_host[i] != -1)
 64                     if(FD_ISSET(connect_host[i],&scanfd))
 65                     {
 66                         memset(buff, 0, BUFFLEN);/*清零*/
 67                         n = recv(connect_host[i], buff, BUFFLEN,0);/*接收发送方数据*/
 68                         if(n > 0 && !strncmp(buff, "TIME", 4))/*判断是否合法接收数据*/
 69                         {
 70                             memset(buff, 0, BUFFLEN);/*清零*/
 71                             now = time(NULL);/*当前时间*/
 72                             sprintf(buff, "%24s\r\n",ctime(&now));/*将时间拷贝入缓冲区*/
 73                             send(connect_host[i], buff, strlen(buff),0);/*发送数据*/
 74                         }
 75                         /*更新文件描述符在数组中的值*/
 76                         connect_host[i] = -1;
 77                         connect_number --;    /*客户端计数器减1*/
 78                         /*关闭客户端*/
 79                         close(connect_host[i]);
 80                     }
 81                 }
 82                 break;
 83         }
 84     }
 85
 86     return NULL;
 87 }
 88
 89 //处理客户端连接函数
 90 static void *handle_connect(void *argv)
 91 {
 92     int s_s = *((int*)argv) ;/*获得服务器侦听套接字文件描述符*/
 93     int s_c = -1;/*连接客户端文件描述符*/
 94     struct sockaddr_in from;
 95     int len = sizeof(from);
 96     /*接收客户端连接*/
 97     for(;;)
 98     {
 99         int i = 0;
100         int s_c = accept(s_s, (struct sockaddr*)&from, &len);/*接收客户端的请求*/
101         printf("a client connect, from:%s\n",inet_ntoa(from.sin_addr));
102         /*查找合适位置,将客户端的文件描述符放入*/
103         for(i=0;i<CLIENTNUM;i++)
104         {
105             if(connect_host[i] == -1)/*找到*/
106             {
107                 /*放入*/
108                 connect_host[i]= s_c;
109
110                 /*客户端计数器加1*/
111                 connect_number ++;
112                 /*继续轮询等待客户端连接*/
113                 break;
114             }
115         }
116     }
117     return NULL;
118 }
119
120 int main(int argc, char *argv[])
121 {
122     int s_s;    /*服务器套接字文件描述符*/
123     struct sockaddr_in local;    /*本地地址*/
124     int i = 0;
125     memset(connect_host, -1, CLIENTNUM);
126
127     /*建立TCP套接字*/
128     s_s = socket(AF_INET, SOCK_STREAM, 0);
129
130     /*初始化地址接哦股*/
131     memset(&local, 0, sizeof(local));/*清零*/
132     local.sin_family = AF_INET;/*AF_INET协议族*/
133     local.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/
134     local.sin_port = htons(SERVER_PORT);/*服务器端口*/
135
136     /*将套接字文件描述符绑定到本地地址和端口*/
137     int err = bind(s_s, (struct sockaddr*)&local, sizeof(local));
138     err = listen(s_s, BACKLOG);/*侦听*/
139
140     pthread_t  thread_do[2];/*线程ID*/
141     /*创建线程处理客户端连接*/
142     pthread_create(&thread_do[0],/*线程ID*/
143                     NULL,/*属性*/
144                     handle_connect,/*线程回调函数*/
145                     (void*)&s_s);        /*线程参数*/
146     /*创建线程处理客户端请求*/
147     pthread_create(&thread_do[1],/*线程ID*/
148                     NULL,/*属性*/
149                     handle_request,/*线程回调函数*/
150                     NULL);        /*线程参数*/
151     /*等待线程结束*/
152     for(i=0;i<2;i++)
153         pthread_join(thread_do[i], NULL);
154
155     close(s_s);
156
157     return 0;
158 }
作者:orange1438
出处:http://www.cnblogs.com/orange1438/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
时间: 2024-10-12 14:04:20

IO复用_select函数的相关文章

IO复用_epoll函数

由于poll()和select()的局限,2.6内核以上引用了event poll机制(就是说的epoll),虽然比前2个实现复杂得多,epoll解决了它们共有的基本性能问题,并增加了新的特性. poll()和select()每次调用的时候,都需要所有被监听的文件的描述符.内核必须遍历所有被监视的文件描述符.当这个表变得很大时,每次调用,都会成为性能降低. epoll把监听注册从实际监听中分离出来,从而解决这个问题:一个系统调用初始化一个epoll上下文,另一个从上下文中加入或删除需要监视的文件

IO复用之——epoll

一. 关于epoll 对于IO复用模型,前面谈论过了关于select和poll函数的使用,select提供给用户一个关于存储事件的数据结构fd_set来统一监测等待事件的就绪,分为读.写和异常事件集:而poll则是用一个个的pollfd类型的结构体管理事件的文件描述符和事件所关心的events,并通过结构体里面的输出型参数revents来通知用户事件的就绪状态: 但是对于上述两种函数,都是需要用户遍历所有的事件集合来确定到底是哪一个或者是哪些事件已经就绪可以进行数据的处理了,因此当要处理等待的事

【Unix网络编程】chapter6 IO复用:select和poll函数

chapter6 6.1 概述 I/O复用典型使用在下列网络应用场合. (1):当客户处理多个描述符时,必须使用IO复用 (2):一个客户同时处理多个套接字是可能的,不过不叫少见. (3):如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字. (4):如果一个服务器既要处理TCP,又要处理UDP (5):如果一个服务器要处理多个服务或多个协议 IO复用并非只限于网络,许多重要的应用程序也需要使用这项技术. 6.2 I/O模型 在Unix下可用的5种I/O模型的基本区别: (1)阻塞式I

epoll实现IO复用,TCP通信

函数原型: 函数说明:该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它. 参数说明: fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符: 每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便:特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率:这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述

IO复用之——poll

一. 关于poll 对于IO复用模型,其优点无疑是免去了对一个个IO事件就绪的等待,转而代之的是同时对多个IO数据的检测,当检测等待的事件中至少有一个就绪的时候,就会返回告诉用户进程"已经有数据准备好了,快看看是哪个赶紧处理",而对于IO复用的实现,除了可以用select函数,另外一个函数仍然支持这种复用IO模型,就是poll函数: 二. poll函数的用法 虽然同样是对多个IO事件进行检测等待,但poll和select多少还是有些不同的: 函数参数中, 先来说nfds,这个是和sel

《深入理解计算机系统》Tiny服务器4——epoll类型IO复用版Tiny

前几篇博客分别讲了基于多进程.select类型的IO复用.poll类型的IO复用以及多线程版本的Tiny服务器模型,并给出了主要的代码.至于剩下的epoll类型的IO复用版,本来打算草草带过,毕竟和其他两种IO复用模型差不太多.但今天在看Michael Kerrisk的<Linux/UNIX系统编程手册>时,看到了一章专门用来讲解epoll函数,及其IO复用模型.于是,自己也就动手把Tiny改版了一下.感兴趣的同学可以参考上述手册的下册1113页,有对于epoll比较详细的讲解. 前边针对IO

IO复用

IO复用简单介绍 IO复用使得程序能同一时候监听多个文件描写叙述符.这对提高程序的性能至关重要.通常.网络程序在下列情况下须要使用IO复用技术: client程序要同一时候处理多个socket. client程序要同一时候处理用户输入和网络连接. TCPserver同一时候处理监听socket和连接socket. server要同一时候处理TCP请求和UDP请求. 须要指出的是.IO复用尽管能同一时候监听多个文件描写叙述符,但它本身是堵塞的.而且当多个文件描写叙述符同一时候就绪时,假设不採取额外

Libevent的IO复用技术和定时事件原理

Libevent 是一个用C语言编写的.轻量级的开源高性能网络库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大:源代码相当精炼.易读:跨平台,支持 Windows. Linux. *BSD 和 Mac Os:支持多种 I/O 多路复用技术, epoll. poll. dev/poll. select 和 kqueue 等:支持 I/O,定时器和信号等事件:注册事件优先级. 1 Libevent中的epoll Libevent重

Linux中的IO复用接口简介(文件监视?)

I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux中,提供了select.poll.epoll三类接口来实现I/O复用. select函数接口 select中主要就是一个select函数,用于监听指定事件的发生,原型如下: 12345 #include<sys/select.h>#include<sys/time.h>int sele