rby1-sdk
Loading...
Searching...
No Matches
udp_server.h
1#pragma once
2
3#include <cstring>
4#include <iostream>
5#include <stdexcept>
6#include <string>
7#if defined(_WIN32)
8#include <winsock2.h>
9#include <ws2tcpip.h>
10#else
11#include <arpa/inet.h>
12#include <fcntl.h>
13#include <sys/socket.h>
14#include <unistd.h>
15#endif
16
17namespace rb {
18
19class UdpServer {
20 public:
21 explicit UdpServer(const int port = 0) {
22#if defined(_WIN32)
23 int startup_result = WSAStartup(MAKEWORD(2, 2), &wsa_data_);
24 if (startup_result) {
25 std::stringstream ss;
26 ss << "Cannot start WinSock (error: " << startup_result << ")";
27 throw std::runtime_error(ss.str());
28 }
29#endif
30
31 // --- IPv4 socket ---
32 fd_v4_ = socket(AF_INET, SOCK_DGRAM, 0);
33#if defined(_WIN32)
34 if (fd_v4_ == INVALID_SOCKET) {
35#else
36 if (fd_v4_ < 0) {
37#endif
38 FailAndCleanup("Create IPv4 UDP socket failed");
39 }
40
41 SetSockoptsV4();
42 BindV4(port);
43
44 // get chosen port (when port == 0)
45 {
46 sockaddr_in bound4{};
47 socklen_t blen4 = sizeof(bound4);
48 if (getsockname(fd_v4_, (sockaddr*)&bound4, &blen4) < 0) {
49 FailAndCleanup("Failed to get IPv4 socket name");
50 }
51 port_ = ntohs(bound4.sin_port);
52 }
53
54 // --- IPv6 socket ---
55 fd_v6_ = socket(AF_INET6, SOCK_DGRAM, 0);
56#if defined(_WIN32)
57 if (fd_v6_ == INVALID_SOCKET) {
58#else
59 if (fd_v6_ < 0) {
60#endif
61 FailAndCleanup("Create IPv6 UDP socket failed");
62 }
63
64 SetSockoptsV6();
65
66 {
67 const int on = 1;
68 setsockopt(fd_v6_, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on));
69 }
70
71 BindV6(port_);
72
73 // --- Non-blocking ---
74 MakeNonBlocking(fd_v4_);
75 MakeNonBlocking(fd_v6_);
76
77#if defined(_WIN32)
78 DWORD no = FALSE;
79 WSAIoctl(fd_v4_, SIO_UDP_CONNRESET, &no, sizeof(no), nullptr, 0, &no, nullptr, nullptr);
80 WSAIoctl(fd_v6_, SIO_UDP_CONNRESET, &no, sizeof(no), nullptr, 0, &no, nullptr, nullptr);
81#endif
82 }
83
84 ~UdpServer() { CloseAll(); }
85
86 int GetPort() const { return port_; }
87
88 int RecvFrom(unsigned char* buffer, size_t max_buf_size, struct sockaddr_storage& client_addr) const {
89 fd_set readfds;
90 FD_ZERO(&readfds);
91 FD_SET(fd_v4_, &readfds);
92 FD_SET(fd_v6_, &readfds);
93#if defined(_WIN32)
94 SOCKET max_fd = (fd_v4_ > fd_v6_) ? fd_v4_ : fd_v6_;
95#else
96 const int max_fd = (fd_v4_ > fd_v6_) ? fd_v4_ : fd_v6_;
97#endif
98
99 timeval tv{};
100 tv.tv_sec = 0;
101 tv.tv_usec = 1000; // 1 ms
102
103 if (const int ready = select((int)max_fd + 1, &readfds, nullptr, nullptr, &tv); ready <= 0) {
104 return 0;
105 }
106
107 if (FD_ISSET(fd_v4_, &readfds)) {
108 socklen_t len = sizeof(client_addr);
109 return (int)recvfrom(fd_v4_, (char*)buffer, max_buf_size, 0, (sockaddr*)&client_addr, &len);
110 }
111 if (FD_ISSET(fd_v6_, &readfds)) {
112 socklen_t len = sizeof(client_addr);
113 return (int)recvfrom(fd_v6_, (char*)buffer, max_buf_size, 0, (sockaddr*)&client_addr, &len);
114 }
115 return 0;
116 }
117
118 void SendTo(unsigned char* buffer, size_t buf_size, const struct sockaddr_storage& client_addr) const {
119 if (client_addr.ss_family == AF_INET) {
120 const socklen_t addrlen = (socklen_t)sizeof(sockaddr_in);
121 sendto(fd_v4_, (const char*)buffer, buf_size, 0, (const sockaddr*)&client_addr, addrlen);
122 } else if (client_addr.ss_family == AF_INET6) {
123 const socklen_t addrlen = (socklen_t)sizeof(sockaddr_in6);
124 sendto(fd_v6_, (const char*)buffer, buf_size, 0, (const sockaddr*)&client_addr, addrlen);
125 } else {
126 // Ignore unsupported family
127 }
128 }
129
130 void ClearBuffer() const {
131 constexpr size_t kBufferSize = 1024;
132 unsigned char buffer[kBufferSize];
133
134 auto drain = [&](int fd) {
135 for (;;) {
136 sockaddr_storage tmp{};
137 socklen_t len = sizeof(tmp);
138#if defined(_WIN32)
139 int flags = 0;
140#else
141 int flags = MSG_DONTWAIT;
142#endif
143 int r = (int)recvfrom(fd, (char*)buffer, kBufferSize, flags, (sockaddr*)&tmp, &len);
144 if (r <= 0)
145 break;
146 }
147 };
148 drain(fd_v4_);
149 drain(fd_v6_);
150 }
151
152 private:
153 void FailAndCleanup(const char* msg) const {
154 CloseAll();
155 throw std::runtime_error(msg);
156 }
157
158 void CloseAll() const {
159#if defined(_WIN32)
160 if (fd_v4_ != INVALID_SOCKET) {
161 shutdown(fd_v4_, SD_BOTH);
162 closesocket(fd_v4_);
163 }
164 if (fd_v6_ != INVALID_SOCKET) {
165 shutdown(fd_v6_, SD_BOTH);
166 closesocket(fd_v6_);
167 }
168 WSACleanup();
169#else
170 if (fd_v4_ >= 0) {
171 shutdown(fd_v4_, SHUT_RDWR);
172 close(fd_v4_);
173 }
174 if (fd_v6_ >= 0) {
175 shutdown(fd_v6_, SHUT_RDWR);
176 close(fd_v6_);
177 }
178#endif
179 }
180
181 void SetSockoptsV4() {
182 const int on = 1;
183 setsockopt(fd_v4_, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
184#ifdef SO_REUSEPORT
185 setsockopt(fd_v4_, SOL_SOCKET, SO_REUSEPORT, (const char*)&on, sizeof(on));
186#endif
187 }
188
189 void SetSockoptsV6() {
190 const int on = 1;
191 setsockopt(fd_v6_, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
192#ifdef SO_REUSEPORT
193 setsockopt(fd_v6_, SOL_SOCKET, SO_REUSEPORT, (const char*)&on, sizeof(on));
194#endif
195 }
196
197 void BindV4(int port) {
198 sockaddr_in addr4{};
199 addr4.sin_family = AF_INET;
200 addr4.sin_addr.s_addr = htonl(INADDR_ANY);
201 addr4.sin_port = htons(port);
202 if (bind(fd_v4_, (sockaddr*)&addr4, sizeof(addr4)) < 0) {
203 FailAndCleanup("Bind failed for IPv4 socket");
204 }
205 }
206
207 void BindV6(int port_same_as_v4) {
208 sockaddr_in6 addr6{};
209 std::memset(&addr6, 0, sizeof(addr6));
210 addr6.sin6_family = AF_INET6;
211 addr6.sin6_addr = in6addr_any;
212 addr6.sin6_port = htons(port_same_as_v4);
213 if (bind(fd_v6_, (sockaddr*)&addr6, sizeof(addr6)) < 0) {
214 FailAndCleanup("Bind failed for IPv6 socket");
215 }
216 }
217
218 static void MakeNonBlocking(
219#if defined(_WIN32)
220 SOCKET fd
221#else
222 int fd
223#endif
224 ) {
225#if defined(_WIN32)
226 u_long mode = 1;
227 ioctlsocket(fd, FIONBIO, &mode);
228#else
229 if (const int flags = fcntl(fd, F_GETFL, 0); flags >= 0) {
230 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
231 }
232#endif
233 }
234
235#if defined(_WIN32)
236 WSADATA wsa_data_{};
237 SOCKET fd_v4_{INVALID_SOCKET};
238 SOCKET fd_v6_{INVALID_SOCKET};
239#else
240 int fd_v4_{-1};
241 int fd_v6_{-1};
242#endif
243 int port_{0};
244};
245
246} // namespace rb
Definition udp_server.h:19