32a89fa7670d7221b54e7c98ffa0f8ce7e255abd
[cubemap] / acceptor.cpp
1 #include <assert.h>
2 #include <errno.h>
3 #include <netinet/in.h>
4 #include <netinet/tcp.h>
5 #include <poll.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/ioctl.h>
9 #include <sys/socket.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #include "acceptor.h"
14 #include "log.h"
15 #include "serverpool.h"
16 #include "state.pb.h"
17 #include "util.h"
18
19 using namespace std;
20
21 extern ServerPool *servers;
22
23 int create_server_socket(int port, SocketType socket_type)
24 {
25         int server_sock;
26         if (socket_type == TCP_SOCKET) {
27                 server_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
28         } else {
29                 assert(socket_type == UDP_SOCKET);
30                 server_sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
31         }
32         if (server_sock == -1) {
33                 log_perror("socket");
34                 exit(1);
35         }
36
37         int one = 1;
38         if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
39                 log_perror("setsockopt(SO_REUSEADDR)");
40                 exit(1);
41         }
42
43         // We want dual-stack sockets. (Sorry, OpenBSD and Windows XP...)
44         int zero = 0;
45         if (setsockopt(server_sock, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) == -1) {
46                 log_perror("setsockopt(IPV6_V6ONLY)");
47                 exit(1);
48         }
49
50         // Set as non-blocking, so the acceptor thread can notice that we want to shut it down.
51         if (ioctl(server_sock, FIONBIO, &one) == -1) {
52                 log_perror("ioctl(FIONBIO)");
53                 exit(1);
54         }
55
56         sockaddr_in6 addr;
57         memset(&addr, 0, sizeof(addr));
58         addr.sin6_family = AF_INET6;
59         addr.sin6_port = htons(port);
60
61         if (bind(server_sock, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) == -1) {
62                 log_perror("bind");
63                 exit(1);
64         }
65
66         if (socket_type == TCP_SOCKET) {
67                 if (listen(server_sock, 128) == -1) {
68                         log_perror("listen");
69                         exit(1);
70                 }
71         }
72
73         return server_sock;
74 }
75         
76 Acceptor::Acceptor(int server_sock, int port)
77         : server_sock(server_sock),
78           port(port)
79 {
80 }
81
82 Acceptor::Acceptor(const AcceptorProto &serialized)
83         : server_sock(serialized.server_sock()),
84           port(serialized.port())
85 {
86 }
87
88 AcceptorProto Acceptor::serialize() const
89 {
90         AcceptorProto serialized;
91         serialized.set_server_sock(server_sock);
92         serialized.set_port(port);
93         return serialized;
94 }
95
96 void Acceptor::close_socket()
97 {
98         safe_close(server_sock);
99 }
100
101 void Acceptor::do_work()
102 {
103         while (!should_stop()) {
104                 if (!wait_for_activity(server_sock, POLLIN, NULL)) {
105                         continue;
106                 }
107
108                 sockaddr_in6 addr;
109                 socklen_t addrlen = sizeof(addr);
110
111                 // Get a new socket.
112                 int sock = accept(server_sock, reinterpret_cast<sockaddr *>(&addr), &addrlen);
113                 if (sock == -1 && errno == EINTR) {
114                         continue;
115                 }
116                 if (sock == -1) {
117                         log_perror("accept");
118                         usleep(100000);
119                         continue;
120                 }
121
122                 // Set the socket as nonblocking.
123                 int one = 1;
124                 if (ioctl(sock, FIONBIO, &one) == -1) {
125                         log_perror("ioctl(FIONBIO)");
126                         exit(1);
127                 }
128
129                 // Enable TCP_CORK for maximum throughput. In the rare case that the
130                 // stream stops entirely, this will cause a small delay (~200 ms)
131                 // before the last part is sent out, but that should be fine.
132                 if (setsockopt(sock, SOL_TCP, TCP_CORK, &one, sizeof(one)) == -1) {
133                         log_perror("setsockopt(TCP_CORK)");
134                         // Can still continue.
135                 }
136
137                 // Pick a server, round-robin, and hand over the socket to it.
138                 servers->add_client(sock);
139         }
140 }