Linux Socket 编程中I/O Multiplexing 主要通过三个函数来实现:select, poll,epoll来实现。I/O Multiplexing,先构造一张有关描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行I/O时,该函数才返回。在返回时,它告诉进程哪些描述符已准备好可以进行I/O。本文具体介绍一下select 和poll的用法,给出简单的demo代码,简要分析一下这两个函数的使用易出错的地方。

#include<sys/select.h>

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

//Returns: count of ready descriptors, 0 on timeout, -1 on error

  中间三个参数readfds、writefds和exceptfds是指向描述符集的指针。这三个描述符集说明了我们关心的可读、可写或出于异常条件的各个描述符,设置为NULL则表示不关心。每个描述符集存放在一个fd_set数据类型中。这种数据类型为每一可能的描述符保持一位。描述符集的函数接口(可能实现为宏)包括:调用FD_ZERO将一个指定的fd_set变量的所有位设置为0;调用FD_SET设置一个fd_set变量的指定位;调用FD_CLR将一指定位清楚;调用FD_ISSET测试一指定位是否设置。

#include <sys/select.h>

int FD_ISSET(int fd, fd_set *fdset);

  //Returns: nonzero if fd is in set, 0 otherwise

void FD_CLR(int fd, fd_set *fdset);

void FD_SET(int fd, fd_set *fdset);

void FD_ZERO(fd_set *fdset);
  

  文件描述符集fdset中的文件描述符的个数是有限制的,最大值由FD_SETSIZE指定,一般为1024.

  Select 最后一个参数用于设置超时值,当select监听达到超时值时还未有关心的事件发生则返回,函数返回值为0.

struct timeval{

  long tv_sec;//second

  long tv_usec;//microsecond

}

  超时参数如果设置为 NULL 则无限等待。

  下面来是一个简单的select Echo server:

