PageRenderTime 29ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/src/SFML/Network/TcpSocket.cpp

http://github.com/LaurentGomila/SFML
C++ | 416 lines | 239 code | 75 blank | 102 comment | 52 complexity | 236b90b66ddb6d97d2ba34737f498b84 MD5 | raw file
  1. ////////////////////////////////////////////////////////////
  2. //
  3. // SFML - Simple and Fast Multimedia Library
  4. // Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org)
  5. //
  6. // This software is provided 'as-is', without any express or implied warranty.
  7. // In no event will the authors be held liable for any damages arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it freely,
  11. // subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented;
  14. // you must not claim that you wrote the original software.
  15. // If you use this software in a product, an acknowledgment
  16. // in the product documentation would be appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such,
  19. // and must not be misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source distribution.
  22. //
  23. ////////////////////////////////////////////////////////////
  24. ////////////////////////////////////////////////////////////
  25. // Headers
  26. ////////////////////////////////////////////////////////////
  27. #include <SFML/Network/TcpSocket.hpp>
  28. #include <SFML/Network/IpAddress.hpp>
  29. #include <SFML/Network/Packet.hpp>
  30. #include <SFML/Network/SocketImpl.hpp>
  31. #include <SFML/System/Err.hpp>
  32. #include <algorithm>
  33. #include <cstring>
  34. #ifdef _MSC_VER
  35. #pragma warning(disable: 4127) // "conditional expression is constant" generated by the FD_SET macro
  36. #endif
  37. namespace
  38. {
  39. // Define the low-level send/receive flags, which depend on the OS
  40. #ifdef SFML_SYSTEM_LINUX
  41. const int flags = MSG_NOSIGNAL;
  42. #else
  43. const int flags = 0;
  44. #endif
  45. }
  46. namespace sf
  47. {
  48. ////////////////////////////////////////////////////////////
  49. TcpSocket::TcpSocket() :
  50. Socket(Tcp)
  51. {
  52. }
  53. ////////////////////////////////////////////////////////////
  54. unsigned short TcpSocket::getLocalPort() const
  55. {
  56. if (getHandle() != priv::SocketImpl::invalidSocket())
  57. {
  58. // Retrieve informations about the local end of the socket
  59. sockaddr_in address;
  60. priv::SocketImpl::AddrLength size = sizeof(address);
  61. if (getsockname(getHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
  62. {
  63. return ntohs(address.sin_port);
  64. }
  65. }
  66. // We failed to retrieve the port
  67. return 0;
  68. }
  69. ////////////////////////////////////////////////////////////
  70. IpAddress TcpSocket::getRemoteAddress() const
  71. {
  72. if (getHandle() != priv::SocketImpl::invalidSocket())
  73. {
  74. // Retrieve informations about the remote end of the socket
  75. sockaddr_in address;
  76. priv::SocketImpl::AddrLength size = sizeof(address);
  77. if (getpeername(getHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
  78. {
  79. return IpAddress(ntohl(address.sin_addr.s_addr));
  80. }
  81. }
  82. // We failed to retrieve the address
  83. return IpAddress::None;
  84. }
  85. ////////////////////////////////////////////////////////////
  86. unsigned short TcpSocket::getRemotePort() const
  87. {
  88. if (getHandle() != priv::SocketImpl::invalidSocket())
  89. {
  90. // Retrieve informations about the remote end of the socket
  91. sockaddr_in address;
  92. priv::SocketImpl::AddrLength size = sizeof(address);
  93. if (getpeername(getHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
  94. {
  95. return ntohs(address.sin_port);
  96. }
  97. }
  98. // We failed to retrieve the port
  99. return 0;
  100. }
  101. ////////////////////////////////////////////////////////////
  102. Socket::Status TcpSocket::connect(const IpAddress& remoteAddress, unsigned short remotePort, Time timeout)
  103. {
  104. // Disconnect the socket if it is already connected
  105. disconnect();
  106. // Create the internal socket if it doesn't exist
  107. create();
  108. // Create the remote address
  109. sockaddr_in address = priv::SocketImpl::createAddress(remoteAddress.toInteger(), remotePort);
  110. if (timeout <= Time::Zero)
  111. {
  112. // ----- We're not using a timeout: just try to connect -----
  113. // Connect the socket
  114. if (::connect(getHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
  115. return priv::SocketImpl::getErrorStatus();
  116. // Connection succeeded
  117. return Done;
  118. }
  119. else
  120. {
  121. // ----- We're using a timeout: we'll need a few tricks to make it work -----
  122. // Save the previous blocking state
  123. bool blocking = isBlocking();
  124. // Switch to non-blocking to enable our connection timeout
  125. if (blocking)
  126. setBlocking(false);
  127. // Try to connect to the remote address
  128. if (::connect(getHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) >= 0)
  129. {
  130. // We got instantly connected! (it may no happen a lot...)
  131. setBlocking(blocking);
  132. return Done;
  133. }
  134. // Get the error status
  135. Status status = priv::SocketImpl::getErrorStatus();
  136. // If we were in non-blocking mode, return immediately
  137. if (!blocking)
  138. return status;
  139. // Otherwise, wait until something happens to our socket (success, timeout or error)
  140. if (status == Socket::NotReady)
  141. {
  142. // Setup the selector
  143. fd_set selector;
  144. FD_ZERO(&selector);
  145. FD_SET(getHandle(), &selector);
  146. // Setup the timeout
  147. timeval time;
  148. time.tv_sec = static_cast<long>(timeout.asMicroseconds() / 1000000);
  149. time.tv_usec = static_cast<long>(timeout.asMicroseconds() % 1000000);
  150. // Wait for something to write on our socket (which means that the connection request has returned)
  151. if (select(static_cast<int>(getHandle() + 1), NULL, &selector, NULL, &time) > 0)
  152. {
  153. // At this point the connection may have been either accepted or refused.
  154. // To know whether it's a success or a failure, we must check the address of the connected peer
  155. if (getRemoteAddress() != IpAddress::None)
  156. {
  157. // Connection accepted
  158. status = Done;
  159. }
  160. else
  161. {
  162. // Connection refused
  163. status = priv::SocketImpl::getErrorStatus();
  164. }
  165. }
  166. else
  167. {
  168. // Failed to connect before timeout is over
  169. status = priv::SocketImpl::getErrorStatus();
  170. }
  171. }
  172. // Switch back to blocking mode
  173. setBlocking(true);
  174. return status;
  175. }
  176. }
  177. ////////////////////////////////////////////////////////////
  178. void TcpSocket::disconnect()
  179. {
  180. // Close the socket
  181. close();
  182. // Reset the pending packet data
  183. m_pendingPacket = PendingPacket();
  184. }
  185. ////////////////////////////////////////////////////////////
  186. Socket::Status TcpSocket::send(const void* data, std::size_t size)
  187. {
  188. if (!isBlocking())
  189. err() << "Warning: Partial sends might not be handled properly." << std::endl;
  190. std::size_t sent;
  191. return send(data, size, sent);
  192. }
  193. ////////////////////////////////////////////////////////////
  194. Socket::Status TcpSocket::send(const void* data, std::size_t size, std::size_t& sent)
  195. {
  196. // Check the parameters
  197. if (!data || (size == 0))
  198. {
  199. err() << "Cannot send data over the network (no data to send)" << std::endl;
  200. return Error;
  201. }
  202. // Loop until every byte has been sent
  203. int result = 0;
  204. for (sent = 0; sent < size; sent += result)
  205. {
  206. // Send a chunk of data
  207. result = ::send(getHandle(), static_cast<const char*>(data) + sent, static_cast<int>(size - sent), flags);
  208. // Check for errors
  209. if (result < 0)
  210. {
  211. Status status = priv::SocketImpl::getErrorStatus();
  212. if ((status == NotReady) && sent)
  213. return Partial;
  214. return status;
  215. }
  216. }
  217. return Done;
  218. }
  219. ////////////////////////////////////////////////////////////
  220. Socket::Status TcpSocket::receive(void* data, std::size_t size, std::size_t& received)
  221. {
  222. // First clear the variables to fill
  223. received = 0;
  224. // Check the destination buffer
  225. if (!data)
  226. {
  227. err() << "Cannot receive data from the network (the destination buffer is invalid)" << std::endl;
  228. return Error;
  229. }
  230. // Receive a chunk of bytes
  231. int sizeReceived = recv(getHandle(), static_cast<char*>(data), static_cast<int>(size), flags);
  232. // Check the number of bytes received
  233. if (sizeReceived > 0)
  234. {
  235. received = static_cast<std::size_t>(sizeReceived);
  236. return Done;
  237. }
  238. else if (sizeReceived == 0)
  239. {
  240. return Socket::Disconnected;
  241. }
  242. else
  243. {
  244. return priv::SocketImpl::getErrorStatus();
  245. }
  246. }
  247. ////////////////////////////////////////////////////////////
  248. Socket::Status TcpSocket::send(Packet& packet)
  249. {
  250. // TCP is a stream protocol, it doesn't preserve messages boundaries.
  251. // This means that we have to send the packet size first, so that the
  252. // receiver knows the actual end of the packet in the data stream.
  253. // We allocate an extra memory block so that the size can be sent
  254. // together with the data in a single call. This may seem inefficient,
  255. // but it is actually required to avoid partial send, which could cause
  256. // data corruption on the receiving end.
  257. // Get the data to send from the packet
  258. std::size_t size = 0;
  259. const void* data = packet.onSend(size);
  260. // First convert the packet size to network byte order
  261. Uint32 packetSize = htonl(static_cast<Uint32>(size));
  262. // Allocate memory for the data block to send
  263. std::vector<char> blockToSend(sizeof(packetSize) + size);
  264. // Copy the packet size and data into the block to send
  265. std::memcpy(&blockToSend[0], &packetSize, sizeof(packetSize));
  266. if (size > 0)
  267. std::memcpy(&blockToSend[0] + sizeof(packetSize), data, size);
  268. // Send the data block
  269. std::size_t sent;
  270. Status status = send(&blockToSend[0] + packet.m_sendPos, blockToSend.size() - packet.m_sendPos, sent);
  271. // In the case of a partial send, record the location to resume from
  272. if (status == Partial)
  273. {
  274. packet.m_sendPos += sent;
  275. }
  276. else if (status == Done)
  277. {
  278. packet.m_sendPos = 0;
  279. }
  280. return status;
  281. }
  282. ////////////////////////////////////////////////////////////
  283. Socket::Status TcpSocket::receive(Packet& packet)
  284. {
  285. // First clear the variables to fill
  286. packet.clear();
  287. // We start by getting the size of the incoming packet
  288. Uint32 packetSize = 0;
  289. std::size_t received = 0;
  290. if (m_pendingPacket.SizeReceived < sizeof(m_pendingPacket.Size))
  291. {
  292. // Loop until we've received the entire size of the packet
  293. // (even a 4 byte variable may be received in more than one call)
  294. while (m_pendingPacket.SizeReceived < sizeof(m_pendingPacket.Size))
  295. {
  296. char* data = reinterpret_cast<char*>(&m_pendingPacket.Size) + m_pendingPacket.SizeReceived;
  297. Status status = receive(data, sizeof(m_pendingPacket.Size) - m_pendingPacket.SizeReceived, received);
  298. m_pendingPacket.SizeReceived += received;
  299. if (status != Done)
  300. return status;
  301. }
  302. // The packet size has been fully received
  303. packetSize = ntohl(m_pendingPacket.Size);
  304. }
  305. else
  306. {
  307. // The packet size has already been received in a previous call
  308. packetSize = ntohl(m_pendingPacket.Size);
  309. }
  310. // Loop until we receive all the packet data
  311. char buffer[1024];
  312. while (m_pendingPacket.Data.size() < packetSize)
  313. {
  314. // Receive a chunk of data
  315. std::size_t sizeToGet = std::min(static_cast<std::size_t>(packetSize - m_pendingPacket.Data.size()), sizeof(buffer));
  316. Status status = receive(buffer, sizeToGet, received);
  317. if (status != Done)
  318. return status;
  319. // Append it into the packet
  320. if (received > 0)
  321. {
  322. m_pendingPacket.Data.resize(m_pendingPacket.Data.size() + received);
  323. char* begin = &m_pendingPacket.Data[0] + m_pendingPacket.Data.size() - received;
  324. std::memcpy(begin, buffer, received);
  325. }
  326. }
  327. // We have received all the packet data: we can copy it to the user packet
  328. if (!m_pendingPacket.Data.empty())
  329. packet.onReceive(&m_pendingPacket.Data[0], m_pendingPacket.Data.size());
  330. // Clear the pending packet data
  331. m_pendingPacket = PendingPacket();
  332. return Done;
  333. }
  334. ////////////////////////////////////////////////////////////
  335. TcpSocket::PendingPacket::PendingPacket() :
  336. Size (0),
  337. SizeReceived(0),
  338. Data ()
  339. {
  340. }
  341. } // namespace sf