Added a way for partial sends over non-blocking TcpSockets to be handled properly.

This commit is contained in:
binary1248 2015-02-06 19:37:53 +01:00 committed by Lukas Dürrenberger
parent 67c7663c80
commit d790114df8
5 changed files with 66 additions and 9 deletions

View File

@ -282,6 +282,7 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::vector<char> m_data; ///< Data stored in the packet std::vector<char> m_data; ///< Data stored in the packet
std::size_t m_readPos; ///< Current reading position 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 bool m_isValid; ///< Reading state of the packet
}; };

View File

@ -54,6 +54,7 @@ public:
{ {
Done, ///< The socket has sent / received the data Done, ///< The socket has sent / received the data
NotReady, ///< The socket is not ready to send / receive data yet 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 Disconnected, ///< The TCP socket has been disconnected
Error ///< An unexpected error happened Error ///< An unexpected error happened
}; };

View File

@ -124,6 +124,9 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Send raw data to the remote peer /// \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. /// This function will fail if the socket is not connected.
/// ///
/// \param data Pointer to the sequence of bytes to send /// \param data Pointer to the sequence of bytes to send
@ -136,6 +139,22 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Status send(const void* data, std::size_t size); 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 /// \brief Receive raw data from the remote peer
/// ///
@ -157,6 +176,10 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Send a formatted packet of data to the remote peer /// \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. /// This function will fail if the socket is not connected.
/// ///
/// \param packet Packet to send /// \param packet Packet to send

View File

@ -37,6 +37,7 @@ namespace sf
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Packet::Packet() : Packet::Packet() :
m_readPos(0), m_readPos(0),
m_sendPos(0),
m_isValid(true) m_isValid(true)
{ {

View File

@ -218,6 +218,18 @@ void TcpSocket::disconnect()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Socket::Status TcpSocket::send(const void* data, std::size_t size) 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 // Check the parameters
if (!data || (size == 0)) 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 // Loop until every byte has been sent
int sent = 0; int result = 0;
int sizeToSend = static_cast<int>(size); for (sent = 0; sent < size; sent += result)
for (int length = 0; length < sizeToSend; length += sent)
{ {
// Send a chunk of data // Send a chunk of data
sent = ::send(getHandle(), static_cast<const char*>(data) + length, sizeToSend - length, flags); result = ::send(getHandle(), static_cast<const char*>(data) + sent, size - sent, flags);
// Check for errors // Check for errors
if (sent < 0) if (result < 0)
return priv::SocketImpl::getErrorStatus(); {
Status status = priv::SocketImpl::getErrorStatus();
if ((status == NotReady) && sent)
return Partial;
return status;
}
} }
return Done; return Done;
@ -294,17 +312,30 @@ Socket::Status TcpSocket::send(Packet& packet)
// First convert the packet size to network byte order // First convert the packet size to network byte order
Uint32 packetSize = htonl(static_cast<Uint32>(size)); Uint32 packetSize = htonl(static_cast<Uint32>(size));
// Allocate memory for the data block to send // Allocate memory for the data block to send
std::vector<char> blockToSend(sizeof(packetSize) + size); std::vector<char> blockToSend(sizeof(packetSize) + size);
// Copy the packet size and data into the block to send // Copy the packet size and data into the block to send
std::memcpy(&blockToSend[0], &packetSize, sizeof(packetSize)); std::memcpy(&blockToSend[0], &packetSize, sizeof(packetSize));
if (size > 0) if (size > 0)
std::memcpy(&blockToSend[0] + sizeof(packetSize), data, size); std::memcpy(&blockToSend[0] + sizeof(packetSize), data, size);
// Send the data block // 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;
} }