- 设置文件描述符
- 利用
select
函数可以同时监听多个文件描述符/监听套接字。
首先将要监听的文件描述符集中到一起,集中时按照监视项(接受、传输、异常)进行区分; - 使用
fd_set
数组变量执行此项操作,该数组是存有
0
0
和1的位数组;
若某个数组位为1,则表示该文件描述符是监听对象。即下图中属于监听对象的文件描述符为
1
1
和3;
- 针对
fd_set
变量的操作是以位为单位进行的,通过下列宏完成
FD_ZERO(fd_set *fdset)
: 将fd_set
变量的所有位初始化为
0
0
FD_SET(int fd, fd_set *fdset)
: 在参数fdset
指向的变量中注册文件描述符fd
的信息。
FD_CLR(int fd, fd_set *fdset)
: 从参数fdset
指向德变量中清除文件描述符fd
的信息。
FD_ISSET(int fd, fd_set *fdset)
:若参数fdset
指向的变量中包含文件描述fd
的信息,则返回真
int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset,
const struct timeval *timeout);
maxfd
:监视对象文件描述符数量。
readset
:将所有关注“是否存在待读取数据”的文件描述符注册到fd_set
型变量,并传递其地址。
writeset
:将所有关注“是否可传输无阻塞数据”的文件描述符注册到fd_set
型变量,并传递其地址。
exceptset
:将所有关注“是否发生异常”的文件描述符注册到fd_set
型变量,并传递其地址。
timeout
:调用select
函数后,为防止陷入无限阻塞的状态,传递超时信息。若不想设置超时,则传递NULL
参数
返回值
:发生错误时返回−1,超时返回
0
0
,因发生关注的事件返回时,返回发生事件的文件描述符(大于0)
struct timeval
{
long tv_sec;
long tv_usec;
}
#include<stdio.h>
#include<unistd.h>
#include<sys/time.h>
#include<sys/select.h>
const size_t BUF_SIZE = 30;
int main(int argc, char *argv[])
{
fd_set reads, temps;
int result, str_len;
char buf[BUF_SIZE];
struct timeval timeout;
FD_ZERO(&reads);
FD_SET(0, &reads);
while(1)
{
temps = reads;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
result = select(1, &temps, 0, 0, &timeout);
if(result == -1)
{
puts("seletc() error!");
break;
}
else if(result == 0)
{
puts("Time-out!");
}
else
{
if(FD_ISSET(0, &temps))
{
str_len = read(0, buf, BUF_SIZE);
buf[str_len] = 0;
printf("message from console: %s", buf);
}
}
}
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<sys/select.h>
const size_t BUF_SIZE = 100;
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_addr, clnt_addr;
struct timeval timeout;
fd_set reads, cpy_reads;
socklen_t addr_sz;
int fd_max, str_len, fd_num;
char buf[BUF_SIZE];
if(argc != 2)
{
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("bind() error");
if(listen(serv_sock, 5) == -1)
error_handling("listen() error");
FD_ZERO(&reads);
FD_SET(serv_sock, &reads);
fd_max = serv_sock;
while(1)
{
cpy_reads = reads;
timeout.tv_sec = 5;
timeout.tv_usec = 5000;
if((fd_num = select(fd_max + 1, &cpy_reads, 0, 0, &timeout)) == -1)
break;
if(fd_num == 0)
continue;
for(int i = 0; i < fd_max + 1; i++)
{
if(FD_ISSET(i, &cpy_reads))
{
if(i == serv_sock)
{
addr_sz = sizeof(clnt_addr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &addr_sz);
FD_SET(clnt_sock, &reads);
if(fd_max < clnt_sock)
fd_max = clnt_sock;
printf("connected client: %d \n", clnt_sock);
}
else
{
str_len = read(i, buf, BUF_SIZE);
if(str_len == 0)
{
FD_CLR(i, &reads);
close(i);
printf("close client: %d \n", i);
}
else
{
write(i, buf, str_len);
}
}
}
}
}
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}