Unreal-RedNetwork/Source/RedNetwork/Private/RedNetworkServer.cpp

367 lines
8.6 KiB
C++

#include "RedNetworkServer.h"
#include "KCPWrap.h"
#include "Logging.h"
#include "Sockets.h"
#include "IPAddress.h"
#include "SocketSubsystem.h"
#include "HAL/UnrealMemory.h"
#include "..\Public\RedNetworkServer.h"
bool URedNetworkServer::Send(int32 ClientID, uint8 Channel, const TArray<uint8>& Data)
{
if (!IsActive() || !Connections.Contains(ClientID)) return false;
const FConnectionInfo& Info = Connections[ClientID];
EnsureChannelCreated(ClientID, Channel);
return Info.KCPUnits[Channel]->Send(Data.GetData(), Data.Num()) == 0;
}
TSharedPtr<FInternetAddr> URedNetworkServer::GetSocketAddr() const
{
if (!SocketPtr) return nullptr;
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
check(SocketSubsystem);
TSharedRef<FInternetAddr> Addr = SocketSubsystem->CreateInternetAddr();
SocketPtr->GetAddress(*Addr);
return Addr;
}
FString URedNetworkServer::GetSocketAddrString() const
{
TSharedPtr<FInternetAddr> Addr = GetSocketAddr();
return Addr ? Addr->ToString(true) : TEXT("");
}
void URedNetworkServer::UpdateKCP()
{
int32 Current = FPlatformTime::Cycles64() / 1000;
for (auto Info : Connections)
{
for (auto KCPUnit : Info.Value.KCPUnits)
{
if (!KCPUnit) continue;
KCPUnit->Update(Current);
}
}
}
void URedNetworkServer::SendHeartbeat()
{
for (auto Info : Connections)
{
SendBuffer.SetNumUninitialized(8, false);
Info.Value.Pass.ToBytes(SendBuffer.GetData());
int32 BytesSend;
SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *Info.Value.Addr);
}
}
void URedNetworkServer::HandleSocketRecv()
{
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
check(SocketSubsystem);
check(SocketPtr);
int32 BytesRead;
while (SocketPtr) {
TSharedRef<FInternetAddr> SourceAddr = SocketSubsystem->CreateInternetAddr();
RecvBuffer.SetNumUninitialized(65535, false);
if (!SocketPtr->RecvFrom(RecvBuffer.GetData(), RecvBuffer.Num(), BytesRead, *SourceAddr)) break;
if (RecvBuffer.Num() < 8) continue;
RecvBuffer.SetNumUninitialized(BytesRead, false);
FRedNetworkPass SourcePass(RecvBuffer.GetData());
if (!SourcePass.IsValid())
{
SendReadyPass(SourceAddr);
continue;
}
RedirectConnection(SourcePass, SourceAddr);
RegisterConnection(SourcePass, SourceAddr);
if (!Connections.Contains(SourcePass.ID)) continue;
Connections[SourcePass.ID].RecvTime = NowTime;
if (RecvBuffer.Num() < 9) continue;
if (Connections.Contains(SourcePass.ID))
{
uint8 Channel = RecvBuffer[8];
EnsureChannelCreated(SourcePass.ID, Channel);
Connections[SourcePass.ID].KCPUnits[Channel]->Input(RecvBuffer.GetData() + 9, RecvBuffer.Num() - 9);
}
}
}
void URedNetworkServer::SendReadyPass(const TSharedRef<FInternetAddr>& SourceAddr)
{
FString SourceAddrStr = SourceAddr->ToString(true);
if (!ReadyPass.Contains(SourceAddrStr))
{
FReadyInfo NewReadyPass;
NewReadyPass.Time = NowTime;
NewReadyPass.Pass.ID = NextReadyID++;
NewReadyPass.Pass.RandKey();
ReadyPass.Add(SourceAddrStr, NewReadyPass);
UE_LOG(LogRedNetwork, Log, TEXT("Ready pass %i from %s."), NewReadyPass.Pass.ID, *SourceAddrStr);
}
const FRedNetworkPass& Pass = ReadyPass[SourceAddrStr].Pass;
SendBuffer.SetNum(8, false);
Pass.ToBytes(SendBuffer.GetData());
int32 BytesSend;
SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *SourceAddr);
UE_LOG(LogRedNetwork, Log, TEXT("Send ready pass %i to %s."), Pass.ID, *SourceAddrStr);
}
void URedNetworkServer::RedirectConnection(const FRedNetworkPass& SourcePass, const TSharedRef<FInternetAddr>& SourceAddr)
{
if (!Connections.Contains(SourcePass.ID) || Connections[SourcePass.ID].Pass.Key != SourcePass.Key) return;
if (!(*Connections[SourcePass.ID].Addr == *SourceAddr))
{
UE_LOG(LogRedNetwork, Log, TEXT("Redirect connection %i from %s to %s."), SourcePass.ID, *Connections[SourcePass.ID].Addr->ToString(true), *SourceAddr->ToString(true));
Connections[SourcePass.ID].Addr = SourceAddr;
}
}
void URedNetworkServer::RegisterConnection(const FRedNetworkPass& SourcePass, const TSharedRef<FInternetAddr>& SourceAddr)
{
FString SourceAddrStr = SourceAddr->ToString(true);
if (!ReadyPass.Contains(SourceAddrStr)) return;
if (ReadyPass[SourceAddrStr].Pass.ID != SourcePass.ID || ReadyPass[SourceAddrStr].Pass.Key != SourcePass.Key) return;
FConnectionInfo NewConnections;
NewConnections.Pass = SourcePass;
NewConnections.RecvTime = NowTime;
NewConnections.Heartbeat = FDateTime::MinValue();
NewConnections.Addr = SourceAddr;
NewConnections.KCPUnits.SetNum(256);
Connections.Add(SourcePass.ID, NewConnections);
ReadyPass.Remove(SourceAddrStr);
UE_LOG(LogRedNetwork, Log, TEXT("Register connection %i."), SourcePass.ID);
OnLogin.Broadcast(SourcePass.ID);
}
void URedNetworkServer::HandleKCPRecv()
{
for (auto Info : Connections)
{
for (int32 Channel = 0; Channel < Info.Value.KCPUnits.Num(); ++Channel)
{
const TSharedPtr<FKCPWrap>& KCPUnit = Info.Value.KCPUnits[Channel];
while (KCPUnit)
{
int32 Size = KCPUnit->PeekSize();
if (Size < 0) break;
RecvBuffer.SetNumUninitialized(Size, false);
Size = KCPUnit->Recv(RecvBuffer.GetData(), RecvBuffer.Num());
if (Size < 0) break;
RecvBuffer.SetNumUninitialized(Size, false);
OnRecv.Broadcast(Info.Key, Channel, RecvBuffer);
}
}
}
}
void URedNetworkServer::HandleExpiredReadyPass()
{
TArray<FString> ReadyPassAddr;
ReadyPass.GetKeys(ReadyPassAddr);
for (const FString& Addr : ReadyPassAddr)
{
if (NowTime - ReadyPass[Addr].Time > TimeoutLimit)
{
UE_LOG(LogRedNetwork, Log, TEXT("Ready pass %i timeout."), ReadyPass[Addr].Pass.ID);
ReadyPass.Remove(Addr);
}
}
}
void URedNetworkServer::HandleExpiredConnection()
{
TArray<int32> ConnectionsAddr;
Connections.GetKeys(ConnectionsAddr);
for (int32 ID : ConnectionsAddr)
{
if (NowTime - Connections[ID].RecvTime > TimeoutLimit)
{
UE_LOG(LogRedNetwork, Log, TEXT("Connections connection %i timeout."), Connections[ID].Pass.ID);
Connections.Remove(ID);
OnUnlogin.Broadcast(ID);
}
}
}
void URedNetworkServer::EnsureChannelCreated(int32 ClientID, uint8 Channel)
{
FConnectionInfo& Info = Connections[ClientID];
if (Info.KCPUnits[Channel]) return;
TSharedPtr<FKCPWrap> KCPUnit = MakeShared<FKCPWrap>(0, FString::Printf(TEXT("Server-%i:%i"), ClientID, Channel));
KCPUnit->SetTurboMode();
KCPUnit->GetKCPCB().logmask = KCPLogMask;
KCPUnit->OutputFunc = [this, ClientID, Channel](const uint8* Data, int32 Count)->int32
{
const FConnectionInfo& Info = Connections[ClientID];
SendBuffer.SetNumUninitialized(9, false);
Info.Pass.ToBytes(SendBuffer.GetData());
SendBuffer[8] = Channel;
if (Count != 0) SendBuffer.Append(Data, Count);
int32 BytesSend;
SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *Info.Addr);
return 0;
};
Info.KCPUnits[Channel] = KCPUnit;
}
void URedNetworkServer::Tick(float DeltaTime)
{
if (!IsActive()) return;
NowTime = FDateTime::Now();
UpdateKCP();
SendHeartbeat();
HandleSocketRecv();
HandleKCPRecv();
HandleExpiredReadyPass();
HandleExpiredConnection();
}
void URedNetworkServer::Activate(bool bReset)
{
if (bReset) Deactivate();
if (bIsActive) return;
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
if (SocketSubsystem == nullptr)
{
UE_LOG(LogRedNetwork, Error, TEXT("Socket subsystem is nullptr."));
return;
}
SocketPtr = SocketSubsystem->CreateSocket(NAME_DGram, TEXT("Red Server Socket"));
if (SocketPtr == nullptr)
{
UE_LOG(LogRedNetwork, Error, TEXT("Socket creation failed."));
return;
}
TSharedRef<FInternetAddr> ServerAddr = SocketSubsystem->CreateInternetAddr();
ServerAddr->SetAnyAddress();
ServerAddr->SetPort(Port);
if (!SocketPtr->Bind(*ServerAddr))
{
UE_LOG(LogRedNetwork, Error, TEXT("Socket bind failed."));
SocketSubsystem->DestroySocket(SocketPtr);
return;
}
if (!SocketPtr->SetNonBlocking())
{
UE_LOG(LogRedNetwork, Error, TEXT("Socket set non-blocking failed."));
SocketSubsystem->DestroySocket(SocketPtr);
return;
}
NextReadyID = 1;
UE_LOG(LogRedNetwork, Log, TEXT("Red Network Server activate."));
bIsActive = true;
}
void URedNetworkServer::Deactivate()
{
if (!bIsActive) return;
TArray<int32> ConnectionsAddr;
Connections.GetKeys(ConnectionsAddr);
for (int32 ID : ConnectionsAddr)
{
OnUnlogin.Broadcast(ID);
}
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
check(SocketSubsystem);
SocketSubsystem->DestroySocket(SocketPtr);
SendBuffer.SetNum(0);
RecvBuffer.SetNum(0);
ReadyPass.Reset();
Connections.Reset();
UE_LOG(LogRedNetwork, Log, TEXT("Red Network Server deactivate."));
bIsActive = false;
}
void URedNetworkServer::BeginDestroy()
{
Deactivate();
Super::BeginDestroy();
}