libevent: identifikace serverových soketů a jednotlivých spojení (pomocí čísel FD)
7 #include <event2/bufferevent.h>
8 #include <event2/buffer.h>
9 #include <event2/listener.h>
10 #include <event2/util.h>
11 #include <event2/event.h>
12 #include <sys/socket.h>
16 static const char MESSAGE[] = "Hello, World!\n";
18 static const char PATH[] = "./roura";
20 static void listener_cb(evutil_socket_t, short, void *);
21 static void conn_read_cb(struct bufferevent *, void *);
22 static void conn_write_cb(struct bufferevent *, void *);
23 static void conn_event_cb(struct bufferevent *, short, void *);
24 static void signal_cb(evutil_socket_t, short, void *);
25 static void print_socket_info(int);
27 int main(int argc, char **argv) {
28 struct event_base *base;
29 evutil_socket_t listener;
30 struct event *listener_event;
31 struct event *signal_event;
33 struct sockaddr_un sun;
35 setvbuf(stdout, NULL, _IONBF, 0);
37 base = event_base_new();
39 fprintf(stderr, "Could not initialize libevent!\n");
43 memset(&sun, 0, sizeof (sun));
44 sun.sun_family = AF_UNIX;
45 strcpy(sun.sun_path, PATH);
47 listener = socket(AF_UNIX, SOCK_STREAM, 0);
48 evutil_make_socket_nonblocking(listener);
50 if (bind(listener, (struct sockaddr*) &sun, sizeof (sun)) < 0) {
51 fprintf(stderr, "Could not create domain socket: %s!\n", PATH);
55 if (listen(listener, 16) < 0) {
56 fprintf(stderr, "Could not listen\n");
60 // identifikátor serverového soketu (v současnosti číslo FD)
61 printf("Nasloucháme: sun_path = %s → socketId = %d\n", sun.sun_path, listener);
63 listener_event = event_new(base, listener, EV_READ | EV_PERSIST, listener_cb, (void*) base);
65 if (!listener_event) {
66 fprintf(stderr, "Could not do event_new\n");
70 event_add(listener_event, NULL);
72 signal_event = evsignal_new(base, SIGINT, signal_cb, (void *) base);
74 if (!signal_event || event_add(signal_event, NULL) < 0) {
75 fprintf(stderr, "Could not create/add a signal event!\n");
79 event_base_dispatch(base);
81 event_free(listener_event);
82 event_free(signal_event);
83 event_base_free(base);
85 // smažeme soket na disku / soubor -- jinak by program příště spadl na bind()
86 // TODO: co když soket někdo přesune a místo něj dá jiný soubor?
93 static void listener_cb(evutil_socket_t listener, short event, void *user_data) {
94 struct event_base *base = (event_base *) user_data;
95 struct bufferevent *bev;
97 struct sockaddr_storage ss;
98 socklen_t slen = sizeof (ss);
99 int fd = accept(listener, (struct sockaddr*) &ss, &slen);
101 fprintf(stderr, "Unable to accept(): %d", fd);
103 } else if (fd > FD_SETSIZE) {
104 // FD_SETSIZE = 1024 -- Proč? Co když bude spojení víc?
105 fprintf(stderr, "fd (%d) > FD_SETSIZE (%d)", fd, FD_SETSIZE);
110 evutil_make_socket_nonblocking(fd);
112 print_socket_info(fd);
114 bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
116 fprintf(stderr, "Error constructing bufferevent!");
117 event_base_loopbreak(base);
121 // identifikátor navázaného spojení (v současnosti číslo FD)
122 int * connectionId = (int*) malloc(sizeof (fd));
126 bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, (void*) connectionId);
127 bufferevent_enable(bev, EV_READ | EV_WRITE);
129 printf("někdo se k nám připojil! socketId = %d → connectionId = %d / bev = %p\n", listener, *connectionId, bev);
131 bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
134 static void conn_read_cb(struct bufferevent *bev, void *user_data) {
135 printf("conn_read_cb: connectionId = %d\n", *((int*) user_data));
137 /* This callback is invoked when there is data to read on bev. */
138 struct evbuffer *input = bufferevent_get_input(bev);
139 struct evbuffer *output = bufferevent_get_output(bev);
140 struct event_base *base = bufferevent_get_base(bev);
142 size_t len = evbuffer_get_length(input);
143 char *data = (char*) malloc(len);
144 evbuffer_copyout(input, data, len);
145 printf("we got some data: %s\n", data);
147 if (memcmp(data, "exit\n", len) == 0) {
148 struct timeval delay = {2, 123};
149 printf("Klient říká, že máme končit; ukončuji program během %ld sekund a %ld mikrosekund.\n", delay.tv_sec, delay.tv_usec);
150 event_base_loopexit(base, &delay);
153 evbuffer_add(output, "echo: ", 6);
154 /* Copy all the data from the input buffer to the output buffer. */
155 evbuffer_add_buffer(output, input);
159 static void conn_write_cb(struct bufferevent *bev, void *user_data) {
160 printf("conn_write_cb: connectionId = %d\n", *((int*) user_data));
162 struct evbuffer *output = bufferevent_get_output(bev);
163 if (evbuffer_get_length(output) == 0) {
164 printf("flushed answer / %p\n", bev);
165 /* nebudeme ukončovat spojení
166 bufferevent_free(bev);
171 static void conn_event_cb(struct bufferevent *bev, short events, void *user_data) {
172 int connectionId = *((int*) user_data);
173 printf("conn_event_cb: connectionId = %d\n", connectionId);
175 if (events & BEV_EVENT_EOF) {
176 printf("Connection closed: connectionId = %d\n", connectionId);
177 } else if (events & BEV_EVENT_ERROR) {
178 printf("Got an error on the connectionId = %d: %s\n", connectionId, strerror(errno));
181 // None of the other events can happen here, since we haven't enabled timeouts
182 bufferevent_free(bev);
186 static void signal_cb(evutil_socket_t sig, short events, void *user_data) {
187 struct event_base *base = (event_base *) user_data;
188 struct timeval delay = {2, 123};
190 printf("Zachycen SIGINT (Ctrl+C); ukončuji program během %ld sekund a %ld mikrosekund.\n", delay.tv_sec, delay.tv_usec);
192 event_base_loopexit(base, &delay);
195 static void print_socket_info(int fd) {
197 unsigned int cl = sizeof (cr);
199 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl) == 0) {
200 printf("připojený klient: pid=%d, uid=%d, gid=%d\n", cr.pid, cr.uid, cr.gid);