Unix网络编程(七、poll)

poll

1
2
3
4
5
6
7
8
9
10
11
12
#include <poll.h>

// 成功返回就绪的文件描述符个数,超时返回0,失败返回-1
int poll(struct pollfd *fdarray, unsigned int nfds, int timeout);

// 该就够用于测试指定fd的某些条件,这些条件由events指定,revents作为结果返回
struct pollfd
{
int fd;
short events;
short revents;
}

events和revents的值如下

图上的值分为三部分。第一部分是处理输入的四个常量值,第二部分是处理输出的三个常量值,第三部分是处理错误的三个常量值。
在使用的时候只需要在对于的文件描述符上绑定需要的events,并将构造好的pollfd结构加入到fdarray中,如果不需要检查该文件描述符上对应的events时,只需要将pollfd结构中的fd置为-1,poll会忽略这样的pollfd结构的events事件,并将revents置为0.

poll相比于select的优势在于用户可以自行构造pollfd结构,将其加入到fdarray中,而不像select那样,需要指定一个固定大小的fd_set结构。不受内核指定的最大文件描述符数量的影响。

用poll改造服务端

话不多说,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <poll.h>
#include <sys/stropts.h>

static const int MAXLINE = 4096;
static const int MAXLISTEN = 1024;
static const int PORT = 45678;
static const int OPEN_MAX = 1024;

#define INFTIM -1

int e_socket(int domain, int type, int protocol);
int e_bind(int sockfd, sockaddr_in *myaddr, socklen_t addrlen);
int e_listen(int sockfd, int backlog);
int e_accept(int sockfd, sockaddr_in *myaddr, socklen_t *addrlen);
void init_clientfd(pollfd clients[], int socklen);
void init_sockaddr(sockaddr_in sockaddr);
int append_clientfd(pollfd clients[], int socklen, int clientfd);
int handle_client_connect(pollfd clients[], int socklen, int *max_use_index);
void handle_client_data(pollfd clients[], int use_count, int ready_count);

int e_socket(int domain, int type, int protocol)
{
int sockfd = socket(domain, type, protocol);
if (sockfd < 0)
{
exit(0);
}
return sockfd;
}

int e_bind(int sockfd, sockaddr_in *myaddr, socklen_t addrlen)
{
int ret = bind(sockfd, (sockaddr *)myaddr, addrlen);
if (ret != 0)
{
close(sockfd);
exit(0);
}
return ret;
}

int e_listen(int sockfd, int backlog)
{
int ret = listen(sockfd, backlog);
if (ret != 0)
{
close(sockfd);
exit(0);
}
return ret;
}

int e_accept(int sockfd, sockaddr_in *myaddr, socklen_t *addrlen)
{
int ret = accept(sockfd, (sockaddr *)myaddr, addrlen);
if (ret == -1)
{
printf("%d", errno);
exit(0);
}
return ret;
}

void init_clientfd(pollfd clients[], int socklen)
{
for (int i = 1; i < socklen; ++i)
{
clients[i].fd = -1;
}
}

void init_sockaddr(sockaddr_in *sockaddr)
{
if (!sockaddr)
{
return;
}

bzero(sockaddr, sizeof(sockaddr_in));
sockaddr->sin_family = AF_INET;
sockaddr->sin_addr.s_addr = htonl(INADDR_ANY);
sockaddr->sin_port = htons(PORT);
}

int append_clientfd(pollfd clients[], int socklen, int clientfd)
{
int i = 0;
for (i = 1; i < socklen; ++i)
{
if (clients[i].fd == -1)
{
clients[i].fd = clientfd;
clients[i].events = POLLRDNORM;
break;
}
}
if (i == socklen)
{
return -1;
}

return i;
}

int handle_client_connect(pollfd clients[], int socklen, int *max_use_index)
{
sockaddr_in clientaddr;
socklen_t clientlen = sizeof(clientaddr);
int connfd = e_accept(clients[0].fd, &clientaddr, &clientlen);

int cur_use = 0;
if ((cur_use = append_clientfd(clients, socklen, connfd)) == -1)
{
return -1;
}

if (*max_use_index < cur_use)
{
*max_use_index = cur_use;
}

return connfd;
}

void handle_client_data(pollfd clients[], int use_count, int ready_count)
{
char buf[MAXLINE] = {0};
for (int i = 1; i <= use_count; ++i) {
int sockfd = clients[i].fd;
if (sockfd < 0) {
continue;
}

if (clients[i].revents & (POLLRDNORM | POLLERR)) {
int read_len = read(sockfd, buf, MAXLISTEN);
if (read_len == 0) {
close(sockfd);
clients[i].fd = -1;
}
else if (read_len < 0) {
if (errno == ECONNRESET) {
close(sockfd);
clients[i].fd = -1;
}
else {
exit(0);
}
}
else {
write(sockfd, buf, read_len);
}

if (--ready_count <= 0) {
break;
}
}
}
}

int main(int argc, const char *argv[])
{

int listen_sock = e_socket(AF_INET, SOCK_STREAM, 0);

sockaddr_in servaddr;
init_sockaddr(&servaddr);

e_bind(listen_sock, &servaddr, sizeof(servaddr));
e_listen(listen_sock, MAXLISTEN);

struct pollfd clients[OPEN_MAX];

clients[0].fd = listen_sock;
clients[0].events = POLLRDNORM;
init_clientfd(clients, OPEN_MAX);

int readycount = 0;
int max_use = 0;
while (true)
{
readycount = poll(clients, OPEN_MAX, INFTIM);

if (clients[0].revents & POLLRDNORM)
{
handle_client_connect(clients, OPEN_MAX, &max_use);
if (--readycount <= 0) {
continue;
}
}

handle_client_data(clients, max_use, readycount);
}
return 0;
}