// simpleEcho.cpp
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <vector>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h> #define SEVER_PORT 1314
#define MAX_LINE_LEN 1024 using namespace std; int main()
{
struct sockaddr_in cli_addr, server_addr;
socklen_t sock_len;
vector<int> client(FD_SETSIZE,-); fd_set rset,allset;
int listenfd, connfd, sockfd, maxfd, nready, ix,maxid, nrcv,one;
char addr_str[INET_ADDRSTRLEN],buf[MAX_LINE_LEN]; bzero(&server_addr,sizeof server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SEVER_PORT); listenfd = socket(AF_INET,SOCK_STREAM,); one = ;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,&one, sizeof(one)); if(bind (listenfd ,(struct sockaddr *)&server_addr ,sizeof server_addr) < )
{
printf("socket bind error" );
return ;
} listen(listenfd ,); FD_ZERO(&allset);
FD_SET(listenfd ,&allset ); maxfd = listenfd ;
maxid = - ; while( )
{
rset = allset; //!
nready = select (maxfd + , &rset,NULL,NULL,NULL); if(nready < )
{
printf("select error! \n" );
exit( );
} if(FD_ISSET (listenfd , &rset ))
{
sock_len = sizeof cli_addr;
connfd = accept (listenfd ,(struct sockaddr *)&cli_addr , &sock_len); printf("recieve from : %s at port %d\n" , inet_ntop(AF_INET,&cli_addr .sin_addr ,addr_str ,INET_ADDRSTRLEN ),cli_addr .sin_port ); for(ix = ; ix < static_cast< int>(client .size()); ix++)
{
if(client[ix] < )
{
client[ix] = connfd ;
break;
}
} printf("client[%d] = %d\n" ,ix ,connfd ); if( FD_SETSIZE == ix)
{
printf("too many client! \n" );
exit( );
} if( connfd > maxfd)
{
maxfd = connfd;
} FD_SET(connfd, &allset ); if(ix > maxid )
{
maxid = ix;
} if(--nready == )
{
continue;
} } for(ix = ; ix <= maxid; ix++) //<=
{
if((sockfd = client [ix ]) < )
{
continue;
} if(FD_ISSET (sockfd ,&rset ))
{
if( == (nrcv = read(sockfd,buf,MAX_LINE_LEN )))
{
close(sockfd);
client[ix] = - ;
FD_CLR(sockfd ,&allset );
}
else
{
printf("RECIEVE: %s \n" ,buf );
write(sockfd,buf,nrcv);
}
} if(--nready == )
{
break;
}
} } return ;
}

  在使用select 的时候要注意两点:

    第一个参数需要是当前所关心的文件描述符中最大的一个+1

    第二需要注意的是select的中间3个参数采用了“value-result”(UNP1的说法)的方式,设置了关心的文件描述符进行select,select返回之后对应描述的fdset中只有有事件发生的对应fd会被设置,其它关心但是没有事件发生的描述符将会从fdset中清除掉,如果不进行重新赋值,下次select就不会关注这些描述符了,因此上述代码中allset每次对rset进行复制。

  来看看如果只在while(1) 之前设置rset,在while(1) 中不在每次select之前赋值会发生什么,在控制台输入: strace ./simpleEcho,另外打开一个控制台窗口输入:nc localhost 1314,这作为一个连接Echo server 的 client,然后输入你想发往Echo Server内容。关键我们来看一下Echo server的情况:

  可以看到 select 首先关注的文件描述符 fd == 3,该描述符是listenfd,然后有client连过来,select关注了 fd 3 和 4,4是accept函数打开的用于与client通信的描述符,当client向server写数据之后select关注的描述就只剩下 fd 4了,也就是当前处于连接状态的描述符,如果client主动关闭,select返回之后,下次监听就没有关注的描述符了,可见select函数的“value-result” 返回方式是这样工作的:每次只返回监听描述符中处于active的,其它处于监听的但是当前没有事件发生的描述符则会从监听的fdset中清除掉。因此在每次select之前需要给关注的fdset重新赋值。

  注1:在进行系统调用调试的时候 strace 是一个利器,简单使用方式如上面在运行程序之前加上 strace 即可。在调试代码逻辑的时候当然还是使用gdb了。

  注2Netcat 或者叫 nc 是 Linux 下的一个用于调试和检查网络工具包。可用于创建 TCP/IP 连接,最大的用途就是用来处理 TCP/UDP 套接字。

  

  select 什么时候会处于准备好并返回呢? UNPv1 上进行了详细介绍:

  下面四个条件任何一个满足的时候套件字准备好读:

  1. 套接口接受缓冲区的数据字节数大于等于套接口接受缓冲区的低潮限度当前值。对这样的套接口读操作将不阻塞并返回一个大于0的值(既准备好读入的数据量)。我们可以用套接口选项SO_RCVLOWAT来设置此低潮限度,对于TCP和UDP套接口,其缺省值为1。

  2. 连接的读这一半关闭(也就是接收了FIN的TCP连接)。对这样的套接口读操作将不阻塞并返回0(记文件结束符)。

  3. 套接口是一个监听的套接口且已完成的连接数为非0。正常情况下这样的套接口上的accpet不会被阻塞。

  4. 有一个套接口错误待处理。对这样的套接口操作将不阻塞并返回一个错误-1,errno设置成明确的错误条件。

  以下三个条件的任何一个满足时,套接口准备好写操作:

  1. 套接口发送缓冲区中可用空间的字节数大于等于套接口发送缓冲区低潮限度的当前值,且或者(i)套接口已连接,或者(ii)套接口不需要连接(例如UDP套接字)。这意味着,如果我们将这样的套接口设置为非阻塞,写操作将不阻塞且返回一个正值(例如由传输层传入的字节数)。我们可以用套接口选项SO_SNDLOWAT来设置此低潮限度,对于TCP和UDP套接口其缺省值为2048.

  2. 连接的写这一半关闭,对这样的套接口写操作将产生信号SIGPIPE。

  3. 有一个套接口错误待处理。对这样的套接口操作写操作将不阻塞且返回一个错误-1,errno设置成明确的错误条件。这些待处理的错误也可通过指定套接口选项SO_ERROR调用getsockopt来取得并清除。

  

  如果一个套接口存在带外数据或者仍处于带外标记,那他有异常条件待处理。

  poll留到下一篇吧……

 但,I/O multiplexing 就是这样用的吗?

