网络编程之IO模型

网络编程之IO模型


阻塞IO

阻塞IO模型

非阻塞IO

非阻塞IO模型

IO复用(select、poll和epoll)

IO复用模型

select

1
2
3
4
5
6
int select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set)
void FD_ISSET(int fd, fd_set *set)
void FD_SET(int fd, fd_set *set)
void FD_ZERO(fd_set *set)

用select管理IO,一旦其中的一个I/O或者多个IO发生我们感兴趣的事件,select返回事件发生的个数,并且返回哪些 I/O发生了事件,遍历这些事件,从而处理事件。
参数:

  • nfds:读、写、异常中的最大文件描述符+1
  • readfs:读集合
  • writefs:写集合
  • exceptfds:异常集合
  • timeout:超时时间

可读:

  • 套接口缓冲区有数据可读
  • 连接的读一半的关闭,即接受到FIN段,读操作将返回0
  • 如果是监听套接口,已完成连接队列不为空时
  • 套接口上发生了一个错误待处理,错误可以通过setsockopt指定SO_ERROR选项来获取

可写:

  • 套接口发送缓存区有空间容纳数据
  • 连接的写一半关闭,即受到RST段之后,再次调用wirte操作
  • 套接口上发生一个错误待处理,错误可以通过getsockopt指定SO_ERROR选项来获取

异常:

  • 套接字存在带外数据

select回射客户/服务器

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
/* client */
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/select.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) \
struct packet {
int len;
char buf[1024];
};
void echo_cli(int sockfd) {
struct packet send_packet;
struct packet recv_packet;
int stdinfd = fileno(stdin);
int maxfd;
fd_set rset;
if (sockfd > stdinfd) {
maxfd = sockfd;
} else {
maxfd = stdinfd;
}
int nread;
while(1) {
FD_SET(stdinfd, &rset);
FD_SET(sockfd, &rset);
nread = select(maxfd+1, &rset, NULL, NULL, NULL);
if (nread < 0) {
ERR_EXIT("select");
} else if (nread == 0) {
continue;
}
int n;
if (FD_ISSET(stdinfd, &rset)) {
if (fgets(send_packet.buf, sizeof(send_packet.buf), stdin) != 0) {
n = strlen(send_packet.buf);
send_packet.len = htonl(n);
write(sockfd, &send_packet, 4+n);
memset(&send_packet, 0, sizeof(send_packet));
}
}
if (FD_ISSET(sockfd, &rset)) {
int ret;
if ((ret = read(sockfd, &recv_packet, 4+n)) < 0) {
ERR_EXIT("readn");
} else if (ret == 0) {
printf("server close\n");
break;
}
fputs(recv_packet.buf, stdout);
memset(&recv_packet, 0, sizeof(recv_packet));
}
}
close(sockfd);
}
int main() {
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
ERR_EXIT("socket");
}
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(5534);
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {
ERR_EXIT("connect");
}
struct sockaddr_in addr;
int len = sizeof(addr);
if (getsockname(sockfd, (struct sockaddr*)&addr, &len) < 0) {
ERR_EXIT("getsockname");
}
printf("ip=%s, port=%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
struct sockaddr_in peeraddr;
int peerlen = sizeof(peeraddr);
if (getpeername(sockfd, (struct sockaddr*)&peeraddr, &peerlen) < 0) {
ERR_EXIT("getsockname");
}
printf("ip=%s, port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
echo_cli(sockfd);
return 0;
}

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
/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
struct packet {
int len;
char buf[1024];
};
void echo_svr(int linstenfd) {
int nready;
int maxfd = linstenfd;
fd_set rset;
fd_set allset;
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(linstenfd, &allset);
int client[FD_SETSIZE];
int conn;
int i;
int maxi = 0;
for (i=0; i<FD_SETSIZE; ++i) {
client[i] = -1;
}
while (1) {
rset = allset;
int i;
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(linstenfd, &rset)) {
struct sockaddr_in clientaddr;
int clientaddlen;
conn = accept(linstenfd, (struct sockaddr*)&clientaddr, &clientaddlen);
if (conn < 0) {
ERR_EXIT("accept");
}
printf("ip=%s, port=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
for (i=0; i<FD_SETSIZE; ++i) {
if (client[i] == -1) {
if (i > maxi) {
maxi = i;
}
break;
}
}
client[i] = conn;
FD_SET(conn, &allset);
if (conn > maxfd) {
maxfd = conn;
}
if (--nready) {
break;
}
}
for (i=0; i<=maxi; ++i) {
conn = client[i];
if (conn == -1) {
continue;
}
if (FD_ISSET(conn, &rset)) {
struct packet recv_packet;
int ret;
memset(&recv_packet, 0, sizeof(recv_packet));
if ((ret = read(conn, &recv_packet.len, sizeof(int))) < 0) {
ERR_EXIT("read len");
} else if (ret == 0) {
printf("client close\n");
client[i] = -1;
break;
}
int n = ntohl(recv_packet.len);
if ((ret = read(conn, recv_packet.buf, n)) < 0) {
ERR_EXIT("read buf");
}
fputs(recv_packet.buf, stdout);
write(conn, &recv_packet, 4+n);
}
}
}
}
int main() {
signal(SIGCHLD, SIG_IGN); // 避免僵尸进程
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
ERR_EXIT("socket");
}
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(5534);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
ERR_EXIT("setsockopt");
}
if (bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {
ERR_EXIT("bind");
}
if (listen(sockfd, SOMAXCONN) < 0) {
ERR_EXIT("listen");
}
int conn;
pid_t pid;
struct sockaddr_in clientaddr;
int clientaddrlen = sizeof(clientaddr);
echo_svr(sockfd);
return 0;
}

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

  • 一个进程能打开的最大文件描述符限制,这可以通过调整内核参数
  • select中的fd_set集合容量的限制(FD_SETSIZE),这需要重新编译内核

