ddnet/src/engine/shared/network_client.cpp
heinrich5991 1a35595bce Unify buffer handling in UDP sockets across operating systems
Previously, only Linux used an internal buffer (for optimized
receiving). Now all OSs use an internal buffer so that the call to
`net_udp_recv` behaves the same on all platforms.
2022-03-05 14:11:03 +01:00

156 lines
3.5 KiB
C++

/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "network.h"
#include <base/system.h>
bool CNetClient::Open(NETADDR BindAddr)
{
// open socket
NETSOCKET Socket;
Socket = net_udp_create(BindAddr);
if(!Socket)
return false;
// clean it
mem_zero(this, sizeof(*this));
// init
m_Socket = Socket;
m_Connection.Init(m_Socket, false);
return true;
}
int CNetClient::Close()
{
// TODO: implement me
return 0;
}
int CNetClient::Disconnect(const char *pReason)
{
//dbg_msg("netclient", "disconnected. reason=\"%s\"", pReason);
m_Connection.Disconnect(pReason);
return 0;
}
int CNetClient::Update()
{
m_Connection.Update();
if(m_Connection.State() == NET_CONNSTATE_ERROR)
Disconnect(m_Connection.ErrorString());
return 0;
}
int CNetClient::Connect(NETADDR *pAddr)
{
m_Connection.Connect(pAddr);
return 0;
}
int CNetClient::ResetErrorString()
{
m_Connection.ResetErrorString();
return 0;
}
int CNetClient::Recv(CNetChunk *pChunk)
{
while(true)
{
// check for a chunk
if(m_RecvUnpacker.FetchChunk(pChunk))
return 1;
// TODO: empty the recvinfo
NETADDR Addr;
unsigned char *pData;
int Bytes = net_udp_recv(m_Socket, &Addr, &pData);
// no more packets for now
if(Bytes <= 0)
break;
bool Sixup = false;
if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data, Sixup) == 0)
{
if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_CONNLESS)
{
pChunk->m_Flags = NETSENDFLAG_CONNLESS;
pChunk->m_ClientID = -1;
pChunk->m_Address = Addr;
pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize;
pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData;
if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_EXTENDED)
{
pChunk->m_Flags |= NETSENDFLAG_EXTENDED;
mem_copy(pChunk->m_aExtraData, m_RecvUnpacker.m_Data.m_aExtraData, sizeof(pChunk->m_aExtraData));
}
return 1;
}
else
{
if(m_Connection.State() != NET_CONNSTATE_OFFLINE && m_Connection.State() != NET_CONNSTATE_ERROR && net_addr_comp(m_Connection.PeerAddress(), &Addr) == 0 && m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr))
m_RecvUnpacker.Start(&Addr, &m_Connection, 0);
}
}
}
return 0;
}
int CNetClient::Send(CNetChunk *pChunk)
{
if(pChunk->m_DataSize >= NET_MAX_PAYLOAD)
{
dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize);
return -1;
}
if(pChunk->m_Flags & NETSENDFLAG_CONNLESS)
{
// send connectionless packet
CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize,
pChunk->m_Flags & NETSENDFLAG_EXTENDED, pChunk->m_aExtraData);
}
else
{
int Flags = 0;
dbg_assert(pChunk->m_ClientID == 0, "erroneous client id");
if(pChunk->m_Flags & NETSENDFLAG_VITAL)
Flags = NET_CHUNKFLAG_VITAL;
m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData);
if(pChunk->m_Flags & NETSENDFLAG_FLUSH)
m_Connection.Flush();
}
return 0;
}
int CNetClient::State()
{
if(m_Connection.State() == NET_CONNSTATE_ONLINE)
return NETSTATE_ONLINE;
if(m_Connection.State() == NET_CONNSTATE_OFFLINE)
return NETSTATE_OFFLINE;
return NETSTATE_CONNECTING;
}
int CNetClient::Flush()
{
return m_Connection.Flush();
}
int CNetClient::GotProblems(int64_t MaxLatency) const
{
if(time_get() - m_Connection.LastRecvTime() > MaxLatency)
return 1;
return 0;
}
const char *CNetClient::ErrorString() const
{
return m_Connection.ErrorString();
}