cpp-doc-网络编程

TCP

例子:回射粘包处理

client.c
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
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m)\
do\
{\
perror(m); \
exit(EXIT_FAILURE); \
}while (0)

struct packet
{
int len;
char buf[1024];
};

ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nread = read(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nread == 0)
{
return count - nleft;
}

bufp += nread;
nleft -= nread;
}

return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nwritten = write(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nwritten == 0)
{
continue;
}

bufp += nwritten;
nleft -= nwritten;
}

return count;
}

int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket");

struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = inet_addr("120.25.1.87");

if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("CONNECT") ;

struct packet sendbuf;
struct packet recvbuf;
memset(&sendbuf, 0, sizeof(sendbuf));
memset(&recvbuf, 0, sizeof(recvbuf));
int n;

while(fgets(sendbuf.buf, sizeof(sendbuf.buf), stdin) != NULL)
{
n = strlen(sendbuf.buf);
sendbuf.len = htonl(n);
writen(sock, &sendbuf, 4 + n) ;

int ret = readn(sock, &recvbuf.len, 4);
if(ret == -1)
{
ERR_EXIT("READ");
}
else if(ret < 4)
{
printf("client close\n");
break;
}

n = ntohl(recvbuf.len);
ret = readn(sock, recvbuf.buf, n);
if(ret == -1)
{
ERR_EXIT("READ");
}
else if(ret < n)
{
printf("client close\n");
break;
}

fputs(recvbuf.buf, stdout);
memset(&sendbuf, 0, sizeof(sendbuf));
memset(&recvbuf, 0, sizeof(recvbuf));
}

close(sock) ;

return 0;
}
server.c
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
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m)\
do\
{\
perror(m); \
exit(EXIT_FAILURE); \
}while (0)

struct packet
{
int len;
char buf[1024];
};

ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nread = read(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nread == 0)
{
return count - nleft;
}

bufp += nread;
nleft -= nread;
}

return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nwritten = write(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nwritten == 0)
{
continue;
}

bufp += nwritten;
nleft -= nwritten;
}

return count;
}

void do_service(int conn)
{
struct packet recvbuf;
int n;
while (1)
{
memset(&recvbuf, 0, sizeof(recvbuf));
int ret = readn(conn, &recvbuf.len, 4);
if(ret == -1)
{
ERR_EXIT("READ");
}
else if(ret < 4)
{
printf("client close\n");
break;
}

n = ntohl(recvbuf.len);
ret = readn(conn, recvbuf.buf, n);
if(ret == -1)
{
ERR_EXIT("READ");
}
else if(ret < n)
{
printf("client close\n");
break;
}

fputs(recvbuf.buf, stdout);
writen(conn, &recvbuf, 4 + n);
}
}

int main(void)
{
int listenfd;
/*if ((listenfd = socket(PF_INET, SOCK_STREAM, 0))<0)*/
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket");
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
ERR_EXIT("SETSOCKOPT") ;

if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("BIND");

if (listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("LISTEN");

struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn;

pid_t pid;
while(1)
{
if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
ERR_EXIT("ACCEPT");


printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

/*copy thread*/
pid = fork();
if(pid == -1)
ERR_EXIT("FORK") ;

if(pid == 0)
{
close(listenfd);
do_service(conn) ;
exit(EXIT_SUCCESS);
}
else
{
close(conn);
}
}
return 0;
}

UDP

例子:回射

client.c
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
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>

#define ERR_EXIT(m)\
do\
{\
perror(m); \
exit(EXIT_FAILURE); \
}while (0)

void echo_cli(int sock)
{
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr));

int ret;
char sendbuf[1024] = {0};
char recvbuf[1024] = {0};
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
sendto(sock, sendbuf, strlen(sendbuf), 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
ret = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, NULL, NULL);
if(ret == -1)
{
if(errno == EINTR)
continue;

ERR_EXIT("RECVFROM");
}

fputs(recvbuf, stdout);
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
}

close(sock);
}

