600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > linux平台IO多路复用 select接口使用例子

linux平台IO多路复用 select接口使用例子

时间:2023-11-16 06:07:06

相关推荐

linux平台IO多路复用 select接口使用例子

这几天在学习net-snmp源码,里面封装了很多select函数调用,这里记录一下linux上select的用法以及相关接口。

先看接口:

//头文件#include <sys/select.h>#include <sys/time.h>#include <sys/types.h>#include <unistd.h>/** 参数nfds表示监听的描述符个数,通常等于最大的描述符加一,select最多同时监听描述符* 数量有个上限,FD_SETSIZE(1024),不同平台这个值可能不同,所以如果程序中监听数量特别* 多的话,建议使用epoll。** 参数 readfds, writefds, exceptfds表示描述符集,可以把我们关心的描述符放到对应的* 描述符数组里面,这三个分别对应着可读、可写和异常事件。可以都设置为NULL,这时候select* 调用就相当于一个更精确的sleep。 ** 参数 timeout表示select超时时间,如果为NULL的话,表示永久阻塞,除非监听的描述符集上* 有事件发生或者收到信号,为0的话,表示立即返回,其它的值则表示相应的等待时间。** 成功返回准备好读写的文件描述符数量,* 返回0表示超时,返回-1表示出错。*/int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);/* 从fdset中清空该文件描述符标志位 */ void FD_CLR(int fd, fd_set *set); /* 判断该文件描述符上是否有事件发生 */int FD_ISSET(int fd, fd_set *set);/* 将该文件描述符添加到fd_set数组中 */void FD_SET(int fd, fd_set *set);/* 初始化fdset */ void FD_ZERO(fd_set *set);每次调用select后,都需要重新清空描述符集并重新添加感兴趣的文件描述符。另外,select返回时会将剩余时间填充到timeout参数中,因此重新调用select的时候也要重新初始化该时间参数。

示例,创建两个udp套接字,使用select循环监听可读事件,注意收到事件处理完成后需要重新对fd_set描述符集进行初始化,

这一点不如epoll使用方便。

/*** Description : linux 环境 select接口使用示例*创建两个udp套接字,然后使用select监听套接字上读事件。 * Date : 1001* Author: mason*/#include <sys/time.h>#include <sys/types.h>#include <unistd.h>#include <stdio.h>#include <sys/errno.h>#include <netinet/in.h>#include <sys/socket.h>#include <sys/types.h>#include <arpa/inet.h>#include <string.h>#define BUFFER_SIZE 512#define log(fmt, arg...) printf("[udptest] %s:%d "fmt, __FUNCTION__, __LINE__, ##arg)void main(){int sock, sock2;int addr_len, recv_len;char buffer[BUFFER_SIZE] = {0};struct sockaddr_in addr, addr2;fd_set rfds;struct timeval tv;int retval, maxfdp1 = 0;/* 创建UDP套接字 */sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock == -1) {log("create socket fail \r\n");return ;} sock2 = socket(AF_INET, SOCK_DGRAM, 0);if (sock2 == -1) {log("create socket2 fail \r\n");close(sock);return ;} /* 设置监听地址 */addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(40000);addr2.sin_family = AF_INET;addr2.sin_addr.s_addr = INADDR_ANY; addr2.sin_port = htons(30000);addr_len = sizeof(struct sockaddr_in);/* 绑定本地监听地址 */if (0 != bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in))){log("bind local listening addr fail,errno : %d \r\n", errno);goto end;} if (0 != bind(sock2, (struct sockaddr *)&addr2, sizeof(struct sockaddr_in))){log("bind local listening addr fail,errno : %d \r\n", errno);goto end;} /* 初始化描述符集 */FD_ZERO(&rfds);/* 添加到描述符集里面 */FD_SET(sock, &rfds);maxfdp1 = maxfdp1 > sock ? (maxfdp1 + 1) : (sock + 1);/* 添加到描述符集里面 */FD_SET(sock2, &rfds);maxfdp1 = maxfdp1 > sock ? (maxfdp1 + 1) : (sock + 1);/* select超时10s */tv.tv_sec = 10;tv.tv_usec = 0;/* 循环监听 */for (;;){/* 只监听读事件 */retval = select(maxfdp1, &rfds, NULL, NULL, &tv);if (retval > 0){/* 判断是否可读 */if (FD_ISSET(sock, &rfds)){recv_len = read(sock, buffer, sizeof(buffer));if (recv_len != -1){log("revc from sock : %s\r\n", buffer);memset(buffer, 0, sizeof(buffer));}}if (FD_ISSET(sock2, &rfds)){recv_len = read(sock2, buffer, sizeof(buffer));if (recv_len != -1){log("revc from sock2 : %s\r\n", buffer);memset(buffer, 0, sizeof(buffer));}}}else if (retval == 0){/* select 超时 */log("select timeout \r\n");}else{log("select error \r\n");}/* 清空标志位 */FD_ZERO(&rfds);/* 重新设置超时 */tv.tv_sec = 5;/* 重新添加到select监听数组中 */FD_SET(sock, &rfds);FD_SET(sock2, &rfds);}end:close(sock);close(sock2);return;}

Makefile:

## Linux 同步IO复用 select接口例子#app:gcc -o select_demo lean:rm -rf *.o select_demo

运行截图:

参考资料:

1. man select/linux/man-pages/man2/select.2.html

2. 《UNIX网络编程卷一 套接字API》第6章 IO多路复用

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。