From d790114df8c3813053f2387db23aade7f8bfde77 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Fri, 6 Feb 2015 19:37:53 +0100 Subject: [PATCH] Added a way for partial sends over non-blocking TcpSockets to be handled properly. --- include/SFML/Network/Packet.hpp | 1 + include/SFML/Network/Socket.hpp | 1 + include/SFML/Network/TcpSocket.hpp | 23 ++++++++++++++ src/SFML/Network/Packet.cpp | 1 + src/SFML/Network/TcpSocket.cpp | 49 ++++++++++++++++++++++++------ 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/include/SFML/Network/Packet.hpp b/include/SFML/Network/Packet.hpp index a400aaed..a3e971e1 100644 --- a/include/SFML/Network/Packet.hpp +++ b/include/SFML/Network/Packet.hpp @@ -282,6 +282,7 @@ private: //////////////////////////////////////////////////////////// std::vector m_data; ///< Data stored in the packet std::size_t m_readPos; ///< Current reading position in the packet + std::size_t m_sendPos; ///< Current send position in the packet (for handling partial sends) bool m_isValid; ///< Reading state of the packet }; diff --git a/include/SFML/Network/Socket.hpp b/include/SFML/Network/Socket.hpp index 8b664530..974514c4 100644 --- a/include/SFML/Network/Socket.hpp +++ b/include/SFML/Network/Socket.hpp @@ -54,6 +54,7 @@ public: { Done, ///< The socket has sent / received the data NotReady, ///< The socket is not ready to send / receive data yet + Partial, ///< The socket sent a part of the data Disconnected, ///< The TCP socket has been disconnected Error ///< An unexpected error happened }; diff --git a/include/SFML/Network/TcpSocket.hpp b/include/SFML/Network/TcpSocket.hpp index 03a69ec7..23f740c2 100644 --- a/include/SFML/Network/TcpSocket.hpp +++ b/include/SFML/Network/TcpSocket.hpp @@ -124,6 +124,9 @@ public: //////////////////////////////////////////////////////////// /// \brief Send raw data to the remote peer /// + /// To be able to handle partial sends over non-blocking + /// sockets, use the send(const void*, std::size_t, std::size_t&) + /// overload instead. /// This function will fail if the socket is not connected. /// /// \param data Pointer to the sequence of bytes to send @@ -136,6 +139,22 @@ public: //////////////////////////////////////////////////////////// Status send(const void* data, std::size_t size); + //////////////////////////////////////////////////////////// + /// \brief Send raw data to the remote peer + /// + /// This function will fail if the socket is not connected. + /// + /// \param data Pointer to the sequence of bytes to send + /// \param size Number of bytes to send + /// \param sent The number of bytes sent will be written here + /// + /// \return Status code + /// + /// \see receive + /// + //////////////////////////////////////////////////////////// + Status send(const void* data, std::size_t size, std::size_t& sent); + //////////////////////////////////////////////////////////// /// \brief Receive raw data from the remote peer /// @@ -157,6 +176,10 @@ public: //////////////////////////////////////////////////////////// /// \brief Send a formatted packet of data to the remote peer /// + /// In non-blocking mode, if this function returns sf::Socket::Partial, + /// you \em must retry sending the same unmodified packet before sending + /// anything else in order to guarantee the packet arrives at the remote + /// peer uncorrupted. /// This function will fail if the socket is not connected. /// /// \param packet Packet to send diff --git a/src/SFML/Network/Packet.cpp b/src/SFML/Network/Packet.cpp index 31fe259f..c8a784ac 100644 --- a/src/SFML/Network/Packet.cpp +++ b/src/SFML/Network/Packet.cpp @@ -37,6 +37,7 @@ namespace sf //////////////////////////////////////////////////////////// Packet::Packet() : m_readPos(0), +m_sendPos(0), m_isValid(true) { diff --git a/src/SFML/Network/TcpSocket.cpp b/src/SFML/Network/TcpSocket.cpp index ac6d6b98..ecf1356b 100644 --- a/src/SFML/Network/TcpSocket.cpp +++ b/src/SFML/Network/TcpSocket.cpp @@ -218,6 +218,18 @@ void TcpSocket::disconnect() //////////////////////////////////////////////////////////// Socket::Status TcpSocket::send(const void* data, std::size_t size) +{ + if (!isBlocking()) + err() << "Warning: Partial sends might not be handled properly." << std::endl; + + std::size_t sent; + + return send(data, size, sent); +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpSocket::send(const void* data, std::size_t size, std::size_t& sent) { // Check the parameters if (!data || (size == 0)) @@ -227,16 +239,22 @@ Socket::Status TcpSocket::send(const void* data, std::size_t size) } // Loop until every byte has been sent - int sent = 0; - int sizeToSend = static_cast(size); - for (int length = 0; length < sizeToSend; length += sent) + int result = 0; + for (sent = 0; sent < size; sent += result) { // Send a chunk of data - sent = ::send(getHandle(), static_cast(data) + length, sizeToSend - length, flags); + result = ::send(getHandle(), static_cast(data) + sent, size - sent, flags); // Check for errors - if (sent < 0) - return priv::SocketImpl::getErrorStatus(); + if (result < 0) + { + Status status = priv::SocketImpl::getErrorStatus(); + + if ((status == NotReady) && sent) + return Partial; + + return status; + } } return Done; @@ -294,17 +312,30 @@ Socket::Status TcpSocket::send(Packet& packet) // First convert the packet size to network byte order Uint32 packetSize = htonl(static_cast(size)); - + // Allocate memory for the data block to send std::vector blockToSend(sizeof(packetSize) + size); - + // Copy the packet size and data into the block to send std::memcpy(&blockToSend[0], &packetSize, sizeof(packetSize)); if (size > 0) std::memcpy(&blockToSend[0] + sizeof(packetSize), data, size); // Send the data block - return send(&blockToSend[0], blockToSend.size()); + std::size_t sent; + Status status = send(&blockToSend[0] + packet.m_sendPos, blockToSend.size() - packet.m_sendPos, sent); + + // In the case of a partial send, record the location to resume from + if (status == Partial) + { + packet.m_sendPos += sent; + } + else if (status == Done) + { + packet.m_sendPos = 0; + } + + return status; }