Skip to content

Commit 034b098

Browse files
committed
epoll
1 parent a2c13d0 commit 034b098

File tree

1 file changed

+253
-3
lines changed

1 file changed

+253
-3
lines changed

programming/network.md

Lines changed: 253 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@
66
```c
77
FILE *cin, *cout;
88
cin = fdopen(sockfd, "r");
9-
setbuf(cin, (char *)0);
9+
setbuf(cin, (char * )0);
1010
cout = fdopen(sockfd, "w");
11-
setbuf(cout, (char *)0);
11+
setbuf(cout, (char * )0);
1212
```
1313
* 可以通过fgets, fread, fscanf等函数对文件流cin进行读操作(从socket中读)。
1414
* 可以通过fputs, fwrite, fprintf等函数对文件流cout进行写操作(写入socket)。
1515
* 需要注意的是,最好在写操作之后加一句fflush(cout),使写入的数据尽快发送。
1616
* 在断开socket连接前,需要先执行fclose(cin); fclose(cout); 再执行close(sockfd)
1717
18-
1918
## 关于send返回值
2019
进程间通过socket通信,一个进程执行`listen`后,即使没有执行`accept`,对方进程`connect`也会成功,
2120
并且`send`也会成功。该进程之后执行`accept`和`read`也都正常。
@@ -26,3 +25,254 @@ setbuf(cout, (char *)0);
2625
2726
## 如何获得信息
2827
`man tcp`
28+
29+
# epoll
30+
epoll是在Linux2.6内核中引入I/O多路复用技术。
31+
32+
```c
33+
int epoll_create(int size);
34+
int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);
35+
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
36+
```
37+
38+
### select的问题
39+
1. 最大并发数限制,由FD_SETSIZE设置,默认值是1024/2048
40+
2. 效率: select每次调用都会线性扫描 **全部** 的FD集合
41+
3. 内核/用户空间 内存拷贝问题
42+
43+
`poll`解决了第一个
44+
45+
### ET和LT模式
46+
1. LT(level triggered)是默认方式, 同时支持block和no-block socket. 内核通知一个文件描述符fd就绪了,然后可以对这个就绪的fd进行IO操作。如果不作操作, 下次调用epoll时内核还是会继续通知, 传统的select/poll都是这种模型的代表。
47+
2. ET(edge-triggered)只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll通知。如果一直不对这个fd作操作,再次调用epoll内核不会发送的通知。
48+
49+
ET和LT的区别在于LT事件不会丢弃,而是只要读buffer里面有数据可以让用户读,则不断的通知你。而ET则只在事件发生之时通知,如果要采用ET模式,需要一直read/write直到出错为止。
50+
51+
### [程序实例](https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c)
52+
```c
53+
#include <stdio.h>
54+
#include <stdlib.h>
55+
#include <string.h>
56+
#include <sys/types.h>
57+
#include <sys/socket.h>
58+
#include <netdb.h>
59+
#include <unistd.h>
60+
#include <fcntl.h>
61+
#include <sys/epoll.h>
62+
#include <errno.h>
63+
64+
#define MAXEVENTS 128
65+
66+
static int make_socket_non_blocking(int sfd)
67+
{
68+
int flags, s;
69+
flags = fcntl(sfd, F_GETFL, 0);
70+
if(flags == -1)
71+
{
72+
perror("fcntl getfl");
73+
return -1;
74+
}
75+
flags |= O_NONBLOCK;
76+
s = fcntl(sfd, F_SETFL, flags);
77+
if(s == -1)
78+
{
79+
perror("fcntl settl");
80+
return -1;
81+
}
82+
return 0;
83+
}
84+
85+
static int create_and_bind(char * port)
86+
{
87+
struct addrinfo hints;
88+
struct addrinfo * result, * rp;
89+
int s, sfd;
90+
memset(&hints, 0, sizeof(struct addrinfo));
91+
hints.ai_family = AF_UNSPEC; /*Return IPv4 and IPv6 choices*/
92+
hints.ai_socktype = SOCK_STREAM; /*We want a TCP socket*/
93+
hints.ai_flags = AI_PASSIVE; /*All interfaces*/
94+
s = getaddrinfo(NULL, port, &hints, &result);
95+
if(s != 0)
96+
{
97+
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
98+
return -1;
99+
}
100+
for(rp = result; rp != NULL; rp = rp->ai_next)
101+
{
102+
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
103+
if(sfd == -1)
104+
continue;
105+
s = bind(sfd, rp->ai_addr, rp->ai_addrlen);
106+
if(s == 0)
107+
{
108+
/*managed to bind successfully!*/
109+
break;
110+
}
111+
close(sfd);
112+
}
113+
if(rp == NULL)
114+
{
115+
fprintf(stderr, "Could not bind\n");
116+
return -1;
117+
}
118+
freeaddrinfo(result);
119+
return sfd;
120+
}
121+
122+
int
123+
main(int argc, char * argv[])
124+
{
125+
int sfd, s;
126+
int epfd;
127+
struct epoll_event event;
128+
struct epoll_event * events;
129+
if(argc != 2)
130+
{
131+
fprintf(stderr, "Usage: %s [port]\n", argv[0]);
132+
exit(EXIT_FAILURE);
133+
}
134+
sfd = create_and_bind(argv[1]);
135+
if(sfd == -1)
136+
abort();
137+
s = make_socket_non_blocking(sfd);
138+
if(s == -1)
139+
abort();
140+
s = listen(sfd, SOMAXCONN);
141+
if(s == -1)
142+
{
143+
perror("listen");
144+
abort();
145+
}
146+
epfd = epoll_create1(0);
147+
if(epfd == -1)
148+
{
149+
perror("epoll_create");
150+
abort();
151+
}
152+
event.data.fd = sfd;
153+
event.events = EPOLLIN | EPOLLET;
154+
s = epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &event);
155+
if(s == -1)
156+
{
157+
perror("epoll_ctl");
158+
abort();
159+
}
160+
events = calloc(MAXEVENTS, sizeof event);
161+
while(1)
162+
{
163+
int n, i;
164+
n = epoll_wait(epfd, events, MAXEVENTS, -1);
165+
for(i = 0; i < n; i++)
166+
{
167+
if((events[i].events & EPOLLERR) ||
168+
(events[i].events & EPOLLHUP) ||
169+
(!(events[i].events & EPOLLIN)))
170+
{
171+
/*An error has occured on this fd,
172+
or the socket is not ready for reading*/
173+
fprintf(stderr, "epoll error\n");
174+
close(events[i].data.fd);
175+
continue;
176+
}
177+
else if(sfd == events[i].data.fd)
178+
{
179+
/*have a notification on the listening socket,
180+
means one or more incoming connections.*/
181+
while(1)
182+
{
183+
struct sockaddr in_addr;
184+
socklen_t in_len;
185+
int infd;
186+
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
187+
in_len = sizeof in_addr;
188+
infd = accept(sfd, &in_addr, &in_len);
189+
if(infd == -1)
190+
{
191+
if((errno == EAGAIN) ||
192+
(errno == EWOULDBLOCK))
193+
{
194+
/*have processed all incoming connections.*/
195+
break;
196+
}
197+
else
198+
{
199+
perror("accept");
200+
break;
201+
}
202+
}
203+
s = getnameinfo(&in_addr, in_len,
204+
hbuf, sizeof hbuf,
205+
sbuf, sizeof sbuf,
206+
NI_NUMERICHOST | NI_NUMERICSERV);
207+
if(s == 0)
208+
{
209+
fprintf(stdout, "Accepted connection on descriptor %d "
210+
"(host=%s, port=%s)\n", infd, hbuf, sbuf);
211+
}
212+
/*Make the incoming socket non-blocking,
213+
add it to the list of fds to monitor.*/
214+
s = make_socket_non_blocking(infd);
215+
if(s == -1)
216+
abort();
217+
event.data.fd = infd;
218+
event.events = EPOLLIN | EPOLLET;
219+
s = epoll_ctl(epfd, EPOLL_CTL_ADD, infd, &event);
220+
if(s == -1)
221+
{
222+
perror("epoll_ctl");
223+
abort();
224+
}
225+
} /*End while*/
226+
} /*End if*/
227+
else
228+
{
229+
/*have data on the fd waiting to be read. Read and display it.
230+
must read whatever data is available completely(in ET mode)
231+
won't get a notification again for the same data.*/
232+
int done = 0;
233+
while(1)
234+
{
235+
ssize_t count;
236+
char buf[512];
237+
count = read(events[i].data.fd, buf, sizeof buf);
238+
if(count == -1)
239+
{
240+
/*If errno != EAGAIN, that means have read all data.
241+
So go back to the main loop.*/
242+
if(errno != EAGAIN)
243+
{
244+
perror("read");
245+
done = 1;
246+
}
247+
break;
248+
}
249+
else if(count == 0)
250+
{
251+
/*End of file. The remote has closed the connection.*/
252+
done = 1;
253+
break;
254+
}
255+
/*Write the buffer to standard output*/
256+
s = write(1, buf, count);
257+
if(s == -1)
258+
{
259+
perror("write");
260+
abort();
261+
}
262+
}
263+
if(done)
264+
{
265+
fprintf(stdout, "Closed connection on descriptor %d\n",
266+
events[i].data.fd);
267+
/*Closing the descriptor will make epoll
268+
remove it from the set of descriptors which are monitored.*/
269+
close(events[i].data.fd);
270+
}
271+
}
272+
}/*End for*/
273+
}
274+
free(events);
275+
close(sfd);
276+
return EXIT_SUCCESS;
277+
}
278+
```

0 commit comments

Comments
 (0)