int main(void)
{
int sock;
if((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
ERR_EXIT("SOCKET");

echo_cli(sock);
return 0 ;
}
server.c
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
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>

#define ERR_EXIT(m)\
do\
{\
perror(m); \
exit(EXIT_FAILURE); \
}while (0)


void echo_srv(int sock)
{
char recvbuf[1024] = {0};
struct sockaddr_in peeraddr;
socklen_t peerlen;
int n;
while(1)
{
peerlen = sizeof(peeraddr);
memset(recvbuf, 0, sizeof(recvbuf));
n = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&peeraddr, &peerlen);
if(n == -1)
{
if(errno == EINTR)
continue;

ERR_EXIT("RECVFROM");
}
else if(n > 0)
{
fputs(recvbuf, stdout);
sendto(sock, recvbuf, n, 0, (struct sockaddr*)&peeraddr, peerlen);
}
}

close(sock);
}

int main(void)
{
int sock;
if((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
ERR_EXIT("SOCKET");

struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("BIND");

echo_srv(sock);
return 0 ;
}

Unix域协议

例子:回射

client.c
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
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/un.h>
#include <unistd.h>

#define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

void echo_cli(int sock)
{
char sendbuf[1024] = {0};
char recvbuf[1024] = {0};
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(sock, sendbuf, strlen(sendbuf));
read(sock, recvbuf, sizeof(recvbuf));

fputs(recvbuf, stdout);

memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
}

close(sock);
}

int main(void)
{
int sock;
if((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
ERR_EXIT("socket");

struct sockaddr_un servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, "/tmp/test_socket");

if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("connect");

echo_cli(sock);

return 0;
}
server.c
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
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/un.h>
#include <unistd.h>

#define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

void echo_srv(int conn)
{
char recvbuf[1024] ;
int n;
while(1)
{
memset(recvbuf, 0, sizeof(recvbuf));
n = read(conn, recvbuf, sizeof(recvbuf));
if(n == -1)
{
if(n == EINTR)
continue;

ERR_EXIT("read");
}
else if(n == 0)
{
printf("client close\n");
break;
}

fputs(recvbuf, stdout);
write(conn, recvbuf, strlen(recvbuf));
}

close(conn);
}

int main(void)
{
int listenfd;
if((listenfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
ERR_EXIT("socket");

unlink("/tmp/test_socket");
struct sockaddr_un servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, "/tmp/test_socket");

if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("bind");

if(listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("listen");

int conn;
pid_t pid;
while(1)
{
conn = accept(listenfd, NULL, NULL);
if(conn == -1)
{
if(conn == EINTR)
continue;
ERR_EXIT("accept");
}

pid = fork();
if(pid == -1)
ERR_EXIT("fork");

if(pid == 0)
{
close(listenfd);
echo_srv(conn);
exit(EXIT_SUCCESS);
}

close(conn);
}

return 0;

}

多路复用

阻塞IO

当用户线程发起一个IO操作后,线程会一直阻塞,直到该操作结束,才可以进行后面的操作。

socket读操作流程图

非阻塞IO

当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。

所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。

非阻塞io模型图

设置非阻塞常用方式:

  1. 创建socket 时指定
1
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
  1. 在使用前通过如下方式设定
1
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);

信号驱动IO

使用信号驱动I/O时,当网络套接字可读后,内核通过发送SIGIO信号通知应用进程,于是应用可以开 始读取数据。该方式并不是异步I/O,因为实际读取数据到应用进程缓存的工作仍然是由应用自己负责的。

信号驱动io图示

异步IO

当用户进程发起一个read操作后,内核收到该read操作后,首先它会立刻返回,所以不会对用户进程 阻塞,然后它会等待数据的准备完成,再把数据拷贝到用户内存,完成之后,它会给用户进程发送一个信号,告诉用户进程read操作已完成.

异步io图示

select

当用户进程调用了select,那么整个进程就会被block,而同时,kernel会 “监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

所以,IO多路复用的特点是通过一种机制,一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入就绪状态,select()函数就可以返回。

这里需要使用两个system call(select 和 recvfrom),而blocking IO只调用了一个system call(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用mutil-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll 的优势并不是对于单个连接能处理得更好,而是在于能同时处理更多的连接。

select模型图

用select实现的并发服务器,能达到的并发数,受两方面限制

  1. 一个进程能打开的最大文件描述符限制。这可以通过调整内核参数。

  2. select中的fd_set集合容量的限制(FD_SETSIZE),这需要重新编译内核。

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
// 可读:
// 1.套接口缓冲区有数据可读
// 2.连接的读一半关闭,即接收到FIN段,读操作将返回0
// 3.如果是监听套接口,已完成连接队列不为空时
// 4.套接口上发生了一个错误待处理,错误可以通过getsockopt指定SO_ERROR选项来获取
// 可写:
// 1.套接口发送缓冲区有空间容纳数据
// 2.连接的写一半关闭,即受到RST段之后,再次调用write操作。(产生SIGPIPE信号)
// 3.套接口上发生一个错误待处理,错误可以通过getsockopt指定SO_ERROR选项来获取。
// 异常:
// 1.套接口存在带外数据
// @nfd:读、写、异常集合中的文件描述符的最大值+1
// @readfds:读集合
// @writefds:写集合
// @exceptfds:异常集合
// @timeout:超时结构体
// struct timeval{
// long tv_sec; // seconds
// long tv_usec; // microseconds
// }
// 返回值:>0 是已就绪的文件句柄的总数,=0 超时, <0 表示出错,错误: errno
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

// 把套接字从集合里面移除
// @fd:要移除的套接字
// @set:要处理的集合
void FD_CLR(int fd, fd_set *set);

// 判断套接字是否在集合里面
// @fd:套接字
// @set:要判断的集合
int FD_ISSET(int fd, fd_set *set);

// 把套接字添加到集合里面
// @fd:套接字
// @set:要添加的集合
void FD_SET(int fd, fd_set *set);

// 清空集合
// @set:要清空的集合
void FD_ZERO(fd_set *set);
例子:回射
client.c
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>


#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>


#define ERR_EXIT(m)\
do\
{\
perror(m); \
exit(EXIT_FAILURE); \
}while (0)

ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nread = read(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nread == 0)
{
return count - nleft;
}

bufp += nread;
nleft -= nread;
}

return count;
}


ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nwritten = write(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nwritten == 0)
{
continue;
}

bufp += nwritten;
nleft -= nwritten;
}

return count;
}




ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while(1)
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if(ret == -1 || errno == EINTR)
continue;

return ret;
}
}


ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = buf;
int nleft = maxline;

