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和端口 ...
随机推荐
- linux基础四----samba&&nginx
一 samba 1环境配置: a.确保linux下防火墙关闭比或开放共享目录权限 iPtables -F b.确保setlinux关闭:setenforce 0 c.配置iP 2安装软件包:yum i ...
- 如何单用户模式破解root密码&救援模式破解root密码
学了几天Linux,终于到了装逼时刻,看看如何破译别人的root密码,哈哈哈哈..... 单用户模式破解root密码 重置Centos 7 Root密码的方式 step1 - 在启动grub菜单,选择 ...
- cocos2dx打飞机项目笔记四:Enemy类和EnemyLayer类
Enemy类没什么内容,就create和init方法,根据参数来创建不同的敌机,头文件代码如下: //飞机的类型 enum planeType {smallPlane, midPlane, bigPl ...
- iOS_Quartz 2D绘图
目 录: 一.基础知识掌握 二.Quartz 2D绘图基础:CGContextRef实现简单地绘制图形 三.CGContextRef实现文字.图片.基于路径的图形绘制 四.在内存中绘制位图 五.添加 ...
- 数据库系统概论学习4-SQL 语句和关系代数(二)单表查询
4.12 字符匹配 精确查询和模糊查询 在这一节之前,我们学习的查询几乎都是精确查询,这就需要我们明确地知道某些属性的具体值.例如我们需要查询 'Wangxiaoxiao' 同学的信息,就需要在WHE ...
- create_workqueue和create_singlethread_workqueue【转】
本文转载自:http://bgutech.blog.163.com/blog/static/18261124320116181119889/ 1. 什么是workqueueLinux中的Workque ...
- EF Code-First 学习之旅
什么是Code-First 基本工作流: 写好应用程序的领域类和上下文类→配置领域类的额外映射→运行程序→Code-First API创建新的数据库或与现有数据库对应→添加种子数据到数据库中测试
- 按F12 IE浏览器的开发工具打不开解决方法
在电脑的任务栏中选中开发人员工具窗体右击,出现以下页面: 点击移动菜单项,然后用键盘的上下左右键移动,直到开发人员工具的最小化窗体出现:
- 比较运算符in/instanceof/typeof 逻辑表达式||/&&
1.比较运算符in in运算符希望它的左侧操作数是一个字符串或可以转换为字符串,希望它的右操作数是一个对象, 如果右侧的对象拥有一个名为左侧操作数值的属性名,那么表达式返回true, eg:var a ...
- sql 触发器回顾
一: 触发器是一种特殊的存储过程﹐它不能被显式地调用﹐而是在往表中插入记录﹑更新记录或者删除记录时被自动地激活.所以触发器可以用来实现对表实施复杂的完整性约束. 二: SQL Server为每个触发器 ...