多路选择I/O提供另一种处理I/O的方法,相比于传统的I/O方法,这种方法更好,更具有效率。多路选择是一种充分利用系统时间的典型。

1、多路选择I/O的概念

当用户需要从网络设备上读数据时,会发生的读操作一般分为两步。

(1)等待数据准备好,等待数据的到达,并且将其复制到内核的缓冲区,该缓冲区在系统态。

(2)复制数据,将数据从内核缓冲区中复制到用户指定的缓冲区中。

一般的读操作形式为:

Int nbytes = read(sfd, buf, MAX);

如果需要的数据没有准备好,例如,数据尚未到达时,read函数发生阻塞,直到所有的数据到达,read函数才将其复制到用户指定的缓冲区,并且返回。如果数据一直未到达,那么read函数将一直阻塞下去,该进程会陷入僵尸状态、这种I/O模型称为阻塞I/O。

为了防止I/O阻塞使进程进入僵死状态,可以使用多路选择I/O。

这种方法的思想是先构造一张需要读取文件描述符的表,调用一个函数轮循这个表中的文件描述符,知道有一个文件描述符可以读写该函数才返回,多路选择I/O需要使用两个系统调用,一个负责检查并返回可用的文件描述符;另一个负责对该文件描述符进行读写。

2、实现多路选择I/O

Linux环境下使用select函数实现多路选择I/O,函数原型如下:

Int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);

头文件: #include <sys/select.h> 

参数说明:

第一个参数maxfdp1表示所关心状态的描述符的个数,正确解释是最大描述符加1。如果maxfdp1的值是2,表示用户关心的描述符数为2;最大的文件描述符为1时,描述符0,和1都会被该函数茶韵,大于1则不关心。一个进程最多可以用于1024个文件描述符,因此maxfdp1的值为(0~1023);

第2、3、4这3个参数是readfds、writefds和exceptfds,分别表示用户关心的可读、可写和异常的各个描述符,这3个参数是3个位向量,每一位对应一个文件描述符的状态,每一位对应一个文件描述符的状态。

第5个参数表示用户期望等待的时间。如果tvptr==NULL表示一直等。

返回值:出错返回-1,返回0表示没有设备准备好,返回值大于0表示准备好的设备数目。

在这个函数中第2、3、4这三个参数是特殊的参数,这三个参数是fd_set数据类型的,fd_set本质上市一个位向量,是一个无符号的整型。其中每一位代表一个设备的状态,如果是1表示被设置,如果是0表示没有被设置。上边的的三个参数分别代表的是可读、可写和异常三种状态,这三个位向量中的每一位代表一个状态。比如readfds是“111000”表示前3个文件可读,后三个文件不可读。

最后通过一个实例来演示使用select函数同时处理多个连接请求的服务器端程序。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include "iolib.h"
#define MAX_LINE 80
int port = ; void my_fun(char *p)
{
if(p == NULL)
exit();
for(; *p != '\0'; p++)
if(*p >= 'A' && *p <= 'Z')
*p = *p - 'A' + 'a';
} int main(void)
{
struct sockaddr_in sin;
struct sockaddr_in cin;
int lfd;
int cfd;
int sfd;
int rdy;
int client[FD_SETSIZE]; ///客户端连接的套接字描述符数组
int maxi;
int maxfd; ///最大连接数
fd_set rest;
fd_set allset;
socklen_t addr_len; ///地址结构的长度
char buf[MAX_LINE];
char addr_p[INET_ADDRSTRLEN];
int i, n;
int len;
int opt = ; ///套接字选项
bzero(&sin, sizeof(sin)); ///填充地址结构
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = hton(port);
/*创建一个面向连接的套接字*/
lfd = socket(AF_INET, SOCK_STREAM, );
if(lfd == -)
{
perror("call to sock");
exit();
} /*设置套接字选项,使用默认选项*/
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /*绑定套接字到地址结构*/
n = bind(lfd, (struct sockaddr_in *) &sin, sizeof(opt));
if(n == -)
{
perror("call to bind");
exit();
} /*开始监听连续请求*/
n = listern(lfd, );
if(n == -)
{
perror("call to listern");
exit();
}
printf("Accepting connecting ...\n");
maxfd = lfd; ///对最大文件描述符进行初始化
maxi = -;
for(i = ; i < FD_SETSIZE; i++) ///初始化客户端连接描述符集合
client[i] = -;
FD_ZERO(&allset); ///清空文件描述符集合
FD_SET(lfd, &allset); ///将监听接字设置在集合内 /*开始服务器程序的死循环*/
while()
{
rset = allset;
/*得到当前可以读的文件描述符*/
rdy = select(maxfd + , &rset, NULL, NULL, NULL);
if(FD_ISSET(lfd, &rest))
{
addr_len = sizeof(cin);
/*创建一个链接描述符*/
cfd = accept(lfd, (struct sockaddr_in *) &cin, &addr_len);
if(cfd == -)
{
perror("fail to accept");
exit();
}
/*查找一个空闲的位置*/
for(i = ; i < FD_SETSIZE; i++)
{
if(client[i] < )
{
client[i] = cfd;
break;
}
}
/*太多的客户端连接,服务器拒绝连接,跳出循环*/
if(i == FD_SETSIZE)
{
printf("too many clients\n");
exit();
}
FD_SET(cfd, &allset); ///设置连接集合
if(cfd > max_fd)
maxfd = cfd;
if(i > maxi)
maxi = i;
if(--rdy <= )
continue;
} for(i = ; i < maxi; i++) ///对每一个连接描述符做处理 { if((sfd = client[i] < )) continue; if(FD_ISSET(sfd, &rset))
{
n = my_read(sfd, buf, MAX_LINE); ///读取数据
if(n == )
{
printf("the other side has been closed\n");
fflush(stdout); ///刷新到输出终端
close(sfd);
FD_CLR(sfd, &allset); ///清空连接描述符数组
client[i] = -;
}
else
{
inet_ntop(AF_INET, &cin.sin_addr, addr_p, sizeof(addr_p));
printf("client IP is %s, port is %d\n", addr_p, ntohs(cin.sin_port));
my_fun(buf);
n = my_write(sfd, buf, len + );
if(n == -)
exit(); }
if(--rdy <= )
break;
}
}
}
close(lfd);
return ;
}

