diff --git a/include/Nazara/Network/TcpClient.hpp b/include/Nazara/Network/TcpClient.hpp index cbf501ffe..fda4671f9 100644 --- a/include/Nazara/Network/TcpClient.hpp +++ b/include/Nazara/Network/TcpClient.hpp @@ -8,6 +8,7 @@ #define NAZARA_TCPCLIENT_HPP #include +#include #include #include #include @@ -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 -#endif // NAZARA_TCPCLIENT_HPP \ No newline at end of file +#endif // NAZARA_TCPCLIENT_HPP diff --git a/include/Nazara/Network/UdpSocket.hpp b/include/Nazara/Network/UdpSocket.hpp index 815f88fc8..96c391436 100644 --- a/include/Nazara/Network/UdpSocket.hpp +++ b/include/Nazara/Network/UdpSocket.hpp @@ -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; diff --git a/src/Nazara/Network/TcpClient.cpp b/src/Nazara/Network/TcpClient.cpp index 6bb26aef8..267e5f67a 100644 --- a/src/Nazara/Network/TcpClient.cpp +++ b/src/Nazara/Network/TcpClient.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #if defined(NAZARA_PLATFORM_WINDOWS) #include @@ -16,6 +16,8 @@ #error Missing implementation: Socket #endif +#include + 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(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(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) diff --git a/src/Nazara/Network/UdpSocket.cpp b/src/Nazara/Network/UdpSocket.cpp index 88f9f10c0..d8461c627 100644 --- a/src/Nazara/Network/UdpSocket.cpp +++ b/src/Nazara/Network/UdpSocket.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #if defined(NAZARA_PLATFORM_WINDOWS) #include @@ -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::max()); + + std::size_t received; + if (!Receive(packet->GetData(), static_cast(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(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();