Network: Add support for sending/receiving NetPacket over UDP/TCP

Former-commit-id: 5c09a5fa8b499e4204d2312f6d04d8554093a5a4
This commit is contained in:
Lynix 2016-02-04 14:51:26 +01:00
parent 6c0215952d
commit 08caff5ea3
4 changed files with 149 additions and 3 deletions

View File

@ -8,6 +8,7 @@
#define NAZARA_TCPCLIENT_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/ByteArray.hpp>
#include <Nazara/Core/Signal.hpp>
#include <Nazara/Core/Stream.hpp>
#include <Nazara/Network/AbstractSocket.hpp>
@ -15,6 +16,8 @@
namespace Nz
{
class NetPacket;
class NAZARA_NETWORK_API TcpClient : public AbstractSocket, public Stream
{
friend class TcpServer;
@ -43,8 +46,10 @@ namespace Nz
inline bool IsKeepAliveEnabled() const;
bool Receive(void* buffer, std::size_t size, std::size_t* received);
bool ReceivePacket(NetPacket* packet);
bool Send(const void* buffer, std::size_t size, std::size_t* sent);
bool SendPacket(const NetPacket& packet);
bool SetCursorPos(UInt64 offset) override;
@ -62,7 +67,16 @@ namespace Nz
void Reset(SocketHandle handle, const IpAddress& peerAddress);
std::size_t WriteBlock(const void* buffer, std::size_t size) override;
struct PendingPacket
{
std::size_t received = 0;
ByteArray data;
UInt16 netcode;
bool headerReceived = false;
};
IpAddress m_peerAddress;
PendingPacket m_pendingPacket;
UInt64 m_keepAliveInterval;
UInt64 m_keepAliveTime;
bool m_isLowDelayEnabled;
@ -72,4 +86,4 @@ namespace Nz
#include <Nazara/Network/TcpClient.inl>
#endif // NAZARA_TCPCLIENT_HPP
#endif // NAZARA_TCPCLIENT_HPP

View File

@ -13,6 +13,8 @@
namespace Nz
{
class NetPacket;
class NAZARA_NETWORK_API UdpSocket : public AbstractSocket
{
public:
@ -37,8 +39,10 @@ namespace Nz
std::size_t QueryMaxDatagramSize();
bool Receive(void* buffer, std::size_t size, IpAddress* from, std::size_t* received);
bool ReceivePacket(NetPacket* packet, IpAddress* from);
bool Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent);
bool SendPacket(const IpAddress& to, const NetPacket& packet);
private:
void OnClose() override;

View File

@ -6,7 +6,7 @@
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <limits>
#include <Nazara/Network/Debug.hpp>
#include <Nazara/Network/NetPacket.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
@ -16,6 +16,8 @@
#error Missing implementation: Socket
#endif
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
SocketState TcpClient::Connect(const IpAddress& remoteAddress)
@ -141,6 +143,66 @@ namespace Nz
return true;
}
bool TcpClient::ReceivePacket(NetPacket* packet)
{
//TODO: Every packet requires at least two Receive call, using an internal buffer of a fixed size would prevent this
NazaraAssert(packet, "Invalid packet");
if (!m_pendingPacket.headerReceived)
{
m_pendingPacket.data.Resize(NetPacket::HeaderSize);
std::size_t received;
if (!Receive(&m_pendingPacket.data[m_pendingPacket.received], NetPacket::HeaderSize - m_pendingPacket.received, &received))
return false;
m_pendingPacket.received += received;
NazaraAssert(m_pendingPacket.received <= NetPacket::HeaderSize, "Received more data than header size");
if (m_pendingPacket.received >= NetPacket::HeaderSize)
{
UInt16 size;
if (!NetPacket::DecodeHeader(m_pendingPacket.data.GetConstBuffer(), &size, &m_pendingPacket.netcode))
{
m_lastError = SocketError_Packet;
NazaraWarning("Invalid header data");
return false;
}
m_pendingPacket.data.Resize(size - NetPacket::HeaderSize);
m_pendingPacket.headerReceived = true;
m_pendingPacket.received = 0;
}
}
// We may have just received the header now
if (m_pendingPacket.headerReceived)
{
UInt16 packetSize = static_cast<UInt16>(m_pendingPacket.data.GetSize()); //< Total packet size
std::size_t received;
if (!Receive(&m_pendingPacket.data[m_pendingPacket.received], packetSize - m_pendingPacket.received, &received))
return false;
m_pendingPacket.received += received;
NazaraAssert(m_pendingPacket.received <= packetSize, "Received more data than packet size");
if (m_pendingPacket.received >= packetSize)
{
// Okay we received the whole packet, copy it
packet->Reset(m_pendingPacket.netcode, m_pendingPacket.data.GetConstBuffer(), m_pendingPacket.data.GetSize());
// And reset every state
m_pendingPacket.data.Clear();
m_pendingPacket.headerReceived = false;
m_pendingPacket.received = 0;
return true;
}
}
return false;
}
bool TcpClient::Send(const void* buffer, std::size_t size, std::size_t* sent)
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle");
@ -183,6 +245,20 @@ namespace Nz
return true;
}
bool TcpClient::SendPacket(const NetPacket& packet)
{
std::size_t size = 0;
const UInt8* ptr = static_cast<const UInt8*>(packet.OnSend(&size));
if (!ptr)
{
m_lastError = SocketError_Packet;
NazaraError("Failed to prepare packet");
return false;
}
return Send(ptr, size, nullptr);
}
bool TcpClient::SetCursorPos(UInt64 offset)
{
NazaraError("SetCursorPos() cannot be used on sequential streams");
@ -215,7 +291,7 @@ namespace Nz
}
SocketState newState = SocketImpl::Connect(m_handle, m_peerAddress, msTimeout, &m_lastError);
NazaraAssert(newState != SocketState_Connecting, "Invalid internal return");
NazaraAssert(newState != SocketState_Connecting, "Invalid internal return"); //< Connect cannot return Connecting is a timeout was specified
// Prevent valid peer address in non-connected state
if (newState == SocketState_NotConnected)

View File

@ -3,6 +3,7 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/UdpSocket.hpp>
#include <Nazara/Network/NetPacket.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
@ -61,6 +62,43 @@ namespace Nz
return true;
}
bool UdpSocket::ReceivePacket(NetPacket* packet, IpAddress* from)
{
// I'm not sure what's the best between having a 65k bytes buffer ready for any datagram size
// or querying the next datagram size every time, for now I'll leave it as is
packet->Reset(NetCode_Invalid, std::numeric_limits<UInt16>::max());
std::size_t received;
if (!Receive(packet->GetData(), static_cast<std::size_t>(packet->GetSize()), from, &received))
{
NazaraError("Failed to receive packet");
return false;
}
if (received == 0)
return false; //< No datagram received
Nz::UInt16 netCode;
Nz::UInt16 packetSize;
if (!NetPacket::DecodeHeader(packet->GetConstData(), &packetSize, &netCode))
{
m_lastError = SocketError_Packet;
NazaraWarning("Invalid header data");
return false;
}
if (packetSize != received)
{
m_lastError = SocketError_Packet;
NazaraWarning("Invalid packet size (packet size is " + String::Number(packetSize) + " bytes, received " + Nz::String::Number(received) + " bytes)");
return false;
}
packet->Resize(received);
packet->SetNetCode(netCode);
return true;
}
bool UdpSocket::Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent)
{
NazaraAssert(to.IsValid(), "Invalid ip address");
@ -77,6 +115,20 @@ namespace Nz
return true;
}
bool UdpSocket::SendPacket(const IpAddress& to, const NetPacket& packet)
{
std::size_t size = 0;
const UInt8* ptr = static_cast<const UInt8*>(packet.OnSend(&size));
if (!ptr)
{
m_lastError = SocketError_Packet;
NazaraError("Failed to prepare packet");
return false;
}
return Send(to, ptr, size, nullptr);
}
void UdpSocket::OnClose()
{
AbstractSocket::OnClose();