多路复用I/O select()
select(),poll(),epoll()的总结:http://www.cnblogs.com/Anker/p/3265058.html
在socket编程中,仅仅使用connect,accept、这些带有阻塞(block)的程序时,如果没有某个时间来满足条件,就会一直处于阻塞状态。可想而知在一些应用中是不能满足要求的,因此就有了select,poll,epoll这些非阻塞的程序来满足需求。这些程序在进程或者线程中执行时不必非要等待事件的发生,一旦执行肯定会有返回值,不同的值反映不同的函数执行情况。如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码告知事件未发生。而进程或线程继续执行,从而提高效率,它们能够监视我们需要的文件描述符的变化情况——读、写就绪或是异常。
select();
函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
先说明两个结构体:
第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set*),将一个给定的文件描述符从集合中删除FD_CLR(int,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。
第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。
具体解释select的参数:
int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
fd_set * readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
fd_set * writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
fd_set * errorfds同上面两个参数的意图,用来监视文件错误异常。
struct timeval * timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态:
第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
返回值:返回状态发生变化的描述符总数。
负值:select错误
正值:某些文件可读写或出错
0:等待超时,没有可读写或错误的文件
使用示例:
客户端
#include <stdio.h>
#include "debug.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> int main(int argc, char **argv)
{
if( != argc)
{
printf("Usage: %s <IP> <PORT>\n", argv[]);
return -;
} int sockfd = socket(AF_INET, SOCK_STREAM, ); //
if(- == sockfd)
errsys("socket"); struct sockaddr_in serveraddr = {};
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[]));
serveraddr.sin_addr.s_addr = inet_addr(argv[]);//IPv4
int len = sizeof serveraddr; if(- == connect(sockfd, (struct sockaddr*)&serveraddr, len))
errsys("connect"); char buf[] = {};
while()
{
printf("mydatabase> ");fflush(stdout);
gets(buf);
int ret = write(sockfd, buf, sizeof buf);
ret = read(sockfd, buf, sizeof buf);
printf("%s\n", buf);
}
close(sockfd);
}
服务器
#include <stdio.h>
#include <stdio.h>
#include "debug.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> int main()
{
int listenfd = socket(AF_INET, SOCK_STREAM, ); //创建server socket
if(- == listenfd)
errsys("socket"); struct sockaddr_in myaddr = {};
struct sockaddr_in clientaddr = {};
myaddr.sin_family = AF_INET; //IPV4
myaddr.sin_port = htons(); //port 8888
myaddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //INADDR_ANY(监听通过任一一张网卡建立的连接)
int len = sizeof myaddr; if(- == bind(listenfd, (struct sockaddr*)&myaddr, len)) //绑定server套接字
errsys("bind");
if(- == listen(listenfd, )) //启动监听、监听等待数量为10(并不会阻塞)
errsys("listen"); fd_set readfds;
fd_set writefds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(listenfd, &readfds); //将server socket加入到描述符集合中 fd_set temprfds = readfds;
fd_set tempwfds = writefds;
int maxfd = listenfd; //最大文件描述符为listenfd #define BUFSIZE 100
#define MAXNFD 1024
int nready;
char buf[MAXNFD][BUFSIZE] = {}; while()
{
temprfds = readfds;
tempwfds = writefds; if(- == (nready = select(maxfd+, &temprfds, &tempwfds, NULL, NULL))) //阻塞,测试文件描述符集合是否变动
//变动文件描述符放在 temprfds和 tempwfds中
errsys("select"); if(FD_ISSET(listenfd, &temprfds)) //客户端连接请求
{
int sockfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len); //获得client socket
//传参回写:len作为参数传入,判断实际长度是否与len相同,还会将实际大小通过len返回
if(- == sockfd)
errsys("accept");
debug("incoming: %s\n", inet_ntoa( clientaddr.sin_addr) );
FD_SET(sockfd, &readfds); //将client socket加入集合
maxfd = maxfd>sockfd?maxfd:sockfd; //更新最大文件描述符
if(--nready==)
continue;
} int fd = ;
for(;fd<=maxfd; fd++)
{
if(fd == listenfd) //客户端连接请求前面已处理
continue; if(FD_ISSET(fd, &temprfds)) //相关描述符就绪
{
int ret = read(fd, buf[fd], sizeof buf[]); //从client读取(接收)数据
if( == ret) //客户端已关闭或超时
{
close(fd); //释放client socket
FD_CLR(fd, &readfds); //从集合中清除 client socket
if(maxfd==fd) --maxfd; //更新最大描述符
continue;
}
FD_SET(fd, &writefds); //成功接收数据,将clientfd加入测试集合
} if(FD_ISSET(fd, &tempwfds)) //向client写入请求就绪
{
int ret = write(fd, buf[fd], sizeof buf[]);
printf("ret %d: %d\n", fd, ret);
FD_CLR(fd, &writefds); //从集合中清除写请求测试
}
}
} close(listenfd);
}
多路复用I/O select()的更多相关文章
- linux下多路复用模型之Select模型
Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模 ...
- IO模型之IO多路复用 异步IO select poll epoll 的用法
IO 模型之 多路复用 IO 多路复用IO IO multiplexing 这个词可能有点陌生,但是如果我说 select/epoll ,大概就都能明白了.有些地方也称这种IO方式为 事件驱动IO ( ...
- IO多路复用模型之select()函数详解
IO复用 我们首先来看看服务器编程的模型,客户端发来的请求服务端会产生一个进程来对其进行服务,每当来一个客户请求就产生一个进程来服务,然而进程不可能无限制的产生,因此为了解决大量客户端访问的问题,引入 ...
- 五种网络IO模型以及多路复用IO中select/epoll对比
下面都是以网络读数据为例 [2阶段网络IO] 第一阶段:等待数据 wait for data 第二阶段:从内核复制数据到用户 copy data from kernel to user 下面是5种网络 ...
- 第五十五节,IO多路复用select模块加socket模块,伪多线并发
IO多路复用select模块加socket模块,伪多线并发,并不是真正的多线程并发,实际通过循环等待还是一个一个处理的 IO多路复用,lo就是文件或数据的输入输出,IO多路复用就是可以多用户操作 IO ...
- 【python】-- IO多路复用(select、poll、epoll)介绍及实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- IO多路复用(select、poll、epoll)介绍及select、epoll的实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)
Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...
- python_day10_IO多路复用
一.python小知识 1.python中无模块作用域 Java /c# 不可以, Python.javascript 可以 for i in range(10): name = i print(i) ...
随机推荐
- Linux常用命令之 查找命令 find(一)
我们都知道在Linux中有着上千条的命令,而常用命令不过百条. 我们也知道在Linux里面“一切皆文件”,那么如何能够快速的找到你想要找的东西就显得尤为重要. find是Linux里面最重要的命令之一 ...
- Python字符串方法
capitalize() 把字符串的第一个字符改为大写 casefold() 把整个字符串的所有字符改为小写 center(width) 将字符串居中,并使用空格填充至长度 width 的新字符串 c ...
- 前端 JavaScript基础
前言 JavaScript 是属于网络的脚本语言,被数百万计的网页用来改进设计.验证表单.检测浏览器.创建cookies,以及更多的应用. 一.如何编写 1.存在形式 方式一:存在js文件中,即写入j ...
- HTML5和CSS3实例教程[总结二]
基于contenteditable属性实现在位编辑 HTML5规范引入了contenteditable属性,它几乎可以用在任何元素上,只要添加这一属性 即可变为可编译区域 <!DOCTYPE h ...
- css如何实现背景透明,文字不透明?
之前做了个半透明弹层,但设置背景半透明时,子元素包含的字体及其它元素也都变成了半透明.对opacity这个属性认识的不透彻,在这里做一些总结,方便以后使用. 背景透明,文字不透明的解决方法: ...
- 打开局域网项目,显示“项目位置不受信任”的解决办法(VS2008)
弄了几天,网上搜了个遍,愣是解决不了,绝望的时候闭着眼睛胡搞,居然解决了,哈哈.... 开发环境:visual studio 2008 项目位置:局域网其他电脑内 出现问题: 1.弹出“”的对话框,如 ...
- (转)弹出窗口lhgDialog API文档
应用到你的项目 如果您使用独立版本的lhgDialog窗口组件,您只需在页面head中引入lhgcore.lhgdialog.min.js文件,4.1.1+版本做了修改可以和jQuerya库同时引用, ...
- [C#]asp.net开发微信公众平台----目录汇总-持续更新
1.[c#]asp.net微信公众平台开发(1)数据库设计 2.[c#]asp.net微信公众平台开发(2)多层架构框架搭建和入口实现 3.[c#]asp.net微信公众平台开发(3)微信消息封装及反 ...
- IIS上部署网站404错误
新装的系统上部署.net网站遇到403.404错误,可能原因记录: 1.应用程序池选择错误,一般选择4.0的 2.ASP.NET4.0应用程序池未安装(一般先安装了framework4.0,后安装ii ...
- C++ list用法
创建一个list实例并赋值: // 创建实例以及赋值 #include <iostream> #include <list> using namespace std; int ...