linux select 学习

时间:2022-09-08 19:14:09

一、select介绍

函数原型:

#include <sys/select.h>
int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);
// 返回值:准备就绪的描述符数,若超时返回0,若出错返回-1

参数说明:

maxfd:是需要监视的最大的文件描述符值+1;readfds/writefds/exceptfds分别对应需要检测的可读文件描述符的集合、可写描述符集合以及异常描述符集合。

tvptr:说明愿意等待多久。

有三种情况:

tvptr=NULL 永远等待。

tvptr->tv_sec==0 && tvptr->tv_usec=0 完全不等待。

tvptr->tv_sec!=0 || tvptr->tv_usec != 0 等待指定时间。若超时,则返回0。

POSIX允许实现中修改tvptr的值,所以在每次select开始时都需要重新设置该值。

对fd_set类型的处理有四个专有函数:

#include <sys/select.h>
int FD_ISSET(int fd, fd_set *fdset); // 测试某一个描述符 返回值:若fd在描述符集中则返回非0,否则返回0
void FD_CLR(int fd, fd_set *fdset); // 删除某一个描述符
void FD_SET(int fd, fd_set *fdset); // 设置某一个描述符
void FD_ZERO(fd_sete *fdset); // 清空

返回值:

select有三个可能的返回值:

返回-1表示出错。

返回0表示没有描述符准备好。

返回正值表示已经准备好的描述符数。该值是三个描述符集中已准备好的描述符的和。

select函数的中间三个参数的任意一个或全部都可以为NULL。如果三个都是NULL,则select提供一个高精度的计时器。

二、select使用

示例1:回显服务器

/*******************************************************************************
* File Name : select.cpp
* Author : zjw
* Email : emp3XzA3MjJAMTYzLmNvbQo= (base64 encode)
* Create Time : 2015年07月15日 星期三 11时52分07秒
*******************************************************************************/ #include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> #include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std; const int SERVER_PORT = ;
const int FD_SIZE = ;
const int RECV_SIZE = ;
const int SEND_SIZE = ; int main(int argc, char **argv)
{
int server = socket(AF_INET, SOCK_STREAM, );
struct sockaddr_in addr_server;
addr_server.sin_family = AF_INET;
addr_server.sin_port = htons(SERVER_PORT);
addr_server.sin_addr.s_addr = INADDR_ANY;
memset(addr_server.sin_zero, , sizeof(addr_server.sin_zero)); if (bind(server, (struct sockaddr*)&addr_server, sizeof(addr_server)))
{
perror("bind failed!");
return -;
} listen(server, ); fd_set fdsr;
int fd[FD_SIZE] = { };
int maxsock = server;
char recvBuf[RECV_SIZE + ];
char sendBuf[SEND_SIZE + ];
map<int, sockaddr_in> mapClients; while ()
{
FD_ZERO(&fdsr);
FD_SET(server, &fdsr); struct timeval tv;
tv.tv_sec = ;
tv.tv_usec = ; for (int i = ; i < ; i++)
{
if (fd[i] != )
{
FD_SET(fd[i], &fdsr);
}
} int ret = select(maxsock + , &fdsr, NULL, NULL, &tv);
if (ret == -)
{
perror("select error!");
return -;
}
else if (ret == )
{
cout << "timeout!" << endl;
continue;
} // check every fd in the fdset
for (int i = ; i < FD_SIZE; i++)
{
if (FD_ISSET(fd[i], &fdsr))
{
memset(recvBuf, , RECV_SIZE + );
memset(sendBuf, , SEND_SIZE + );
ret = recv(fd[i], recvBuf, RECV_SIZE, );
sprintf(sendBuf, "Your said:%s", recvBuf);
send(fd[i], sendBuf, SEND_SIZE, );
if (ret <= )
{
cout << "client " << fd[i] << " close" << endl;
mapClients.erase(fd[i]);
close(fd[i]);
FD_CLR(fd[i], &fdsr);
fd[i] = ;
}
else
{
cout << "client " << fd[i] << " ip[" << inet_ntoa(mapClients[fd[i]].sin_addr) << "]" << " said:" << recvBuf << endl;
}
}
} // check server socket
if (FD_ISSET(server, &fdsr))
{
int client;
sockaddr_in addr_client;
socklen_t len;
client = accept(server, (struct sockaddr*)&addr_client, &len);
if (client == -)
{
perror("accept error!");
return -;
} for (int i = ; i < FD_SIZE; i++)
{
if (fd[i] == )
{
fd[i] = client;
mapClients.insert(make_pair<int, sockaddr_in>(client, addr_client));
break;
}
}
}
maxsock = server;
for (int i = ; i < FD_SIZE; i++)
{
if (fd[i] > maxsock)
{
maxsock = fd[i];
}
}
} return ;
}

Makefile:

echo: select.cpp
g++ -o $@ $< clean:
rm -rf echo

执行结果:

server端:echo

linux select 学习

client端:telnet

linux select 学习

提示:使用telnet测试非常方便,但是退出telnet有点麻烦,输出ctrl+],然后输入quit即可退出。

示例2:定时器

/*******************************************************************************
* File Name : timer.cpp
* Author : zjw
* Email : emp3XzA3MjJAMTYzLmNvbQo= (base64 encode)
* Create Time : 2015年07月21日 星期二 16时45分16秒
*******************************************************************************/
#include <sys/select.h>
#include <unistd.h>
#include <iostream>
using namespace std; int main(int argc, char **argv)
{
struct timeval tv; while ()
{
tv.tv_sec = ;
tv.tv_usec = ;
select(, NULL, NULL, NULL, &tv);
cout << "Five second have passed." << endl;
} return ;
}