while(1)
{
ret = recv_peek(sockfd, bufp, nleft);
if(ret < 0)
return ret;
else if(ret == 0)
return ret;

nread = ret;
int i;
for(i = 0; i < nread; i++)
{
if(bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+1);
if(ret != i+1)
exit(EXIT_FAILURE);

return ret;
}
}

if(nread > nleft)
exit(EXIT_FAILURE) ;

nleft -= nread;
ret = readn(sockfd, bufp, nread);
if(ret != nread)
exit(EXIT_FAILURE) ;

bufp += nread;
}

return -1;
}


void echo_cli(int sock)
{
fd_set rset;
FD_ZERO(&rset);

int nready;
int maxfd;
int fd_stdin = fileno(stdin);
if(fd_stdin > sock)
maxfd = fd_stdin;
else
maxfd = sock;

char sendbuf[1024] = {0};
char recvbuf[1024] = {0};

while(1)
{
FD_SET(fd_stdin, &rset);
FD_SET(sock, &rset);
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if(nready == -1)
ERR_EXIT("select");

if(nready == 0)
continue;

if(FD_ISSET(sock, &rset))
{
int ret = readline(sock, recvbuf, sizeof(recvbuf)) ;
if(ret == -1)
{
ERR_EXIT("READ");
}
else if(ret == 0)
{
printf("server close\n");
break;
}


fputs(recvbuf, stdout);
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
}

if(FD_ISSET(fd_stdin, &rset))
{
if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
break;

writen(sock, sendbuf, strlen(sendbuf)) ;
}
}

close(sock);
}

int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket");

struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = inet_addr("120.25.1.87");

if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("CONNECT") ;

struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if(getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0)
ERR_EXIT("GETSOCKNAME");

printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));

echo_cli(sock);

return 0;
}
server.c
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include <sys/wait.h>

#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m)\
do\
{\
perror(m); \
exit(EXIT_FAILURE); \
}while (0)

ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nread = read(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nread == 0)
{
return count - nleft;
}

bufp += nread;
nleft -= nread;
}

return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nwritten = write(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nwritten == 0)
{
continue;
}

bufp += nwritten;
nleft -= nwritten;
}

return count;
}

ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while(1)
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if(ret == -1 || errno == EINTR)
continue;

return ret;
}
}

ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = buf;
int nleft = maxline;

while(1)
{
ret = recv_peek(sockfd, bufp, nleft);
if(ret < 0)
return ret;
else if(ret == 0)
return ret;

nread = ret;
int i;
for(i = 0; i < nread; i++)
{
if(bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+1);
if(ret != i+1)
exit(EXIT_FAILURE);

return ret;
}
}

if(nread > nleft)
exit(EXIT_FAILURE) ;

nleft -= nread;
ret = readn(sockfd, bufp, nread);
if(ret != nread)
exit(EXIT_FAILURE) ;

