23 int startup_result = WSAStartup(MAKEWORD(2, 2), &wsa_data_);
26 ss <<
"Cannot start WinSock (error: " << startup_result <<
")";
27 throw std::runtime_error(ss.str());
32 fd_v4_ = socket(AF_INET, SOCK_DGRAM, 0);
34 if (fd_v4_ == INVALID_SOCKET) {
38 FailAndCleanup(
"Create IPv4 UDP socket failed");
47 socklen_t blen4 =
sizeof(bound4);
48 if (getsockname(fd_v4_, (sockaddr*)&bound4, &blen4) < 0) {
49 FailAndCleanup(
"Failed to get IPv4 socket name");
51 port_ = ntohs(bound4.sin_port);
55 fd_v6_ = socket(AF_INET6, SOCK_DGRAM, 0);
57 if (fd_v6_ == INVALID_SOCKET) {
61 FailAndCleanup(
"Create IPv6 UDP socket failed");
68 setsockopt(fd_v6_, IPPROTO_IPV6, IPV6_V6ONLY, (
const char*)&on,
sizeof(on));
74 MakeNonBlocking(fd_v4_);
75 MakeNonBlocking(fd_v6_);
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);
86 int GetPort()
const {
return port_; }
88 int RecvFrom(
unsigned char* buffer,
size_t max_buf_size,
struct sockaddr_storage& client_addr)
const {
91 FD_SET(fd_v4_, &readfds);
92 FD_SET(fd_v6_, &readfds);
94 SOCKET max_fd = (fd_v4_ > fd_v6_) ? fd_v4_ : fd_v6_;
96 const int max_fd = (fd_v4_ > fd_v6_) ? fd_v4_ : fd_v6_;
103 if (
const int ready = select((
int)max_fd + 1, &readfds,
nullptr,
nullptr, &tv); ready <= 0) {
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);
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);
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);
130 void ClearBuffer()
const {
131 constexpr size_t kBufferSize = 1024;
132 unsigned char buffer[kBufferSize];
134 auto drain = [&](
int fd) {
136 sockaddr_storage tmp{};
137 socklen_t len =
sizeof(tmp);
141 int flags = MSG_DONTWAIT;
143 int r = (int)recvfrom(fd, (
char*)buffer, kBufferSize, flags, (sockaddr*)&tmp, &len);
153 void FailAndCleanup(
const char* msg)
const {
155 throw std::runtime_error(msg);
158 void CloseAll()
const {
160 if (fd_v4_ != INVALID_SOCKET) {
161 shutdown(fd_v4_, SD_BOTH);
164 if (fd_v6_ != INVALID_SOCKET) {
165 shutdown(fd_v6_, SD_BOTH);
171 shutdown(fd_v4_, SHUT_RDWR);
175 shutdown(fd_v6_, SHUT_RDWR);
181 void SetSockoptsV4() {
183 setsockopt(fd_v4_, SOL_SOCKET, SO_REUSEADDR, (
const char*)&on,
sizeof(on));
185 setsockopt(fd_v4_, SOL_SOCKET, SO_REUSEPORT, (
const char*)&on,
sizeof(on));
189 void SetSockoptsV6() {
191 setsockopt(fd_v6_, SOL_SOCKET, SO_REUSEADDR, (
const char*)&on,
sizeof(on));
193 setsockopt(fd_v6_, SOL_SOCKET, SO_REUSEPORT, (
const char*)&on,
sizeof(on));
197 void BindV4(
int port) {
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");
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");
218 static void MakeNonBlocking(
227 ioctlsocket(fd, FIONBIO, &mode);
229 if (
const int flags = fcntl(fd, F_GETFL, 0); flags >= 0) {
230 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
237 SOCKET fd_v4_{INVALID_SOCKET};
238 SOCKET fd_v6_{INVALID_SOCKET};