Socket 编程IO Multiplexing的更多相关文章

  1. 02--Java Socket编程--IO方式

    一.基础知识 1. TCP状态转换知识,可参考: http://www.cnblogs.com/qlee/archive/2011/07/12/2104089.html 2. 数据传输 3. TCP/ ...

  2. Java 中的 IO 与 socket 编程 [ 复习 ]

    一.Unix IO 与 IPC Unix IO:Open-Read or Write-Close IPC:open socket - receive and send to socket - clos ...

  3. IO和socket编程

    五一假期结束了,突然想到3周前去上班的路上看到槐花开的正好.放假也没能采些做槐花糕,到下周肯定就老了.一年就开一次的东西,比如牡丹,花期也就一周.而花开之时,玫瑰和月季无法与之相比.明日黄花蝶也愁.想 ...

  4. Socket网络编程-IO各种概念及多路复用

    Socket网络编程-IO各种概念及多路复用 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.操作系统相关知识 1>.同步和异步  函数或方法被调用的时候,调用者是否得到最 ...

  5. PHP实现系统编程(一) --- 网络Socket及IO多路复用【网摘】

    一直以来,PHP很少用于socket编程,毕竟是一门脚本语言,效率会成为很大的瓶颈,但是不能说PHP就无法用于socket编程,也不能说PHP的socket编程性能就有多么的低,例如知名的一款PHP ...

  6. 从 Socket 编程谈谈 IO 模型(三)

    快过年啦,估计很多朋友已在摸鱼的路上.而我为了兄弟们年后的追逐,却在苦苦寻觅.规划,导致文章更新晚了些,各位猿粉谅解. 上期分享,我们结合新春送祝福的场景,通过一坨坨的代码让 BIO.NIO 编程过程 ...

  7. winsock教程- windows下的socket编程(c语言实现)

    winsock教程- windows下的socket编程(c语言实现) 使用winsock进行socket 编程     这是一个学习windows下socket编程(c语言)的快速指南.这是因为一下 ...

  8. 4.6 并发编程/IO模型

    并发编程/IO模型 背景概念 IO模型概念 IO模型分类 阻塞IO  (blocking IO) 特点: 两个阶段(等待数据和拷贝数据两个阶段)都被block 设置 server.setsockopt ...

  9. {python之IO多路复用} IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) IO模型比较分析 selectors模块

    python之IO多路复用 阅读目录 一 IO模型介绍 二 阻塞IO(blocking IO) 三 非阻塞IO(non-blocking IO) 四 多路复用IO(IO multiplexing) 五 ...

随机推荐

  1. 让TQ2440也用上设备树(2)

    作者 作者:彭東林 郵箱:pengdonglin137@163.com QQ:405728433 软件版本 Linux-4.10.17 概述 在之前的博客里介绍了TQ2440上移植设备树的方法,其实, ...

  2. lufylegend:图形变形2

    下面来详细讲解一下drawtriangles函数的使用方法.并且使用drawtriangles函数实现下面这种处理效果 因为这个方法是从AS3移植而来,所以它的使用方法和AS3基本一致,这里是AS3的 ...

  3. .NET:强签名程序集的加载问题 之 版本重定向

    背景 多数解决方案会包含多个项目,某些支持插件架构的解决方案中,更是包含多个插件项目,这些项目会使用一些第三方NuGet Packages,如果管理不慎,解决方案中会出现多个版本的引用,这在编译期间不 ...

  4. 深入浅出!从语义角度分析隐藏在Unity协程背后的原理

    Unity的协程使用起来比较方便,但是由于其封装和隐藏了太多细节,使其看起来比较神秘.比如协程是否是真正的异步执行?协程与线程到底是什么关系?本文将从语义角度来分析隐藏在协程背后的原理,并使用C++来 ...

  5. 豪斯医生第一季/全集House M.D 1迅雷下载

    豪斯医生 第一季 House M.D. Season 1 (2004)本季看点:态度无礼,表情凶恶,跛足拄着一根藤棍,永远是牛仔裤运动鞋的便装打扮而不是整洁的白大褂,普林斯顿大学附属医院的格雷戈·豪斯 ...

  6. Ioc模式和MEF

    IOC模式 Ioc模式(又称DI:Dependency Injection 依赖注射). 分离关注( Separation of Concerns : SOC)是Ioc模式和AOP产生最原始动力,通过 ...

  7. java 生成zip文件并导出

    总结一下,关于Java下载zip文件并导出的方法,浏览器导出. String downloadName = "下载文件名称.zip"; downloadName = Browser ...

  8. 第一章 Java加解密简介

    1.加密算法: 移位.替代(古典加密) 对称加密:DES.AES 非对称加密:RSA 散列函数算法(单向加密):MD5.SHA.Mac 数字签名算法:RSA.DSA 其中,前三种主要完成数据的加解密: ...

  9. [leetcode]Anagrams @ Python

    原题地址:https://oj.leetcode.com/problems/anagrams/ 题意: Given an array of strings, return all groups of ...

  10. 移动前端调试工具-Weinre真机调试

    之前做移动前端调试页面的时候就是简单的使用Chrome模拟器调试,能满足基本基本的需求,后来发现了基于Web Inspector(Webkit)的远程调试工具Weinre,可以在PC端直接调试运行在移 ...