unix下网络编程之I/O复用(二)
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复用(二)的更多相关文章
- unix下网络编程之I/O复用(三)
poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时, ...
- 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 ...
- unix下网络编程之I/O复用(五)
前言 本章节是用基本的Linux/Unix基本函数加上select调用编写一个完整的服务器和客户端例子,可在Linux(ubuntu)和Unix(freebsd)上运行,客户端和服务端的功能如下: 客 ...
- unix下网络编程之I/O复用(四)
首先需要了解的是select函数: select函数 #include<sys/select.h> #include<sys/time.h> int select (int m ...
- TCP/IP网络编程之I/O复用
基于I/O复用的服务端 在前面章节的学习中,我们看到了当有新的客户端请求时,服务端进程会创建一个子进程,用于处理和客户端的连接和处理客户端的请求.这是一种并发处理客户端请求的方案,但并不是一个很好的方 ...
- linux网络编程之socket编程(十二)
今天继续学习socket编程,期待的APEC会议终于在京召开了,听说昨晚鸟巢那灯火通明,遍地礼花,有点08年奥运会的架势,有种冲动想去瞅见一下习大大的真容,"伟大的祖国,我爱你~~~&quo ...
- linux网络编程之posix线程(二)
继续接着上次的posix线程来学习: 回顾一下创建线程的函数: pthread_att_t属性变量是需要进行初始化才能够用的,一定初始化了属性变量,它就包含了线程的多种属性的值,那到底有哪些属性了,下 ...
- python3网络编程之socketserver
本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...
- GO语言的进阶之路-网络编程之socket
GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...
随机推荐
- Shell 语句
一 test 测试: 测试命令 test [ ] [[ ]] (( ))打开man test 逐一介绍每个参数 浮点计算:echo 'scale=2;1/3'|bc -l 测试操作命令执行后会返回到 ...
- Python编程-多态、封装、特性
一.多态与多态性 1.多态 (1)什么是多态 多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承) 序列类型有多种形态:字符串,列表,元组. 动物有多种形态:人,狗,猪 文 ...
- 主攻ASP.NET.4.5.1 MVC5.0之重生:创建UIHelper通用自定义分页和选择开关与PagesHelper和IsSelect简单用法
@helper放入地方 分页效果 选择开关编辑调用 <dl> <dd class="dc1">是否主管:</dd> <dd> @UI ...
- R语言的输出函数cat,sink,writeLines,write.table
根据输出的方向分为输出到屏幕和输出到文件. 1.cat函数即能输出到屏幕,也能输出到文件. 使用方式:cat(... , file = "", sep = " " ...
- JVM内存的堆、栈和方法区
JVM的内存分为堆.栈.方法区和程序计数器4个区域 存储内容:基本类型,对象引用,对象本身,class,常量,static变量 堆: 拥有者:所有线程 内容:对象本身,不存放基本类型和对象引用 垃圾回 ...
- cocos2dx打飞机项目笔记五:CCSpriteBatchNode 的使用
在上一节里,在头文件看到 定义了一个 CCSpriteBatchNode* batchNode;,在addEnemy方法里看到 batchNode->addChild(enemy); 新建的敌机 ...
- Zabbix3.2 客户端安装
查看客户端环境: # cat /etc/redhat-release CentOS Linux release (Core) # uname -r -.el7.x86_64 Centos7 客户端: ...
- java基础(4)-数组(1)
数组:存储同一种数据类型的多个元素的容器数组初始化: 元素类型[] 数组名 = new 元素类型[数组长度]int [] arr = new int[5] 元素类型[] 数组名 = new 元素类型[ ...
- Spring初学之泛型依赖注入
主要讲泛型依赖注入,所以核心在java文件,配置文件中只需配置扫描包即可,如下: <?xml version="1.0" encoding="UTF-8" ...
- VS不显示引用的几种情况
1.动画事件 2协程调用(以字符串的形式) 3.Invoke调用(以字符串的形式)