select函数

该函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或是多个事件发生或经历一段指定的时间后才唤醒它。我们调用select告知内核对哪些描述字(就读、写或异常条件)感兴趣以及等待多长时间。我们感兴趣的描述字不局限于套接口,任何描述字都可以使用select来测试。

select函数原型:

#include<sys/select.h>
#include<sys/time.h>
int select (int maxfd , fd_set *readset ,fd_set *writeset, fd_set *exceptionset , const struct timeval * timeout);
返回:就绪描述字的正数目,0——超时,-1——出错

select函数的参数介绍:maxfd表示待测试的描述字个数,其值应该为最大的描述字+1,中间的readset,writeset,exceptionset指定我们要让内核测试读、写、异常条件的描述字,最后一个参数告知内核等待所指定描述字中的任何一个就绪可花多长时间。

timeval结构:

struct timeval {
long tv_sec; //seconds
long tv_usec ; //microseconds
}

timeval参数有三种可能值:1、NULL:代表永远等待下去,相当于完全阻塞。2、一个固定的值,代表等待一段固定的时间。3、timeval的属性值为0,表示根本不等待,检查描述字之后立即返回,也就是说事非阻塞的。

fd_set结构:

fd_set结构表示一个描述字集。它典型的应该以一个整数数组来表示,其中每个整数中的每一位对应一个描述字。关于fd_set有以下四个宏:

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

select函数修改由指针readset,writeset,exceptionset所指向的描述字集,因而这三个参数都是值-结果参数。也就是说,在select函数执行过程中,会修改其中的值。调用该函数时,我们指定关心的描述字的值,该函数返回时,结果指示哪些描述字已就绪。该函数返回后,我们使用FD_ISSET来测试fd_set数据类型中的描述字。描述字集中任何与未就绪的描述字对应的位返回时均清为0.为此,每次重新调用select函数中,我们都得再次把所有描述字集合中的所关心的位置为1。这也是在稍候的通信例子里,我们设置resset和allset两个集合的原因所在。

select函数返回某个套接口就绪的条件:

select函数的通信例子:一个简单的TCP回射服务器程序

SelectServer.c: 使用select机制的服务器端程序

  1 #include <stdio.h>
2 #include <string.h>
3 #include <arpa/inet.h>
4 #include <netinet/in.h>
5 #include <sys/socket.h>
6 #include <sys/select.h>
7
8 const static int MAXLINE = 1024;
9 const static int SERV_PORT = 10001;
10
11 int main1()
12 {
13 int i , maxi , maxfd, listenfd , connfd , sockfd ;
14 /*nready 描述字的数量*/
15 int nready ,client[FD_SETSIZE];
16 int n ;
17 /*创建描述字集合,由于select函数会把未有事件发生的描述字清零,所以我们设置两个集合*/
18 fd_set rset , allset;
19 char buf[MAXLINE];
20 socklen_t clilen;
21 struct sockaddr_in cliaddr , servaddr;
22 /*创建socket*/
23 listenfd = socket(AF_INET , SOCK_STREAM , 0);
24 /*定义sockaddr_in*/
25 memset(&servaddr , 0 ,sizeof(servaddr));
26 servaddr.sin_family = AF_INET;
27 servaddr.sin_port = htons(SERV_PORT);
28 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
29
30 bind(listenfd, (struct sockaddr *) & servaddr , sizeof(servaddr));
31 listen(listenfd , 100);
32 /*listenfd 是第一个描述字*/
33 /*最大的描述字,用于select函数的第一个参数*/
34 maxfd = listenfd;
35 /*client的数量,用于轮询*/
36 maxi = -1;
37 /*init*/
38 for(i=0 ;i<FD_SETSIZE ; i++)
39 client[i] = -1;
40 FD_ZERO(&allset);
41 FD_SET(listenfd, &allset);
42
43 for (;;)
44 {
45 rset = allset;
46 /*只select出用于读的描述字,阻塞无timeout*/
47 nready = select(maxfd+1 , &rset , NULL , NULL , NULL);
48 if(FD_ISSET(listenfd,&rset))
49 {
50 clilen = sizeof(cliaddr);
51 connfd = accept(listenfd , (struct sockaddr *) & cliaddr , &clilen);
52 /*寻找第一个能放置新的描述字的位置*/
53 for (i=0;i<FD_SETSIZE;i++)
54 {
55 if(client[i]<0)
56 {
57 client[i] = connfd;
58 break;
59 }
60 }
61 /*找不到,说明client已经满了*/
62 if(i==FD_SETSIZE)
63 {
64 printf("Too many clients , over stack .\n");
65 return -1;
66 }
67 FD_SET(connfd,&allset);//设置fd
68 /*更新相关参数*/
69 if(connfd > maxfd) maxfd = connfd;
70 if(i>maxi) maxi = i;
71 if(nready<=1) continue;
72 else nready --;
73 }
74
75 for(i=0 ; i<=maxi ; i++)
76 {
77 if (client[i]<0) continue;
78 sockfd = client[i];
79 if(FD_ISSET(sockfd,&rset))
80 {
81 n = read(sockfd , buf , MAXLINE);
82 if (n==0)
83 {
84 /*当对方关闭的时候,server关闭描述字,并将set的sockfd清空*/
85 close(sockfd);
86 FD_CLR(sockfd,&allset);
87 client[i] = -1;
88 }
89 else
90 {
91 buf[n]='\0';
92 printf("Socket %d said : %s\n",sockfd,buf);
93 write(sockfd,buf,n); //Write back to client
94 }
95 nready --;
96 if(nready<=0) break;
97 }
98 }
99
100 }
101 return 0;
102 }

