在很长的时间内,相比于Windows平台众多的选择,select模式都是linux平台下网络通信的不多选择之一(在epoll未出生之前)。这种模式在面对大量短连接的时候,比OCOT有高的效率,可以避免频繁的上下文切换。
select系统调用是用来让我们的程序监视多个文件描述符(file descriptor)的状态变化的。程序会停在select这里等待,直到遇到以下情况之一select会退出:
(1) 监控的句柄至少有一个发生变化,返回值大于1。
(2) 等待时间已到,返回值等于0。
(3) 收到信号退出,返回值小于0。
select操作的内部会在指定的时间内,不断的检测受控集合中的套接字是否可读,可写或有错误,在select返回时只保留发生状态变化的套接字。通过判断select的返回值,就可以了解该调用是否有文件描述符可读或是select调用退出等信息。
要想让程序运行,光有一个select的调用不是不够的,我们还需要一系列的配套数据结构和算法来帮助将套接字归类,判断是否有指定的套接字等等,结构体fd_set和宏FD_ZERO,FD_CLR,FD_SET,FD_ISSET等就是我们所需要的。通过定义一个fd_set的对象,例如可读集合,有相同需求的套接字都可以放置到这个对象中,可以通过FD_SET这个宏来完成,当然在这之间需要利用FD_ZERO将这个对象初始化。等完成了以上操作后,便会调用select查询是否有套接字可读。等待select完成后,如果有套接字可读,就可以利用FD_ISSET判断是否为监听套接字,转而执行accept操作。如果一个套接字已经关闭,就可以利用FD_CLR将其从fd_set对象中剔除。
服务器端源代码:
1
#include <iostream>
2
#include <sys/socket.h>
3
#include <sys/types.h>
4
#include <arpa/inet.h>
5
#include <sys/select.h>
6
#include <sys/ioctl.h>
7
using namespace std;
8
9
int main()
10

{
11
12
int server_sock = socket(AF_INET,SOCK_STREAM,0);
13
if(server_sock < 0)
14
{
15
cout << "create socket error" << endl;
16
return -1;
17
}
18
19
struct sockaddr_in server_addr;
20
memset(&server_addr,0,sizeof(server_addr));
21
server_addr.sin_family = AF_INET;
22
server_addr.sin_port = htons(5555);
23
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
24
25
if(bind(server_sock,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0)
26
{
27
cout << "bind socket error" << endl;
28
close(server_sock);
29
return -1;
30
}
31
32
listen(server_sock,10);
33
34
fd_set read_set;
35
fd_set test_set;
36
FD_ZERO(&read_set);
37
FD_SET(server_sock,&read_set);
38
39
struct timeval tm;
40
tm.tv_sec = 5;
41
tm.tv_usec = 500;
42
43
44
while(true)
45
{
46
int nread = 0;
47
test_set = read_set;
48
int ret = select(FD_SETSIZE,&test_set,NULL,NULL,&tm);
49
50
if(ret < 0)
51
{
52
cout << "select error" << endl;
53
close(server_sock);
54
return -1;
55
}
56
else if(ret == 0)
57
{
58
//cout << "waitout" << endl;
59
continue;
60
}
61
else
62
{
63
for(int fd=0; fd<FD_SETSIZE; ++fd)
64
{
65
if(FD_ISSET(fd,&test_set))
66
{
67
//如果有新的连接到达
68
if(fd == server_sock)
69
{
70
int sock = accept(server_sock,NULL,NULL);
71
FD_SET(sock,&read_set);
72
}
73
else
74
{
75
ioctl(fd,FIONREAD,&nread);
76
77
if(nread == 0) //客户端已经关闭
78
{
79
close(fd);
80
FD_CLR(fd,&read_set);
81
cout << "client has removed " << fd << endl;
82
}
83
else
84
{
85
char buf[128];
86
recv(fd,buf,128,0);
87
cout << buf << endl;
88
send(fd,buf,strlen(buf)+1,0);
89
}
90
}
91
92
}
93
94
}
95
96
}
97
98
}
99
}
100