实现裸的 UDP 连接与通信
This commit is contained in:
parent
313de8c620
commit
f28b0ecb94
3
Source/RSHWNetwork/Private/Logging.cpp
Normal file
3
Source/RSHWNetwork/Private/Logging.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "Logging.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogRSHWNetwork);
|
6
Source/RSHWNetwork/Private/Logging.h
Normal file
6
Source/RSHWNetwork/Private/Logging.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogRSHWNetwork, Log, All);
|
256
Source/RSHWNetwork/Private/RSHWNetworkClient.cpp
Normal file
256
Source/RSHWNetwork/Private/RSHWNetworkClient.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
#include "RSHWNetworkClient.h"
|
||||
|
||||
#include "Logging.h"
|
||||
#include "Sockets.h"
|
||||
#include "IPAddress.h"
|
||||
#include "SocketSubsystem.h"
|
||||
|
||||
URSHWNetworkClient::URSHWNetworkClient(const FObjectInitializer & ObjectInitializer)
|
||||
: ServerAddr(ISocketSubsystem::Get()->CreateInternetAddr())
|
||||
{
|
||||
ServerAddr->SetLoopbackAddress();
|
||||
ServerAddr->SetPort(25565);
|
||||
}
|
||||
|
||||
bool URSHWNetworkClient::SetHandler(TScriptInterface<IRSHWNetworkClientHandler> InHandlerObject)
|
||||
{
|
||||
if (bIsRunning) return false;
|
||||
HandlerObject = InHandlerObject;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool URSHWNetworkClient::SetServerAddr(TSharedRef<FInternetAddr> InServerAddr)
|
||||
{
|
||||
if (bIsRunning) return false;
|
||||
if (!InServerAddr->IsValid()) return false;
|
||||
ServerAddr = InServerAddr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool URSHWNetworkClient::SetServerAddrByString(const FString & InServerAddr)
|
||||
{
|
||||
if (bIsRunning) return false;
|
||||
|
||||
TSharedRef<FInternetAddr> NewServerAddr = ISocketSubsystem::Get()->CreateInternetAddr();
|
||||
|
||||
bool bIsValid;
|
||||
NewServerAddr->SetPort(ServerAddr->GetPort());
|
||||
NewServerAddr->SetIp(*InServerAddr, bIsValid);
|
||||
|
||||
if (bIsValid)
|
||||
{
|
||||
ServerAddr = NewServerAddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool URSHWNetworkClient::Send(const TArray<uint8>& Data)
|
||||
{
|
||||
if (!bIsRunning || !(ClientPass.ID | ClientPass.Key)) return false;
|
||||
|
||||
SendBuffer.SetNumUninitialized(8, false);
|
||||
|
||||
SendBuffer[0] = ClientPass.ID >> 0;
|
||||
SendBuffer[1] = ClientPass.ID >> 8;
|
||||
SendBuffer[2] = ClientPass.ID >> 16;
|
||||
SendBuffer[3] = ClientPass.ID >> 24;
|
||||
|
||||
SendBuffer[4] = ClientPass.Key >> 0;
|
||||
SendBuffer[5] = ClientPass.Key >> 8;
|
||||
SendBuffer[6] = ClientPass.Key >> 16;
|
||||
SendBuffer[7] = ClientPass.Key >> 24;
|
||||
|
||||
SendBuffer.Append(Data);
|
||||
|
||||
int32 BytesSend;
|
||||
return SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *ServerAddr) && BytesSend == SendBuffer.Num();
|
||||
}
|
||||
|
||||
void URSHWNetworkClient::Login()
|
||||
{
|
||||
if (bIsRunning) return;
|
||||
|
||||
if (HandlerObject.GetInterface() == nullptr)
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("HandlerObject is nullptr in '%s'."), *GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
|
||||
|
||||
if (SocketSubsystem == nullptr)
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("Socket subsystem is nullptr in '%s'."), *GetName());
|
||||
HandlerObject->OnLoginFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
SocketPtr = SocketSubsystem->CreateSocket(NAME_DGram, FString::Printf(TEXT("RSHW Client Socket in '%s'."), *GetName()));
|
||||
|
||||
if (SocketPtr == nullptr)
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("Socket creation failed in '%s'."), *GetName());
|
||||
HandlerObject->OnLoginFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SocketPtr->SetNonBlocking())
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("Socket set non-blocking failed in '%s'."), *GetName());
|
||||
SocketSubsystem->DestroySocket(SocketPtr);
|
||||
HandlerObject->OnLoginFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
ClientPass.ID = 0;
|
||||
ClientPass.Key = 0;
|
||||
bIsRunning = true;
|
||||
StartupTime = FDateTime::Now();
|
||||
LastRecvTime = StartupTime;
|
||||
LastHeartbeat = FDateTime::MinValue();
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("RSHW network client '%s' try login."), *GetName());
|
||||
|
||||
}
|
||||
|
||||
void URSHWNetworkClient::Unlogin()
|
||||
{
|
||||
if (!bIsRunning) return;
|
||||
|
||||
HandlerObject->OnUnlogin();
|
||||
|
||||
ResetRunningData();
|
||||
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("RSHW network client '%s' unlogin."), *GetName());
|
||||
}
|
||||
|
||||
void URSHWNetworkClient::ResetRunningData()
|
||||
{
|
||||
bIsRunning = false;
|
||||
|
||||
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
|
||||
check(SocketSubsystem);
|
||||
SocketSubsystem->DestroySocket(SocketPtr);
|
||||
|
||||
SendBuffer.SetNum(0);
|
||||
RecvBuffer.SetNum(0);
|
||||
DataBuffer.SetNum(0);
|
||||
}
|
||||
|
||||
void URSHWNetworkClient::BeginDestroy()
|
||||
{
|
||||
Unlogin();
|
||||
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
|
||||
void URSHWNetworkClient::Tick(float DeltaTime)
|
||||
{
|
||||
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
|
||||
check(SocketSubsystem);
|
||||
|
||||
const FDateTime NowTime = FDateTime::Now();
|
||||
|
||||
// send heartbeat
|
||||
{
|
||||
if (NowTime - LastHeartbeat > FTimespan::FromSeconds(1.0))
|
||||
{
|
||||
SendBuffer.SetNumUninitialized(8, false);
|
||||
|
||||
SendBuffer[0] = ClientPass.ID >> 0;
|
||||
SendBuffer[1] = ClientPass.ID >> 8;
|
||||
SendBuffer[2] = ClientPass.ID >> 16;
|
||||
SendBuffer[3] = ClientPass.ID >> 24;
|
||||
|
||||
SendBuffer[4] = ClientPass.Key >> 0;
|
||||
SendBuffer[5] = ClientPass.Key >> 8;
|
||||
SendBuffer[6] = ClientPass.Key >> 16;
|
||||
SendBuffer[7] = ClientPass.Key >> 24;
|
||||
|
||||
int32 BytesSend;
|
||||
if (SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *ServerAddr) && BytesSend == SendBuffer.Num())
|
||||
{
|
||||
LastHeartbeat = NowTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle socket recv
|
||||
{
|
||||
int32 BytesRead;
|
||||
TSharedRef<FInternetAddr> SourceAddr = SocketSubsystem->CreateInternetAddr();
|
||||
|
||||
while (true) {
|
||||
|
||||
RecvBuffer.SetNumUninitialized(65535, false);
|
||||
|
||||
if (!SocketPtr->RecvFrom(RecvBuffer.GetData(), RecvBuffer.Num(), BytesRead, *SourceAddr)) break;
|
||||
|
||||
if (BytesRead < 8) continue;
|
||||
RecvBuffer.SetNumUninitialized(BytesRead, false);
|
||||
|
||||
FRSHWNetworkPass SourcePass;
|
||||
SourcePass.ID = 0;
|
||||
SourcePass.Key = 0;
|
||||
|
||||
SourcePass.ID |= (int32)RecvBuffer[0] << 0;
|
||||
SourcePass.ID |= (int32)RecvBuffer[1] << 8;
|
||||
SourcePass.ID |= (int32)RecvBuffer[2] << 16;
|
||||
SourcePass.ID |= (int32)RecvBuffer[3] << 24;
|
||||
|
||||
SourcePass.Key |= (int32)RecvBuffer[4] << 0;
|
||||
SourcePass.Key |= (int32)RecvBuffer[5] << 8;
|
||||
SourcePass.Key |= (int32)RecvBuffer[6] << 16;
|
||||
SourcePass.Key |= (int32)RecvBuffer[7] << 24;
|
||||
|
||||
// is registration request
|
||||
if (!(ClientPass.ID | ClientPass.Key))
|
||||
{
|
||||
ClientPass = SourcePass;
|
||||
HandlerObject->OnLogin();
|
||||
}
|
||||
|
||||
if (SourcePass.ID == ClientPass.ID && SourcePass.Key == ClientPass.Key)
|
||||
{
|
||||
LastRecvTime = NowTime;
|
||||
}
|
||||
|
||||
// is heartbeat request
|
||||
if ((SourcePass.ID | SourcePass.Key) && RecvBuffer.Num() == 8) continue;
|
||||
|
||||
// is server request
|
||||
if (SourcePass.ID == ClientPass.ID && SourcePass.Key == ClientPass.Key)
|
||||
{
|
||||
DataBuffer.SetNumUninitialized(RecvBuffer.Num() - 8, false);
|
||||
FMemory::Memcpy(DataBuffer.GetData(), RecvBuffer.GetData() + 8, RecvBuffer.Num() - 8);
|
||||
HandlerObject->OnRecv(DataBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle login timeout
|
||||
{
|
||||
if (!(ClientPass.ID | ClientPass.Key) && NowTime - StartupTime > TimeoutLimit)
|
||||
{
|
||||
ResetRunningData();
|
||||
|
||||
UE_LOG(LogRSHWNetwork, Warning, TEXT("RSHW network client '%s' login timeout."), *GetName());
|
||||
HandlerObject->OnLoginFailure();
|
||||
}
|
||||
}
|
||||
|
||||
// handle timeout
|
||||
{
|
||||
if ((ClientPass.ID | ClientPass.Key) && NowTime - LastRecvTime > TimeoutLimit)
|
||||
{
|
||||
ResetRunningData();
|
||||
|
||||
UE_LOG(LogRSHWNetwork, Warning, TEXT("RSHW network client '%s' timeout."), *GetName());
|
||||
HandlerObject->OnUnlogin();
|
||||
}
|
||||
}
|
||||
}
|
340
Source/RSHWNetwork/Private/RSHWNetworkServer.cpp
Normal file
340
Source/RSHWNetwork/Private/RSHWNetworkServer.cpp
Normal file
@ -0,0 +1,340 @@
|
||||
#include "RSHWNetworkServer.h"
|
||||
|
||||
#include "Logging.h"
|
||||
#include "Sockets.h"
|
||||
#include "IPAddress.h"
|
||||
#include "SocketSubsystem.h"
|
||||
#include "HAL/UnrealMemory.h"
|
||||
|
||||
bool URSHWNetworkServer::SetHandler(TScriptInterface<IRSHWNetworkServerHandler> InHandlerObject)
|
||||
{
|
||||
if (bIsRunning) return false;
|
||||
HandlerObject = InHandlerObject;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool URSHWNetworkServer::SetBindPort(int32 InPort)
|
||||
{
|
||||
if (bIsRunning) return false;
|
||||
Port = InPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool URSHWNetworkServer::Send(int32 ClientID, const TArray<uint8>& Data)
|
||||
{
|
||||
if (!bIsRunning || !Registration.Contains(ClientID)) return false;
|
||||
|
||||
const FRegistrationInfo& Info = Registration[ClientID];
|
||||
|
||||
SendBuffer.SetNumUninitialized(8, false);
|
||||
|
||||
SendBuffer[0] = Info.Pass.ID >> 0;
|
||||
SendBuffer[1] = Info.Pass.ID >> 8;
|
||||
SendBuffer[2] = Info.Pass.ID >> 16;
|
||||
SendBuffer[3] = Info.Pass.ID >> 24;
|
||||
|
||||
SendBuffer[4] = Info.Pass.Key >> 0;
|
||||
SendBuffer[5] = Info.Pass.Key >> 8;
|
||||
SendBuffer[6] = Info.Pass.Key >> 16;
|
||||
SendBuffer[7] = Info.Pass.Key >> 24;
|
||||
|
||||
SendBuffer.Append(Data);
|
||||
|
||||
int32 BytesSend;
|
||||
return SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *Info.Addr) && BytesSend == SendBuffer.Num();
|
||||
}
|
||||
|
||||
bool URSHWNetworkServer::RunServer()
|
||||
{
|
||||
if (bIsRunning) return true;
|
||||
|
||||
if (HandlerObject.GetInterface() == nullptr)
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("HandlerObject is nullptr in '%s'."), *GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
|
||||
|
||||
if (SocketSubsystem == nullptr)
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("Socket subsystem is nullptr in '%s'."), *GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
SocketPtr = SocketSubsystem->CreateSocket(NAME_DGram, FString::Printf(TEXT("RSHW Server Socket in '%s'."), *GetName()));
|
||||
|
||||
if (SocketPtr == nullptr)
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("Socket creation failed in '%s'."), *GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
TSharedRef<FInternetAddr> ServerAddr = SocketSubsystem->CreateInternetAddr();
|
||||
|
||||
ServerAddr->SetAnyAddress();
|
||||
ServerAddr->SetPort(Port);
|
||||
|
||||
if (!SocketPtr->Bind(*ServerAddr))
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("Socket bind failed in '%s'."), *GetName());
|
||||
SocketSubsystem->DestroySocket(SocketPtr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SocketPtr->SetNonBlocking())
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Error, TEXT("Socket set non-blocking failed in '%s'."), *GetName());
|
||||
SocketSubsystem->DestroySocket(SocketPtr);
|
||||
return false;
|
||||
}
|
||||
|
||||
NextRegistrationID = 1;
|
||||
|
||||
bIsRunning = true;
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("RSHW network server '%s' start."), *GetName());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void URSHWNetworkServer::StopServer()
|
||||
{
|
||||
if (!bIsRunning) return;
|
||||
|
||||
TArray<int32> RegistrationAddr;
|
||||
Registration.GetKeys(RegistrationAddr);
|
||||
|
||||
for (int32 ID : RegistrationAddr)
|
||||
{
|
||||
HandlerObject->OnUnlogin(ID);
|
||||
}
|
||||
|
||||
ResetRunningData();
|
||||
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("RSHW network server '%s' stop."), *GetName());
|
||||
}
|
||||
|
||||
void URSHWNetworkServer::ResetRunningData()
|
||||
{
|
||||
bIsRunning = false;
|
||||
|
||||
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
|
||||
check(SocketSubsystem);
|
||||
SocketSubsystem->DestroySocket(SocketPtr);
|
||||
|
||||
SendBuffer.SetNum(0);
|
||||
RecvBuffer.SetNum(0);
|
||||
DataBuffer.SetNum(0);
|
||||
|
||||
PreRegistration.Reset();
|
||||
Registration.Reset();
|
||||
}
|
||||
|
||||
void URSHWNetworkServer::BeginDestroy()
|
||||
{
|
||||
StopServer();
|
||||
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
|
||||
void URSHWNetworkServer::Tick(float DeltaTime)
|
||||
{
|
||||
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get();
|
||||
check(SocketSubsystem);
|
||||
|
||||
const FDateTime NowTime = FDateTime::Now();
|
||||
|
||||
// send heartbeat
|
||||
{
|
||||
TArray<int32> RegistrationAddr;
|
||||
Registration.GetKeys(RegistrationAddr);
|
||||
|
||||
for (int32 ID : RegistrationAddr)
|
||||
{
|
||||
if (NowTime - Registration[ID].Heartbeat > FTimespan::FromSeconds(1.0))
|
||||
{
|
||||
SendBuffer.SetNum(8, false);
|
||||
|
||||
SendBuffer[0] = Registration[ID].Pass.ID >> 0;
|
||||
SendBuffer[1] = Registration[ID].Pass.ID >> 8;
|
||||
SendBuffer[2] = Registration[ID].Pass.ID >> 16;
|
||||
SendBuffer[3] = Registration[ID].Pass.ID >> 24;
|
||||
|
||||
SendBuffer[4] = Registration[ID].Pass.Key >> 0;
|
||||
SendBuffer[5] = Registration[ID].Pass.Key >> 8;
|
||||
SendBuffer[6] = Registration[ID].Pass.Key >> 16;
|
||||
SendBuffer[7] = Registration[ID].Pass.Key >> 24;
|
||||
|
||||
int32 BytesSend;
|
||||
if (SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *Registration[ID].Addr) && BytesSend == SendBuffer.Num())
|
||||
{
|
||||
Registration[ID].Heartbeat = NowTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle socket recv
|
||||
{
|
||||
int32 BytesRead;
|
||||
TSharedRef<FInternetAddr> SourceAddr = SocketSubsystem->CreateInternetAddr();
|
||||
|
||||
while (true) {
|
||||
|
||||
RecvBuffer.SetNumUninitialized(65535, false);
|
||||
|
||||
if (!SocketPtr->RecvFrom(RecvBuffer.GetData(), RecvBuffer.Num(), BytesRead, *SourceAddr)) break;
|
||||
|
||||
if (BytesRead < 8) continue;
|
||||
RecvBuffer.SetNumUninitialized(BytesRead, false);
|
||||
|
||||
FRSHWNetworkPass SourcePass;
|
||||
SourcePass.ID = 0;
|
||||
SourcePass.Key = 0;
|
||||
|
||||
SourcePass.ID |= (int32)RecvBuffer[0] << 0;
|
||||
SourcePass.ID |= (int32)RecvBuffer[1] << 8;
|
||||
SourcePass.ID |= (int32)RecvBuffer[2] << 16;
|
||||
SourcePass.ID |= (int32)RecvBuffer[3] << 24;
|
||||
|
||||
SourcePass.Key |= (int32)RecvBuffer[4] << 0;
|
||||
SourcePass.Key |= (int32)RecvBuffer[5] << 8;
|
||||
SourcePass.Key |= (int32)RecvBuffer[6] << 16;
|
||||
SourcePass.Key |= (int32)RecvBuffer[7] << 24;
|
||||
|
||||
FString SourceAddrStr = SourceAddr->ToString(true);
|
||||
|
||||
// is pre-register pass request
|
||||
if (!(SourcePass.ID | SourcePass.Key))
|
||||
{
|
||||
if (!PreRegistration.Contains(SourceAddrStr))
|
||||
{
|
||||
FPreRegistrationInfo NewRegistration;
|
||||
NewRegistration.Time = NowTime;
|
||||
NewRegistration.Pass.ID = NextRegistrationID++;
|
||||
NewRegistration.Pass.Key ^= FMath::Rand() << 0;
|
||||
NewRegistration.Pass.Key ^= FMath::Rand() << 8;
|
||||
NewRegistration.Pass.Key ^= FMath::Rand() << 16;
|
||||
NewRegistration.Pass.Key ^= FMath::Rand() << 24;
|
||||
|
||||
PreRegistration.Add(SourceAddrStr, NewRegistration);
|
||||
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("Pre register pass [ %s - %i:%i ] in '%s'."), *SourceAddrStr, NewRegistration.Pass.ID, NewRegistration.Pass.Key, *GetName());
|
||||
}
|
||||
|
||||
const FRSHWNetworkPass& Pass = PreRegistration[SourceAddrStr].Pass;
|
||||
|
||||
SendBuffer.SetNum(8, false);
|
||||
|
||||
SendBuffer[0] = Pass.ID >> 0;
|
||||
SendBuffer[1] = Pass.ID >> 8;
|
||||
SendBuffer[2] = Pass.ID >> 16;
|
||||
SendBuffer[3] = Pass.ID >> 24;
|
||||
|
||||
SendBuffer[4] = Pass.Key >> 0;
|
||||
SendBuffer[5] = Pass.Key >> 8;
|
||||
SendBuffer[6] = Pass.Key >> 16;
|
||||
SendBuffer[7] = Pass.Key >> 24;
|
||||
|
||||
int32 BytesSend;
|
||||
if (SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *SourceAddr) && BytesSend == SendBuffer.Num())
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("Send pre registration pass [ %s - %i:%i ] in '%s'."), *SourceAddrStr, Pass.ID, Pass.Key, *GetName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// redirect connection
|
||||
if (Registration.Contains(SourcePass.ID))
|
||||
{
|
||||
if (!(*Registration[SourcePass.ID].Addr == *SourceAddr))
|
||||
{
|
||||
Registration[SourcePass.ID].Addr = SourceAddr;
|
||||
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("Redirect connection [ %i:%i ] in '%s'."), SourcePass.ID, SourcePass.Key, *GetName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// register connection
|
||||
{
|
||||
bool bIsValidRegistration = false;
|
||||
if (PreRegistration.Contains(SourceAddrStr))
|
||||
{
|
||||
if (PreRegistration[SourceAddrStr].Pass.ID == SourcePass.ID && PreRegistration[SourceAddrStr].Pass.Key == SourcePass.Key)
|
||||
{
|
||||
bIsValidRegistration = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsValidRegistration)
|
||||
{
|
||||
FRegistrationInfo NewRegistration;
|
||||
NewRegistration.Pass = SourcePass;
|
||||
NewRegistration.RecvTime = NowTime;
|
||||
NewRegistration.Heartbeat = FDateTime::MinValue();
|
||||
NewRegistration.Addr = SourceAddr;
|
||||
|
||||
Registration.Add(SourcePass.ID, NewRegistration);
|
||||
|
||||
PreRegistration.Remove(SourceAddrStr);
|
||||
|
||||
HandlerObject->OnLogin(SourcePass.ID);
|
||||
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("Register connection [ %i:%i ] in '%s'."), SourcePass.ID, SourcePass.Key, *GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Registration.Contains(SourcePass.ID))
|
||||
{
|
||||
Registration[SourcePass.ID].RecvTime = NowTime;
|
||||
}
|
||||
|
||||
// is heartbeat request
|
||||
if ((SourcePass.ID | SourcePass.Key) && RecvBuffer.Num() == 8) continue;
|
||||
|
||||
// is client request
|
||||
if (Registration.Contains(SourcePass.ID))
|
||||
{
|
||||
DataBuffer.SetNumUninitialized(RecvBuffer.Num() - 8, false);
|
||||
FMemory::Memcpy(DataBuffer.GetData(), RecvBuffer.GetData() + 8, RecvBuffer.Num() - 8);
|
||||
HandlerObject->OnRecv(SourcePass.ID, DataBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle pre-registration timeout
|
||||
{
|
||||
TArray<FString> PreRegistrationAddr;
|
||||
PreRegistration.GetKeys(PreRegistrationAddr);
|
||||
|
||||
for (const FString& Addr : PreRegistrationAddr)
|
||||
{
|
||||
if (NowTime - PreRegistration[Addr].Time > TimeoutLimit)
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("Pre-registration pass [ %i:%i ] timeout in '%s'."), PreRegistration[Addr].Pass.ID, PreRegistration[Addr].Pass.Key, *GetName());
|
||||
|
||||
PreRegistration.Remove(Addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle running timeout
|
||||
{
|
||||
TArray<int32> RegistrationAddr;
|
||||
Registration.GetKeys(RegistrationAddr);
|
||||
|
||||
for (int32 ID : RegistrationAddr)
|
||||
{
|
||||
if (NowTime - Registration[ID].RecvTime > TimeoutLimit)
|
||||
{
|
||||
UE_LOG(LogRSHWNetwork, Log, TEXT("Registration pass [ %i:%i ] timeout in '%s'."), Registration[ID].Pass.ID, Registration[ID].Pass.Key, *GetName());
|
||||
|
||||
Registration.Remove(ID);
|
||||
|
||||
HandlerObject->OnUnlogin(ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
Source/RSHWNetwork/Private/RSHWNetworkType.cpp
Normal file
1
Source/RSHWNetwork/Private/RSHWNetworkType.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "RSHWNetworkType.h"
|
105
Source/RSHWNetwork/Public/RSHWNetworkClient.h
Normal file
105
Source/RSHWNetwork/Public/RSHWNetworkClient.h
Normal file
@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Misc/DateTime.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "RSHWNetworkType.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "RSHWNetworkClient.generated.h"
|
||||
|
||||
class FInternetAddr;
|
||||
|
||||
UINTERFACE()
|
||||
class RSHWNETWORK_API URSHWNetworkClientHandler : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class RSHWNETWORK_API IRSHWNetworkClientHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
virtual void OnLogin() { }
|
||||
|
||||
virtual void OnLoginFailure() { }
|
||||
|
||||
virtual void OnRecv(const TArray<uint8>& Data) { }
|
||||
|
||||
virtual void OnUnlogin() { }
|
||||
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class RSHWNETWORK_API URSHWNetworkClient : public UObject, public FTickableGameObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
URSHWNetworkClient(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
bool IsRunning() const { return bIsRunning; }
|
||||
|
||||
bool SetHandler(TScriptInterface<IRSHWNetworkClientHandler> InHandlerObject);
|
||||
|
||||
TScriptInterface<IRSHWNetworkClientHandler> GetHandler() const { return HandlerObject; }
|
||||
|
||||
bool SetServerAddr(TSharedRef<FInternetAddr> InServerAddr);
|
||||
|
||||
TSharedRef<FInternetAddr> GetServerAddr() const { return ServerAddr.ToSharedRef(); }
|
||||
|
||||
bool SetServerAddrByString(const FString& InServerAddr = TEXT("127.0.0.1:25565"));
|
||||
|
||||
FString GetServerAddrByString() const { return ServerAddr->ToString(true); }
|
||||
|
||||
bool Send(const TArray<uint8>& Data);
|
||||
|
||||
void Login();
|
||||
|
||||
void Unlogin();
|
||||
|
||||
public:
|
||||
|
||||
FTimespan TimeoutLimit = FTimespan::FromSeconds(8.0);
|
||||
|
||||
private:
|
||||
|
||||
bool bIsRunning = false;
|
||||
|
||||
TSharedPtr<FInternetAddr> ServerAddr;
|
||||
|
||||
UPROPERTY()
|
||||
TScriptInterface<IRSHWNetworkClientHandler> HandlerObject;
|
||||
|
||||
private:
|
||||
|
||||
FSocket* SocketPtr;
|
||||
|
||||
TArray<uint8> SendBuffer;
|
||||
TArray<uint8> RecvBuffer;
|
||||
TArray<uint8> DataBuffer;
|
||||
|
||||
FDateTime StartupTime;
|
||||
|
||||
FRSHWNetworkPass ClientPass;
|
||||
|
||||
FDateTime LastRecvTime;
|
||||
FDateTime LastHeartbeat;
|
||||
|
||||
void ResetRunningData();
|
||||
|
||||
private:
|
||||
|
||||
//~ Begin UObject Interface
|
||||
virtual void BeginDestroy() override;
|
||||
//~ End UObject Interface
|
||||
|
||||
//~ Begin FTickableGameObject Interface
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual bool IsTickable() const override { return !IsTemplate() && bIsRunning; }
|
||||
virtual TStatId GetStatId() const override { return GetStatID(); }
|
||||
//~ End FTickableGameObject Interface
|
||||
|
||||
};
|
109
Source/RSHWNetwork/Public/RSHWNetworkServer.h
Normal file
109
Source/RSHWNetwork/Public/RSHWNetworkServer.h
Normal file
@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "RSHWNetworkType.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "RSHWNetworkServer.generated.h"
|
||||
|
||||
class FSocket;
|
||||
|
||||
UINTERFACE()
|
||||
class RSHWNETWORK_API URSHWNetworkServerHandler : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class RSHWNETWORK_API IRSHWNetworkServerHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
virtual void OnLogin(int32 ClientID) { }
|
||||
|
||||
virtual void OnRecv(int32 ClientID, const TArray<uint8>& Data) { }
|
||||
|
||||
virtual void OnUnlogin(int32 ClientID) { }
|
||||
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class RSHWNETWORK_API URSHWNetworkServer : public UObject, public FTickableGameObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
bool IsRunning() const { return bIsRunning; }
|
||||
|
||||
bool SetHandler(TScriptInterface<IRSHWNetworkServerHandler> InHandlerObject);
|
||||
|
||||
TScriptInterface<IRSHWNetworkServerHandler> GetHandler() const { return HandlerObject; }
|
||||
|
||||
bool SetBindPort(int32 InPort = 25565);
|
||||
|
||||
int32 GetBindPort() const { return Port; }
|
||||
|
||||
bool Send(int32 ClientID, const TArray<uint8>& Data);
|
||||
|
||||
bool RunServer();
|
||||
|
||||
void StopServer();
|
||||
|
||||
public:
|
||||
|
||||
FTimespan TimeoutLimit = FTimespan::FromSeconds(8.0);
|
||||
|
||||
private:
|
||||
|
||||
bool bIsRunning = false;
|
||||
|
||||
int32 Port = 25565;
|
||||
|
||||
UPROPERTY()
|
||||
TScriptInterface<IRSHWNetworkServerHandler> HandlerObject;
|
||||
|
||||
private:
|
||||
|
||||
FSocket* SocketPtr;
|
||||
|
||||
TArray<uint8> SendBuffer;
|
||||
TArray<uint8> RecvBuffer;
|
||||
TArray<uint8> DataBuffer;
|
||||
|
||||
int32 NextRegistrationID;
|
||||
|
||||
struct FPreRegistrationInfo
|
||||
{
|
||||
FDateTime Time;
|
||||
FRSHWNetworkPass Pass;
|
||||
};
|
||||
|
||||
TMap<FString, FPreRegistrationInfo> PreRegistration;
|
||||
|
||||
struct FRegistrationInfo
|
||||
{
|
||||
FRSHWNetworkPass Pass;
|
||||
FDateTime RecvTime;
|
||||
FDateTime Heartbeat;
|
||||
TSharedPtr<FInternetAddr> Addr;
|
||||
};
|
||||
|
||||
TMap<int32, FRegistrationInfo> Registration;
|
||||
|
||||
void ResetRunningData();
|
||||
|
||||
private:
|
||||
|
||||
//~ Begin UObject Interface
|
||||
virtual void BeginDestroy() override;
|
||||
//~ End UObject Interface
|
||||
|
||||
//~ Begin FTickableGameObject Interface
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual bool IsTickable() const override { return !IsTemplate() && bIsRunning; }
|
||||
virtual TStatId GetStatId() const override { return GetStatID(); }
|
||||
//~ End FTickableGameObject Interface
|
||||
|
||||
};
|
9
Source/RSHWNetwork/Public/RSHWNetworkType.h
Normal file
9
Source/RSHWNetwork/Public/RSHWNetworkType.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
struct FRSHWNetworkPass
|
||||
{
|
||||
int32 ID;
|
||||
int32 Key;
|
||||
};
|
@ -38,6 +38,8 @@ public class RSHWNetwork : ModuleRules
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"Networking",
|
||||
"Sockets",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
Reference in New Issue
Block a user