多路选择I/O的更多相关文章

  1. 屏蔽信号的多路选择I/O

    前边提到了多路I/O的方法,这一章屏蔽信号的多路选择与之前的多路I/O一致,只是增加了屏蔽信号的作用.多路选择I/O中我们使用的是select函数,屏蔽信号的多路选择I/O使用的是pselect函数, ...

  2. golang开发:select多路选择

    select 是 Golang 中的一个控制结构,语法上类似于switch 语句,只不过select是用于 goroutine 间通信的 ,每个 case 必须是一个通信操作,要么是发送要么是接收,s ...

  3. (二)js选择结构

    1.js的执行顺序. a)    一般按照书写的顺序来执行. b)    另外一种是通过判断然后执行下一项语句. 注:一般讲js语句写在body内容的最后来执行. 2.js的结构 a)    顺序结构 ...

  4. C语言程序设计(五) 选择控制结构

    第五章 选择控制结构 分治策略:任务分解细化 程序设计语言:为了让计算机执行由高级语言编写的程序指令,必须把这些指令从高级语言形式转换成计算机能理解的机器语言形式,这种转换是由编译器来完成的 算法:为 ...

  5. spring boot(四):thymeleaf使用详解

    在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用.thymeleaf 是新一代的模板引擎,在spring4. ...

  6. php的socket通信

    socket通常叫做'套接字',用于描述IP地址和端口,是一个通信链的句柄.应用程序通过套接字向网络发出请求或者应答忘了请求.socket既不是程序,也不是协议,其只是操作系统提供的通信层的一组抽象A ...

  7. 深入浅出讲解:php的socket通信

    对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问:1.         什么是TCP/IP.UDP?2.         Socke ...

  8. HTTP协议中POST、GET、HEAD、PUT等请求方法以及一些常见错误

    (来源:http://www.tuicool.com/articles/Ermmmyn) HTTP请求方法: 常用方法: Get\Post\Head (1)Get方法. 取回请求URL标志的任何信息, ...

  9. HTTP协议及其请求头分析

    HTTP协议及其请求头分析 HTTP协议及其请求头分析   众所周知,Internet的基本协议是TCP/IP协议,目前广泛采用的FTP.Archie Gopher等是建立在TCP/IP协议之上的应用 ...

随机推荐

  1. CSDN日报20170411 ——《怎样给自己的私活项目标价》

    [程序人生]怎样给自己的私活项目标价 作者:瞬息之间 非常早之前讲过我们"怎么接私活的心得技巧".相信非常多同学听了心里痒痒的.据我认识的(无论是现实生活还是网上接触的)朋友来看. ...

  2. AssetsManager 在ios更新失败解决方案

    AssetsManager在安卓平台使用正常,但是到ios就不行了,最后发现是 cocos2d\cocos\network\CCDownloader-apple.mm中的 - (void)URLSes ...

  3. 《C++ Primer Plus》第4章 学习笔记

    数组.结构和指针是C++的3中符合类型.数组可以在一个数据对象中存储多个同种类型的值.通过使用索引或下标,可以访问数组中各个元素.结构可以将多个不同类型的值存储在同一个数据对象中,可以使用成员关系运算 ...

  4. 《算法竞赛入门经典》学习笔记 2.4.4 C++中的输入输出

    2.4.3 64位整数输入输出long long除了cin,cout,也可以借助于printf和scanf语句,但对应的占位符缺是和平台与编译器相关的:在Linux中,gcc很同意的用%lld:在Wi ...

  5. CSS解决图片缩小不变形

    我会在图片上加: <img style="max-width:80px;max-height:80px;"> 限制其最大宽度和高度

  6. git--简单操作

    Git简介 一.      安装 下载地址: https://git-scm.com/downloads: https://pan.baidu.com/s/1kU5OCOB#list/path=%2F ...

  7. java面向对象基础回顾

    (49)  (0) 面向对象 啥是面向对象 什么是多态多态的机制 接口和抽象类区别 个人理解 代码实现 面向对象 学习java大家接触到的最多的话语无非就是面向对象,可能大家没有仔细研究过这个问题,但 ...

  8. CentOS7部署Haproxy 1.7.2

    一.环境准备 1.操作系统 CentOS-7-x86_64-1611 2.Haproxy版本1.7.2 3.Haproxy服务器IP 192.168.186.131.web1服务器安装并启动Nginx ...

  9. Python巨蟒全栈开发目录

    巨蟒python全栈开发-第一阶段 基础数据类型&基础 1.什么是计算机&&python的简介(待补充) 2.while循环&&格式化输出&&运 ...

  10. ERR_PTR,PTR_ERR还有IS_ERR函数详解

    内核中的函数常常返回指针,问题是如果出错,也希望能够通过返回的指针体现出来. 总体来说,如果内核返回一个指针,那么有三种情况:合法指针,NULL指针和非法指针. 1)合法指针:内核返回的指针一般是指向 ...