【IO复用之select】
select
承接上篇socket基础编程模板
基础的socket,accept方式一次只能监听一个socket,为了监听多个链接的socket,最直观的方法就是把要监听的socket组成数组进行遍历。
select用fd_set来表示监听的socket集合。fd_set是一个数组,该数组的每个元素的每个bit位表示一个文件描述符,即位图。数组的大小取决于内核支持的打开文件描述符的最大数量FD_SIZE,通常是1024。
函数原型:
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
1.nfds指定监听文件描述的总数,通常被设置成文件描述中的最大值加1,因为文件描述是从0开始计数的。
2.readfds、writefds、exceptfds分别表示可读、可写、异常事件对应的文件描述符集合。注意:这个三个参数既是入参,也是出参。在select函数返回时,内核会修改这三个参数,将就绪的文件描述符对应的bit位置位。因此,每次调用select都要重新设置这三个值。
3. timeout时用来设置select系统调用的超时时间。
struct timeval
{
long tv_sec; /* 秒数 */
long tv_usec; /* 微秒 */
};
如果tv_sec和tv_usec都传入0,select将立即返回。如果timeout传入NULL,select将一直阻塞,直到描述符集合中有就绪的文件描述符。
位操作过于繁琐,可以使用一下宏定义操作:
#include <sys/select.h>
FD_ZERO(fd_set *fdset); /* 清除fdset的所有位 */
FD_SET(int fd, fd_set *fdset); /* 设置fdset的位fd */
FD_CLR(int fd, fd_set *fdset); /* 清除fdset的位fd */
int FD_ISSET(int fd, fd_set *fdset); /* 测试fdset的位fd是否设置 */
编程实例(代码来自《Linux高性能服务器编程》)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc <= 2)
{
printf("usage: %s ip_address port_number\n", basename(argv[0]));
return 1;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
// address.sin_addr.s_addr = inet_addr(ip);
address.sin_port = htons(port);
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);
int ret = bind(listenfd, (struct sockaddr *)&address, sizeof(address));
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd, (struct sockaddr *)&client_address, &client_addrlength);
if (connfd < 0)
{
printf("errno is %d\n", errno);
close(listenfd);
}
char buf[1024];
fd_set read_fds;
fd_set exception_fds;
FD_ZERO(&read_fds);
FD_ZERO(&exception_fds);
while (1)
{
memset(buf, '\0', sizeof(buf));
FD_SET(connfd, &read_fds);
FD_SET(connfd, &exception_fds);
ret = select(connfd + 1, &read_fds, NULL, &exception_fds, NULL);
if (ret < 0)
{
printf("selection failure\n");
break;
}
if (FD_ISSET(connfd, &read_fds))
{
ret = recv(connfd, buf, sizeof(buf) - 1, 0);
if (ret <= 0)
{
break;
}
printf("get %d bytes of normal data: %s\n", ret, buf);
}
else if (FD_ISSET(connfd, &exception_fds))
{
ret = recv(connfd, buf, sizeof(buf) - 1, MSG_OOB);
if (ret <= 0)
{
break;
}
printf("get %d bytes of oob data: %s\n", ret, buf);
}
}
close(connfd);
close(listenfd);
return 0;
}

浙公网安备 33010602011771号