Clone of Bael'Zharon's Respite @ https://github.com/boardwalk/bzr

Socket.cpp 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * Bael'Zharon's Respite
  3. * Copyright (C) 2014 Daniel Skorupski
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include "net/Socket.h"
  19. #include "Core.h"
  20. #include "Log.h"
  21. #ifdef _WIN32
  22. #include <ws2tcpip.h>
  23. #else
  24. #include <errno.h>
  25. #include <fcntl.h>
  26. #include <netinet/in.h>
  27. #include <sys/socket.h>
  28. #include <unistd.h>
  29. #endif
  30. #ifdef _WIN32
  31. #define CLOSE_SOCKET closesocket
  32. typedef SSIZE_T ssize_t;
  33. static const SocketType kBadSocket = INVALID_SOCKET;
  34. static const int kSocketError = SOCKET_ERROR;
  35. static int getError() { return WSAGetLastError(); }
  36. static bool isWouldBlockError(int err) { return err == WSAEWOULDBLOCK; }
  37. #else
  38. #define CLOSE_SOCKET close
  39. static const SocketType kBadSocket = -1;
  40. static const int kSocketError = -1;
  41. static int getError() { return errno; }
  42. static bool isWouldBlockError(int err) { return errno == EAGAIN || errno == EWOULDBLOCK; }
  43. #endif
  44. Socket::Socket()
  45. {
  46. sock_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  47. if(sock_ == kBadSocket)
  48. {
  49. throw runtime_error("Failed to create socket");
  50. }
  51. sockaddr_in addr;
  52. memset(&addr, 0, sizeof(addr));
  53. addr.sin_family = AF_INET;
  54. addr.sin_port = 0;
  55. addr.sin_addr.s_addr = htonl(INADDR_ANY);
  56. if(::bind(sock_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) != 0)
  57. {
  58. throw runtime_error("Failed to bind socket");
  59. }
  60. #ifdef _WIN32
  61. u_long nonblock = 1;
  62. if(ioctlsocket(sock_, FIONBIO, &nonblock) != 0)
  63. #else
  64. int flags = fcntl(sock_, F_GETFL);
  65. if(fcntl(sock_, F_SETFL, flags | O_NONBLOCK) != 0)
  66. #endif
  67. {
  68. throw runtime_error("Failed to set nonblocking mode");
  69. }
  70. }
  71. Socket::~Socket()
  72. {
  73. CLOSE_SOCKET(sock_);
  74. }
  75. bool Socket::wait(chrono::microseconds timeout)
  76. {
  77. int nfds = static_cast<int>(sock_) + 1;
  78. fd_set readfds;
  79. FD_ZERO(&readfds);
  80. FD_SET(sock_, &readfds);
  81. timeval tv;
  82. tv.tv_sec = static_cast<long>(timeout.count() / 1000000);
  83. tv.tv_usec = static_cast<long>(timeout.count() % 1000000);
  84. int ret = select(nfds, &readfds, nullptr, nullptr, &tv);
  85. if(ret == kSocketError)
  86. {
  87. throw runtime_error("select failed");
  88. }
  89. return ret == 1;
  90. }
  91. bool Socket::recv(Packet& packet)
  92. {
  93. BEGIN:
  94. sockaddr_in from;
  95. socklen_t fromLen = sizeof(from);
  96. ssize_t recvLen = recvfrom(sock_,
  97. reinterpret_cast<char*>(&packet.header),
  98. static_cast<int>(sizeof(packet.header) + sizeof(packet.payload)),
  99. /*flags*/ 0,
  100. reinterpret_cast<sockaddr*>(&from),
  101. &fromLen);
  102. if(recvLen == kSocketError)
  103. {
  104. int err = getError();
  105. if(isWouldBlockError(err))
  106. {
  107. return false;
  108. }
  109. throw runtime_error("recvfrom failed");
  110. }
  111. packet.address = Address(htonl(from.sin_addr.s_addr), htons(from.sin_port));
  112. if(recvLen < static_cast<ssize_t>(sizeof(PacketHeader)))
  113. {
  114. LOG(Net, Warn) << packet.address << " dropping packet smaller than header\n";
  115. goto BEGIN;
  116. }
  117. if(recvLen != static_cast<ssize_t>(sizeof(PacketHeader) + packet.header.size))
  118. {
  119. LOG(Net, Warn) << packet.address << " dropping packet with bad size in header\n";
  120. goto BEGIN;
  121. }
  122. return true;
  123. }
  124. void Socket::send(const Packet& packet)
  125. {
  126. assert(packet.header.size <= sizeof(packet.payload));
  127. BEGIN:
  128. sockaddr_in to;
  129. memset(&to, 0, sizeof(to));
  130. to.sin_family = AF_INET;
  131. to.sin_port = htons(packet.address.port());
  132. to.sin_addr.s_addr = htonl(packet.address.ip());
  133. ssize_t sendLen = sendto(sock_,
  134. reinterpret_cast<const char*>(&packet.header),
  135. static_cast<int>(sizeof(packet.header) + packet.header.size),
  136. /*flags*/ 0,
  137. reinterpret_cast<sockaddr*>(&to),
  138. sizeof(to));
  139. if(sendLen == kSocketError)
  140. {
  141. int err = getError();
  142. // this should only happen if we somehow fill up the the os's buffers (e.g. never)
  143. // we'll just wait until the fd is writable
  144. if(isWouldBlockError(err))
  145. {
  146. int nfds = static_cast<int>(sock_) + 1;
  147. fd_set writefds;
  148. FD_ZERO(&writefds);
  149. FD_SET(sock_, &writefds);
  150. if(select(nfds, nullptr, &writefds, nullptr, nullptr) != 1)
  151. {
  152. throw runtime_error("select failed");
  153. }
  154. goto BEGIN;
  155. }
  156. LOG(Net, Error) << "sendto failed with " << err << "\n";
  157. throw runtime_error("sendto failed");
  158. }
  159. }
  160. #ifdef _WIN32
  161. struct Startup
  162. {
  163. Startup()
  164. {
  165. WSAData wsaData;
  166. int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
  167. if(err != 0)
  168. {
  169. throw runtime_error("WSAStartup failed");
  170. }
  171. }
  172. ~Startup()
  173. {
  174. WSACleanup();
  175. }
  176. };
  177. static Startup g_startup;
  178. #endif