Linux中select函数的使用 select() Linux linux函数 select
阻塞式I/O编程有两个特点:
一、如果一个发现I\O有输入,读取的过程中,另外一个也有了输入,这时候不会产生任何反应.这就需要你的程序语句去用到select函数的时候才知道有数据输入。
二、程序去select的时候,如果没有数据输入,程序会一直等待,直到有数据位置,也就是程序中无需循环和sleep。
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序
(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。
可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
下面man下select函数的:
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, structtimeval *timeout);
第一, struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Linux下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。
fd_set集合可以通过一些宏由人为来操作,
比如
清空集合FD_ZERO(fd_set*);
将一个给定的文件描述符加入集合之中FD_SET(int,fd_set *);
将一个给定的文件描述符从集合中删除FD_CLR(int,fd_set*);
检查集合中指定的文件描述符是否可以读写FD_ISSET(int,fd_set* )。
第二,structtimeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。
具体解释select的参数:
int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!
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时间内阻塞,超时时间之内有事件到来就返回了,文件无变化返回0,有变化返回一个正值;
返回值:
负值:select错误
正值:某些文件可读写或出错
0:等待超时,没有可读写或错误的文件
举个简单的例子:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
fd_setreadfds;
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO,&readfds);
intret;
charbuf[256]={0};
structtimeval tv={5,1000};
// tv.tv_sec=5;
// tv.tv_usec=1000;
ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,&tv);
//ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,NULL);
//ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,0);
printf("ret=%d\n",ret);
if(ret==-1)
{
perror("selsecterror ");
exit(EXIT_FAILURE);
}
elseif(ret)
{
if(FD_ISSET(STDIN_FILENO,&readfds))
read(STDIN_FILENO,buf,256);
printf("readfrom stdin msg : %s\n",buf);
}
else
printf("timeout\n");
return0;
}
运行结果:3中状态:
1. 错误。
2. 超时,在指定的时间内没有检测到用户的输入。Selsect()返回0如图:
3.用户输入了,即检测到了标准输入已经准备好了
下面我们看一下socket编程中select()函数的使用。
在此之贴出select函数部分。
客户端:
while(1)
{
FD_ZERO(&readfds);
FD_SET(sockfd,&readfds);
FD_SET(STDIN_FILENO,&readfds);
maxfd=sockfd;
printf("before select %d\n",readfds);
if((ret=select(maxfd+1,&readfds,NULL,NULL,NULL))==-1)
{
perror("select:");
exit(EXIT_FAILURE);
}
//哪个文件描述符准备好了就将那个所对应的位设置为1,其他的设置为0
//eg:socket=3准备好了则readfds为00000100(8)
//select 函数的返回值为:准备好的文件描述符的个数。
DEBUG("ret=%d\n",ret);
printf("after select %d\n",readfds);
if(FD_ISSET(sockfd,&readfds))
{ printf("sockfd select %d\n",readfds);
if(read(sockfd,read_buf,sizeof(read_buf))<=0)
{
perror("read <=0 ");
break;
}
printf("%s\n",read_buf);
memset(read_buf,0,sizeof(read_buf));
}
if(FD_ISSET(STDIN_FILENO,&readfds)) //有标准输入描述符准备好了
{ printf("STDIN_FILENO select %d\n",readfds);
read(STDIN_FILENO,buf,sizeof(buf));
//或 scanf("%s",buf);//从标准输入设备的缓冲区中取出内容放到buf中
write(sockfd, buf, strlen(buf)+1);
}
}
服务端:
void server_write_read(int sockfd)
{
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sockfd,&rfds);
int i, maxfd = sockfd,ret=-1;
int newfd;
struct sockaddr_in client;
socklen_t len = sizeof(client);
char buf[MAXBUF] = {'\0'};
while(1)
{
FD_ZERO(&rfds); //clear set
for(i = 0; i < MAXCLIENT; i++)
{
// add fd to set
if(connfd[i].fd != 0)
{
FD_SET(connfd[i].fd ,&rfds);
}
//get tje max fd
if(connfd[i].fd != 0 &&connfd[i].fd> maxfd)
{
maxfd=connfd[i].fd;
}
}
FD_SET(sockfd,&rfds);
printf("server_write_read before select %d\n",rfds);
if((ret=select(maxfd+1,&rfds,NULL,NULL,NULL))==-1)
{
perror("select:");
continue;
}
printf("server_write_read after select %d\n",rfds);
if(FD_ISSET(sockfd, &rfds))
{ printf("server_write_read sockfd %d\n",rfds);
newfd = accept(sockfd, (struct sockaddr *)&client, &len);
if(-1 == newfd)
system_error("accept");
else
printf("welcome %s was connected.\n", inet_ntoa(client.sin_addr));
DEBUG("newfd=%d\n",newfd);
//save socket description
for(i = 0; i < MAXCLIENT; i++)
{
if(connfd[i].fd == 0)
{
connfd[i].fd = newfd;
break;
}
}
}
for(i = 0; i < MAXCLIENT; i++)
{
if(FD_ISSET(connfd[i].fd, &rfds))
{
if(connfd[i].fd == 0)continue;
if ((read(connfd[i].fd, buf, MAXBUF)) <= 0)
{DEBUG("server read <=0\n");
for(i = 0; i < MAXCLIENT; i++)
{
if(connfd[i].fd==newfd)
{
connfd[i].fd=0;
break;
}
}
close(newfd);
FD_CLR(newfd, &rfds);
}
else
{
int j=0;
printf("// buf=%s\n",buf);
for(; chat_func[j].func; j++)
{
if (buf[0] == chat_func[j].protocol)
{
chat_func[j].func(buf, connfd[i].fd);
break;
}
}
memset(buf,0,MAXBUF);
}
}
}
}
}
Linux中select函数的使用 select() Linux linux函数 select相关推荐
- Linux 中 dlopen、dlsym、dlclose、dlerror函数
编译时候要加入 -ldl (指定dl库) dlopen 基本定义 功能:打开一个动态链接库 [喝小酒的网摘]http://blog.const.net.cn/a/17154.htm 包含头文件: ...
- linux中oracle静默安装失败,oracle 11 Linux 静默安装 步骤及错误解决(更新中)
oracle 11 Linux 静默安装 步骤及错误解决(更新中) oracle 11 Linux 静默安装 步骤及错误解决(更新中) [待更新] oracle成功安装完成后 显示的页面如下 : 此时 ...
- linux中lockf的例子,小何讲Linux: 文件锁及其实例
1. 文件锁基本概念 Linux中软件.硬件资源都是文件(一切皆文件),文件在多用户环境中是可共享的. 文件锁是用于解决资源的共享使用的一种机制:当多个用户需要共享一个文件时,Linux通常采用的方 ...
- linux中c语言结构体详解,Linux C语言结构体-学习笔记
Linux C语言结构体简介 前面学习了c语言的基本语法特性,本节进行更深入的学习. 预处理程序. 编译指令: 预处理, 宏定义, 建立自己的数据类型:结构体,联合体,动态数据结构 c语言表达式工具 ...
- linux中文件记录的时间参数,【Linux】stat命令查看文件的三个时间参数
在Windows中创建一个文件都会有相应的创建时间,修改时间,访问时间来记录文件的一些属性.在Linux中也不例外,文件也有三个时间来记录文件的变动,这三个时间分别是Modification t ...
- ubuntu 安装yum_如何在 Linux 中安装微软的 .NET Core SDK | Linux 中国
本分步操作指南文章解释了如何在 Linux 中安装 .NET Core SDK 以及如何使用 .NET 开发出第一个应用程序.-- Sk致谢译自 | ostechnix.com 作者 | Sk译者 | ...
- 查看linux中的sh指向哪,什么是Linux的Shell脚本和怎么执行脚本?
1.什么是Shell?shell shell是外壳的意思,就是操做系统的外壳.咱们能够经过shell命令来操做和控制操做系统,好比Linux中的Shell命令就包括ls.cd.pwd等等.总结来讲,S ...
- linux中cd命令及范例,15个Linux中的“cd”命令的实际示例
在Linux中"CD"( 更改目录 )命令是新手以及系统管理员中最重要和最广泛使用的命令之一. 对于无头的服务器上管理员,"CD"是导航到一个目录来查看日志,执 ...
- linux中如何运行html文件路径问题,Linux中如何查询运行文件的全路径的方法
在linux中,有些地方需要使用绝对路径,对于一些命令,如java.mysql等,需要使用到运行文件所在的路径,给大家介绍一个命令,来查询这个路径. which 可以通过which查询运行文件的所在路 ...
- linux中c语言生日快乐_生日快乐,Linux:27岁
linux中c语言生日快乐 Linux今天庆祝了另一个生日-27岁! 与我们分享激动之情,我们无比高兴. 我们的许多读者都是Linux用户,粉丝,书呆子...形容他们的形容词清单不胜枚举. 你会怎么称 ...
最新文章
- leetcode_1. Two Sum
- 我国拟开展2016年新型智慧城市评价工作
- 克隆Centos 无法上网
- 排序和顺序统计学(2)——快速排序
- Scala模式匹配(类似Java的switch)
- webmin升级php,Webmin php-lib.pl修改
- 201521123057 《Java程序设计》第12周学习总结
- TensorFlow 第四步 多层神经网络 Mnist手写数字识别
- memcache 防火墙策略
- 用Spire.doc来合并邮件
- 51Nod-2148 字符出现位置【水题】
- Latex中参考文献排序
- 对研发经理这一岗位的个人理解
- html5使用mescroll
- qqxml图片代码_QQxml卡片代码合集超大图、清明上河图、官方认证推荐等
- 【190302】VC+ 视频捕捉与录像+实例源码源代码
- 2017双11核心技术揭秘—双十一海量数据下EagleEye的使命和挑战
- 精通CSS(5.6.3-end)PixyFairypureCSSToolHintscale
- HWND与HANDLE的区别
- Tuscany SCA V1.0中的扩展机制和启动过程中的扩展点[11月29日更新]