poll

1
2
3
4
5
6
7
8
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd;
short events;
short revents;
}

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
/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <poll.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
struct packet {
int len;
char buf[1024];
};
void echo_svr(int linstenfd) {
int nready;
struct pollfd client[2048];
int conn;
int i;
int maxi = 0;
client[0].fd = linstenfd;
client[0].events = POLLIN;
for (i=1; i<2048; ++i) {
client[i].fd = -1;
}
while (1) {
int i;
nready = poll(client, maxi+1, -1);
if (nready == -1) {
if (errno == EINTR) {
continue;
}
ERR_EXIT("poll");
}
if (nready == 0) {
continue;
}
if (client[0].revents & POLLIN) {
struct sockaddr_in clientaddr;
int clientaddlen;
conn = accept(client[0].fd, (struct sockaddr*)&clientaddr, &clientaddlen);
if (conn < 0) {
ERR_EXIT("accept");
}
printf("ip=%s, port=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
for (i=0; i<2048; ++i) {
if (client[i].fd == -1) {
if (i > maxi) {
maxi = i;
}
break;
}
}
client[i].fd = conn;
client[i].events = POLLIN;
if (--nready) {
break;
}
}
for (i=1; i<=maxi; ++i) {
conn = client[i].fd;
if (conn == -1) {
continue;
}
if (client[i].revents & POLLIN) {
struct packet recv_packet;
int ret;
memset(&recv_packet, 0, sizeof(recv_packet));
if ((ret = read(conn, &recv_packet.len, sizeof(int))) < 0) {
ERR_EXIT("read len");
} else if (ret == 0) {
printf("client close\n");
client[i].fd = -1;
break;
}
int n = ntohl(recv_packet.len);
if ((ret = read(conn, recv_packet.buf, n)) < 0) {
ERR_EXIT("read buf");
}
fputs(recv_packet.buf, stdout);
write(conn, &recv_packet, 4+n);
}
}
}
}
int main() {
signal(SIGCHLD, SIG_IGN); // 避免僵尸进程
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
ERR_EXIT("socket");
}
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(5534);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
ERR_EXIT("setsockopt");
}
if (bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {
ERR_EXIT("bind");
}
if (listen(sockfd, SOMAXCONN) < 0) {
ERR_EXIT("listen");
}
int conn;
pid_t pid;
struct sockaddr_in clientaddr;
int clientaddrlen = sizeof(clientaddr);
echo_svr(sockfd);
return 0;
}

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
/*client.c*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <poll.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) \
struct packet {
int len;
char buf[1024];
};
void echo_cli(int sockfd) {
struct packet send_packet;
struct packet recv_packet;
int stdinfd = fileno(stdin);
struct pollfd pollfd[2];
int nread;
pollfd[0].fd = stdinfd;
pollfd[0].events = POLLIN;
pollfd[1].fd = sockfd;
pollfd[1].events = POLLIN;
while(1) {
nread = poll(pollfd, 2, -1);
if (nread < 0) {
ERR_EXIT("poll");
} else if (nread == 0) {
continue;
}
int n;
if (pollfd[0].revents & POLLIN) {
if (fgets(send_packet.buf, sizeof(send_packet.buf), stdin) != 0) {
n = strlen(send_packet.buf);
send_packet.len = htonl(n);
write(sockfd, &send_packet, 4+n);
memset(&send_packet, 0, sizeof(send_packet));
}
}
if (pollfd[1].revents & POLLIN) {
int ret;
if ((ret = read(sockfd, &recv_packet, 4+n)) < 0) {
ERR_EXIT("readn");
} else if (ret == 0) {
printf("server close\n");
break;
}
fputs(recv_packet.buf, stdout);
memset(&recv_packet, 0, sizeof(recv_packet));
}
}
close(sockfd);
}
int main() {
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
ERR_EXIT("socket");
}
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(5534);
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {
ERR_EXIT("connect");
}
struct sockaddr_in addr;
int len = sizeof(addr);
if (getsockname(sockfd, (struct sockaddr*)&addr, &len) < 0) {
ERR_EXIT("getsockname");
}
printf("ip=%s, port=%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
struct sockaddr_in peeraddr;
int peerlen = sizeof(peeraddr);
if (getpeername(sockfd, (struct sockaddr*)&peeraddr, &peerlen) < 0) {
ERR_EXIT("getsockname");
}
printf("ip=%s, port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
echo_cli(sockfd);
return 0;
}

epoll

1
2
3
4
5
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_create1(int flags);
int epoll_ctl(int epfd, int op, int fd, struct epoll_evect *event);
int epoll_wait(int epfd, struct epoll_evect *events, int maxevents, int timeout);

epoll与select、poll区别

I/O复用方式 原理
select select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:1 单个进程可监视的fd数量被限制2 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大3 对socket进行扫描时是线性扫描
poll poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
epoll 在前面说到的复制问题上,epoll使用mmap减少复制开销。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。
I/O复用方式 一个进程所能打开的最大连接数
select 单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是32*32,同理64位机器上FD_SETSIZE为32*64),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。
poll poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的
epoll 虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接
I/O复用方式 FD剧增后带来的IO效率问题
select 因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。
poll 同上
epoll 因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。
I/O复用方式 消息传递方式
select 内核需要将消息传递到用户空间,都需要内核拷贝动作
poll 同上
epoll epoll通过内核和用户空间共享一块内存来实现的。

EPOLLIN:水平触发的模式,完全靠kernel epoll驱动,应用程序只需要处理从epoll_wait返回的fds,这些fds我们认为他们处于就绪状态

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

epoll实现回射客户/服务器

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
/*server.cpp*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <sys/epoll.h>
#include <iostream>
#include <vector>
typedef std::vector<struct epoll_event> EventList;
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
struct packet {
int len;
char buf[1024];
};
void echo_svr(int linstenfd) {
int epollfd = epoll_create1(EPOLL_CLOEXEC);
if (epollfd < 0) {
ERR_EXIT("epoll_create1");
}
EventList eventlist(16);
struct epoll_event event;
event.data.fd = linstenfd;
event.events = EPOLLIN | EPOLLET;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, linstenfd, &event) == -1) {
ERR_EXIT("epoll_ctl");
}
int count = 0;
int nready;
int conn;
int i;
while (1) {
nready = epoll_wait(epollfd, &(*eventlist.begin()), static_cast<int>(eventlist.size()), -1);
if (nready == -1) {
if (errno == EINTR) {
continue;
}
ERR_EXIT("poll");
}
if (nready == 0) {
continue;
}
for (i=0; i< nready; ++i) {
if (eventlist[i].data.fd == linstenfd) {
struct sockaddr_in clientaddr;
int clientaddrlen = sizeof(clientaddr);
int conn = accept(linstenfd, (struct sockaddr*)&clientaddr, (socklen_t*)&clientaddrlen);
if (conn < 0) {
ERR_EXIT("accept");
}
printf("ip=%s, port=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
if (++count >= eventlist.size()) {
eventlist.resize(2*count);
}
event.data.fd = conn;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, conn, &event);
} else if (eventlist[i].events & EPOLLIN) {
conn = eventlist[i].data.fd;
struct packet recv_packet;
int ret;
memset(&recv_packet, 0, sizeof(recv_packet));
if ((ret = read(conn, &recv_packet.len, sizeof(int))) < 0) {
ERR_EXIT("read len");
} else if (ret == 0) {
printf("client close\n");
event = eventlist[i];
epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event);
break;
}
int n = ntohl(recv_packet.len);
if ((ret = read(conn, recv_packet.buf, n)) < 0) {
ERR_EXIT("read buf");
}
fputs(recv_packet.buf, stdout);
write(conn, &recv_packet, 4+n);
}
}
}
}
int main() {
signal(SIGCHLD, SIG_IGN); // 避免僵尸进程
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
ERR_EXIT("socket");
}
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(5534);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
ERR_EXIT("setsockopt");
}
if (bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) {
ERR_EXIT("bind");
}
if (listen(sockfd, SOMAXCONN) < 0) {
ERR_EXIT("listen");
}
echo_svr(sockfd);
return 0;
}

epoll LT工作模式

epoll ET工作模式


信号驱动IO

信号驱动IO模型

异步IO

异步IO模型