Iinux中select函数的使用

Similar documents
Slide 1

/ / (FC 3)...

W. Richard Stevens UNIX Sockets API echo Sockets TCP OOB IO C struct C/C++ UNIX fork() select(2)/poll(2)/epoll(4) IO IO CPU 100% libevent UNIX CPU IO

第一章 概论

untitled

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例

校友会系统白皮书feb_08

UDP 8.2 TCP/IP OSI OSI 3 OSI TCP/IP IP TCP/IP TCP/IP Transport Control Protocol TCP User Datagram Protocol UDP TCP TCP/IP IP TCP TCP/IP TC

嵌入式Linux块设备驱动开发解析

威 福 髮 藝 店 桃 園 市 蘆 竹 區 中 山 里 福 祿 一 街 48 號 地 下 一 樓 50,000 獨 資 李 依 純 105/04/06 府 經 登 字 第 號 宏 品 餐 飲 桃 園 市 桃 園 區 信 光 里 民

Microsoft PowerPoint - os_4.ppt

网络程序设计(socketAPI)

FortiADC SLB Virtual Server L7 方式部署介绍 版本 1.0 时间支持的版本作者状态反馈 2015 年 10 月 FortiADC v4.3.x 刘康明已审核

Chapter #

新・明解C言語入門編『索引』

untitled

提问袁小兵:

<4D F736F F D20C9CFBAA3CAD0BCC6CBE3BBFAB5C8BCB6BFBCCAD4C8FDBCB6BFBCCAD4B4F3B8D95FBDA8D2E9B8E55F5F E646F63>

Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.07.doc

C6_ppt.PDF

PowerPoint Presentation

VoIP Make a Rtp Call VoIP Abstract... 2 VoIP RTP...3 Socket IP...9 Config Two Voice-hub

上海市教育考试院关于印发新修订的

四川省普通高等学校

FY.DOC

int *p int a 0x00C7 0x00C7 0x00C int I[2], *pi = &I[0]; pi++; char C[2], *pc = &C[0]; pc++; float F[2], *pf = &F[0]; pf++;

第1章 QBASIC语言概述

C语言的应用.PDF






ebook15-12

Guava学习之Resources

一个开放源码的嵌入式仿真环境 ― SkyEye

Data Server_new_.doc

Symantec™ Sygate Enterprise Protection 防护代理安装使用指南

Linux网络编程socket错误分析

ebook15-10

epub 33-8

MASQUERADE # iptables -t nat -A POSTROUTING -s / o eth0 -j # sysctl net.ipv4.ip_forward=1 # iptables -P FORWARD DROP #

ebook15-C

untitled

Slide 1

项目采购需求编写模板

目 錄 頁 次 政 風 法 令 宣 導 一 行 政 中 立 實 務 探 討 二 收 到 支 付 命 令 不 可 置 之 不 理 3 9 公 務 機 密 維 護 宣 導 一 淺 談 駭 客 攻 擊 14 二 何 不 自 行 設 計 入 侵 防 禦 系 統? 17 安 全 維 護 宣 導 認 識 爆 竹

CC213

《C语言程序设计》教材习题参考答案

? 這 全 都 是 市 政 府 提 供 給 我 的 資 料 低 底 盤 公 車 計 畫 96 年 預 算 新 台 幣 4,500 萬 元 97 年 預 算 新 台 幣 1 億 6,500 萬 元 98 年 預 算 新 台 幣 3 億 2,300 萬 元, 共 有 307 台 低 底 盤 公 車,99

关于建立境内违法互联网站黑名单管理制度的通知

<4D F736F F F696E74202D FC2B2B3F85FA44AAB49B0BBB4FABB50B977A8BEA874B2CEC2B2A4B6BB50C0B3A5CE2E707074>

(Methods) Client Server Microsoft Winsock Control VB 1 VB Microsoft Winsock Control 6.0 Microsoft Winsock Control 6.0 1(a). 2

chap07.key

穨CAS1042快速安速說明.doc

Microsoft Word - 实用案例.doc

HKCERT


穨CAS1042中文手冊.doc

Red Flag Linux Desktop 4.0 Red Flag Linux Desktop 4.0 1

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

untitled

文件

Chap07

CC213

新版 明解C++入門編

ebook67-9

1 Project New Project 1 2 Windows 1 3 N C test Windows uv2 KEIL uvision2 1 2 New Project Ateml AT89C AT89C51 3 KEIL Demo C C File

0 配置 Host MIB 设备 V ( 简体版 ) 0 Update: 2016/1/30

Microsoft PowerPoint - 4. 数组和字符串Arrays and Strings.ppt [兼容模式]

JLX

Transcription:

