Added a way for partial sends over non-blocking TcpSockets to be handled properly.
This commit is contained in:
parent
67c7663c80
commit
d790114df8
@ -282,6 +282,7 @@ private:
|
||||
////////////////////////////////////////////////////////////
|
||||
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_sendPos; ///< Current send position in the packet (for handling partial sends)
|
||||
bool m_isValid; ///< Reading state of the packet
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -37,6 +37,7 @@ namespace sf
|
||||
////////////////////////////////////////////////////////////
|
||||
Packet::Packet() :
|
||||
m_readPos(0),
|
||||
m_sendPos(0),
|
||||
m_isValid(true)
|
||||
{
|
||||
|
||||
|
@ -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<int>(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<const char*>(data) + length, sizeToSend - length, flags);
|
||||
result = ::send(getHandle(), static_cast<const char*>(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<Uint32>(size));
|
||||
|
||||
|
||||
// Allocate memory for the data block to send
|
||||
std::vector<char> 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;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user