-
Notifications
You must be signed in to change notification settings - Fork 8
/
tcpserver.c
148 lines (134 loc) · 4.66 KB
/
tcpserver.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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#include <sys/time.h>
#include <sys/resource.h>
#define MAX_OPENFILES_DEFAULT 1024 * 1024
#define MAX_OPENFILES_TARGET 1024 * 1024 * 256
static void readcb(struct bufferevent *bev, void *ctx)
{
/* This callback is invoked when there is data to read on bev. */
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);
/* Copy all the data from the input buffer to the output buffer. */
evbuffer_add_buffer(output, input);
}
static void eventcb(struct bufferevent *bev, short events, void *ctx)
{
if (events & BEV_EVENT_ERROR)
perror("Error from bufferevent");
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
bufferevent_free(bev);
}
}
static void accept_conn_cb(struct evconnlistener *listener,
evutil_socket_t fd, struct sockaddr *address,
int socklen, void *ctx)
{
char host[NI_MAXHOST];
char port[NI_MAXSERV];
getnameinfo(address, socklen, host, NI_MAXHOST, port, NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV);
printf("Got new connection from %s:%s\n", host, port);
/* Setup a bufferevent */
struct event_base *base = evconnlistener_get_base(listener);
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, readcb, NULL, eventcb, NULL);
bufferevent_enable(bev, EV_READ|EV_WRITE);
}
static void
accept_error_cb(struct evconnlistener *listener, void *ctx)
{
struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
fprintf(stderr, "Got an error %d (%s) on the listener. "
"Shutting down.\n", err, evutil_socket_error_to_string(err));
event_base_loopexit(base, NULL);
}
int main(int argc, char** argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in6 sin;
struct rlimit limit_openfiles;
FILE *nr_open;
int ret;
int port = 4242;
if (argc > 1) {
port = atoi(argv[1]);
}
if (port <= 0 || port > 65535) {
fprintf(stderr, "Invalid port\n");
return 1;
}
/* Setup limit on number of open files. */
/* First, set soft limit to hard limit */
ret = getrlimit(RLIMIT_NOFILE, &limit_openfiles);
if (ret != 0) {
perror("Failed to get limit on number of open files");
return 1;
}
limit_openfiles.rlim_cur = limit_openfiles.rlim_max;
ret = setrlimit(RLIMIT_NOFILE, &limit_openfiles);
/* Then try to increase the limit to the maximum (needs to be root) */
limit_openfiles.rlim_cur = MAX_OPENFILES_DEFAULT;
limit_openfiles.rlim_max = MAX_OPENFILES_DEFAULT;
ret = setrlimit(RLIMIT_NOFILE, &limit_openfiles);
if (ret != 0) {
perror("Failed to increase limit on number of open files to MAX_OPENFILES_DEFAULT");
fprintf(stderr, "Try to run this program as root.\n");
} else {
/* Try to increase the limit even further. */
nr_open = fopen("/proc/sys/fs/nr_open", "w");
if (nr_open == NULL) {
perror("Failed to open /proc/sys/fs/nr_open for writing");
} else {
fprintf(nr_open, "%d\n", MAX_OPENFILES_TARGET);
fclose(nr_open);
}
limit_openfiles.rlim_cur = MAX_OPENFILES_TARGET;
limit_openfiles.rlim_max = MAX_OPENFILES_TARGET;
ret = setrlimit(RLIMIT_NOFILE, &limit_openfiles);
if (ret != 0) {
perror("Failed to increase limit on number of open files to MAX_OPENFILES_TARGET");
}
}
/* Display final limit */
ret = getrlimit(RLIMIT_NOFILE, &limit_openfiles);
if (ret != 0) {
perror("Failed to get limit on number of open files");
return 1;
}
printf("Maximum number of TCP clients: %ld\n", limit_openfiles.rlim_cur);
base = event_base_new();
if (!base) {
fprintf(stderr, "Couldn't open event base\n");
return 1;
}
/* Clear the sockaddr before using it, in case there are extra
* platform-specific fields that can mess us up. */
memset(&sin, 0, sizeof(sin));
sin.sin6_family = AF_INET6;
/* Listen on the given port, on :: */
sin.sin6_port = htons(port);
listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, 8192,
(struct sockaddr*)&sin, sizeof(sin));
if (!listener) {
perror("Couldn't create listener");
return 1;
}
char l_host[NI_MAXHOST];
char l_port[NI_MAXSERV];
getnameinfo((struct sockaddr*)&sin, sizeof(sin), l_host, NI_MAXHOST,
l_port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
printf("Listening on %s port %s\n", l_host, l_port);
evconnlistener_set_error_cb(listener, accept_error_cb);
return event_base_dispatch(base);
}