linux select用法
select 是linux i/o 复用技术之一
man 2 select
#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, struct timeval *timeout);
nfds是监听文件描述符的总数。它通常被设置为select监听的所有文件描述符的最大值加1.
readfds, writefds, exceptfds指向可读、可写、异常等事件对应的文件描述符集合。应用程序调用select时,通过这3个参数,传入自己感兴趣的文件描述符。select返回时,内核通过修改它们来通知应用程序哪些文件描述符已经就绪。我们看到,应用程序和内核都修改了这些参数。
fd_set 是结构体,定义如下:
typedef struct
{
/* XPG4.2 requires this member name. Otherwise avoid the name
from the global namespace. */
#ifdef __USE_XOPEN
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif
} fd_set;
该结构体只包含一个整形数组。其中,每个元素的一个位,标记一个文件描述符。fd_set 能容纳的文件描述符数量由__FD_SETSIZE决定,默认1024.所以select能处理的最大文件描述符个数为1024.
对这个结构体的操作使用位运算。select.h文件中,提供了用来操作的宏
# define __FD_ZERO(set) \
do { \
unsigned int __i; \
fd_set *__arr = (set); \
for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) \
__FDS_BITS (__arr)[__i] = 0; \
} while (0) #define __FD_SET(d, set) \
((void) (__FDS_BITS (set)[__FD_ELT (d)] |= __FD_MASK (d)))
#define __FD_CLR(d, set) \
((void) (__FDS_BITS (set)[__FD_ELT (d)] &= ~__FD_MASK (d)))
#define __FD_ISSET(d, set) \
((__FDS_BITS (set)[__FD_ELT (d)] & __FD_MASK (d)) != 0)
timeout参数设置超时时间,这是timeval结构体。使用指针是因为内核将修改它来告诉应用程序select等了多久。
struct timeval {
__kernel_time_t tv_sec; /* seconds */
__kernel_suseconds_t tv_usec; /* microseconds */
};
如果timeout变量结构体成员都是0,则select立即返回。timeout为NULL,select一直阻塞,直到有文件描述符就绪。
select成功时返回就绪文件描述符总数。如果超时时间内没有就绪的,返回0. select失败时返回-1 并设置errno。如果select等待期间,程序收到信号,select立即返回-1,并设置errno为EINTR。
例子代码,服务端程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h> #include <set> using namespace std; #define BUFSIZE 10 int createSocket()
{
struct sockaddr_in servaddr;
int listenfd = -1; if (-1 == (listenfd = socket(PF_INET, SOCK_STREAM, 0)))
{
fprintf(stderr, "socket: %d, %s\n", errno, strerror(errno));
exit(1);
} int reuseaddr = 1;
if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)))
{
fprintf(stderr, "setsockopt: %d, %s\n", errno, strerror(errno));
exit(1);
} memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = PF_INET;
servaddr.sin_port = htons(8008);
inet_pton(PF_INET, "0.0.0.0", &servaddr.sin_addr); if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)))
{
fprintf(stderr, "bind: %d, %s\n", errno, strerror(errno));
exit(1);
} if (-1 == listen(listenfd, 5))
{
fprintf(stderr, "listen: %d, %s\n", errno, strerror(errno));
exit(1);
} return listenfd;
} int main(int argc, char const *argv[])
{
fd_set rfds;
char buf[BUFSIZE] = {0};
int listenfd = createSocket(); // 保存所有的文件描述符
set<int> fdset;
fdset.insert(listenfd); while (1)
{
FD_ZERO(&rfds);
// 每次都要重新设置rfds.因为select返回时,rfds被内核改变,里面只保存了就绪的文件描述符
for (int fd : fdset)
{
FD_SET(fd, &rfds);
}
int ret = select(*fdset.rbegin()+1, &rfds, NULL, NULL, NULL);
if (ret > 0)
{
for (int fd : fdset)
{
// 有新的连接
if (fd == listenfd && FD_ISSET(fd, &rfds))
{
struct sockaddr_in client;
socklen_t addrlen = sizeof(client);
int cfd = -1; if (-1 == (cfd = accept(listenfd, (struct sockaddr*)&client, &addrlen)))
{
fprintf(stderr, "accept: %d, %s\n", errno, strerror(errno));
exit(1);
}
// 添加到 fd_set 结构体,并记录到 set
FD_SET(cfd, &rfds);
fdset.insert(cfd);
printf("get connection fd: %d\n", cfd);
}
else if (FD_ISSET(fd, &rfds))
{
int lenrecv = -1;
memset(buf, 0, BUFSIZE); lenrecv = recv(fd, buf, BUFSIZE-1, 0);
if (lenrecv > 0)
{
printf("%s\n", buf);
}
else if (0 == lenrecv)
{
// 客户端退出,删除文件描述符,并关闭
fdset.erase(fd);
FD_CLR(fd, &rfds);
printf("delete connection fd: %d\n", fd);
close(fd);
}
else
{
fprintf(stderr, "recv: %d, %s\n", errno, strerror(errno));
exit(1);
} }
}
}
else
{
fprintf(stderr, "select: %d, %s\n", errno, strerror(errno));
exit(1);
}
} close(listenfd); return 0;
}
linux select用法的更多相关文章
- linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例
除了自己实现之外,还有个c语言写的基于事件的开源网络库:libevent http://www.cnblogs.com/Anker/p/3265058.html 最简单的select示例: #incl ...
- linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例【转】
转自:https://www.cnblogs.com/welhzh/p/4950341.html 除了自己实现之外,还有个c语言写的基于事件的开源网络库:libevent http://www.cnb ...
- linux select函数详解
linux select函数详解 在Linux中,我们可以使用select函数实现I/O端口的复用,传递给 select函数的参数会告诉内核: •我们所关心的文件描述符 •对每个描述符,我们所关心的状 ...
- select用法&原理详解(源码剖析)(转)
今天遇到了在select()前后fd_set的变化问题,查了好久终于找到一个有用的帖子了,很赞,很详细!!原文链接如下: select用法&原理详解(源码剖析) 我的问题是: 如下图示:在se ...
- linux curl用法详解
linux curl用法详解 curl的应用方式,一是可以直接通过命令行工具,另一种是利用libcurl库做上层的开发.本篇主要总结一下命令行工具的http相关的应用, 尤其是http下载方面 ...
- html select用法总结
本文将介绍select 原生的常用方法,这些都是经过测试,兼容ie6到ie10,及chrome,火狐等,也就是说大部分浏览器都兼容.如果大家发现有不兼容的情况,可以跟我留言. 我们对基本的用法了如指掌 ...
- linux—select具体解释
linux—select具体解释 select系统调用时用来让我们的程序监视多个文件句柄的状态变化的.程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变. 关于文件句柄,事 ...
- [转载]expect spawn、linux expect 用法小记
原文地址:expect spawn.linux expect 用法小记作者:悟世 使用expect实现自动登录的脚本,网上有很多,可是都没有一个明白的说明,初学者一般都是照抄.收藏.可是为什么要这么写 ...
- Linux Select之坑
最近在写一个demo程序,调用select()来监听socket状态,流程如下: r_set 初始化 timeout 初始化3秒超时 loop{ select(ntfs, &r_set, nu ...
随机推荐
- hdu 3530 区间和在一定范围内最长区间
http://acm.hust.edu.cn/vjudge/problem/11253 这题要找到区间和在[m,k]范围内的最长区间 用两个单调序列保存区间最大值和最小值.当最大值-最小值>k时 ...
- Sharepoint2010新建一个用户的方法
最近在做关于SharePoint的相关开发,在开发中需要用到测试用户进行相关权限的测试,所以就需要创建一个新的用户进行,但是在网上找了很久都没有找到关于创建一个新用户的资料,最后终于在http://w ...
- Vue.js - Day2
品牌管理案例 添加新品牌 删除品牌 根据条件筛选品牌 1.x 版本中的filterBy指令,在2.x中已经被废除: filterBy - 指令 <tr v-for="item in ...
- java分页三个类 PageBean ResponseUtil StringUtil
package ssmy.page; /** * 分页类 * @author Jesse * */public class PageBean { private int page;//第几页 priv ...
- u-boot分析(四)---设置异常向量表|设置SVC模式
u-boot分析(四) 通过前三篇的分析,我们对u-boot已经有了整体的认识和掌握,但是我们仍然对于其部分硬件是如何初始化的不太清楚,所以接下来几篇博文我将会对我们在http://www.cnblo ...
- [原创]Centos7 安装配置ASP.NET Core+Nginx+Supervisor
序言 此教程安装的都是最新版本的. 一键安装 有了这个神器,下面的教程就不用做了!只需运行几行代码,直接打开浏览器就可以访问! cd /home/ wget https://files.cnblogs ...
- Win10 设备补丁更新
用户对客户端设备补丁更新保持怀疑态度,因为他们担心他们的计算机会在未经许可的情况下突然自己重启,丢失数据.虽然,您可以在更新后推迟重新启动并安排选择的时间,具体取决于更新Windows在未经您许可的情 ...
- java研发常见问题总结2
1. String.StringBuffer与StringBuilder之间区别 关于这三个类在字符串处理中的位置不言而喻,那么他们到底有什么优缺点,到底什么时候该用谁呢?下面我们从以下几点说明一下 ...
- java日期时间Date类
java.util包提供了Date类来封装当前的日期和时间. Date类提供两个构造函数来实例化Date对象. 第一个构造函数使用当前日期和时间来初始化对象. Date( ) 第二个构造函数接收一个参 ...
- 使用Excel调用ABAP系统的函数
效果:在excel里创建一个按钮,开发一些VB script,可以连接指定的ABAP系统并执行系统里的ABAP function module. 在这里例子里执行ABAP系统的函数TH_USER_LI ...