-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtcp_epoll_edge.c
More file actions
124 lines (122 loc) · 3.44 KB
/
Copy pathtcp_epoll_edge.c
File metadata and controls
124 lines (122 loc) · 3.44 KB
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
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
static int set_nonblock(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) return -1;
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) return -1;
return 0;
}
int main(int argc, char **argv)
{
#if !defined(__linux__)
fprintf(stderr, "tcp_epoll_edge is Linux-only\n");
return 1;
#else
int port = 12345;
if (argc > 1) port = atoi(argv[1]);
int srv = socket(AF_INET, SOCK_STREAM, 0);
if (srv < 0) {
perror("socket");
return 1;
}
int opt = 1;
setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
struct sockaddr_in addr;
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if (bind(srv, (struct sockaddr *)&addr, sizeof addr) < 0) {
perror("bind");
close(srv);
return 1;
}
if (listen(srv, 128) < 0) {
perror("listen");
close(srv);
return 1;
}
if (set_nonblock(srv) < 0) {
perror("set_nonblock");
close(srv);
return 1;
}
int ep = epoll_create1(0);
if (ep < 0) {
perror("epoll_create1");
close(srv);
return 1;
}
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = srv;
if (epoll_ctl(ep, EPOLL_CTL_ADD, srv, &ev) < 0) {
perror("epoll_ctl");
close(srv);
close(ep);
return 1;
}
printf("listening on port %d\n", port);
struct epoll_event events[64];
for (;;) {
int n = epoll_wait(ep, events, 64, -1);
if (n < 0) {
if (errno == EINTR) continue;
perror("epoll_wait");
break;
}
for (int i = 0; i < n; i++) {
int fd = events[i].data.fd;
if (fd == srv) {
for (;;) {
struct sockaddr_in caddr;
socklen_t clen = sizeof caddr;
int c = accept(srv, (struct sockaddr *)&caddr, &clen);
if (c < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
perror("accept");
break;
}
set_nonblock(c);
struct epoll_event cev;
cev.events = EPOLLIN | EPOLLET;
cev.data.fd = c;
epoll_ctl(ep, EPOLL_CTL_ADD, c, &cev);
}
} else {
for (;;) {
char buf[4096];
ssize_t r = read(fd, buf, sizeof buf);
if (r == 0) {
close(fd);
epoll_ctl(ep, EPOLL_CTL_DEL, fd, NULL);
break;
} else if (r < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
perror("read");
close(fd);
epoll_ctl(ep, EPOLL_CTL_DEL, fd, NULL);
break;
} else {
write(fd, buf, r);
}
}
}
}
}
close(srv);
close(ep);
return 0;
#endif
}