bufp += nread;
}

return -1;
}

void echo_service(int conn)
{
char recvbuf[1024];
while (1)
{
memset(&recvbuf, 0, sizeof(recvbuf));
int ret = readline(conn, recvbuf, 1024);
if(ret == -1)
ERR_EXIT("readline");

if(ret == 0)
{
printf("client close\n");
break;
}

fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));
}
}

void handle_sigchld(int sig)
{
/*wait(NULL);*/
while(waitpid(-1, NULL, WNOHANG) > 0)
;
}

int main(void)
{
/*signal(SIGCHLD, SIG_IGN);*/
signal(SIGCHLD, handle_sigchld);

int listenfd;
/*if ((listenfd = socket(PF_INET, SOCK_STREAM, 0))<0)*/
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket");


struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
ERR_EXIT("SETSOCKOPT") ;

if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("BIND");

if (listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("LISTEN");

struct sockaddr_in peeraddr;
socklen_t peerlen;
int conn;
int i;
int client[FD_SETSIZE];
int maxi = 0;

for(i = 0; i < FD_SETSIZE; i++)
{
client[i] = -1;
}

int nready;
int maxfd = listenfd ;
fd_set rset;
fd_set allset;
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
while(1)
{
rset = allset;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if(nready == -1)
{
if(errno == EINTR)
continue;

ERR_EXIT("SELECT");
}

if(nready == 0)
continue;

if(FD_ISSET(listenfd, &rset))
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);

if(conn == -1)
ERR_EXIT("accept");

for(i = 0; i < FD_SETSIZE; i++)
{
if(client[i] < 0)
{
client[i] = conn;
if(i > maxi)
maxi = i ;

break;
}
}

if(i == FD_SETSIZE)
{
fprintf(stderr, "too many clients\n");
exit(EXIT_FAILURE);
}

printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
FD_SET(conn, &allset);
if(conn > maxfd)
maxfd = conn;

if(--nready <= 0)
continue;
}

for(i=0; i<= maxi; i++)
{
conn = client[i];
if(conn == -1)
continue;

if(FD_ISSET(conn, &rset))
{
char recvbuf[1024] = {0};
int ret = readline(conn, recvbuf, 1024);
if(ret == -1)
ERR_EXIT("readline");

if(ret == 0)
{
printf("client close\n");
FD_CLR(conn, &allset);
client[i] = -1;
}

fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));

if(--nready <= 0)
break;
}
}
}

return 0;
}

poll

和select 一样,如果没有事件发生,则进入休眠状态,如果在规定时间内有事件发生,则返回成功,规定时间过后仍然没有事件发生则返回失败。可见,等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高CPU的效率。

poll 和select 区别: select 有文件句柄上线设置,值为FD_SETSIZE,而poll 理论上没有限制!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 使用pool等待描述符事件
// @fds:可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回
// struct pollfd {
// int fd;                /*文件描述符   open打开的那个*/
// short events;     /*请求的事件类型,监视驱动文件的事件掩码*/ POLLIN | POLLOUT
// short revents;    /*驱动文件实际返回的事件*/
// }
// @nfds:监测驱动文件的个数
// @timeout:超时时间,单位是ms
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

// 将fd_set类型变量的所有位都设为 0
int FD_ZERO(fd_set *fdset);

// 清除fd某个位
int FD_CLR(int fd, fd_set *fdset);

// 设置fd的某个位
int FD_SET(int fd, fd_set *fd_set);

// 检测fd是否包含某个位
int FD_ISSET(int fd, fd_set *fdset);

例子:回射

server.c
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<sys/wait.h>
#include<poll.h>


#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>


#define ERR_EXIT(m)\
do\
{\
perror(m); \
exit(EXIT_FAILURE); \
}while (0)

ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nread = read(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nread == 0)
{
return count - nleft;
}

bufp += nread;
nleft -= nread;
}

return count;
}


ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nwritten = write(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nwritten == 0)
{
continue;
}

bufp += nwritten;
nleft -= nwritten;
}

return count;
}


ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while(1)
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if(ret == -1 || errno == EINTR)
continue;

return ret;
}
}


ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = buf;
int nleft = maxline;

while(1)
{
ret = recv_peek(sockfd, bufp, nleft);
if(ret < 0)
return ret;
else if(ret == 0)
return ret;

nread = ret;
int i;
for(i = 0; i < nread; i++)
{
if(bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+1);
if(ret != i+1)
exit(EXIT_FAILURE);

return ret;
}
}

