NazaraEngine/src/Nazara/Network/UdpSocket.cpp

307 lines
8.2 KiB
C++

// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// 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>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <Nazara/Network/Posix/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
/*!
* \ingroup network
* \class Nz::UdpSocket
* \brief Network class that represents a UDP socket, allowing for sending/receiving datagrams.
*/
/*!
* \brief Binds a specific IpAddress
* \return State of the socket
*
* \param address Address to bind
*
* \remark Produces a NazaraAssert if socket is invalid
* \remark Produces a NazaraAssert if address is invalid
*/
SocketState UdpSocket::Bind(const IpAddress& address)
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created");
NazaraAssert(address.IsValid(), "Invalid address");
SocketState state = SocketImpl::Bind(m_handle, address, &m_lastError);
if (state == SocketState_Bound)
m_boundAddress = SocketImpl::QuerySocketAddress(m_handle);
UpdateState(state);
return state;
}
/*!
* \brief Enables broadcasting
*
* \param broadcasting Should the UDP broadcast
*
* \remark Produces a NazaraAssert if socket is invalid
*/
void UdpSocket::EnableBroadcasting(bool broadcasting)
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle");
if (m_isBroadCastingEnabled != broadcasting)
{
SocketImpl::SetBroadcasting(m_handle, broadcasting, &m_lastError);
m_isBroadCastingEnabled = broadcasting;
}
}
/*!
* \brief Gets the maximum datagram size allowed
* \return Number of bytes
*
* \remark Produces a NazaraAssert if socket is invalid
*/
std::size_t UdpSocket::QueryMaxDatagramSize()
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created");
return SocketImpl::QueryMaxDatagramSize(m_handle, &m_lastError);
}
/*!
* \brief Receives the data available
* \return true If data received
*
* \param buffer Raw memory to write
* \param size Size of the buffer
* \param from IpAddress of the peer
* \param received Optional argument to get the number of bytes received
*
* \remark Produces a NazaraAssert if socket is invalid
* \remark Produces a NazaraAssert if buffer and its size is invalid
*/
bool UdpSocket::Receive(void* buffer, std::size_t size, IpAddress* from, std::size_t* received)
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created");
NazaraAssert(buffer && size > 0, "Invalid buffer");
int read;
if (!SocketImpl::ReceiveFrom(m_handle, buffer, static_cast<int>(size), from, &read, &m_lastError))
{
switch (m_lastError)
{
case SocketError_ConnectionClosed:
m_lastError = SocketError_NoError;
read = 0;
break;
default:
return false;
}
}
if (received)
*received = read;
return true;
}
/*!
* \brief Receive multiple datagram from one peer
* \return true If data were sent
*
* \param to Destination IpAddress (must match socket protocol)
* \param buffers A pointer to an array of NetBuffer containing buffers and size data
* \param bufferCount Number of buffers available
* \param from IpAddress of the peer
* \param received Optional argument to get the number of bytes received
*/
bool UdpSocket::ReceiveMultiple(NetBuffer* buffers, std::size_t bufferCount, IpAddress* from, std::size_t* received)
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created");
NazaraAssert(buffers && bufferCount > 0, "Invalid buffer");
int read;
if (!SocketImpl::ReceiveMultiple(m_handle, buffers, bufferCount, from, &read, &m_lastError))
{
switch (m_lastError)
{
case SocketError_ConnectionClosed:
m_lastError = SocketError_NoError;
read = 0;
break;
default:
return false;
}
}
if (received)
*received = read;
return true;
}
/*!
* \brief Receives the packet available
* \return true If packet received
*
* \param packet Packet to receive
* \param from IpAddress of the peer
*
* \remark Produces a NazaraAssert if packet is invalid
* \remark Produces a NazaraWarning if packet's header is invalid
*/
bool UdpSocket::ReceivePacket(NetPacket* packet, IpAddress* from)
{
NazaraAssert(packet, "Invalid packet");
// 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());
packet->Resize(std::numeric_limits<UInt16>::max());
std::size_t received;
if (!Receive(packet->GetData(), static_cast<std::size_t>(packet->GetSize()), from, &received))
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;
}
/*!
* \brief Sends the data available
* \return true If data sended
*
* \param to IpAddress of the peer
* \param buffer Raw memory to read
* \param size Size of the buffer
* \param sent Optional argument to get the number of bytes sent
*
* \remark Produces a NazaraAssert if peer address is invalid
* \remark Produces a NazaraAssert if protocol of communication is not the same than the peer
* \remark Produces a NazaraAssert if buffer and its size is invalid
*/
bool UdpSocket::Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent)
{
NazaraAssert(to.IsValid(), "Invalid ip address");
NazaraAssert(to.GetProtocol() == m_protocol, "IP Address has a different protocol than the socket");
NazaraAssert(buffer && size > 0, "Invalid buffer");
int byteSent;
if (!SocketImpl::SendTo(m_handle, buffer, static_cast<int>(size), to, &byteSent, &m_lastError))
return false;
if (sent)
*sent = byteSent;
return true;
}
/*!
* \brief Sends multiple buffers as one datagram
* \return true If data were sent
*
* \param to Destination IpAddress (must match socket protocol)
* \param buffers A pointer to an array of NetBuffer containing buffers and size data
* \param size Number of NetBuffer to send
* \param sent Optional argument to get the number of bytes sent
*/
bool UdpSocket::SendMultiple(const IpAddress& to, const NetBuffer* buffers, std::size_t bufferCount, std::size_t* sent)
{
NazaraAssert(to.IsValid(), "Invalid ip address");
NazaraAssert(to.GetProtocol() == m_protocol, "IP Address has a different protocol than the socket");
NazaraAssert(buffers && bufferCount > 0, "Invalid buffer");
int byteSent;
if (!SocketImpl::SendMultiple(m_handle, buffers, bufferCount, to, &byteSent, &m_lastError))
return false;
if (sent)
*sent = byteSent;
return true;
}
/*!
* \brief Sends the packet available
* \return true If packet sent
*
* \param to IpAddress of the peer
* \param packet Packet to send
*
* \remark Produces a NazaraError if packet could not be prepared for sending
*/
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);
}
/*!
* \brief Operation to do when closing socket
*/
void UdpSocket::OnClose()
{
AbstractSocket::OnClose();
m_boundAddress = IpAddress::Invalid;
}
/*!
* \brief Operation to do when opening socket
*/
void UdpSocket::OnOpened()
{
AbstractSocket::OnOpened();
m_boundAddress = IpAddress::Invalid;
m_isBroadCastingEnabled = false;
}
}