Iinux 中 select 函数的使用 阻塞式 I/O 编程有两个特点 : 一 如果一个发现 I\O 有输入, 读取的过程中, 另外一个也有了输入, 这时候不会产生任何反应, 也就是需要你的程序语句去 select 的时候才知道有数据输入 二 程序去 select 的时候, 如果没有数据输入, 程序会一直等待, 直到有数据位置, 也就是程序中无需循环和 sleep Select 在 Socket 编程中还是比较重要的, 可是对于初学 Socket 的人来说都不太爱用 Select 写程序, 他们只是习惯写诸如 connect accept recv 或 recvfrom 这样的阻塞程序 ( 所谓阻塞方式 block, 顾名思义, 就是进程或是线程执行到这些函数时必须等待某个事件的发生, 如果事件没有发生, 进程或线程就被阻塞, 函数不能立即返回 ) 可是使用 Select 就可以完成非阻塞 ( 所谓非阻塞方式 non-block, 就是进程或线程执行此函数时不必非要等待事件的发生, 一旦执行肯定返回, 以返回值的不同来反映函数的执行情况, 如果事件发生则与阻塞方式相同, 若事件没有发生则返回一个代码来告知事件未发生, 而进程或线程继续执行, 所以效率较高 ) 方式工作的程序, 它能够监视我们需要监视的文件描述符的变化情况 读写或是异常 下面详细介绍一下! Select 的函数格式 ( 我所说的是 Unix 系统下的伯克利 socket 编程, 和 windows 下的有区别, 一会儿说明 ): int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); 先说明两个结构体 : 第一,struct fd_set 可以理解为一个集合, 这个集合中存放的是文件描述符 (file descriptor), 即文件句柄, 这可以是我们所说的普通意义的文件, 当然 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: 等待超时, 没有可读写或错误的文件 在有了 select 后可以写出像样的网络程序来! 举个简单的例子, 就是从网络上接受数据写入一个文件中 例子 : main() int sock; FILE *fp; struct fd_set fds; struct timeval timeout=3,0}; //select 等待 3 秒,3 秒轮询, 要非阻塞就置 0

char buffer[256]=0}; //256 字节的接收缓冲区 /* 假定已经建立 UDP 连接, 具体过程不写, 简单, 当然 TCP 也同理, 主机 ip 和 port 都已经给定, 要写的文件已经打开 sock=socket(...); bind(...); fp=fopen(...); */ while(1) FD_ZERO(&fds); // 每次循环都要清空集合, 否则不能检测描述符变化 FD_SET(sock,&fds); // 添加描述符 FD_SET(fp,&fds); // 同上 maxfdp=sock>fp?sock+1:fp+1; // 描述符最大值加 1 switch(select(maxfdp,&fds,&fds,null,&timeout)) //select 使用 case -1: exit(-1);break; //select 错误, 退出程序 case 0:break; // 再次轮询 default: if(fd_isset(sock,&fds)) // 测试 sock 是否可读, 即是否网络上有数据 recvfrom(sock,buffer,256,...);// 接受网络数据 if(fd_isset(fp,&fds)) // 测试文件是否可写 fwrite(fp,buffer...);// 写入文件 buffer 清空 ;

}// end if break; }// end switch }//end while }//end main select() 的机制中提供一 fd_set 的数据结构, 实际上是一 long 类型的数组, 每一个数组元素都能与一打开的文件句柄 ( 不管是 Socket 句柄, 还是其他文件或命名管道或设备句柄 ) 建立联系, 建立联系的工作由程序员完成, 当调用 select() 时, 由内核根据 IO 状态修改 fd_set 的内容, 由此来通知执行了 select() 的进程哪一 Socket 或文件可读, 下面具体解释 : #include <sys/types.h> #include <sys/times.h> #include <sys/select.h> int select(nfds, readfds, writefds, exceptfds, timeout) int nfds; fd_set *readfds, *writefds, *exceptfds; struct timeval *timeout; ndfs:select 监视的文件句柄数, 视进程中打开的文件数而定, 一般设为呢要监视各文件中的最大文件号加一 readfds:select 监视的可读文件句柄集合 writefds: select 监视的可写文件句柄集合 exceptfds:select 监视的异常文件句柄集合 timeout: 本次 select() 的超时结束时间 ( 见 /usr/sys/select.h, 可精确至百万分之一秒!) 当 readfds 或 writefds 中映象的文件可读或可写或超时, 本次 select() 就结束返回 程序员利用一组系统提供的宏在 select() 结束时便可判断哪一文件可读或可写 对 Socket 编程特别有用的就是 readfds 几只相关的宏解释如下 : FD_ZERO(fd_set *fdset): 清空 fdset 与所有文件句柄的联系 FD_SET(int fd, fd_set *fdset): 建立文件句柄 fd 与 fdset 的联系 FD_CLR(int fd, fd_set *fdset): 清除文件句柄 fd 与 fdset 的联系 FD_ISSET(int fd, fdset *fdset): 检查 fdset 联系的文件句柄 fd 是否可读写,>0 表示可读写 ( 关于 fd_set 及相关宏的定义见 /usr/include/sys/types.h)