if(nread > nleft)
exit(EXIT_FAILURE) ;

nleft -= nread;
ret = readn(sockfd, bufp, nread);
if(ret != nread)
exit(EXIT_FAILURE) ;

bufp += nread;
}

return -1;
}


void echo_service(int conn)
{
char recvbuf[1024];
while (1)
{
memset(&recvbuf, 0, sizeof(recvbuf));
int ret = readline(conn, recvbuf, 1024);
if(ret == -1)
ERR_EXIT("readline");

if(ret == 0)
{
printf("client close\n");
break;
}

fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));
}
}


void handle_sigchld(int sig)
{
/*wait(NULL);*/
while(waitpid(-1, NULL, WNOHANG) > 0)
;
}

int main(void)
{
signal(SIGCHLD, handle_sigchld);

int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket");


struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);


int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
ERR_EXIT("SETSOCKOPT") ;

if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("BIND");


if (listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("LISTEN");


struct sockaddr_in peeraddr;
socklen_t peerlen;
int conn;

int i;
struct pollfd client[2048];
int maxi = 0;

for(i = 0; i < 2048; i++)
{
client[i].fd = -1;
}

int count = 0;
int nready;
client[0].fd = listenfd;
client[0].events = POLLIN;

while(1)
{
nready = poll(client, maxi+1, -1);
if(nready == -1)
{
if(errno == EINTR)
continue;


ERR_EXIT("SELECT");
}

if(nready == 0)
continue;

if(client[0].revents & POLLIN)
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);

if(conn == -1)
ERR_EXIT("accept");

for(i = 0; i < 2048; i++)
{
if(client[i].fd < 0)
{
client[i].fd = conn;
if(i > maxi)
maxi = i ;

break;
}
}

if(i == 2048)
{
fprintf(stderr, "too many clients\n");
exit(EXIT_FAILURE);
}

printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
printf("count = %d\n", ++count);

client[i].events = POLLIN;

if(--nready <= 0)
continue;
}

for(i=1; i<= maxi; i++)
{
conn = client[i].fd;
if(conn == -1)
continue;

if(client[i].events & POLLIN)
{
char recvbuf[1024] = {0};
int ret = readline(conn, recvbuf, 1024);
if(ret == -1)
ERR_EXIT("readline");

if(ret == 0)
{
printf("client close\n");
client[i].fd = -1;
close(conn);
}

fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));

if(--nready <= 0)
break;
}
}
}

return 0;
}

epoll

epoll的实现是基于回调的,如果fd有期望的事件发生就通过回调函数将其加入epoll就绪队列中,也就是说它只关心“活跃”的fd,与fd数目无关。它不会随着监听fd数目的增加而降低效率。

epoll不仅会高数应用程序有I/P事件到来,还会告诉应用程序相关的信息,这些信息是应用程序填充的,因此根据这些应用程序就能定位到事件,而不必遍历整个fd集合

内核/用户空间上内存拷贝问题,如何让内核把fd消息通知给用户空间呢?在这个问题上select/poll采用了内核拷贝方法。而epoll采用了共享内存的方式。

epoll事件驱动流程图
  • epoll两种模式

EPOLLET(边缘触发、水平出发):此模式下,系统仅仅通知应用程序哪些fds变成了就绪状态,一旦fd变成了就绪状态,epoll将不再关注这个fd的任何状态信息,(从epoll队里移除)直到应用程序通过读写操作触发EAGAIN状态,epoll认为这个fd又变成了空闲状态,那么epoll又重新关注这个fd的状态变化(重新加入epoll队列)。随着epoll_wait的返回,队列中的fds是在减少的,所以在大并发系统中,EPOLLET更有优势。但是对程序员的要求也更高。

EPOLLLT(电平触发、边缘触发):完全靠kernal epoll驱动,应用程序只需要处理从epoll_wait返回的fds,这些fds我们认为它们处于就绪状态。

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
// 创建EPOLL 句柄
int epoll_create(int size);

// 向EPOLL对象中添加、修改或者删除感兴趣的事件
// @op取值:
// EPOLL_CTL_ADD 添加新的事件到epoll中
// EPOLL_CTL_MOD 修改EPOLL中的事件
// EPOLL_CTL_DEL 删除epoll中的事件
// @events取值:
// EPOLLIN 表示有数据可以读出(接受连接、关闭连接)
// EPOLLOUT 表示连接可以写入数据发送(向服务器发起连接,连接成功事件)
// EPOLLERR 表示对应的连接发生错误
// EPOLLHUP 表示对应的连接被挂起
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