三、参考

http://blog.csdn.net/turkeyzhou/article/details/8609360

四、疑问

select是如何判断每个描述符可读、可写或者有异常的?

每个描述符没有设置为非阻塞的,那么如果select使用recv来尝试读,它是如何做到不阻塞的?或者说,select将这些描述符设置为非阻塞的了?还是它又另外的方法来测试可读?

select为什么有最大连接限制?不理解所谓的FD_SETSIZE。

linux select 学习的更多相关文章

  1. linux 驱动学习笔记01--Linux 内核的编译

    由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...

  2. linux内核学习之一:环境搭建--安装Debian7&period;3

    本系列文章假设读者已对linux有一定的了解,其实学习linux内核不需要有很深的关于linux的知识,只需要了解以下内容:linux基础知识及基本shell命令:现代操作系统的基本概念:C语言和gc ...

  3. Linux&period;NET学习手记(7)

    前一篇中,我们简单的讲述了下如何在Linux.NET中部署第一个ASP.NET MVC 5.0的程序.而目前微软已经提出OWIN并致力于发展VNext,接下来系列中,我们将会向OWIN方向转战. 早在 ...

  4. Linux&period;NET学习手记(8)

    上一回合中,我们讲解了Linux.NET面对OWIN需要做出的准备,以及介绍了如何将两个支持OWIN协议的框架:SignalR以及NancyFX以OwinHost的方式部署到Linux.NET当中.这 ...

  5. 关于《Linux&period;NET学习手记(8)》的补充说明

    早前的一两天<Linux.NET学习手记(8)>发布了,这一篇主要是讲述OWIN框架与OwinHost之间如何根据OWIN协议进行通信构成一套完整的系统.文中我们还直接学习如何直接操作OW ...

  6. Linux LVM学习总结&mdash&semi;&mdash&semi;扩展卷组VG

    Linux服务器由于应用变更或需求的缘故,有可能出现分区空间不足的情况,此时往往需要进行扩容(要增加分区的空间),而采用LVM的好处就是可以在不需停机的情况下可以方便地调整各个分区大小.如下所示,分区 ...

  7. linux的学习记录随笔

    为什么学习linux 因为操作系统是一种介质,你要接触其中的东西,首先必须要有介质,而linux在服务器端是老大哥的地位,所以呢,学习linux吧. 学习的方式 可以看视频 imooc.百度传课.网易 ...

  8. Linux LVM学习总结&mdash&semi;&mdash&semi;创建卷组VG

    在Linux平台如何创建一个卷组(VG)呢?下面简单介绍一下卷组(VG)的创建步骤.本文实验平台为Red Hat Enterprise Linux Server release 6.6 (Santia ...

  9. 别出心裁的Linux命令学习法

    别出心裁的Linux命令学习法 操作系统操作系统为你完成所有"硬件相关.应用无关"的工作,以给你方便.效率.安全.操作系统的功能我总结为两点:管家婆和服务生: 管家婆:通过进程.虚 ...

随机推荐

  1. Aspose&period;Words简单生成word文档

    Aspose.Words简单生成word文档 Aspose.Words.Document doc = new Aspose.Words.Document(); Aspose.Words.Documen ...

  2. smarty汇总

    Smarty:模板技术 实现功能:前后分离. 原理:主要通过Smarty核心类实现,调用display方法,将模板文件读取,用正则进行替换,替换完保存到临时文 件,将临时文件加载到当前页面. 配置文件 ...

  3. Effective Java 77 For instance control&comma; prefer enum types to readResolve

    The readResolve feature allows you to substitute another instance for the one created by readObject ...

  4. 用原生js实现一个页面乘法口诀表

    今天我自己用js实现了一个页面乘法口诀表(如图)来共享给大家,做的不是很好,如果大家有新的想法可以跟我交流哦. 代码如下: <!doctype html><html lang=&qu ...

  5. ES6新特性 Class的实现

    ES5之前类的继承是靠原型实现的,而这一过程的实现又涉及到一大堆的原型定义,特别是ES5推出了Object.definePorperty()方法后,代码更加晦涩.但是这种方式正是javascript这 ...

  6. Android内存泄露的原因

    (一)释放对象的引用,误将一个本来生命周期短的对象存放到一个生命周期相对较长的对象中,也称“对象游离“.隐蔽的内部类(Anonymous Inner Class): mHandler = new Ha ...

  7. RDIFramework&period;NET ━ &period;NET快速信息化系统开发框架 V3&period;2-&gt&semi;新增&OpenCurlyDoubleQuote;行政区域管理”,同时大批量树采用异步加载

    行政区划:简称政区,是国家为了进行分级管理而实行的区域划分.中国现行的行政区划实行如下原则:1.全国分为省.自治区.直辖市:2.省.自治区分为自治州.县.自治县.市:3.自治州分为县.自治县.市:4. ...

  8. javascript 时间函数整理

    对Javascript日期的部分函数做个小结: var myDate = new Date();//定义时间函数 myDate.getYear();  //获取当前年份(2位) myDate.getF ...

  9. 删除List集合中的元素方法

    List集合是我们平时使用的最多的集合了,一般用来存放从数据库中查询的对象数据,但有时我们会从中筛选不需要的数据,第一次使用这种方式: 使用增强for循环遍历,使用list的remove方法删除不符合 ...

  10. macOS下appstore提示未能完成该操作的解决办法

    macOS下App Store下载软件,提示:未能完成该操作.(com.apple.commerce.client 错误 500.) 解决办法: 在终端输入 defaults write com.ap ...