这样, 你的 socket 只需在有东东读的时候才读入, 大致如下 :... int sockfd; fd_set fdr; struct timeval timeout =..;... for(;;) FD_ZERO(&fdR); FD_SET(sockfd, &fdr); switch (select(sockfd + 1, &fdr, NULL, &timeout)) case -1: error handled by u; case 0: timeout hanled by u; default: if (FD_ISSET(sockfd)) now u read or recv something; /* if sockfd is father and server socket, u can now accept() */ } } } 所以一个 FD_ISSET(sockfd) 就相当通知了 sockfd 可读 至于 struct timeval 在此的功能, 请 man select 不同的 timeval 设置使使 select() 表现出超时结束 无超时阻塞和轮询三种特性 由于 timeval 可精确至百万分之一秒, 所以 Windows 的 SetTimer() 根本不算什么 你可以用 select() 做一个超级时钟 FD_ACCEPT 的实现? 依然如上, 因为客户方 socket 请求连接时, 会发送连接请求报文, 此时 select() 当然会结束,FD_ISSET(sockfd) 当然大于零, 因为有报文可读嘛! 至于这方面的应用, 主要在于服务方的父 Socket, 你若不喜欢主动 accept(), 可改为如上机制来 accept() 至于 FD_CLOSE 的实现及处理, 颇费了一堆 cpu 处理时间, 未完待续 -- 讨论关于利用 select() 检测对方 Socket 关闭的问题 : 仍然是本地 Socket 有东东可读, 因为对方 Socket 关闭时, 会发一个关闭连接通知报文, 会马上被 select() 检测到的 关于 TCP 的连接 ( 三次握手 ) 和关闭 ( 二次握手 ) 机制, 敬请参考有关 TCP/IP 的书籍

不知是什么原因,UNIX 好象没有提供通知进程关于 Socket 或 Pipe 对方关闭的信号, 也可能是 cpu 所知有限 总之, 当对方关闭, 一执行 recv() 或 read(), 马上回返回 -1, 此时全局变量 errno 的值是 115, 相应的 sys_errlist[errno] 为 "Connect refused"( 请参考 /usr/include/sys/errno.h) 所以, 在上篇的 for(;;)...select() 程序块中, 当有东西可读时, 一定要检查 recv() 或 read() 的返回值, 返回 -1 时要作出关断本地 Socket 的处理, 否则 select() 会一直认为有东西读, 其结果曾几令 cpu 伤心欲断针脚 不信你可以试试 : 不检查 recv() 返回结果, 且将收到的东东 ( 实际没收到 ) 写至标准输出... 在有名管道的编程中也有类似问题出现 具体处理详见拙作 : 发布一个有用的 Socket 客户方原码 至于主动写 Socket 时对方突然关闭的处理则可以简单地捕捉信号 SIGPIPE 并作出相应关断本地 Socket 等等的处理 SIGPIPE 的解释是 : 写入无读者方的管道 在此不作赘述, 请详 man signal 以上是 cpu 在作 tcp/ip 数据传输实验积累的经验, 若有错漏, 请狂炮击之 唉, 昨天在 hacker 区被一帮孙子轰得差点儿没短路 ren cpu( 奔腾的心 ) z80 补充关于 select 在异步 ( 非阻塞 )connect 中的应用, 刚开始搞 socket 编程的时候我一直都用阻塞式的 connect, 非阻塞 connect 的问题是由于当时搞 proxy scan 而提出的呵呵通过在网上与网友们的交流及查找相关 FAQ, 总算知道了怎么解决这一问题. 同样用 select 可以很好地解决这一问题. 大致过程是这样的 : 1. 将打开的 socket 设为非阻塞的, 可以用 fcntl(socket, F_SETFL, O_NDELAY) 完成 ( 有的系统用 FNEDLAY 也可 ). 2. 发 connect 调用, 这时返回 -1, 但是 errno 被设为 EINPROGRESS, 意即 connect 仍旧在进行还没有完成. 3. 将打开的 socket 设进被监视的可写 ( 注意不是可读 ) 文件集合用 select 进行监视, 如果可写, 用 getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int)); 来得到 error 的值, 如果为零, 则 connect 成功. 在许多 unix 版本的 proxyscan 程序你都可以看到类似的过程, 另外在 solaris 精华区 -> 编程技巧中有一个通用的带超时参数的 connect 模块.