Client.c: 简单的客户端程序

 1 #include <stdio.h>
2 #include <string.h>
3 #include <arpa/inet.h>
4 #include <netinet/in.h>
5 #include <sys/socket.h>
6
7 #define MAXLINE 1024
8 int main()
9 {
10 int sockfd ,n;
11 char buf [MAXLINE];
12 sockfd = socket(AF_INET,SOCK_STREAM ,0);
13 struct sockaddr_in servaddr;
14 memset(&servaddr, 0 ,sizeof(servaddr));
15 servaddr.sin_family = AF_INET;
16 servaddr.sin_port = htons(10001);
17 inet_pton( AF_INET ,"127.0.0.1" , &servaddr.sin_addr ) ;
18
19 connect(sockfd,(struct sockaddr *)&servaddr , sizeof(servaddr));
20 while(1)
21 {
22 printf("type some words ...\n");
23 scanf("%s",buf);
24 write(sockfd,buf,sizeof(buf));
25 n = read(sockfd,buf,MAXLINE);
26 printf("%d bytes received \n ",n);
27 buf[n] = '\0';
28 printf("%s\n",buf);
29 }
30 close(sockfd);
31 return 0;
32 }

 

总结:

在本文中,介绍了select函数在I/O复用中相关内容,并给出了一个典型的TCP反射程序的样例,使用select可以处理多个客户端的并发需求。在下一篇文章中,将会重点介绍另一种常见的I/O复用函数poll的使用。

unix下网络编程之I/O复用(二)的更多相关文章

  1. unix下网络编程之I/O复用(三)

    poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时, ...

  2. unix下网络编程之I/O复用(一)

    什么是I/O复用? What we need is the capability to tell the kernel that we want to be notified if one or mo ...

  3. unix下网络编程之I/O复用(五)

    前言 本章节是用基本的Linux/Unix基本函数加上select调用编写一个完整的服务器和客户端例子,可在Linux(ubuntu)和Unix(freebsd)上运行,客户端和服务端的功能如下: 客 ...

  4. unix下网络编程之I/O复用(四)

    首先需要了解的是select函数: select函数 #include<sys/select.h> #include<sys/time.h> int select (int m ...

  5. TCP/IP网络编程之I/O复用

    基于I/O复用的服务端 在前面章节的学习中,我们看到了当有新的客户端请求时,服务端进程会创建一个子进程,用于处理和客户端的连接和处理客户端的请求.这是一种并发处理客户端请求的方案,但并不是一个很好的方 ...

  6. linux网络编程之socket编程(十二)

    今天继续学习socket编程,期待的APEC会议终于在京召开了,听说昨晚鸟巢那灯火通明,遍地礼花,有点08年奥运会的架势,有种冲动想去瞅见一下习大大的真容,"伟大的祖国,我爱你~~~&quo ...

  7. linux网络编程之posix线程(二)

    继续接着上次的posix线程来学习: 回顾一下创建线程的函数: pthread_att_t属性变量是需要进行初始化才能够用的,一定初始化了属性变量,它就包含了线程的多种属性的值,那到底有哪些属性了,下 ...

  8. python3网络编程之socketserver

    本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...

  9. GO语言的进阶之路-网络编程之socket

    GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...

随机推荐

  1. LVS/DR 配置

    LVS/DR 配置 实验环境 三台主机:Linux Centos 6.4 32位 调度器Director:192.168.1.160(公网IP).192.168.1.100(VIP) HTTP真实服务 ...

  2. HDU 3449 Consumer

    这是一道依赖背包问题.背包问题通常的解法都是由0/1背包拓展过来的,这道也不例外.我最初想到的做法是,由于有依赖关系,先对附件做个DP,得到1-w的附件背包结果f[i]表示i花费得到的最大收益,然后把 ...

  3. 一步一步粗谈linux文件系统(三)----超级块(superblock)【转】

    本文转载自:https://blog.csdn.net/fenglifeng1987/article/details/8302921 超级块是来描述整个文件系统信息的,可以说是一个全局的数据结构,可以 ...

  4. jq点击切换按钮最简洁代码

    <div id="swphoto">    <img src="1.jpg">    <img src="2.jpg&q ...

  5. hive学习7(条件函数case)

    case函数 语法: CASE WHEN a THEN b [WHEN c THEN d]* [ELSE e] END 说明:如果a为TRUE,则返回b:如果c为TRUE,则返回d:否则返回e 实例 ...

  6. windows系统JDK的安装及环境配置

    本文转载至:http://blog.csdn.net/sweetburden2011/article/details/8881181 一:JDK的安装 1.   首先上甲骨文公司的官方网站下载JDK的 ...

  7. 安装,配置webpack.

    1.下载node.js 2.在需要用到webpack的项目下打开命令窗口运行npm init生成package.js 3.安装webpack,使用npm install webpack --save- ...

  8. makefile下$(wildcard $^),$^,$@,$?,$<,$(@D),$(@F) 含义

    makefile下$(wildcard $^),$^,$@,$?,$<,$(@D),$(@F)代表的不同含义 $(filter-out $(PHONY) $(wildcard $^),$^) 常 ...

  9. QT国际化示例, 检测系统语言,设置适合语言,按键切换显示语言

    1.效果如下图,开启就自动检测系统语言,选择系统语言显示, UI有控件设置,在中文和英文之间切换.. 2. 源码 dialog.h #ifndef DIALOG_H #define DIALOG_H ...

  10. UVA 11029 || Lightoj 1282 Leading and Trailing 数学

    Leading and Trailing You are given two integers: n and k, your task is to find the most significant ...