// struct epoll_event{
// __uint32_t events;
// epoll_data_t data;
//}

// typedef union epoll_data{
// void *ptr;
// int fd;
// uint32_t u32;
// uint64_t u64;
// }epoll_data_t

// 收集在epoll监控的事件中已经发生的事件
// @epfd:epoll的描述符。
// @events:则是分配好的 epoll_event结构体数组,epoll将会把发生的事件复制到 events数组中(events不可以是空指针,内核只负责把数据复制到这个 events数组中,不会去帮助我们在用户态中分配内存。内核这种做法效率很高)。
// @maxevents: 本次可以返回的最大事件数目,通常 maxevents参数与预分配的events数组的大小是相等的。
// @timeout: 表示在没有检测到事件发生时最多等待的时间(单位为毫秒),如果 timeout为0,立刻返回,不会等待。-1表示无限期阻塞
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

例子:回射

server.c
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/epoll.h>

#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>

#include<vector>
#include<algorithm>

typedef std::vector<struct epoll_event> EventList;

#define ERR_EXIT(m)\
do\
{\
perror(m); \
exit(EXIT_FAILURE); \
}while (0)

void activate_nonblock(int fd)
{
int ret;
int flags = fcntl(fd, F_GETFL);
if(flags == -1)
ERR_EXIT("FCNTL");

flags |= O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if(ret == -1)
ERR_EXIT("FCNTL");
}


ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nread = read(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nread == 0)
{
return count - nleft;
}

bufp += nread;
nleft -= nread;
}

return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;

while(nleft > 0)
{
if((nwritten = write(fd, bufp, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nwritten == 0)
{
continue;
}

bufp += nwritten;
nleft -= nwritten;
}

return count;
}




ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while(1)
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if(ret == -1 || errno == EINTR)
continue;

return ret;
}
}


ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = (char*)buf;
int nleft = maxline;

while(1)
{
ret = recv_peek(sockfd, bufp, nleft);
if(ret < 0)
return ret;
else if(ret == 0)
return ret;

nread = ret;
int i;
for(i = 0; i < nread; i++)
{
if(bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+1);
if(ret != i+1)
exit(EXIT_FAILURE);

return ret;
}
}

if(nread > nleft)
exit(EXIT_FAILURE) ;

nleft -= nread;
ret = readn(sockfd, bufp, nread);
if(ret != nread)
exit(EXIT_FAILURE) ;

bufp += nread;
}

return -1;
}


void handle_sigchld(int sig)
{
/*wait(NULL);*/
while(waitpid(-1, NULL, WNOHANG) > 0)
;
}


void handle_sigpipe(int sig)
{
printf("recv a sig=%d\n", sig);
}



int main(void)
{
int count = 0;
signal(SIGCHLD, handle_sigchld);
signal(SIGCHLD, handle_sigpipe);

int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket");


struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);


int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
ERR_EXIT("SETSOCKOPT") ;

if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("BIND");


if (listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("LISTEN");

std::vector<int> clients;
int epollfd;
epollfd = epoll_create1(EPOLL_CLOEXEC);

struct epoll_event event;
event.data.fd = listenfd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);

EventList events(16);
struct sockaddr_in peeraddr;
socklen_t peerlen;
int conn;
int i;

int nready;
while(1)
{
nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -1);
if(nready == -1)
{
if(errno == EINTR)
continue;


ERR_EXIT("EPOLL_WAIT");
}

if(nready == 0)
continue;

if((size_t)nready == events.size())
events.resize(events.size()*2);

for(i=0; i<nready; i++)
{
if(events[i].data.fd == listenfd)
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);

if(conn == -1)
ERR_EXIT("accept");

printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
printf("count = %d\n", ++count);
clients.push_back(conn);

activate_nonblock(conn);

event.data.fd = conn;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, conn, &event);
}
else if(events[i].events & EPOLLIN)
{
conn = events[i].data.fd;
if(conn < 0)
continue;

char recvbuf[1024] = {0};
int ret = readline(conn, recvbuf, 1024);
if(ret == -1)
ERR_EXIT("READLINE");

if(ret == 0)
{
printf("client close\n");
close(conn);

event = events[i];
epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event);
clients.erase(std::remove(clients.begin(), clients.end(), conn), clients.end());


}

fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));
}
}
}


return 0;
}