新建3d客户端

This commit is contained in:
liuyunhui
2025-09-15 17:17:30 +08:00
parent 1fe5a72ca4
commit fded7b9a51
38 changed files with 5874 additions and 0 deletions
@@ -0,0 +1,125 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#include "DNSClientSocketServer.h"
UDNSClientSocketServer::UDNSClientSocketServer(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
onresolveDomainEventDelegate.AddDynamic(this, &UDNSClientSocketServer::resolveDomainEventDelegate);
}
void UDNSClientSocketServer::resolveDomainEventDelegate(const FString IP) {}
void UDNSClientSocketServer::resolveDomain(ISocketSubsystem * socketSubSystem, FString domainP, bool useDNSCache, FString dnsIP ) {
domain = domainP;
resolving = true;
if (useDNSCache) {
if (dnsCache.Find(domain) != nullptr) {
ip = *dnsCache.Find(domain);
resolving = false;
return;
}
}
FIPv4Endpoint Endpoint;
FString socketName;
//FIPv4Endpoint::Parse("127.0.0.1:12345", Endpoint);
//socket = FUdpSocketBuilder(*socketName).AsReusable().WithBroadcast().BoundToEndpoint(Endpoint);
socket = FUdpSocketBuilder(*socketName).AsReusable().WithBroadcast();
if (socket == nullptr || socket == NULL) {
UE_LOG(LogTemp, Error, TEXT("UE4 could not init a UDP socket to resolve %s on %s."), *domain, *dnsIP);
resolving = false;
return;
}
else {
FTimespan ThreadWaitTime = FTimespan::FromMilliseconds(100);
FUdpSocketReceiver* udpSocketReceiver = new FUdpSocketReceiver(socket, ThreadWaitTime, TEXT("UE4 DNSClient"));
udpSocketReceiver->OnDataReceived().BindUObject(this, &UDNSClientSocketServer::UDPReceiver);
udpSocketReceiver->Start();
TSharedRef<FInternetAddr> addr = socketSubSystem->CreateInternetAddr();
bool bIsValid;
addr->SetIp(*dnsIP, bIsValid);
addr->SetPort(53);
if (bIsValid) {
TArray<unsigned char> dnsRequest;
//id (ramdom choose)
dnsRequest.Add(0x22);
dnsRequest.Add(0x76);
//head
dnsRequest.Add(0x1);
dnsRequest.Add(0x0);
dnsRequest.Add(0x0);
dnsRequest.Add(0x1);
dnsRequest.Add(0x0);
dnsRequest.Add(0x0);
dnsRequest.Add(0x0);
dnsRequest.Add(0x0);
dnsRequest.Add(0x0);
dnsRequest.Add(0x0);
//domain
//split in domain parts than add the length as byte and the part as bytes
TArray<FString> domainParts;
domain.ParseIntoArray(domainParts, TEXT("."), true);
for (int i = 0; i < domainParts.Num(); i++) {
FTCHARToUTF8 Convert(*domainParts[i]);
dnsRequest.Add(Convert.Length());
dnsRequest.Append((uint8*)Convert.Get(), Convert.Length());
}
//foot
dnsRequest.Add(0x0);
dnsRequest.Add(0x0);
dnsRequest.Add(0x1);
dnsRequest.Add(0x0);
dnsRequest.Add(0x1);
int32 sent;
socket->SendTo((uint8*)dnsRequest.GetData(), dnsRequest.Num(), sent, *addr);
}
}
}
void UDNSClientSocketServer::UDPReceiver(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt) {
//TSharedPtr<FInternetAddr> peerAddr = EndPt.ToInternetAddr();
//FString ip = peerAddr->ToString(false);
//int32 port = peerAddr->GetPort();
////UE_LOG(LogTemp, Error, TEXT("peerAddr:%s port:%i"), *peerAddr->ToString(false), peerAddr->GetPort());
TArray<uint8> byteArray;
byteArray.Append(ArrayReaderPtr->GetData(), ArrayReaderPtr->Num());
FString ipAdress;
if (byteArray.Num() > 10) {
for (int32 i = (byteArray.Num() - 4); i < byteArray.Num(); i++) {
uint32 ipTmp = byteArray.GetData()[i] << 0;
ipAdress += FString::FromInt(ipTmp)+".";
}
}
ipAdress.RemoveFromEnd(".");
//UE_LOG(LogTemp, Display, TEXT("DNS Resolved IP:%s"), *recvMessage);
ip = ipAdress;
dnsCache.Add(domain, ip);
resolving = false;
UDNSClientSocketServer* self = this;
AsyncTask(ENamedThreads::GameThread, [ipAdress,self]() {
self->onresolveDomainEventDelegate.Broadcast(ipAdress);
});
}
bool UDNSClientSocketServer::isResloving(){
return resolving;
}
FString UDNSClientSocketServer::getIP(){
return ip;
}
@@ -0,0 +1,14 @@
// Copyright 2018-2020 David Romanski (Socke). All Rights Reserved.
#include "EventBean.h"
UEventBean::UEventBean(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {
}
//void UEventBean::JavascriptEventTag(const FString ID, const FString ClassName, const FString Value, const TArray<FString>& args) {}
void UEventBean::registeredEventDelegate(const FString message, const TArray<uint8>& byteArray)
{
}
@@ -0,0 +1,428 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#include "FileFunctionsSocketServer.h"
UFileFunctionsSocketServer* UFileFunctionsSocketServer::fileFunctionsSocketServer;
UFileFunctionsSocketServer::UFileFunctionsSocketServer(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {
fileFunctionsSocketServer = this;
}
UFileFunctionsSocketServer* UFileFunctionsSocketServer::getFileFunctionsSocketServerTarget() {
return fileFunctionsSocketServer;
}
FString UFileFunctionsSocketServer::getCleanDirectory(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
if (directoryType == EFileFunctionsSocketServerDirectoryType::E_ad) {
return FPaths::ConvertRelativePathToFull(filePath);
}
else {
FString ProjectDir = FPaths::ProjectDir();
return FPaths::ConvertRelativePathToFull(ProjectDir + filePath);
}
}
void UFileFunctionsSocketServer::writeBytesToFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, TArray<uint8> bytes, bool& success) {
success = FFileHelper::SaveArrayToFile(bytes, *getCleanDirectory(directoryType, filePath));
}
void UFileFunctionsSocketServer::addBytesToFileAndCloseIt(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, TArray<uint8> bytes, bool& success) {
FArchive* writer = IFileManager::Get().CreateFileWriter(*getCleanDirectory(directoryType, filePath), EFileWrite::FILEWRITE_Append);
if (!writer) {
success = false;
return;
}
writer->Seek(writer->TotalSize());
writer->Serialize(bytes.GetData(), bytes.Num());
writer->Close();
delete writer;
success = true;
}
//void UFileFunctionsSocketServer::splittFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, int32 parts, bool& success){
// if (parts <= 0)
// parts = 1;
// FArchive* reader = IFileManager::Get().CreateFileReader(*getCleanDirectory(directoryType, filePath));
// if (!reader) {
// success = false;
// return;
// }
//
// int64 splittAfterBytes = reader->TotalSize()/ ((int64)parts);
// TArray<uint8> bytes;
//
// for (int32 i = 0; i < parts; i++){
// bytes.AddUninitialized(splittAfterBytes);
// reader->Serialize(bytes.GetData(), splittAfterBytes);
// if (FFileHelper::SaveArrayToFile(bytes, *getCleanDirectory(directoryType, filePath)) == false) {
// success = false;
// return;
// }
// splittAfterBytes =
// reader->Seek();
// }
//
//}
TArray<uint8> UFileFunctionsSocketServer::readBytesFromFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool& success) {
TArray<uint8> result;
success = FFileHelper::LoadFileToArray(result, *getCleanDirectory(directoryType, filePath));
return result;
}
void UFileFunctionsSocketServer::readStringFromFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool& success, FString& data) {
data.Empty();
success = FFileHelper::LoadFileToString(data, *getCleanDirectory(directoryType, filePath));
}
void UFileFunctionsSocketServer::writeStringToFile(EFileFunctionsSocketServerDirectoryType directoryType, FString data, FString filePath, EFileFunctionsSocketServerEncodingOptions fileEncoding, bool& success) {
success = FFileHelper::SaveStringToFile(data, *getCleanDirectory(directoryType, filePath), (FFileHelper::EEncodingOptions)fileEncoding);
}
void UFileFunctionsSocketServer::getMD5FromFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool& success, FString& MD5) {
getMD5FromFileAbsolutePath(getCleanDirectory(directoryType, filePath), success, MD5);
}
void UFileFunctionsSocketServer::getMD5FromFileAbsolutePath(FString filePath, bool& success, FString& MD5) {
MD5.Empty();
FArchive* reader = IFileManager::Get().CreateFileReader(*filePath);
if (!reader) {
success = false;
return;
}
TArray<uint8> byteArrayTmp;
int64 totalSize = reader->TotalSize();
int64 loadedBytes = 0;
int64 leftUploadBytes = 1024;
if (totalSize < leftUploadBytes)
leftUploadBytes = totalSize;
uint8 Digest[16];
FMD5 Md5Gen;
while ((loadedBytes + leftUploadBytes) <= totalSize) {
byteArrayTmp.Reset(leftUploadBytes);
byteArrayTmp.AddUninitialized(leftUploadBytes);
reader->Serialize(byteArrayTmp.GetData(), byteArrayTmp.Num());
loadedBytes += leftUploadBytes;
reader->Seek(loadedBytes);
Md5Gen.Update(byteArrayTmp.GetData(), byteArrayTmp.Num());
}
leftUploadBytes = totalSize - loadedBytes;
if (leftUploadBytes > 0) {
byteArrayTmp.Reset(leftUploadBytes);
byteArrayTmp.AddUninitialized(leftUploadBytes);
reader->Serialize(byteArrayTmp.GetData(), byteArrayTmp.Num());
loadedBytes += leftUploadBytes;
Md5Gen.Update(byteArrayTmp.GetData(), byteArrayTmp.Num());
}
if (reader != nullptr) {
reader->Close();
delete reader;
}
if (totalSize != loadedBytes) {
success = false;
return;
}
Md5Gen.Final(Digest);
for (int32 i = 0; i < 16; i++) {
MD5 += FString::Printf(TEXT("%02x"), Digest[i]);
}
success = true;
}
void UFileFunctionsSocketServer::stringToBase64String(FString string, FString& base64String) {
base64String.Empty();
FTCHARToUTF8 Convert(*string);
TArray<uint8> bytes;
bytes.Append(((uint8*)((ANSICHAR*)Convert.Get())), Convert.Length());
base64String = FBase64::Encode(bytes);
}
void UFileFunctionsSocketServer::base64StringToString(FString& string, FString base64String) {
string.Empty();
TArray<uint8> bytes;
if (FBase64::Decode(*base64String, bytes)) {
bytes.Add(0x00);// null-terminator
char* Data = (char*)bytes.GetData();
string = FString(UTF8_TO_TCHAR(Data));
}
}
void UFileFunctionsSocketServer::bytesToBase64String(TArray<uint8> bytes, FString& base64String) {
base64String.Empty();
base64String = FBase64::Encode(bytes);
}
TArray<uint8> UFileFunctionsSocketServer::base64StringToBytes(FString base64String, bool& success) {
TArray<uint8> fileData;
if (base64String.Len() % 2 != 0 || base64String.Len() < 4) {
success = false;
return fileData;
}
success = FBase64::Decode(*base64String, fileData);
return fileData;
}
void UFileFunctionsSocketServer::fileToBase64String(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool& success, FString& base64String, FString& fileName) {
base64String.Empty();
fileName.Empty();
TArray<uint8> fileData;
if (!FFileHelper::LoadFileToArray(fileData, *getCleanDirectory(directoryType, filePath))) {
success = false;
return;
}
base64String = FBase64::Encode(fileData);
success = true;
}
bool UFileFunctionsSocketServer::fileExists(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
return FPaths::FileExists(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketServer::fileExistsAbsolutePath(FString filePath) {
return FPaths::FileExists(*filePath);
}
bool UFileFunctionsSocketServer::directoryExists(EFileFunctionsSocketServerDirectoryType directoryType, FString path) {
return FPaths::DirectoryExists(*getCleanDirectory(directoryType, path));
}
int64 UFileFunctionsSocketServer::fileSize(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().FileSize(*getCleanDirectory(directoryType, filePath));
}
int64 UFileFunctionsSocketServer::fileSizeAbsolutePath(FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().FileSize(*filePath);
}
bool UFileFunctionsSocketServer::deleteFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketServer::deleteFileAbsolutePath(FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*filePath);
}
bool UFileFunctionsSocketServer::deleteDirectory(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().DeleteDirectory(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketServer::isReadOnly(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().IsReadOnly(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketServer::moveFile(EFileFunctionsSocketServerDirectoryType directoryTypeTo, FString filePathTo, EFileFunctionsSocketServerDirectoryType directoryTypeFrom, FString filePathFrom) {
return FPlatformFileManager::Get().GetPlatformFile().MoveFile(*getCleanDirectory(directoryTypeTo, filePathTo), *getCleanDirectory(directoryTypeFrom, filePathFrom));
}
bool UFileFunctionsSocketServer::setReadOnly(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool bNewReadOnlyValue) {
return FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*getCleanDirectory(directoryType, filePath), bNewReadOnlyValue);
}
FDateTime UFileFunctionsSocketServer::getTimeStamp(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().GetTimeStamp(*getCleanDirectory(directoryType, filePath));
}
void UFileFunctionsSocketServer::setTimeStamp(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, FDateTime DateTime) {
FPlatformFileManager::Get().GetPlatformFile().SetTimeStamp(*getCleanDirectory(directoryType, filePath), DateTime);
}
FDateTime UFileFunctionsSocketServer::getAccessTimeStamp(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().GetAccessTimeStamp(*getCleanDirectory(directoryType, filePath));
}
FString UFileFunctionsSocketServer::getFilenameOnDisk(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().GetFilenameOnDisk(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketServer::createDirectory(EFileFunctionsSocketServerDirectoryType directoryType, FString path) {
return FPlatformFileManager::Get().GetPlatformFile().CreateDirectory(*getCleanDirectory(directoryType, path));
}
void UFileFunctionsSocketServer::getAllFilesFromDirectory(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, int32& count, TArray<FString>& files, TArray<FString>& filePaths, FString fileType) {
if (filePath.Len() < 1) {
return;
}
files.Empty();
filePaths.Empty();
FString dir = getCleanDirectory(directoryType, filePath);
FPaths::NormalizeDirectoryName(filePath);
if (!FPaths::DirectoryExists(dir)) {
return;
}
IFileManager& FileManager = IFileManager::Get();
dir += "/" + fileType;
FileManager.FindFiles(files, *dir, true, false);
filePath += "/";
for (int32 i = 0; i < files.Num(); i++) {
filePaths.Add((filePath + files[i]));
//UE_LOG(LogTemp, Display, TEXT("->%s"), *files[i]);
}
count = files.Num();
}
FFileFunctionsSocketServerOpenFile UFileFunctionsSocketServer::openFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
FArchive* writer = IFileManager::Get().CreateFileWriter(*getCleanDirectory(directoryType, filePath), EFileWrite::FILEWRITE_Append);
FFileFunctionsSocketServerOpenFile file;
file.writer = writer;
return file;
}
int64 UFileFunctionsSocketServer::addBytesToFile(FFileFunctionsSocketServerOpenFile openFile, TArray<uint8> bytes) {
//UE_LOG(LogTemp, Warning, TEXT("xxxxx WRITE: %i"), bytes.Num());
if (openFile.writer != nullptr) {
openFile.writer->Seek(openFile.writer->TotalSize());
openFile.writer->Serialize(bytes.GetData(), bytes.Num());
//openFile.writer->Flush();
return openFile.writer->TotalSize();
}
return 0;
}
void UFileFunctionsSocketServer::closeFile(FFileFunctionsSocketServerOpenFile openFile) {
if (openFile.writer != nullptr) {
openFile.writer->Close();
openFile.writer = nullptr;
}
}
FString UFileFunctionsSocketServer::encryptMessageWithAES(FString message, FString key) {
if (message.IsEmpty() || key.Len() != 32) {
return FString();
}
TArray<uint8> data = FStringToByteArray(message);
data.Add(0x00);// null-terminator
const int64 encryptedFileSize = Align(data.Num(), FAES::AESBlockSize);
if (data.Num() < encryptedFileSize) {
data.AddUninitialized(encryptedFileSize - data.Num());
}
FAES::EncryptData(data.GetData(), encryptedFileSize, TCHAR_TO_ANSI(*key));
FString encryptedBase64String = FString();
bytesToBase64String(data, encryptedBase64String);
return encryptedBase64String;
}
FString UFileFunctionsSocketServer::decryptMessageWithAES(FString message, FString key) {
if (message.IsEmpty() || key.Len() != 32) {
UE_LOG(LogTemp, Error, TEXT("decryptMessageFromAES: Wrong key length."));
return FString();
}
bool success = false;
TArray<uint8> data = base64StringToBytes(message, success);
const int64 encryptedFileSize = Align(data.Num(), FAES::AESBlockSize);
if (data.Num() < encryptedFileSize) {
UE_LOG(LogTemp, Error, TEXT("decryptMessageFromAES: Wrong string length. This is not an AES encrypted string."));
return FString();
}
FAES::DecryptData(data.GetData(), encryptedFileSize, TCHAR_TO_ANSI(*key));
return FString(UTF8_TO_TCHAR((char*)data.GetData()));
}
TArray<uint8> UFileFunctionsSocketServer::FStringToByteArray(FString s) {
FTCHARToUTF8 Convert(*s);
TArray<uint8> data;
data.Append((uint8*)Convert.Get(), Convert.Length());
return data;
}
FString UFileFunctionsSocketServer::int64ToString(int64 Num) {
FString str = FString();
const TCHAR* DigitToChar = TEXT("9876543210123456789");
constexpr int64 ZeroDigitIndex = 9;
bool bIsNumberNegative = Num < 0;
const int64 TempBufferSize = 32; // 32 is big enough
TCHAR TempNum[TempBufferSize];
int64 TempAt = TempBufferSize; // fill the temp string from the top down.
// Convert to string assuming base ten.
do
{
TempNum[--TempAt] = DigitToChar[ZeroDigitIndex + (Num % 10)];
Num /= 10;
} while (Num);
if (bIsNumberNegative)
{
TempNum[--TempAt] = TEXT('-');
}
const TCHAR* CharPtr = TempNum + TempAt;
const int64 NumChars = TempBufferSize - TempAt;
str.Append(CharPtr, NumChars);
return str;
}
void UFileFunctionsSocketServer::readBytesFromFileInPartsAsync(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, int32 bufferSize, float delayBetweenReadsInSeconds) {
UFileFunctionsSocketServer::getFileFunctionsSocketServerTarget()->readBytesFromFileInPartsAsyncInternal(directoryType, filePath, bufferSize, delayBetweenReadsInSeconds);
}
void UFileFunctionsSocketServer::readBytesFromFileInPartsAsyncInternal(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, int32 bufferSize, float delayBetweenReadsInSeconds) {
FString dir = UFileFunctionsSocketServer::getCleanDirectory(directoryType, filePath);
if (readFileInPartsThreads.Find(*dir) != nullptr) {
UE_LOG(LogTemp, Warning, TEXT("ReadBytesFromFileInPartsAsync: %s is being read already. Operation canceled."), *dir);
return;
}
FReadFileInPartsSocketServerThread* readThread = new FReadFileInPartsSocketServerThread(dir, bufferSize, delayBetweenReadsInSeconds);
readFileInPartsThreads.Add(dir, readThread);
}
void UFileFunctionsSocketServer::cancelReadBytesFromFileInParts(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
UFileFunctionsSocketServer::getFileFunctionsSocketServerTarget()->cancelReadBytesFromFileInPartsInternal(directoryType, filePath);
}
void UFileFunctionsSocketServer::cancelReadBytesFromFileInPartsInternal(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath) {
FString dir = UFileFunctionsSocketServer::getCleanDirectory(directoryType, filePath);
if (readFileInPartsThreads.Find(*dir) != nullptr) {
(*readFileInPartsThreads.Find(*dir))->stopThread();
}
}
void UFileFunctionsSocketServer::cleanReadBytesFromFileInParts(FString cleanDir) {
if (readFileInPartsThreads.Find(*cleanDir) != nullptr) {
readFileInPartsThreads.Remove(*cleanDir);
}
}
@@ -0,0 +1,207 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#include "RCONServer.h"
URCONServer::URCONServer(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {
}
void URCONServer::startRCONServer(FString serverID, ERCONPasswordType passwordTypeP, FString passwordOrFileP
, bool& success, FString& errorMessage) {
success = false;
errorMessage = "";
if (serverID.IsEmpty() || USocketServerBPLibrary::getSocketServerTarget()->getTcpServerMap().Find(serverID) == nullptr) {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): ServerID not found: %s"), *serverID);
success = false;
errorMessage = "ServerID not found: "+serverID;
return;
}
passwordType = passwordTypeP;
if (passwordType != ERCONPasswordType::E_parameter) {
EFileFunctionsSocketServerDirectoryType passwordDirectoryType = EFileFunctionsSocketServerDirectoryType::E_gd;
if (passwordType == ERCONPasswordType::E_ad) {
passwordDirectoryType = EFileFunctionsSocketServerDirectoryType::E_ad;
}
passwordOrFile = UFileFunctionsSocketServer::getCleanDirectory(passwordDirectoryType, passwordOrFileP);
if (FPaths::DirectoryExists(FPaths::GetPath(passwordOrFile)) == false) {
UFileFunctionsSocketServer::createDirectory(passwordDirectoryType, FPaths::GetPath(passwordOrFile));
}
if (FPaths::FileExists(passwordOrFile) == false) {
UFileFunctionsSocketServer::writeStringToFile(passwordDirectoryType, "", passwordOrFile, EFileFunctionsSocketServerEncodingOptions::E_AutoDetect, success);
}
if (FPaths::FileExists(passwordOrFile) == false) {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): Can't read the password file: %s"), *passwordOrFile);
success = false;
errorMessage = "Can't read the password file: " + passwordOrFile;
return;
}
}
else {
passwordOrFile = passwordOrFileP;
}
success = true;
}
void URCONServer::receiveTCPMessageEvent(const FString sessionID, const FString message, const TArray<uint8>& byteArray, const FString serverID) {
USocketServerTCP* tcpServer = *USocketServerBPLibrary::getSocketServerTarget()->getTcpServerMap().Find(serverID);
if (tcpServer == nullptr) {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): Server not found: %s"), *serverID);
return;
}
//https://developer.valvesoftware.com/wiki/Source_RCON_Protocol
//packet to small
if (byteArray.Num() < 14) {
tcpServer->removeClientSession(sessionID);
return;
}
//packet to big
if (byteArray.Num() > 4096) {
tcpServer->removeClientSession(sessionID);
return;
}
int32 size = 0;
FMemory::Memcpy(&size, byteArray.GetData(), 4);
//packet to small
if (size < 10) {
tcpServer->removeClientSession(sessionID);
return;
}
int32 id = 0;
FMemory::Memcpy(&id, byteArray.GetData() + 4, 4);
int32 type = 0;
FMemory::Memcpy(&type, byteArray.GetData() + 8, 4);
TArray<uint8> bodyTextArray;
bodyTextArray.AddUninitialized(byteArray.Num()-12);
FMemory::Memcpy(bodyTextArray.GetData(), byteArray.GetData() + 12, byteArray.Num() - 12);
FString bodyText = FString(UTF8_TO_TCHAR(bodyTextArray.GetData()));
bodyTextArray.Empty();
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): size:%i id:%i type:%i body:%s"),size, id,type,*bodyText);
switch (type)
{
case 0:
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): Server response sent to the server? Request will be ignored."));
break;
case 2:
USocketServerBPLibrary::getSocketServerTarget()->onreceiveRCONRequestEventDelegate.Broadcast(sessionID,serverID,id, bodyText);
break;
case 3:
authResponse(sessionID, serverID, bodyText, id);
break;
default:
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): Unsupported request. Connection is closed."));
tcpServer->removeClientSession(sessionID);
return;
}
}
void URCONServer::authResponse(FString sessionID, FString serverID, FString password, int32 rconID){
//check password
if (password.IsEmpty() || passwordOrFile.IsEmpty()) {
rconID = -1;
sendResponse(sessionID, serverID, -1, 2, FString());
return;
}
bool found = false;
if (passwordType == ERCONPasswordType::E_parameter) {
if (password.Equals(passwordOrFile)) {
found = true;
}
}
else {
FString passwordData = FString();
FFileHelper::LoadFileToString(passwordData, *passwordOrFile);
TArray<FString> passwords;
passwordData.ParseIntoArray(passwords, TEXT("\n"), true);
for (int32 i = 0; i < passwords.Num(); i++)
{
if (passwords[i].TrimEnd().Equals(password)) {
found = true;
break;
}
}
}
if (found) {
sendResponse(sessionID, serverID, rconID, 2, FString());
}
else {
sendResponse(sessionID, serverID, -1, 2, FString());
USocketServerTCP* tcpServer = *USocketServerBPLibrary::getSocketServerTarget()->getTcpServerMap().Find(serverID);
if (tcpServer != nullptr) {
tcpServer->removeClientSession(sessionID);
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): Wrong password. Connection is closed."));
}
}
}
bool URCONServer::sendResponse(FString sessionID, FString serverID, int32 id, int32 type, FString body){
USocketServerTCP* tcpServer = *USocketServerBPLibrary::getSocketServerTarget()->getTcpServerMap().Find(serverID);
if (tcpServer == nullptr) {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): Server not found. Can't send response: %s"), *serverID);
return false;
}
FTCHARToUTF8 Convert(*body);
//4 (id) + 4 (type) + 1 (minimum body) + 1 (null terminator) = 10
int32 size = 10;
TArray<uint8> response;
size += Convert.Length();
response.AddZeroed(size + 4); // 4 = the size itself as int32
FMemory::Memcpy(response.GetData(), &size, 4);
FMemory::Memcpy(response.GetData() + 4, &id, 4);
FMemory::Memcpy(response.GetData() + 8, &type, 4);
if (Convert.Length() > 0) {
FMemory::Memcpy(response.GetData() + 12, Convert.Get(), Convert.Length());
}
//packet to big
if (response.Num() > 4096) {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin (RCON): Answer too big. A data packet may have a maximum size of 4096 bytes. Size is %i"), response.Num());
return false;
}
tcpServer->sendTCPMessageToClient(sessionID, FString(), response, false);
return true;
}
@@ -0,0 +1,22 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#include "SocketServer.h"
#define LOCTEXT_NAMESPACE "FSocketServerModule"
void FSocketServerModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FSocketServerModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FSocketServerModule, SocketServer)
@@ -0,0 +1,66 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketServerCleanerThread.h"
FSocketServerCleanerThread::FSocketServerCleanerThread() {
FString threadName = "FSocketServerPluginCleanerThread_" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_Lowest);
}
void FSocketServerCleanerThread::addSession(FSocketServerPluginSession& session) {
session.addToCleanerTime = FDateTime::Now().GetTicks();
sessionQueue.Enqueue(session);
}
void FSocketServerCleanerThread::changeSettings(bool showLogsP, int32 minLiveTimeInSecondsP){
showLogs = showLogsP;
minLiveTimeInSeconds = minLiveTimeInSecondsP;
}
uint32 FSocketServerCleanerThread::Run() {
while (true) {
TArray<FSocketServerPluginSession> tryItAgain;
while (sessionQueue.IsEmpty() == false) {
FSocketServerPluginSession session;
sessionQueue.Dequeue(session);
//if (session.recieverThread == nullptr && session.sendThread == nullptr) {
// continue;
//}
//one second = 10000000 ticks
if ((FDateTime::Now().GetTicks() - session.addToCleanerTime) < (10000000 * minLiveTimeInSeconds)) {
tryItAgain.Add(session);
continue;
}
if (showLogs) {
UE_LOG(LogTemp, Display, TEXT("SocketServer: Clean Session: %s"), *session.sessionID);
}
delete session.tcpSendThread;
delete session.tcpRecieverThread;
delete session.tcpFileHandlerThread;
delete session.udpServerThread;
delete session.udpSocketReceiver;
delete session.udpSendThread;
delete session.socket;
}
for (int32 i = 0; i < tryItAgain.Num(); i++) {
sessionQueue.Enqueue(tryItAgain[i]);
}
tryItAgain.Empty();
FPlatformProcess::Sleep(minLiveTimeInSeconds);
}
return 0;
};
@@ -0,0 +1,259 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#include "SocketServerTCP.h"
USocketServerTCP::USocketServerTCP(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {
this->AddToRoot();
/*UE_LOG(LogTemp, Warning, TEXT("USocketServerPluginTCPServer 1"));*/
}
void USocketServerTCP::startTCPServer(FIPandPortStruct ipStructP,FString IPPP, int32 portP, EReceiveFilterServer receiveFilterP,
ESocketServerTCPSeparator messageWrappingP, FString serverIDP, bool isFileServer, FString Aes256bitKeyP, bool resumeFilesP, bool writeHandShakeToLogEditorOnlyP) {
ipAndPortStruct = ipStructP;
serverPort = portP;
receiveFilter = receiveFilterP;
serverID = serverIDP;
fileServer = isFileServer;
aesKey = Aes256bitKeyP;
resumeFiles = resumeFilesP;
messageWrapping = messageWrappingP;
writeHandShakeToLogEditorOnly = writeHandShakeToLogEditorOnlyP;
USocketServerBPLibrary::socketServerBPLibrary->getTcpSeparator(tcpByteSeparator, tcpStringSeparator);
socketServerTCPThread = new FSocketServerTCPThread(this, receiveFilter,run);
}
void USocketServerTCP::stopTCPServer() {
TArray<FString> toRemoveSessionKeys;
for (auto& element : getClientSessions()) {
toRemoveSessionKeys.Add(element.Key);
}
for (int32 i = 0; i < toRemoveSessionKeys.Num(); i++) {
removeClientSession(toRemoveSessionKeys[i]);
}
toRemoveSessionKeys.Empty();
run = false;
if (socketServerTCPThread != nullptr) {
socketServerTCPThread->stopThread();
delete socketServerTCPThread;
socketServerTCPThread = nullptr;
}
}
void USocketServerTCP::sendTCPMessage(TArray<FString> clientSessionIDs, FString message, TArray<uint8> byteArray, bool addLineBreak){
if (message.Len() > 0) {
//if (messageWrapping == ESocketServerTCPSeparator::E_StringSeparator) {
// message = tcpMessageHeader + message + tcpMessageFooter;
//}
if (addLineBreak) {
message.Append("\r\n");
}
}
for (auto& sessionID : clientSessionIDs) {
if (clientSessions.Find(sessionID) != nullptr) {
FSocketServerPluginSession& session = *clientSessions.Find(sessionID);
if (session.protocol == EServerSocketConnectionProtocol::E_TCP) {
if (session.tcpSendThread == nullptr) {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin: The thread for sending data has not yet been initialized. Data is not sent."));
}
else {
session.tcpSendThread->sendMessage(message, byteArray);
}
}
}
}
}
void USocketServerTCP::sendTCPMessageToClient(FString clientSessionID, FString message, TArray<uint8> byteArray, bool addLineBreak) {
if (message.Len() > 0) {
//if (messageWrapping == ESocketServerTCPSeparator::E_StringSeparator) {
// message = tcpMessageHeader + message + tcpMessageFooter;
//}
if (addLineBreak) {
message.Append("\r\n");
}
}
if (clientSessions.Find(clientSessionID) != nullptr) {
FSocketServerPluginSession& session = *clientSessions.Find(clientSessionID);
if (session.protocol == EServerSocketConnectionProtocol::E_TCP) {
if (session.tcpSendThread == nullptr) {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin: The thread for sending data has not yet been initialized. Data is not sent."));
}
else {
session.tcpSendThread->sendMessage(message, byteArray);
}
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin: Session not found: %s"),*clientSessionID);
}
}
FIPandPortStruct USocketServerTCP::getServerIpAndPortStruct() {
return ipAndPortStruct;
}
FString USocketServerTCP::getIP() {
return serverIP;
}
int32 USocketServerTCP::getPort() {
return serverPort;
}
FString USocketServerTCP::getServerID() {
return serverID;
}
bool USocketServerTCP::hasResume() {
return resumeFiles;
}
void USocketServerTCP::initTCPClientThreads(FSocketServerPluginSession& sessionP, EReceiveFilterServer receiveFilterP){
if (fileServer) {
sessionP.tcpFileHandlerThread = new FSocketServerTCPFileHandlerThread(this, sessionP);
//tcpFileHandlerThread = new FSocketServerTCPFileHandlerThread(this, sessionP);
}
else {
sessionP.tcpRecieverThread = new FSocketServerTCPClientReceiveDataThread(this, sessionP, receiveFilterP);
sessionP.tcpSendThread = new FSocketServerTCPClientSendDataThread(this, sessionP);
}
}
void USocketServerTCP::addClientSession(FSocketServerPluginSession& session) {
if (session.sessionID.IsEmpty() == false) {
//UE_LOG(LogTemp, Warning, TEXT("ADD Session:%s"), *session.sessionID);
clientSessions.Add(session.sessionID, session);
}
}
//FClientSocketSession USocketServerPluginTCPServer::getClientSession(FString key) {
// if (clientSessions.Find(key) == nullptr)
// return FClientSocketSession();
// return *clientSessions.Find(key);
//}
void USocketServerTCP::removeClientSession(FString key) {
//close client socket
if (clientSessions.Find(key) != nullptr) {
FSocketServerPluginSession& session = *clientSessions.Find(key);
if (session.tcpRecieverThread != nullptr) {
session.tcpRecieverThread->stopThread();
}
if (session.tcpSendThread != nullptr) {
session.tcpSendThread->stopThread();
}
if (session.tcpFileHandlerThread != nullptr) {
session.tcpFileHandlerThread->stopThread();
}
USocketServerBPLibrary::socketServerBPLibrary->cleanConnection(session);
}
if (clientSessions.Remove(key)) {
//UE_LOG(LogTemp, Warning, TEXT("Remove3 Session:%s"), *key);
USocketServerBPLibrary::socketServerBPLibrary->unregisterClientEvent(key);
}
}
TMap<FString, FSocketServerPluginSession> USocketServerTCP::getClientSessions(){
return clientSessions;
}
//EHTTPSocketServerFileDownloadResumeType USocketServerPluginTCPServer::getifFileExistThen(){
// return ifFileExistThen;
//}
FString USocketServerTCP::encryptMessage(FString message) {
//#if WITH_EDITOR
// if (writeHandShakeToLogEditorOnly) {
UE_LOG(LogTemp, Warning, TEXT("TCP File Server Handshake: %s"),*message);
// }
//#endif
return UFileFunctionsSocketServer::encryptMessageWithAES(message, aesKey);
}
FString USocketServerTCP::decryptMessage(FString message) {
message = UFileFunctionsSocketServer::decryptMessageWithAES(message, aesKey);
//#if WITH_EDITOR
// if (writeHandShakeToLogEditorOnly) {
UE_LOG(LogTemp, Warning, TEXT("TCP File Server Handshake: %s"),*message);
// }
//#endif
return message;
}
struct FSocketServerToken USocketServerTCP::getTokenStruct(FString token){
FSocketServerToken sst;
if (USocketServerBPLibrary::socketServerBPLibrary->fileTokenMap.Find(token) != nullptr) {
sst = *USocketServerBPLibrary::socketServerBPLibrary->fileTokenMap.Find(token);
}
return sst;
}
void USocketServerTCP::removeTokenFromStruct(FString token) {
if (USocketServerBPLibrary::socketServerBPLibrary->fileTokenMap.Find(token) != nullptr) {
USocketServerBPLibrary::socketServerBPLibrary->fileTokenMap.Remove(token);
}
}
FString USocketServerTCP::getCleanDir(EFileFunctionsSocketServerDirectoryType directoryType, FString fileDirectory) {
return UFileFunctionsSocketServer::getCleanDirectory(directoryType, fileDirectory);
}
void USocketServerTCP::getMD5FromFile(FString filePathP, bool& success, FString& MD5) {
UFileFunctionsSocketServer::getMD5FromFileAbsolutePath(filePathP, success, MD5);
}
void USocketServerTCP::deleteFile(FString filePathP) {
UFileFunctionsSocketServer::deleteFileAbsolutePath(filePathP);
}
bool USocketServerTCP::isRun(){
return run;
}
int64 USocketServerTCP::fileSize(FString filePathP) {
return UFileFunctionsSocketServer::fileSizeAbsolutePath(filePathP);
}
FString USocketServerTCP::int64ToString(int64 num) {
return UFileFunctionsSocketServer::int64ToString(num);
}
void USocketServerTCP::getTcpSeparator(FString& stringSeparator, uint8& byteSeparator, ESocketServerTCPSeparator& messageWrappingP) {
messageWrappingP = messageWrapping;
stringSeparator = tcpStringSeparator;
tcpByteSeparator = byteSeparator;
}
void USocketServerTCP::readDataLength(TArray<uint8>& byteDataArray, int32& byteLenght) {
if (FGenericPlatformProperties::IsLittleEndian() && byteDataArray[0] == 0x00) {
FMemory::Memcpy(&byteLenght, byteDataArray.GetData() + 1, 4);
}
else {
//endian fits not. swap bytes that contains the length
byteDataArray.SwapMemory(1, 4);
byteDataArray.SwapMemory(2, 3);
FMemory::Memcpy(&byteLenght, byteDataArray.GetData() + 1, 4);
}
}
@@ -0,0 +1,245 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketServerTCPClientReceiveDataThread.h"
FSocketServerTCPClientReceiveDataThread::FSocketServerTCPClientReceiveDataThread(USocketServerTCP* tcpServerP,
FSocketServerPluginSession& sessionP,
EReceiveFilterServer receiveFilterP) :
tcpServer(tcpServerP),
session(sessionP),
receiveFilter(receiveFilterP){
FString threadName = "FTCPClientReceiveDataFromServerThread" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_BelowNormal);
}
FSocketServerTCPClientReceiveDataThread::~FSocketServerTCPClientReceiveDataThread() {
delete thread;
//thread = nullptr;
}
uint32 FSocketServerTCPClientReceiveDataThread::Run() {
//FPlatformProcess::Sleep(0.1);
//tcpServer->removeClientSession(session.sessionID);
FString serverID = tcpServer->getServerID();
FSocket* clientSocket = session.socket;
FString sessionID = session.sessionID;
//message wrapping
FString stringSeparator = FString();
uint8 byteSeparator = 0x00;
ESocketServerTCPSeparator messageWrapping = ESocketServerTCPSeparator::E_None;
tcpServer->getTcpSeparator(stringSeparator, byteSeparator, messageWrapping);
TArray<TCHAR> stringSeparatorArray = stringSeparator.GetCharArray();
if (stringSeparatorArray.Num() > 0 && stringSeparatorArray.Last() == 0x00) {
stringSeparatorArray.RemoveAt(stringSeparatorArray.Num() - 1, 1, true);
}
if (messageWrapping == ESocketServerTCPSeparator::E_LengthSeparator && stringSeparatorArray.Num() == 0) {
messageWrapping = ESocketServerTCPSeparator::E_None;
UE_LOG(LogTemp, Warning, TEXT("Socket Sever Plugin: Separator mode is set to String but there is no String Separator. Mode changed to none."));
}
//switch to gamethread
AsyncTask(ENamedThreads::GameThread, [sessionID, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerConnectionEventDelegate.Broadcast(EServerSocketConnectionEventType::E_Client, true, "Client connected", sessionID, serverID);
});
uint32 DataSize = 0;
//FArrayReaderPtr Datagram = MakeShareable(new FArrayReader(true));
TArray<uint8> dataFromSocket;
int64 ticks1;
int64 ticks2;
TArray<uint8> byteDataArray;
TArray<uint8> byteDataArrayCache;
bool hasData = false;
int32 lastDataLengthFromHeader = 0;
while (run && clientSocket != nullptr && tcpServer->isRun()) {
//ESocketConnectionState::SCS_Connected does not work https://issues.unrealengine.com/issue/UE-27542
//Compare ticks is a workaround to get a disconnect. clientSocket->Wait() stop working after disconnect. (Another bug?)
//If it doesn't wait any longer, ticks1 and ticks2 should be the same == disconnect.
ticks1 = FDateTime::Now().GetTicks();
clientSocket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(1));
ticks2 = FDateTime::Now().GetTicks();
hasData = clientSocket->HasPendingData(DataSize);;
/* if (tcpServer->isRun()) {
hasData = clientSocket->HasPendingData(DataSize);
}
else {
deathConnection = true;
}*/
if (!hasData && ticks1 == ticks2) {
deathConnection = true;
//UE_LOG(LogTemp, Display, TEXT("TCP End xxx: %s:%i"), *session.ip, session.port);
break;
}
if (hasData) {
dataFromSocket.SetNumUninitialized(DataSize);
int32 BytesRead = 0;
if (clientSocket->Recv(dataFromSocket.GetData(), dataFromSocket.Num(), BytesRead)) {
switch (messageWrapping)
{
case ESocketServerTCPSeparator::E_None:
triggerMessageEvent(dataFromSocket, sessionID, serverID);
break;
case ESocketServerTCPSeparator::E_ByteSeparator:
for (int32 i = 0; i < dataFromSocket.Num(); i++) {
byteDataArrayCache.Add(dataFromSocket[i]);
if (dataFromSocket[i] == byteSeparator) {
triggerMessageEvent(byteDataArrayCache, sessionID, serverID, false);
byteDataArrayCache.Empty();
}
}
break;
case ESocketServerTCPSeparator::E_StringSeparator:
for (int32 i = 0; i < dataFromSocket.Num(); i++) {
;
if ((TCHAR)dataFromSocket[i] == stringSeparatorArray[0]) {
bool found = true;
for (int32 j = 1; j < stringSeparatorArray.Num(); j++) {
i++;
if (dataFromSocket.Num() <= (i)) {
found = false;
break;
}
if ((TCHAR)dataFromSocket[i] != stringSeparatorArray[j]) {
found = false;
break;
}
}
if (found) {
triggerMessageEvent(byteDataArrayCache, sessionID, serverID, true);
byteDataArrayCache.Empty();
}
}
else {
byteDataArrayCache.Add(dataFromSocket[i]);
}
}
break;
case ESocketServerTCPSeparator::E_LengthSeparator:
if (lastDataLengthFromHeader == 0 && dataFromSocket.Num() >= 5) {
tcpServer->readDataLength(dataFromSocket, lastDataLengthFromHeader);
if (dataFromSocket.Num() == 5) {
dataFromSocket.Empty();
continue;
}
byteDataArrayCache.Append(dataFromSocket.GetData() + 5, dataFromSocket.Num() - 5);
dataFromSocket.Empty();
}
else {
byteDataArrayCache.Append(dataFromSocket.GetData(), dataFromSocket.Num());
}
int32 maxLoops = 1000;//to prevent endless loop
while (byteDataArrayCache.Num() > 0 && byteDataArrayCache.Num() >= lastDataLengthFromHeader && maxLoops > 0) {
maxLoops--;
byteDataArray.Append(byteDataArrayCache.GetData(), lastDataLengthFromHeader);
byteDataArrayCache.RemoveAt(0, lastDataLengthFromHeader, true);
triggerMessageEvent(byteDataArray, sessionID, serverID);
//UE_LOG(LogTemp, Display, TEXT("%s"), *mainMessage);
byteDataArray.Empty();
if (byteDataArrayCache.Num() == 0) {
lastDataLengthFromHeader = 0;
break;
}
if (byteDataArrayCache.Num() > 5) {
tcpServer->readDataLength(byteDataArrayCache, lastDataLengthFromHeader);
byteDataArrayCache.RemoveAt(0, 5, true);
}
}
break;
}
}
dataFromSocket.Empty();
}
}
if (!deathConnection && clientSocket != nullptr) {
//UE_LOG(LogTemp, Display, TEXT("TCP Close"));
clientSocket->Close();
}
//else {
// if (deathConnection) {
// UE_LOG(LogTemp, Display, TEXT("TCP2 Close true"));
// }
// else {
// UE_LOG(LogTemp, Display, TEXT("TCP2 Close false"));
// }
//}
//UE_LOG(LogTemp, Display, TEXT("TCP Connected: %s:%i"), *session.ip, session.port);
//switch to gamethread
USocketServerTCP* tcpServerGlobal = tcpServer;
AsyncTask(ENamedThreads::GameThread, [sessionID, serverID, tcpServerGlobal]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerConnectionEventDelegate.Broadcast(EServerSocketConnectionEventType::E_Client, false, "Client disconnected", sessionID, serverID);
//clean up socket in main thread because race condition
tcpServerGlobal->removeClientSession(sessionID);
});
dataFromSocket.Empty();
byteDataArray.Empty();
byteDataArrayCache.Empty();
return 0;
}
void FSocketServerTCPClientReceiveDataThread::triggerMessageEvent(TArray<uint8>& byteDataArray, FString& sessionID, FString& serverID, bool addNullTerminator) {
//if (receiveFilter == EReceiveFilterServer::E_SAB || receiveFilter == EReceiveFilterServer::E_B) {
// byteDataArray.Append(dataFromSocket.GetData(), dataFromSocket.Num());
//}
FString mainMessage = FString();
if (receiveFilter == EReceiveFilterServer::E_SAB || receiveFilter == EReceiveFilterServer::E_S) {
if (addNullTerminator)
byteDataArray.Add(0x00);// null-terminator
mainMessage = FString(UTF8_TO_TCHAR((char*)byteDataArray.GetData()));
if (receiveFilter == EReceiveFilterServer::E_S) {
byteDataArray.Empty();
}
}
//switch to gamethread
AsyncTask(ENamedThreads::GameThread, [mainMessage, sessionID, byteDataArray, serverID]() {
//UE_LOG(LogTemp, Display, TEXT("TCP:%s"), *recvMessage);
USocketServerBPLibrary::socketServerBPLibrary->onserverReceiveTCPMessageEventDelegate.Broadcast(sessionID, mainMessage, byteDataArray, serverID);
if (USocketServerBPLibrary::socketServerBPLibrary->getResiteredClientEvent(sessionID) != nullptr) {
USocketServerBPLibrary::socketServerBPLibrary->getResiteredClientEvent(sessionID)->onregisteredEventDelegate.Broadcast(mainMessage, byteDataArray);
}
});
mainMessage.Empty();
}
void FSocketServerTCPClientReceiveDataThread::stopThread() {
run = false;
Stop();
}
@@ -0,0 +1,205 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketServerTCPClientSendDataThread.h"
FSocketServerTCPClientSendDataThread::FSocketServerTCPClientSendDataThread(USocketServerTCP* tcpServerP, FSocketServerPluginSession& sessionP) :
tcpServer(tcpServerP),
session(sessionP) {
FString threadName = "FTCPClientSendDataToServerThread_" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_BelowNormal);
}
FSocketServerTCPClientSendDataThread::~FSocketServerTCPClientSendDataThread() {
delete thread;
}
uint32 FSocketServerTCPClientSendDataThread::Run() {
FString serverID = tcpServer->getServerID();
socket = session.socket;
FString sessionID = session.sessionID;
//message wrapping
FString stringSeparator = FString();
uint8 byteSeparator = 0x00;
ESocketServerTCPSeparator messageWrapping = ESocketServerTCPSeparator::E_None;
tcpServer->getTcpSeparator(stringSeparator, byteSeparator, messageWrapping);
TArray<TCHAR> stringSeparatorArray = stringSeparator.GetCharArray();
if (stringSeparatorArray.Num() > 0 && stringSeparatorArray.Last() == 0x00) {
stringSeparatorArray.RemoveAt(stringSeparatorArray.Num() - 1, 1, true);
}
if (messageWrapping == ESocketServerTCPSeparator::E_LengthSeparator && stringSeparatorArray.Num() == 0) {
messageWrapping = ESocketServerTCPSeparator::E_None;
UE_LOG(LogTemp, Warning, TEXT("Socket Sever Plugin: Separator mode is set to String but there is no String Separator. Mode changed to none."));
}
while (run) {
//Wait a bit in case someone tries to send something right after the connection is established to avoid hitting a dead connection.
if (waitForInit) {
waitForInit = false;
FPlatformProcess::Sleep(0.5);
}
if (socket == nullptr || tcpServer->clientSessions.Find(sessionID) == nullptr) {
//UE_LOG(LogTemp, Error, TEXT("Socket not found."));
return 0;
}
// try to connect to the server
if (socket == nullptr || run == false) {
//UE_LOG(LogTemp, Error, TEXT("Connection not exist."));
//switch to gamethread
if (messageQueue.IsEmpty() == false || byteArrayQueue.IsEmpty() == false) {
AsyncTask(ENamedThreads::GameThread, [sessionID, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerConnectionEventDelegate.Broadcast(EServerSocketConnectionEventType::E_Client, false, "Connection not exist", sessionID, serverID);
});
}
return 0;
}
int32 sent = 0;
if (socket != nullptr) {
while (messageQueue.IsEmpty() == false) {
FString m;
messageQueue.Dequeue(m);
FTCHARToUTF8 Convert(*m);
sent = 0;
TArray<uint8> byteCache;
switch (messageWrapping)
{
case ESocketServerTCPSeparator::E_None:
byteCache.Append((uint8*)Convert.Get(), Convert.Length());
break;
case ESocketServerTCPSeparator::E_ByteSeparator:
byteCache.Append((uint8*)Convert.Get(), Convert.Length());
byteCache.Add(byteSeparator);
break;
case ESocketServerTCPSeparator::E_StringSeparator:
{
m += stringSeparator;
FTCHARToUTF8 ConvertWithSeparator(*m);
byteCache.Append((uint8*)ConvertWithSeparator.Get(), ConvertWithSeparator.Length());
}
break;
case ESocketServerTCPSeparator::E_LengthSeparator:
if (FGenericPlatformProperties::IsLittleEndian()) {
byteCache.Add(0x00);
}
else {
byteCache.Add(0x01);
}
byteCache.SetNum(5);
int32 dataLength = Convert.Length();
FMemory::Memcpy(byteCache.GetData() + 1, &dataLength, 4);
byteCache.Append((uint8*)Convert.Get(), Convert.Length());
break;
}
socket->Send(byteCache.GetData(), byteCache.Num(), sent);
}
while (byteArrayQueue.IsEmpty() == false) {
TArray<uint8> byteCache;
byteArrayQueue.Dequeue(byteCache);
sent = 0;
switch (messageWrapping)
{
case ESocketServerTCPSeparator::E_ByteSeparator:
byteCache.Add(byteSeparator);
break;
case ESocketServerTCPSeparator::E_StringSeparator:
{
FTCHARToUTF8 ConvertWithSeparator(*stringSeparator);
byteCache.Append((uint8*)ConvertWithSeparator.Get(), ConvertWithSeparator.Length());
}
break;
case ESocketServerTCPSeparator::E_LengthSeparator:
byteCache.InsertZeroed(0, 5);
if (FGenericPlatformProperties::IsLittleEndian() == false) {
uint8 a = 0x01;
FMemory::Memcpy(byteCache.GetData(), &a, 1);
}
int32 dataLength = byteCache.Num() - 5;
FMemory::Memcpy(byteCache.GetData() + 1, &dataLength, 4);
break;
}
socket->Send(byteCache.GetData(), byteCache.Num(), sent);
}
}
else {
//UE_LOG(LogTemp, Error, TEXT("Connection lost"));
AsyncTask(ENamedThreads::GameThread, [sessionID, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerConnectionEventDelegate.Broadcast(EServerSocketConnectionEventType::E_Client, false, "Connection lost", sessionID, serverID);
});
}
if (run) {
pauseThread(true);
//workaround. suspend do not work on all platforms. lets sleep
while (paused && run) {
FPlatformProcess::Sleep(0.01);
}
}
}
run = false;
return 0;
}
FRunnableThread* FSocketServerTCPClientSendDataThread::getThread() {
return thread;
}
void FSocketServerTCPClientSendDataThread::setThread(FRunnableThread* threadP) {
thread = threadP;
}
void FSocketServerTCPClientSendDataThread::stopThread() {
run = false;
if (thread != nullptr) {
pauseThread(false);
}
Stop();
}
bool FSocketServerTCPClientSendDataThread::isRun() {
return run;
}
void FSocketServerTCPClientSendDataThread::setMessage(FString messageP, TArray<uint8> byteArrayP) {
if (messageP.Len() > 0)
messageQueue.Enqueue(messageP);
if (byteArrayP.Num() > 0)
byteArrayQueue.Enqueue(byteArrayP);
}
void FSocketServerTCPClientSendDataThread::sendMessage(FString messageP, TArray<uint8> byteArrayP) {
if (messageP.Len() > 0)
messageQueue.Enqueue(messageP);
if (byteArrayP.Num() > 0)
byteArrayQueue.Enqueue(byteArrayP);
pauseThread(false);
}
void FSocketServerTCPClientSendDataThread::pauseThread(bool pause) {
paused = pause;
thread->Suspend(pause);
}
@@ -0,0 +1,515 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketServerTCPFileHandlerThread.h"
FSocketServerTCPFileHandlerThread::FSocketServerTCPFileHandlerThread(USocketServerTCP* tcpServerP, FSocketServerPluginSession& sessionP) :
tcpServer(tcpServerP),
session(sessionP) {
FString threadName = "FTCPFileHandlerThread" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_BelowNormal);
}
FSocketServerTCPFileHandlerThread::~FSocketServerTCPFileHandlerThread() {
delete thread;
}
uint32 FSocketServerTCPFileHandlerThread::Run() {
FString message = readMessageFromClient();
//send file to client
if (message.StartsWith("REQUEST_FILE_FROM_SERVER_|_", ESearchCase::CaseSensitive)) {
message.RemoveFromStart("REQUEST_FILE_FROM_SERVER_|_", ESearchCase::CaseSensitive);
FSocketServerToken tokenStruct = tcpServer->getTokenStruct(message);
if (tokenStruct.token.IsEmpty() && tokenStruct.token.Equals(message) == false) {
triggerFileTransferOverTCPInfoEvent("Token not found.", session.sessionID, FString(), false);
}
else {
doRequestFileFromServer(tokenStruct);
}
}
else {
//get file from client
if (message.StartsWith("SEND_FILE_TO_SERVER_|_", ESearchCase::CaseSensitive)) {
doSendFileToClient(message);
}
}
run = false;
FPlatformProcess::Sleep(3);
if (session.socket != nullptr) {
session.socket->Close();
}
USocketServerTCP* tcpServerGlobal = tcpServer;
FString sessionID = session.serverID;
FString serverID = session.serverID;
AsyncTask(ENamedThreads::GameThread, [sessionID, serverID, tcpServerGlobal]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerConnectionEventDelegate.Broadcast(EServerSocketConnectionEventType::E_Client, false, "Client disconnected", sessionID, serverID);
//clean up socket in main thread because race condition
tcpServerGlobal->removeClientSession(sessionID);
});
return 0;
}
void FSocketServerTCPFileHandlerThread::doRequestFileFromServer(FSocketServerToken tokenStruct){
FString fullFilePath = tcpServer->getCleanDir(tokenStruct.directoryType, tokenStruct.fileDirectory);
if (FPaths::FileExists(fullFilePath) == false) {
triggerFileTransferOverTCPInfoEvent("File not found,", session.sessionID, fullFilePath, false);
return;
}
bool md5okay = false;
FString md5Server = FString();
tcpServer->getMD5FromFile(fullFilePath, md5okay, md5Server);
FString response = "REQUEST_FILE_FROM_SERVER_ACCEPTED_|_" + tokenStruct.token + "_|_" + md5Server + "_|_" + tcpServer->int64ToString(tcpServer->fileSize(fullFilePath)) + "_|_" + FPaths::GetCleanFilename(fullFilePath);
sendMessageToClient(response);
FString message = readMessageFromClient();
TArray<FString> lines;
message.ParseIntoArray(lines, TEXT("_|_"), true);
if (lines.Num() == 3) {
if (lines[0].Equals("REQUEST_FILE_FROM_SERVER_ACCEPTED") && lines[1].Equals(tokenStruct.token) && lines[2].Len() > 0) {
int64 startPosition = FCString::Atoi64(*lines[2]);
//download
FArchive* reader = IFileManager::Get().CreateFileReader(*fullFilePath);
if (reader == nullptr || reader->TotalSize() == 0) {
if (reader != nullptr) {
reader->Close();
}
delete reader;
return;
}
fileSize = reader->TotalSize();
int64 readSize = 0;
int64 lastPosition = startPosition;
int64 bytesSentSinceLastTick = 0;
int32 bufferSize = 1024 * 64;
int32 dataSendBySocket = 0;
float percent = 0.f;
float mbit = 0.f;
TArray<uint8> buffer;
int64 lastTimeTicks = FDateTime::Now().GetTicks();
if (bufferSize > fileSize) {
bufferSize = fileSize;
}
if (lastPosition > 0) {
reader->Seek(lastPosition);
}
while (run && lastPosition < fileSize) {
if ((lastPosition + bufferSize) > fileSize) {
bufferSize = fileSize - lastPosition;
}
//buffer.Reset(bufferSize);
buffer.Empty();
buffer.AddUninitialized(bufferSize);
reader->Serialize(buffer.GetData(), buffer.Num());
lastPosition += buffer.Num();
//socket->Send((uint8*)((ANSICHAR*)Convert.Get()), Convert.Length(), dataSendBySocket);
session.socket->Send(buffer.GetData(), buffer.Num(), dataSendBySocket);
//slowdown for tests
//FPlatformProcess::Sleep(0.01f);
//fire event every second
//one second = 10000000 ticks
if (((FDateTime::Now().GetTicks()) - lastTimeTicks) >= 10000000) {
mbit = ((float)lastPosition - (float)bytesSentSinceLastTick) / 1024 / 1024 * 8;
lastTimeTicks = FDateTime::Now().GetTicks();
bytesSentSinceLastTick = lastPosition;
percent = ((float)lastPosition / fileSize) * 100;
triggerFileOverTCPProgress(session.sessionID, fullFilePath, percent, mbit, lastPosition, fileSize);
}
}
if (lastPosition == fileSize) {
mbit = ((float)lastPosition - (float)bytesSentSinceLastTick) / 1024 / 1024 * 8;
percent = ((float)lastPosition / fileSize) * 100;
triggerFileOverTCPProgress(session.sessionID, fullFilePath, percent, mbit, lastPosition, fileSize);
}
else {
triggerFileTransferOverTCPInfoEvent("Error while sending the file.", session.sessionID, fullFilePath, false);
}
buffer.Empty();
if (reader != nullptr) {
reader->Close();
reader = nullptr;
}
message = readMessageFromClient();
lines.Empty();
message.ParseIntoArray(lines, TEXT("_|_"), true);
if (lines.Num() == 3) {
if (lines[0].Equals("REQUEST_FILE_FROM_SERVER_END") && lines[1].Equals(tokenStruct.token) && lines[2].Equals("OKAY")) {
triggerFileTransferOverTCPInfoEvent("File transfer successful.", session.sessionID, fullFilePath, true);
}
else {
triggerFileTransferOverTCPInfoEvent("File transfer failed.", session.sessionID, fullFilePath, false);
}
}
}
}
}
void FSocketServerTCPFileHandlerThread::doSendFileToClient(FString message){
TArray<FString> lines;
message.ParseIntoArray(lines, TEXT("_|_"), true);
if (lines.Num() == 5) {
if (lines[0].Equals("SEND_FILE_TO_SERVER") && lines[1].Len() > 0 && lines[2].Len() > 0 && lines[3].Len() > 0 && lines[4].Len() > 0) {
FArchive* writer = nullptr;
int64 bytesDownloaded = 0;
FString fullFilePath = FString();
FString md5Client = lines[2];
FString token = lines[1];
struct FSocketServerToken tokenStruct = tcpServer->getTokenStruct(token);
if (tokenStruct.token.IsEmpty() && tokenStruct.token.Equals(token) == false) {
triggerFileTransferOverTCPInfoEvent("Token not found.", session.sessionID, fullFilePath, false);
run = false;
return;
}
if (tokenStruct.deleteAfterUse) {
tcpServer->removeTokenFromStruct(token);
}
FString downloadDir = tcpServer->getCleanDir(tokenStruct.directoryType, tokenStruct.fileDirectory);
FString fileName = lines[3];
if (downloadDir.EndsWith("/")) {
fullFilePath = downloadDir + fileName;
}
else {
fullFilePath = downloadDir + "/" + fileName;
}
if (md5Client.IsEmpty()) {
triggerFileTransferOverTCPInfoEvent("MD5 string from client is missing.", session.sessionID, fullFilePath, false);
run = false;
return;
}
if (FPaths::DirectoryExists(downloadDir) == false) {
triggerFileTransferOverTCPInfoEvent("Directory not found.", session.sessionID, fullFilePath, false);
run = false;
return;
}
fileSize = FCString::Atoi64(*lines[4]);
if (fileSize <= 0) {
triggerFileTransferOverTCPInfoEvent("Client has reported a file size of 0 or lower.", session.sessionID, fullFilePath, false);
run = false;
return;
}
/* if (tcpServer->hasResume() && lines[4].Equals("0") == false) {
if (FPaths::FileExists(fullFilePath)) {
lastByte = FCString::Atoi64(*lines[4]);
}
}*/
if (tcpServer->hasResume()) {
writer = IFileManager::Get().CreateFileWriter(*fullFilePath, EFileWrite::FILEWRITE_Append);
}
else {
writer = IFileManager::Get().CreateFileWriter(*fullFilePath);
}
if (!writer) {
triggerFileTransferOverTCPInfoEvent("Can't create file.", session.sessionID, fullFilePath, false);
writer->Close();
delete writer;
run = false;
return;
}
bytesDownloaded = writer->TotalSize();
if (bytesDownloaded > fileSize) {
triggerFileTransferOverTCPInfoEvent("File on server bigger than on client. Cancel.", session.sessionID, fullFilePath, false);
run = false;
return;
}
if (bytesDownloaded == fileSize) {
writer->Close();
sendEndMessage(fullFilePath, token, md5Client);
run = false;
return;
}
FString response = "SEND_FILE_TO_SERVER_ACCEPTED_|_" + token + "_|_" + FString::FromInt(bytesDownloaded);
sendMessageToClient(response);
//send file to client
int64 ticks1 = 0;
int64 ticks2 = 0;
int64 ticksDownload = FDateTime::Now().GetTicks();
int64 lastByte = 0;
int32 bytesRead = 0;
uint32 dataSize = 0;
TArray<uint8> recvDataArray;
while (run && session.socket != nullptr) {
ticks1 = FDateTime::Now().GetTicks();
session.socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(WaitForRead));
ticks2 = FDateTime::Now().GetTicks();
bool hasData = session.socket->HasPendingData(dataSize);
if (!hasData && ticks1 == ticks2) {
//fireConnectionEvent(false, 0, "TCP connection broken.");
run = false;
return;
}
if (!hasData) {
run = false;
return;
}
if (hasData) {
recvDataArray.SetNumUninitialized(dataSize);
if (session.socket->Recv(recvDataArray.GetData(), recvDataArray.Num(), bytesRead)) {
writer->Serialize(recvDataArray.GetData(), recvDataArray.Num());
//show progress each second
if ((ticksDownload + 10000000) <= FDateTime::Now().GetTicks()) {
writer->Flush();
int64 bytesSendLastSecond = bytesDownloaded - lastByte;
//float speed = ((float)bytesSendLastSecond) / 125000;
float mbit = ((float)bytesSendLastSecond) / 1024 / 1024 * 8;
float sent = ((float)bytesDownloaded) / 1048576;
float left = 0;
float percent = 0;
if (fileSize > 0) {
left = ((float)(fileSize - bytesDownloaded)) / 1048576;
percent = ((float)bytesDownloaded / (float)fileSize * 100);
}
triggerFileOverTCPProgress(session.sessionID, fullFilePath, percent, mbit, lastByte, fileSize);
ticksDownload = FDateTime::Now().GetTicks();
lastByte = bytesDownloaded;
}
bytesDownloaded += bytesRead;
}
}
if (bytesDownloaded >= fileSize) {
//receive file finish
triggerFileOverTCPProgress(session.sessionID, fullFilePath, 100, 0, bytesDownloaded, fileSize);
if (writer != nullptr) {
writer->Close();
delete writer;
writer = nullptr;
}
sendEndMessage(fullFilePath, token, md5Client);
run = false;
}
}
if (writer != nullptr) {
writer->Close();
delete writer;
}
}
else {
triggerFileTransferOverTCPInfoEvent("File request incorrect (0).", session.sessionID, "", false);
run = false;
}
}
}
void FSocketServerTCPFileHandlerThread::triggerFileOverTCPProgress(FString sessionIDP, FString filePathP, float percentP, float mbitP, int64 bytesReceivedP, int64 fileSizeP) {
AsyncTask(ENamedThreads::GameThread, [sessionIDP, filePathP, percentP, mbitP, bytesReceivedP, fileSizeP]() {
USocketServerBPLibrary::socketServerBPLibrary->onfileTransferOverTCPProgressEventDelegate.Broadcast(sessionIDP, filePathP, percentP, mbitP, bytesReceivedP, fileSizeP);
});
}
void FSocketServerTCPFileHandlerThread::triggerFileTransferOverTCPInfoEvent(FString messageP, FString sessionIDP, FString filePathP, bool successP) {
AsyncTask(ENamedThreads::GameThread, [messageP, sessionIDP, filePathP, successP]() {
USocketServerBPLibrary::socketServerBPLibrary->onfileTransferOverTCPInfoEventDelegate.Broadcast(messageP, sessionIDP, filePathP, successP);
});
}
void FSocketServerTCPFileHandlerThread::sendEndMessage(FString fullFilePathP, FString tokenP, FString md5ClientP) {
bool md5okay = false;
FString md5Server = FString();
tcpServer->getMD5FromFile(fullFilePathP, md5okay, md5Server);
FString response = "SEND_FILE_TO_SERVER_END_|_" + tokenP + "_|_";
if (md5okay && md5ClientP.Equals(md5Server)) {
triggerFileTransferOverTCPInfoEvent("File successfully received.", session.sessionID, fullFilePathP, true);
response += "OKAY";
}
else {
triggerFileTransferOverTCPInfoEvent("File received but MD5 does not match. Corrupted file will be deleted if resume is not disabled.", session.sessionID, fullFilePathP, false);
response += "MD5ERROR";
if (tcpServer->hasResume() == false) {
tcpServer->deleteFile(fullFilePathP);
}
}
sendMessageToClient(response);
}
FString FSocketServerTCPFileHandlerThread::readMessageFromClient(){
int64 ticks1 = 0;
int64 ticks2 = 0;
int32 lastDataLengthFromHeader = 0;
FString message = FString();
TArray<uint8> byteDataArrayCache;
TArray<uint8> byteDataArray;
uint32 dataSize = 0;
TArray<FString> lines;
while (run && session.socket != nullptr) {
ticks1 = FDateTime::Now().GetTicks();
session.socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(WaitForRead));
ticks2 = FDateTime::Now().GetTicks();
bool hasData = session.socket->HasPendingData(dataSize);
if (!hasData && ticks1 == ticks2) {
//fireConnectionEvent(false, 0, "TCP connection broken.");
return "";
}
if (!hasData) {
return message;
}
if (hasData) {
TArray<uint8> dataFromSocket;
dataFromSocket.SetNumUninitialized(dataSize);
int32 BytesRead = 0;
if (session.socket->Recv(dataFromSocket.GetData(), dataFromSocket.Num(), BytesRead)) {
if (lastDataLengthFromHeader == 0 && dataFromSocket.Num() >= 5) {
tcpServer->readDataLength(dataFromSocket, lastDataLengthFromHeader);
if (dataFromSocket.Num() == 5) {
dataFromSocket.Empty();
continue;
}
byteDataArrayCache.Append(dataFromSocket.GetData() + 5, dataFromSocket.Num() - 5);
dataFromSocket.Empty();
}
else {
byteDataArrayCache.Append(dataFromSocket.GetData(), dataFromSocket.Num());
}
int32 maxLoops = 1000;//to prevent endless loop
while (byteDataArrayCache.Num() > 0 && byteDataArrayCache.Num() >= lastDataLengthFromHeader && maxLoops > 0) {
maxLoops--;
byteDataArray.Append(byteDataArrayCache.GetData(), lastDataLengthFromHeader);
byteDataArrayCache.RemoveAt(0, lastDataLengthFromHeader, true);
if (byteDataArrayCache.Num() == 0) {
lastDataLengthFromHeader = 0;
break;
}
if (byteDataArrayCache.Num() > 5) {
tcpServer->readDataLength(byteDataArrayCache, lastDataLengthFromHeader);
byteDataArrayCache.RemoveAt(0, 5, true);
}
}
byteDataArray.Add(0x00);// null-terminator
message = FString(UTF8_TO_TCHAR((char*)byteDataArray.GetData()));
byteDataArray.Empty();
if (message.IsEmpty() == false) {
message = tcpServer->decryptMessage(message);
}
return message;
}
}
}
if (message.IsEmpty() == false) {
message = tcpServer->decryptMessage(message);
}
return message;
}
void FSocketServerTCPFileHandlerThread::sendMessageToClient(FString message){
message = tcpServer->encryptMessage(message);
TArray<uint8> byteCache;
FTCHARToUTF8 Convert(*message);
int32 sent = 0;
if (FGenericPlatformProperties::IsLittleEndian()) {
byteCache.Add(0x00);
}
else {
byteCache.Add(0x01);
}
byteCache.SetNum(5);
int32 dataLength = Convert.Length();
FMemory::Memcpy(byteCache.GetData() + 1, &dataLength, 4);
byteCache.Append((uint8*)Convert.Get(), Convert.Length());
session.socket->Send(byteCache.GetData(), byteCache.Num(), sent);
byteCache.Empty();
}
void FSocketServerTCPFileHandlerThread::stopThread() {
run = false;
}
@@ -0,0 +1,126 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketServerTCPThread.h"
FSocketServerTCPThread::FSocketServerTCPThread(USocketServerTCP* tcpServerP, EReceiveFilterServer receiveFilterP, bool& runP) :
tcpServer(tcpServerP),
receiveFilter(receiveFilterP),
run(runP) {
FString threadName = "FSocketServerTCPThread_" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_BelowNormal);
}
FSocketServerTCPThread::~FSocketServerTCPThread(){
delete thread;
}
uint32 FSocketServerTCPThread::Run() {
FIPandPortStruct ipAndPortStruct = tcpServer->getServerIpAndPortStruct();
int32 port = tcpServer->getPort();
FString serverID = tcpServer->getServerID();
bool createServer = true;
FSocket* listenerSocket = nullptr;
ISocketSubsystem* socketSubSystem = USocketServerBPLibrary::getSocketSubSystem();
if (socketSubSystem != nullptr) {
TSharedRef<FInternetAddr> internetAddr = socketSubSystem->CreateInternetAddr();
internetAddr->SetIp(*ipAndPortStruct.ip, ipAndPortStruct.success);
internetAddr->SetPort(ipAndPortStruct.port);
if (ipAndPortStruct.success) {
listenerSocket = socketSubSystem->CreateSocket(NAME_Stream, *FString("USocketServerBPLibraryListenerSocket"), internetAddr->GetProtocolType());
}
if (listenerSocket != nullptr) {
listenerSocket->SetLinger(false, 0);
if (!listenerSocket->Bind(*internetAddr)) {
if (listenerSocket != nullptr) {
listenerSocket->Close();
if (socketSubSystem != nullptr)
socketSubSystem->DestroySocket(listenerSocket);
listenerSocket = nullptr;
UE_LOG(LogTemp, Error, TEXT("(211) TCP Server not started. Can't bind %s:%i. Please check IP,Port or your firewall."), *ipAndPortStruct.ip, port);
}
createServer = false;
}
if (createServer && !listenerSocket->Listen(8)) {
if (listenerSocket != nullptr) {
listenerSocket->Close();
if (socketSubSystem != nullptr)
socketSubSystem->DestroySocket(listenerSocket);
listenerSocket = nullptr;
UE_LOG(LogTemp, Error, TEXT("(212) TCP Server not started. Can't listen on %s:%i. Please check IP,Port or your firewall."), *ipAndPortStruct.ip, port);
}
createServer = false;
}
}
}
if (!createServer || listenerSocket == nullptr) {
UE_LOG(LogTemp, Error, TEXT("(210) TCP Server not started on %s:%i. Please check IP,Port or your firewall."), *ipAndPortStruct.ip, port);
AsyncTask(ENamedThreads::GameThread, [serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerConnectionEventDelegate.Broadcast(EServerSocketConnectionEventType::E_Server, false, "TCP Server not started. Please check IP,Port or your firewall.", TEXT(""), serverID);
USocketServerBPLibrary::getSocketServerTarget()->stopTCPServer(serverID);
});
}
else {
//switch to gamethread
AsyncTask(ENamedThreads::GameThread, [serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerConnectionEventDelegate.Broadcast(EServerSocketConnectionEventType::E_Server, true, "TCP Server started.", TEXT(""), serverID);
});
while (run) {
bool pending;
listenerSocket->WaitForPendingConnection(pending, FTimespan::FromSeconds(1));
if (pending) {
//UE_LOG(LogTemp, Display, TEXT("TCP Client: Pending connection"));
FSocketServerPluginSession session = FSocketServerPluginSession();
session.sessionID = FGuid::NewGuid().ToString();
session.serverID = serverID;
TSharedPtr<FInternetAddr> remoteAddress = USocketServerBPLibrary::getSocketSubSystem()->CreateInternetAddr();
session.socket = listenerSocket->Accept(*remoteAddress, session.sessionID);
/*FPlatformProcess::Sleep(0.1);
session.socket->Close();
USocketServerBPLibrary::getSocketSubSystem()->DestroySocket(session.socket);*/
session.ip = remoteAddress->ToString(false);
session.port = remoteAddress->GetPort();
session.protocol = EServerSocketConnectionProtocol::E_TCP;
tcpServer->initTCPClientThreads(session, receiveFilter);
tcpServer->addClientSession(session);
}
}
}
////wait for client disconnects
//FPlatformProcess::Sleep(2);
if (listenerSocket && listenerSocket != nullptr) {
listenerSocket->Close();
if (socketSubSystem != nullptr)
socketSubSystem->DestroySocket(listenerSocket);
}
listenerSocket = nullptr;
//switch to gamethread
AsyncTask(ENamedThreads::GameThread, [serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerConnectionEventDelegate.Broadcast(EServerSocketConnectionEventType::E_Server, false, "TCP Server stopped. Depending on the operating system it can take some time until the port is free again.", TEXT(""), serverID);
});
return 0;
}
void FSocketServerTCPThread::stopThread() {
run = false;
}
@@ -0,0 +1,367 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#include "SocketServerUDP.h"
USocketServerUDP::USocketServerUDP(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {
this->AddToRoot();
}
void USocketServerUDP::startUDPServer(FIPandPortStruct ipStructP,FString IPP, int32 portP, bool multicastP,
EReceiveFilterServer receiveFilterP, FString serverIDP, int32 maxPacketSizeP) {
ipAndPortStruct = ipStructP;
serverIP = IPP;
serverPort = portP;
receiveFilter = receiveFilterP;
serverID = serverIDP;
maxPacketSize = maxPacketSizeP;
if (maxPacketSize < 1 || maxPacketSize > 65507)
maxPacketSize = 65507;
serverThread = new FSocketServerUDPThread(this, multicastP);
sendThread = new FSocketServerUDPClientSendDataThread(this);
}
void USocketServerUDP::stopUDPServer() {
TArray<FString> toRemoveSessionKeys;
for (auto& element : getClientSessions()) {
toRemoveSessionKeys.Add(element.Key);
}
for (int32 i = 0; i < toRemoveSessionKeys.Num(); i++) {
removeClientSession(toRemoveSessionKeys[i]);
}
toRemoveSessionKeys.Empty();
FSocketServerPluginSession session;
if (sendThread != nullptr) {
sendThread->stopThread();
session.udpSendThread = sendThread;
}
if (serverThread != nullptr) {
serverThread->stopThread();
session.udpServerThread = serverThread;
}
if (socketReceiver != nullptr) {
socketReceiver->Stop();
socketReceiver->Exit();
session.udpSocketReceiver = socketReceiver;
}
if (socket && socket != nullptr) {
socket->Close();
}
USocketServerBPLibrary::socketServerBPLibrary->cleanConnection(session);
}
void USocketServerUDP::sendUDPMessage(TArray<FString> clientSessionIDs, FString message, TArray<uint8> byteArray, bool asynchronous, ESocketServerUDPSocketType socketType) {
for (auto& sessionID : clientSessionIDs) {
FSocketServerPluginSession* sessionPointer = clientSessions.Find(sessionID);
if (sessionPointer != nullptr) {
FSocketServerPluginSession& session = *sessionPointer;
if (session.protocol == EServerSocketConnectionProtocol::E_UDP) {
FSocket* socketUDP = socket;
if (socketUDP == nullptr || socketType == ESocketServerUDPSocketType::E_SSS_CLIENT)
socketUDP = session.socket;
if (asynchronous) {
sendThread->sendMessage(session.ip, session.port, message, byteArray, socketUDP);
return;
}
int32 sent = 0;
TSharedRef<FInternetAddr> addr = USocketServerBPLibrary::getSocketSubSystem()->CreateInternetAddr();
bool bIsValid;
addr->SetIp(*session.ip, bIsValid);
addr->SetPort(session.port);
if (bIsValid) {
if (byteArray.Num() > 0) {
sendBytes(socketUDP, byteArray, sent, addr);
}
if (message.Len() > 0) {
FTCHARToUTF8 Convert(*message);
byteArray.Append((uint8*)Convert.Get(), Convert.Length());
sendBytes(socketUDP, byteArray, sent, addr);
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin: Can't send data. Wrong adress."));
}
}
}
else {
FString serverIDGlobal = serverID;
AsyncTask(ENamedThreads::GameThread, [sessionID, serverIDGlobal]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(false, "Can't send message. SessionID not found on this server: " + sessionID, serverIDGlobal);
});
}
}
}
void USocketServerUDP::sendUDPMessageToClient(FString clientSessionID, FString message, TArray<uint8> byteArray, bool asynchronous, ESocketServerUDPSocketType socketType) {
FSocketServerPluginSession* sessionPointer = clientSessions.Find(clientSessionID);
if (sessionPointer != nullptr) {
FSocketServerPluginSession& session = *sessionPointer;
if (session.protocol == EServerSocketConnectionProtocol::E_UDP) {
FSocket* socketUDP = socket;
if (socketUDP == nullptr || socketType == ESocketServerUDPSocketType::E_SSS_CLIENT)
socketUDP = session.socket;
if (asynchronous) {
sendThread->sendMessage(session.ip, session.port, message, byteArray, socketUDP);
return;
}
int32 sent = 0;
TSharedRef<FInternetAddr> addr = USocketServerBPLibrary::getSocketSubSystem()->CreateInternetAddr();
bool bIsValid;
addr->SetIp(*session.ip, bIsValid);
addr->SetPort(session.port);
if (bIsValid) {
if (byteArray.Num() > 0) {
sendBytes(socketUDP, byteArray, sent, addr);
}
if (message.Len() > 0) {
FTCHARToUTF8 Convert(*message);
byteArray.Append((uint8*)Convert.Get(), Convert.Length());
sendBytes(socketUDP, byteArray, sent, addr);
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin: Can't send data. Wrong adress."));
}
}
}
else {
FString serverIDGlobal = serverID;
AsyncTask(ENamedThreads::GameThread, [clientSessionID, serverIDGlobal]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(false, "Can't send message. SessionID not found on this server: " + clientSessionID, serverIDGlobal);
});
}
}
void USocketServerUDP::sendUDPMessageTo(FString ip, int32 port, FString message, TArray<uint8> byteArray, bool asynchronous) {
int32 sent = 0;
FSocket* socketUDP = socket;
if (socketUDP == nullptr) {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin: Can't send data. Connection broken. Restart your UDP server %s"),*serverID);
return;
}
if (asynchronous) {
sendThread->sendMessage(ip,port,message, byteArray,socketUDP);
return;
}
TSharedRef<FInternetAddr> addr = USocketServerBPLibrary::getSocketSubSystem()->CreateInternetAddr();
bool bIsValid;
addr->SetIp(*ip, bIsValid);
addr->SetPort(port);
if (bIsValid) {
if (byteArray.Num() > 0) {
sendBytes(socketUDP, byteArray, sent, addr);
}
if (message.Len() > 0) {
FTCHARToUTF8 Convert(*message);
byteArray.Append((uint8*)Convert.Get(), Convert.Length());
sendBytes(socketUDP, byteArray, sent, addr);
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin: Can't send data. Wrong adress."));
}
}
void USocketServerUDP::UDPReceiverSocketServerPlugin(FArrayReaderPtr& ArrayReaderPtr, TSharedRef<FInternetAddr> remoteAddress) {
FString sessionID = remoteAddress.Get().ToString(true);
FSocketServerPluginSession* sessionPointer = clientSessions.Find(sessionID);
if (sessionPointer == nullptr) {
//FString socketName;
//FSocket* receiverSocket = FUdpSocketBuilder(*socketName);
ISocketSubsystem* socketSubsystem = USocketServerBPLibrary::getSocketSubSystem();
FSocket* receiverSocket = socketSubsystem->CreateSocket(NAME_DGram, *sessionID, remoteAddress->GetProtocolType());
//create and save session
FSocketServerPluginSession session;
session.sessionID = sessionID;
session.serverID = serverID;
session.ip = remoteAddress.Get().ToString(false);
session.port = remoteAddress.Get().GetPort();
session.socket = receiverSocket;
session.protocol = EServerSocketConnectionProtocol::E_UDP;
addClientSession(session);
}
TArray<uint8> byteArray;
if (receiveFilter == EReceiveFilterServer::E_SAB || receiveFilter == EReceiveFilterServer::E_B) {
byteArray.Append(ArrayReaderPtr->GetData(), ArrayReaderPtr->Num());
}
FString recvMessage;
if (receiveFilter == EReceiveFilterServer::E_SAB || receiveFilter == EReceiveFilterServer::E_S) {
ArrayReaderPtr->Add(0x00);// null-terminator
char* Data = (char*)ArrayReaderPtr->GetData();
recvMessage = FString(UTF8_TO_TCHAR(Data));
}
FString serverIDGlobal = serverID;
//switch to gamethread
AsyncTask(ENamedThreads::GameThread, [recvMessage, sessionID, byteArray, serverIDGlobal]() {
USocketServerBPLibrary::socketServerBPLibrary->onserverReceiveUDPMessageEventDelegate.Broadcast(sessionID, recvMessage, byteArray, serverIDGlobal);
if (USocketServerBPLibrary::socketServerBPLibrary->getResiteredClientEvent(sessionID) != nullptr) {
USocketServerBPLibrary::socketServerBPLibrary->getResiteredClientEvent(sessionID)->onregisteredEventDelegate.Broadcast(recvMessage, byteArray);
}
});
byteArray.Empty();
recvMessage.Empty();
}
//do not work with ipv6
//void USocketServerPluginUDPServer::UDPReceiver(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt) {
//
// FString sessionID = EndPt.ToString();
// FClientSocketSession* sessionPointer = clientSessions.Find(sessionID);
// if (sessionPointer == nullptr) {
// TSharedRef<FInternetAddr> remoteAddress = EndPt.ToInternetAddr();
//
// FString socketName;
// FSocket* receiverSocket = FUdpSocketBuilder(*socketName);
//
// //create and save session
// FClientSocketSession session;
// session.sessionID = sessionID;
// session.serverID = serverID;
// session.ip = remoteAddress.Get().ToString(false);
// session.port = remoteAddress.Get().GetPort();
// session.socket = receiverSocket;
// session.protocol = EServerSocketConnectionProtocol::E_UDP;
// addClientSession(session);
// }
// TArray<uint8> byteArray;
// if (receiveFilter == EReceiveFilterServer::E_SAB || receiveFilter == EReceiveFilterServer::E_B) {
// byteArray.Append(ArrayReaderPtr->GetData(), ArrayReaderPtr->Num());
// }
//
// FString recvMessage;
// if (receiveFilter == EReceiveFilterServer::E_SAB || receiveFilter == EReceiveFilterServer::E_S) {
// ArrayReaderPtr->Add(0x00);// null-terminator
// char* Data = (char*)ArrayReaderPtr->GetData();
// recvMessage = FString(UTF8_TO_TCHAR(Data));
// }
//
// FString serverIDGlobal = serverID;
// //switch to gamethread
// AsyncTask(ENamedThreads::GameThread, [recvMessage, sessionID, byteArray, serverIDGlobal]() {
// USocketServerBPLibrary::socketServerBPLibrary->onserverReceiveUDPMessageEventDelegate.Broadcast(sessionID, recvMessage, byteArray, serverIDGlobal);
// if (USocketServerBPLibrary::socketServerBPLibrary->getResiteredClientEvent(sessionID) != nullptr) {
// USocketServerBPLibrary::socketServerBPLibrary->getResiteredClientEvent(sessionID)->onregisteredEventDelegate.Broadcast(recvMessage, byteArray);
// }
// });
// byteArray.Empty();
// recvMessage.Empty();
//}
FIPandPortStruct USocketServerUDP::getServerIpAndPortStruct() {
return ipAndPortStruct;
}
FString USocketServerUDP::getIP(){
return serverIP;
}
int32 USocketServerUDP::getPort() {
return serverPort;
}
void USocketServerUDP::setSocketReceiver(FUdpSocketReceiver* socketReceiverP, FSocket* socketP) {
socketReceiver = socketReceiverP;
socket = socketP;
}
FUdpSocketReceiver* USocketServerUDP::getSocketReceiver() {
return socketReceiver;
}
FSocket* USocketServerUDP::getSocket() {
return socket;
}
FString USocketServerUDP::getServerID(){
return serverID;
}
void USocketServerUDP::addClientSession(FSocketServerPluginSession& session){
if (session.sessionID.IsEmpty() == false) {
//UE_LOG(LogTemp, Warning, TEXT("ADD Session:%s"), *session.sessionID);
clientSessions.Add(session.sessionID, session);
}
}
FSocketServerPluginSession* USocketServerUDP::getClientSession(FString key){
return clientSessions.Find(key);
}
void USocketServerUDP::removeClientSession(FString key){
if (clientSessions.Remove(key)) {
//UE_LOG(LogTemp, Warning, TEXT("Remove Session:%s"), *key);
USocketServerBPLibrary::socketServerBPLibrary->unregisterClientEvent(key);
}
}
TMap<FString, FSocketServerPluginSession> USocketServerUDP::getClientSessions(){
return clientSessions;
}
void USocketServerUDP::sendBytes(FSocket*& socketP, TArray<uint8>& byteArray, int32& sent, TSharedRef<FInternetAddr>& addr){
if (byteArray.Num() > maxPacketSize) {
TArray<uint8> byteArrayTemp;
for (int32 i = 0; i < byteArray.Num(); i++) {
byteArrayTemp.Add(byteArray[i]);
if (byteArrayTemp.Num() == maxPacketSize) {
sent = 0;
socketP->SendTo(byteArrayTemp.GetData(), byteArrayTemp.Num(), sent, *addr);
byteArrayTemp.Empty();
}
}
if (byteArrayTemp.Num() > 0) {
sent = 0;
socketP->SendTo(byteArrayTemp.GetData(), byteArrayTemp.Num(), sent, *addr);
byteArrayTemp.Empty();
}
}
else {
sent = 0;
socketP->SendTo(byteArray.GetData(), byteArray.Num(), sent, *addr);
}
byteArray.Empty();
}
@@ -0,0 +1,108 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketServerUDPClientSendDataThread.h"
FSocketServerUDPClientSendDataThread::FSocketServerUDPClientSendDataThread(USocketServerUDP* udpServerP) :
udpServer(udpServerP) {
FString threadName = "FUDPClientSendDataToServerThread_" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_BelowNormal);
}
FSocketServerUDPClientSendDataThread::~FSocketServerUDPClientSendDataThread() {
delete thread;
}
uint32 FSocketServerUDPClientSendDataThread::Run() {
//FString serverID = tcpServer->getServerID();
//socket = session.socket;
//FString sessionID = session.sessionID;
while (run && thread == nullptr) {
FPlatformProcess::Sleep(0.1);
}
TSharedRef<FInternetAddr> addr = USocketServerBPLibrary::getSocketSubSystem()->CreateInternetAddr();
int32 sent = 0;
bool bIsValid = false;
while (run) {
while (messageQueue.IsEmpty() == false) {
FSendUDPMessageStruct messageStuct;
messageQueue.Dequeue(messageStuct);
addr->SetIp(*messageStuct.ip, bIsValid);
addr->SetPort(messageStuct.port);
if (bIsValid && messageStuct.socketUDP != nullptr) {
if (messageStuct.bytes.Num() > 0) {
udpServer->sendBytes(messageStuct.socketUDP, messageStuct.bytes, sent, addr);
}
if (messageStuct.message.Len() > 0) {
FTCHARToUTF8 Convert(*messageStuct.message);
messageStuct.bytes.Append((uint8*)Convert.Get(), Convert.Length());
udpServer->sendBytes(messageStuct.socketUDP, messageStuct.bytes, sent, addr);
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("SimpleSocketServer Plugin: Can't send data. Wrong adress."));
}
}
if (run) {
pauseThread(true);
//workaround. suspend do not work on all platforms. lets sleep
while (paused && run) {
FPlatformProcess::Sleep(0.01);
}
}
}
run = false;
return 0;
}
FRunnableThread* FSocketServerUDPClientSendDataThread::getThread() {
return thread;
}
void FSocketServerUDPClientSendDataThread::setThread(FRunnableThread* threadP) {
thread = threadP;
}
void FSocketServerUDPClientSendDataThread::stopThread() {
run = false;
if (thread != nullptr) {
pauseThread(false);
}
}
bool FSocketServerUDPClientSendDataThread::isRun() {
return run;
}
void FSocketServerUDPClientSendDataThread::sendMessage(FString ip, int32 port, FString message, TArray<uint8> bytes, FSocket* socketUDP) {
FSendUDPMessageStruct messageStuct;
messageStuct.ip = ip;
messageStuct.port = port;
messageStuct.message = message;
messageStuct.bytes = bytes;
messageStuct.socketUDP = socketUDP;
messageQueue.Enqueue(messageStuct);
pauseThread(false);
}
void FSocketServerUDPClientSendDataThread::pauseThread(bool pause) {
paused = pause;
thread->Suspend(pause);
}
@@ -0,0 +1,193 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketServerUDPThread.h"
FSocketServerUDPThread::FSocketServerUDPThread(USocketServerUDP* udpServerP, bool multicastP) :
udpServer(udpServerP),
multicast(multicastP) {
FString threadName = "FServerUDPThread_" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_BelowNormal);
}
FSocketServerUDPThread::~FSocketServerUDPThread() {
delete thread;
}
uint32 FSocketServerUDPThread::Run() {
FIPandPortStruct ipAndPortStruct = udpServer->getServerIpAndPortStruct();
FSocket* listenerSocket = udpServer->getSocket();
FUdpSocketReceiver* udpSocketReceiver = nullptr;
FString ip = ipAndPortStruct.ip;
FString adress = ip + ":" + FString::FromInt(udpServer->getPort());
FString serverID = udpServer->getServerID();
// create the socket
FString socketName;
ISocketSubsystem* socketSubsystem = USocketServerBPLibrary::getSocketSubSystem();
if (multicast) {
TSharedPtr<class FInternetAddr> addr = socketSubsystem->CreateInternetAddr();
addr->SetAnyAddress();
addr->SetPort(udpServer->getPort());
listenerSocket = socketSubsystem->CreateSocket(NAME_DGram, *socketName, addr->GetProtocolType());
if (listenerSocket == nullptr || listenerSocket == NULL) {
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
AsyncTask(ENamedThreads::GameThread, [adress, SocketErr, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to open UDP Server: " + adress + " | " + SocketErr, serverID);
});
thread = nullptr;
return 0;
}
if (!listenerSocket->Bind(*addr)) {
UE_LOG(LogTemp, Error, TEXT("Unable to open UDP Server"));
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
AsyncTask(ENamedThreads::GameThread, [adress, SocketErr, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to open UDP Server: " + adress + " | " + SocketErr, serverID);
});
thread = nullptr;
return 0;
}
if (!listenerSocket->SetBroadcast(true)) {
UE_LOG(LogTemp, Error, TEXT("Unable to set Broadcast"));
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
AsyncTask(ENamedThreads::GameThread, [adress, SocketErr, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to set Broadcast: " + adress + " | " + SocketErr, serverID);
});
thread = nullptr;
return 0;
}
if (!listenerSocket->SetMulticastLoopback(true)) {
UE_LOG(LogTemp, Error, TEXT("Unable to set Multicast Loopback"));
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
AsyncTask(ENamedThreads::GameThread, [adress, SocketErr, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to set Multicast Loopback: " + adress + " | " + SocketErr, serverID);
});
thread = nullptr;
return 0;
}
bool validIP = true;
addr->SetIp(*ip, validIP);
if (!validIP) {
UE_LOG(LogTemp, Error, TEXT("SocketServer UDP. Can't set ip"));
AsyncTask(ENamedThreads::GameThread, [adress, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to open UDP Server: " + adress + " | Can't set ip.", serverID);
});
thread = nullptr;
return 0;
}
if (!listenerSocket->JoinMulticastGroup(*addr)) {
UE_LOG(LogTemp, Error, TEXT("Unable to join Multicast Group"));
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
AsyncTask(ENamedThreads::GameThread, [adress, SocketErr, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to join Multicast Group: " + adress + " | " + SocketErr, serverID);
});
thread = nullptr;
return 0;
}
}
else {
TSharedPtr<class FInternetAddr> addr = socketSubsystem->CreateInternetAddr();
bool validIP = true;
addr->SetPort(udpServer->getPort());
addr->SetIp(*ip, validIP);
listenerSocket = socketSubsystem->CreateSocket(NAME_DGram, *socketName, addr->GetProtocolType());
if (listenerSocket == nullptr || listenerSocket == NULL) {
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
AsyncTask(ENamedThreads::GameThread, [adress, SocketErr, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to open UDP Server: " + adress + " | " + SocketErr, serverID);
});
thread = nullptr;
return 0;
}
if (!validIP) {
UE_LOG(LogTemp, Error, TEXT("SocketServer UDP. Can't set ip"));
AsyncTask(ENamedThreads::GameThread, [adress, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to open UDP Server: " + adress + " | Can't set ip.", serverID);
});
thread = nullptr;
return 0;
}
listenerSocket->SetReuseAddr();
listenerSocket->SetNonBlocking();
if (!listenerSocket->Bind(*addr)) {
UE_LOG(LogTemp, Error, TEXT("Unable to open UDP Server"));
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
AsyncTask(ENamedThreads::GameThread, [adress, SocketErr, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "Unable to open UDP Server: " + adress + " | " + SocketErr, serverID);
});
thread = nullptr;
return 0;
}
}
//do not work with ipv6
FTimespan ThreadWaitTime = FTimespan::FromMilliseconds(100);
//FString threadName = "SocketServerBPLibUDPReceiverThread_" + FString::FromInt(FDateTime::Now().GetTicks());
//udpSocketReceiver = new FUdpSocketReceiver(listenerSocket, ThreadWaitTime, *threadName);
//udpSocketReceiver->OnDataReceived().BindUObject(udpServer, &USocketServerPluginUDPServer::UDPReceiver);
//udpSocketReceiver->Start();
udpServer->setSocketReceiver(udpSocketReceiver, listenerSocket);
//udpServer->initUDPClientThreads(listenerSocket);
AsyncTask(ENamedThreads::GameThread, [adress, serverID]() {
USocketServerBPLibrary::socketServerBPLibrary->onsocketServerUDPConnectionEventDelegate.Broadcast(true, "UDP Server started: " + adress, serverID);
});
//copy of FUdpSocketReceiver.h to get IPv6 working
while (run) {
if (!listenerSocket->Wait(ESocketWaitConditions::WaitForRead, ThreadWaitTime)) {
continue;
}
TSharedRef<FInternetAddr> Sender = socketSubsystem->CreateInternetAddr();
uint32 Size;
while (listenerSocket->HasPendingData(Size)) {
FArrayReaderPtr Reader = MakeShared<FArrayReader, ESPMode::ThreadSafe>(true);
Reader->SetNumUninitialized(FMath::Min(Size, 65507u));
int32 Read = 0;
if (listenerSocket->RecvFrom(Reader->GetData(), Reader->Num(), Read, *Sender))
{
Reader->RemoveAt(Read, Reader->Num() - Read, false);
udpServer->UDPReceiverSocketServerPlugin(Reader, Sender);
// UE_LOG(LogTemp, Error, TEXT("%s_%s"), *Sender.Get().ToString(true), *Sender->GetProtocolType().ToString());
}
}
}
if (listenerSocket != nullptr) {
listenerSocket->Close();
socketSubsystem->DestroySocket(listenerSocket);
listenerSocket = nullptr;
}
return 0;
}
void FSocketServerUDPThread::stopThread() {
run = false;
}
@@ -0,0 +1,36 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
#include "SocketServerBPLibrary.h"
#include "DNSClientSocketServer.generated.h"
UCLASS()
class SOCKETSERVER_API UDNSClientSocketServer : public UObject
{
GENERATED_UCLASS_BODY()
public:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FresolveDomainEventDelegate, FString, IP);
UFUNCTION()
void resolveDomainEventDelegate(const FString IP);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|Events|ResolveDomain")
FresolveDomainEventDelegate onresolveDomainEventDelegate;
void resolveDomain(ISocketSubsystem * socketSubSystem, FString domain, bool useDNSCache = true, FString dnsIP = FString("8.8.8.8"));
void UDPReceiver(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt);
FSocket* socket = nullptr;
bool isResloving();
FString getIP();
private:
bool resolving;
FString ip;
FString domain;
TMap<FString, FString> dnsCache;
};
@@ -0,0 +1,24 @@
// Copyright 2018-2020 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "EventBean.generated.h"
UCLASS(Blueprintable, BlueprintType)
class UEventBean : public UObject
{
GENERATED_UCLASS_BODY()
public:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FregisteredEventDelegate, const FString, message, const TArray<uint8>&, byteArray);
UFUNCTION()
void registeredEventDelegate(const FString message, const TArray<uint8>& byteArray);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|Register|Events")
FregisteredEventDelegate onregisteredEventDelegate;
private:
};
@@ -0,0 +1,243 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
#include "SocketServerBPLibrary.h"
#include "FileFunctionsSocketServer.generated.h"
class FReadFileInPartsSocketServerThread;
UCLASS(Blueprintable, BlueprintType)
class SOCKETSERVER_API UFileFunctionsSocketServer : public UObject
{
GENERATED_UCLASS_BODY()
public:
UFUNCTION()
static UFileFunctionsSocketServer* getFileFunctionsSocketServerTarget();
static UFileFunctionsSocketServer* fileFunctionsSocketServer;
static FString getCleanDirectory(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void writeBytesToFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, TArray<uint8> bytes, bool& success);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void addBytesToFileAndCloseIt(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, TArray<uint8> bytes, bool& success);
//UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
// static void splittFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, int32 parts, bool& success);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static TArray<uint8> readBytesFromFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool& success);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void readStringFromFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool& success, FString& data);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void writeStringToFile(EFileFunctionsSocketServerDirectoryType directoryType, FString data, FString filePath, EFileFunctionsSocketServerEncodingOptions fileEncoding, bool& success);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void getMD5FromFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool& success, FString& MD5);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void getMD5FromFileAbsolutePath(FString filePath, bool& success, FString& MD5);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void stringToBase64String(FString string, FString& base64String);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void base64StringToString(FString& string, FString base64String);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void bytesToBase64String(TArray<uint8> bytes, FString& base64String);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static TArray<uint8> base64StringToBytes(FString base64String, bool& success);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void fileToBase64String(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool& success, FString& base64String, FString& fileName);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool fileExists(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool fileExistsAbsolutePath(FString filePath);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool directoryExists(EFileFunctionsSocketServerDirectoryType directoryType, FString path);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static int64 fileSize(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static int64 fileSizeAbsolutePath(FString filePath);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool deleteFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool deleteFileAbsolutePath(FString filePath);
/** Delete a directory and return true if the directory was deleted or otherwise does not exist. **/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool deleteDirectory(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
/** Return true if the file is read only. **/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool isReadOnly(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
/** Attempt to move a file. Return true if successful. Will not overwrite existing files. **/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool moveFile(EFileFunctionsSocketServerDirectoryType directoryTypeTo, FString filePathTo, EFileFunctionsSocketServerDirectoryType directoryTypeFrom, FString filePathFrom);
/** Attempt to change the read only status of a file. Return true if successful. **/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool setReadOnly(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, bool bNewReadOnlyValue);
/** Return the modification time of a file. Returns FDateTime::MinValue() on failure **/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static FDateTime getTimeStamp(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
/** Sets the modification time of a file **/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void setTimeStamp(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, FDateTime DateTime);
/** Return the last access time of a file. Returns FDateTime::MinValue() on failure **/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static FDateTime getAccessTimeStamp(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
/** For case insensitive filesystems, returns the full path of the file with the same case as in the filesystem */
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static FString getFilenameOnDisk(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
/** Create a directory and return true if the directory was created or already existed. **/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static bool createDirectory(EFileFunctionsSocketServerDirectoryType directoryType, FString path);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void getAllFilesFromDirectory(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, int32& count, TArray<FString>& files, TArray<FString>& filePaths, FString fileType = "*.*");
//AES
/**
* Encrypts a string with AES in 256bit and returns the encrypted string as Base64 string.
* @param message The string to be encrypted
* @param keyIn256Bit The key must be a string with 32 characters. Please use ASCII characters only!
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|AES")
static FString encryptMessageWithAES(FString message, FString keyIn256Bit);
/**
* Decrypts a Base64 string that has been encrypted in AES with 256bit and returns the string.
* @param message The string to be decrypted
* @param keyIn256Bit The key must be a string with 32 characters. Please use ASCII characters only!
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|AES")
static FString decryptMessageWithAES(FString encryptedBase64Message, FString keyIn256Bit);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|String")
static FString int64ToString(int64 num);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static struct FFileFunctionsSocketServerOpenFile openFile(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static int64 addBytesToFile(struct FFileFunctionsSocketServerOpenFile openFile, TArray<uint8>bytes);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void closeFile(struct FFileFunctionsSocketServerOpenFile openFile);
/**
* With this function you can read a file piece by piece. This reduces the RAM consumption to almost zero and files can be read in infinite size.
*@param bufferSize In bytes. This is the size of the file pieces that are being read.
*@param delayBetweenReadsInSeconds Specified in seconds. The higher the value, the slower the file is read (0.0001 minimum). When sending data over the network/internet, please make sure not to send data too fast. SSDs can read data much faster than you can send it over a network. This means that the data ends up in some buffers (RAM) and can also cause them to overflow.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File", meta = (AdvancedDisplay = 2))
static void readBytesFromFileInPartsAsync(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, int32 bufferSize = 65536, float delayBetweenReadsInSeconds = 0.01f);
void readBytesFromFileInPartsAsyncInternal(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath, int32 bufferSize = 65536, float delayBetweenReadsInSeconds = 0.01f);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|File")
static void cancelReadBytesFromFileInParts(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
void cancelReadBytesFromFileInPartsInternal(EFileFunctionsSocketServerDirectoryType directoryType, FString filePath);
void cleanReadBytesFromFileInParts(FString cleanDir);
TMap<FString, FReadFileInPartsSocketServerThread*> readFileInPartsThreads;
private:
static TArray<uint8> FStringToByteArray(FString s);
};
/* asynchronous Thread*/
class SOCKETSERVER_API FReadFileInPartsSocketServerThread : public FRunnable {
public:
FReadFileInPartsSocketServerThread(FString cleanDirP, int32 bufferSizeP, float delayBetweenReadsInSecondsP) :
cleanDir(cleanDirP),
bufferSize(bufferSizeP),
delayBetweenReadsInSeconds(delayBetweenReadsInSecondsP)
{
FString threadName = "FReadFileInPartsSocketServerThread" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_Normal);
}
virtual uint32 Run() override {
FArchive* reader = IFileManager::Get().CreateFileReader(*cleanDir);
if (reader == nullptr || reader->TotalSize() == 0) {
AsyncTask(ENamedThreads::GameThread, []() {
USocketServerBPLibrary::getSocketServerTarget()->onreadBytesFromFileInPartsEventDelegate.Broadcast(0, 0, true, TArray<uint8>());
});
if (reader != nullptr) {
reader->Close();
}
delete reader;
return 0;
}
if (delayBetweenReadsInSeconds <= 0) {
delayBetweenReadsInSeconds = 0.0001f;
}
int64 fileSize = reader->TotalSize();
int64 readSize = 0;
int64 lastPosition = 0;
TArray<uint8> buffer;
if (bufferSize > fileSize) {
bufferSize = fileSize;
}
while (run && lastPosition < fileSize) {
if ((lastPosition + bufferSize) > fileSize) {
bufferSize = fileSize - lastPosition;
}
//buffer.Reset(bufferSize);
buffer.Empty();
buffer.AddUninitialized(bufferSize);
reader->Serialize(buffer.GetData(), buffer.Num());
lastPosition += buffer.Num();
//UE_LOG(LogTemp, Warning, TEXT("xxxxx READ: %i"), buffer.Num());
AsyncTask(ENamedThreads::GameThread, [fileSize, lastPosition, buffer]() {
USocketServerBPLibrary::getSocketServerTarget()->onreadBytesFromFileInPartsEventDelegate.Broadcast(fileSize, lastPosition, false, buffer);
});
FPlatformProcess::Sleep(delayBetweenReadsInSeconds);
}
AsyncTask(ENamedThreads::GameThread, [fileSize, lastPosition]() {
USocketServerBPLibrary::getSocketServerTarget()->onreadBytesFromFileInPartsEventDelegate.Broadcast(fileSize, lastPosition, true, TArray<uint8>());
});
UFileFunctionsSocketServer::getFileFunctionsSocketServerTarget()->cleanReadBytesFromFileInParts(cleanDir);
//buffer.Empty();
if (reader != nullptr) {
reader->Close();
}
delete reader;
thread = nullptr;
return 0;
}
void stopThread() {
run = false;
}
void setDelayBetweenReadsInSeconds(float d) {
delayBetweenReadsInSeconds = d;
if (delayBetweenReadsInSeconds <= 0) {
delayBetweenReadsInSeconds = 0.001f;
}
}
protected:
bool run = true;
FString cleanDir;
int32 bufferSize;
float delayBetweenReadsInSeconds;
//USocketServerBPLibrary* mainLib = USocketServerBPLibrary::getSocketServerTarget();
FRunnableThread* thread = nullptr;
};
@@ -0,0 +1,32 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
#include "RCONServer.generated.h"
UCLASS(Blueprintable, BlueprintType)
class SOCKETSERVER_API URCONServer : public UObject
{
GENERATED_UCLASS_BODY()
public:
UFUNCTION()
void receiveTCPMessageEvent(const FString sessionID, const FString message, const TArray<uint8>& byteArray, const FString serverID);
void startRCONServer(FString serverID, ERCONPasswordType passwordType, FString passwordOrFile,
bool& success, FString& errorMessage);
bool sendResponse(FString sessionID, FString serverID, int32 id, int32 type, FString body);
private:
FString passwordOrFile = FString();
ERCONPasswordType passwordType = ERCONPasswordType::E_parameter;
TArray<FString> commandList;
void authResponse(FString sessionID, FString serverID, FString password, int32 rconID);
};
@@ -0,0 +1,267 @@
// Copyright 2017-2022 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "EngineGlobals.h"
#include "Engine/Engine.h"
#include "EventBean.h"
#include "Sockets.h"
#include "SocketSubsystem.h"
#include "Interfaces/IPv4/IPv4Endpoint.h"
#include "Common/UdpSocketReceiver.h"
#include "Common/UdpSocketBuilder.h"
#include "Async/Async.h"
#include "Containers/Queue.h"
#include "Misc/DateTime.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "HAL/PlatformFileManager.h"
#include "HAL/FileManager.h"
#include "Misc/Base64.h"
#include "Misc/SecureHash.h"
#include "Misc/AES.h"
#include "Modules/ModuleManager.h"
#include "SocketServer.generated.h"
class FSocketServerTCPClientSendDataThread;
class FSocketServerTCPClientReceiveDataThread;
class FSocketServerTCPFileHandlerThread;
class FUdpSocketReceiver;
class FSocketServerUDPThread;
class FSocketServerUDPClientSendDataThread;
UENUM(BlueprintType)
enum class EFileFunctionsSocketServerEncodingOptions : uint8
{
E_AutoDetect UMETA(DisplayName = "AutoDetect"),
E_ForceAnsi UMETA(DisplayName = "ForceAnsi"),
E_ForceUnicode UMETA(DisplayName = "ForceUnicode"),
E_ForceUTF8 UMETA(DisplayName = "ForceUTF8"),
E_ForceUTF8WithoutBOM UMETA(DisplayName = "ForceUTF8WithoutBOM")
};
UENUM(BlueprintType)
enum class EFileFunctionsSocketServerDirectoryType : uint8
{
E_gd UMETA(DisplayName = "Game directory"),
E_ad UMETA(DisplayName = "Absolute directory")
};
UENUM(BlueprintType)
enum class EServerSocketConnectionEventType : uint8
{
E_Server UMETA(DisplayName = "Server"),
E_Client UMETA(DisplayName = "Client")
};
UENUM(BlueprintType)
enum class EServerSocketConnectionProtocol : uint8
{
E_NotSet UMETA(DisplayName = "NotSet"),
E_TCP UMETA(DisplayName = "TCP"),
E_UDP UMETA(DisplayName = "UDP")
};
UENUM(BlueprintType)
enum class EServerSocketConnectionCheckPortType : uint8
{
E_TCP UMETA(DisplayName = "TCP"),
E_UDP UMETA(DisplayName = "UDP")
};
UENUM(BlueprintType)
enum class EReceiveFilterServer : uint8
{
E_SAB UMETA(DisplayName = "Message And Bytes"),
E_S UMETA(DisplayName = "Message"),
E_B UMETA(DisplayName = "Bytes")
};
UENUM(BlueprintType)
enum class ESocketPlatformServer : uint8
{
E_SSS_SYSTEM UMETA(DisplayName = "System"),
E_SSS_DEFAULT UMETA(DisplayName = "Auto"),
E_SSS_WINDOWS UMETA(DisplayName = "WINDOWS"),
E_SSS_MAC UMETA(DisplayName = "MAC"),
E_SSS_IOS UMETA(DisplayName = "IOS"),
E_SSS_UNIX UMETA(DisplayName = "UNIX"),
E_SSS_ANDROID UMETA(DisplayName = "ANDROID"),
E_SSS_PS4 UMETA(DisplayName = "PS4"),
E_SSS_XBOXONE UMETA(DisplayName = "XBOXONE"),
E_SSS_HTML5 UMETA(DisplayName = "HTML5"),
E_SSS_SWITCH UMETA(DisplayName = "SWITCH")
};
UENUM(BlueprintType)
enum class ESocketServerUDPSocketType : uint8
{
E_SSS_SERVER UMETA(DisplayName = "Use Server Socket"),
E_SSS_CLIENT UMETA(DisplayName = "Use Client Socket")
};
UENUM(BlueprintType)
enum class ESocketServerTCPSeparator : uint8
{
E_None UMETA(DisplayName = "None"),
E_ByteSeparator UMETA(DisplayName = "Separate via one Byte"),
E_StringSeparator UMETA(DisplayName = "Separate via String"),
E_LengthSeparator UMETA(DisplayName = "Separate by Length")
};
UENUM(BlueprintType)
enum class ERCONPasswordType : uint8
{
E_parameter UMETA(DisplayName = "As String Parameter"),
E_gd UMETA(DisplayName = "As File in Game directory"),
E_ad UMETA(DisplayName = "As File in Absolute directory")
};
USTRUCT()
struct FSendUDPMessageStruct {
GENERATED_USTRUCT_BODY()
FString ip;
int32 port;
FString message;
TArray<uint8> bytes;
FSocket* socketUDP = nullptr;
};
USTRUCT(BlueprintType)
struct FFileFunctionsSocketServerOpenFile
{
GENERATED_USTRUCT_BODY()
FArchive* writer = nullptr;
};
USTRUCT()
struct FIPandPortStruct {
GENERATED_USTRUCT_BODY()
bool success = false;
FString ip = FString();
FString errorMessage = FString();
int32 port = 0;
};
USTRUCT(BlueprintType)
struct FSocketServerPluginSession
{
GENERATED_USTRUCT_BODY()
FString ip = FString();
int32 port = 0;
int64 addToCleanerTime = 0;
FString sessionID = FString();
FString serverID = FString();
FSocket* socket = nullptr;
FSocketServerTCPClientSendDataThread* tcpSendThread = nullptr;
FSocketServerTCPClientReceiveDataThread* tcpRecieverThread = nullptr;
FSocketServerTCPFileHandlerThread* tcpFileHandlerThread = nullptr;
FUdpSocketReceiver* udpSocketReceiver = nullptr;
FSocketServerUDPThread* udpServerThread = nullptr;
FSocketServerUDPClientSendDataThread* udpSendThread = nullptr;
EServerSocketConnectionProtocol protocol;
};
USTRUCT(BlueprintType)
struct FSocketServerDownloadFileInfo
{
GENERATED_USTRUCT_BODY()
float size;
float megaBytesReceived;
float megaBytesLeft;
float percentDownload;
float megaBit;
FString fileName;
FString serverID;
};
//USTRUCT(BlueprintType)
//struct FSocketServerUploadFileInfo
//{
// GENERATED_USTRUCT_BODY()
//
// float size;
// float megaBytesSend;
// float megaBytesLeft;
// float percentUpload;
// float megaBit;
// FString fileName;
// FString serverID;
//};
USTRUCT(BlueprintType)
struct FSocketServerToken
{
GENERATED_USTRUCT_BODY()
FString token = FString();
bool deleteAfterUse = false;
EFileFunctionsSocketServerDirectoryType directoryType;
FString fileDirectory = FString();
};
#ifndef __FileFunctionsSocketServer
#define __FileFunctionsSocketServer
#include "FileFunctionsSocketServer.h"
#endif
#ifndef __RCONServer
#define __RCONServer
#include "RCONServer.h"
#endif
#ifndef __SocketServerCleanerThread
#define __SocketServerCleanerThread
#include "SocketServerCleanerThread.h"
#endif
#ifndef __SocketServerBPLibrary
#define __SocketServerBPLibrary
#include "SocketServerBPLibrary.h"
#endif
#ifndef __SocketServerUDP
#define __SocketServerUDP
#include "SocketServerUDP.h"
#endif
#ifndef __SocketServerTCP
#define __SocketServerTCP
#include "SocketServerTCP.h"
#endif
class FSocketServerModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
@@ -0,0 +1,499 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
#include "SocketServerBPLibrary.generated.h"
class FSocketServerCleanerThread;
class USocketServerUDP;
class USocketServerTCP;
class URCONServer;
UCLASS()
class SOCKETSERVER_API USocketServerBPLibrary : public UObject
{
GENERATED_UCLASS_BODY()
public:
static USocketServerBPLibrary *socketServerBPLibrary;
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer")
static USocketServerBPLibrary* getSocketServerTarget();
//Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FsocketServerConnectionEventDelegate, EServerSocketConnectionEventType, type, bool, success, FString, message, FString, sessionID, FString, serverID);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FserverReceiveTCPMessageEventDelegate, FString, sessionID, FString, message, const TArray<uint8>&, byteArray, FString, serverID);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FreadBytesFromFileInPartsEventDelegate, int64, fileSize, int64, position, bool, end, const TArray<uint8>&, byteArray);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FsocketServerUDPConnectionEventDelegate, bool, success, FString, message, FString, serverID);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FserverReceiveUDPMessageEventDelegate, FString, sessionID, FString, message, const TArray<uint8>&, byteArray,FString, serverID);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FfileTransferOverTCPProgressEventDelegate, FString, sessionID, FString, filePath, float, percent, float, mbit, int64, bytesTransferred, int64, fileSize);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FfileTransferOverTCPInfoEventDelegate, FString, message, FString, sessionID, FString, filePath, bool, success);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FreceiveRCONRequestEventDelegate, FString, sessionID, FString, serverID, int32, requestID, FString, request);
UFUNCTION()
void socketServerConnectionEventDelegate(const EServerSocketConnectionEventType type, const bool success, const FString message, const FString sessionID, const FString serverID);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|TCP|Events|ConnectionInfo")
FsocketServerConnectionEventDelegate onsocketServerConnectionEventDelegate;
UFUNCTION()
void serverReceiveTCPMessageEventDelegate(const FString sessionID, const FString message, const TArray<uint8>& byteArray, const FString serverID);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|TCP|Events|ReceiveMessage")
FserverReceiveTCPMessageEventDelegate onserverReceiveTCPMessageEventDelegate;
UFUNCTION()
void socketServerUDPConnectionEventDelegate(const bool success, const FString message, const FString serverID);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|UDP|Events|ConnectionInfo")
FsocketServerUDPConnectionEventDelegate onsocketServerUDPConnectionEventDelegate;
UFUNCTION()
void serverReceiveUDPMessageEventDelegate(const FString sessionID, const FString message, const TArray<uint8>& byteArray, const FString serverID);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|UDP|Events|ReceiveMessage")
FserverReceiveUDPMessageEventDelegate onserverReceiveUDPMessageEventDelegate;
UFUNCTION()
void fileTransferOverTCPProgressEventDelegate(const FString sessionID, const FString filePath, const float percent, const float mbit, const int64 bytesTransferred, const int64 fileSize);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|TCP|Events|File|FileTransferOverTCPProgress")
FfileTransferOverTCPProgressEventDelegate onfileTransferOverTCPProgressEventDelegate;
UFUNCTION()
void fileTransferOverTCPInfoEventDelegate(const FString message, const FString sessionID, const FString filePath, const bool success);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|TCP|Events|File|FileTransferOverTCPInfo")
FfileTransferOverTCPInfoEventDelegate onfileTransferOverTCPInfoEventDelegate;
UFUNCTION()
void readBytesFromFileInPartsEventDelegate(const int64 fileSize, const int64 position, const bool end, const TArray<uint8>& byteArray);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|SpecialFunctions|File|Events|ReadBytesFromFileInPartsAsync")
FreadBytesFromFileInPartsEventDelegate onreadBytesFromFileInPartsEventDelegate;
UFUNCTION()
void receiveRCONRequestEventDelegate(const FString sessionID, const FString serverID, const int32 requestID, const FString request);
UPROPERTY(BlueprintAssignable, Category = "SocketServer|TCP|Events|RCON|ReceiveRCONRequest")
FreceiveRCONRequestEventDelegate onreceiveRCONRequestEventDelegate;
/**
*Get all Session IDs
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static void serverPluginGetSocketSessionIds(const FString optionalServerID, TArray<FString>& sessionIDs);
void serverPluginGetSocketSessionIdsNonStatic(const FString optionalServerID, TArray<FString>& sessionIDs);
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static void serverPluginGetSocketSessionInfo(const FString sessionID, bool &sessionFound, FString &IP, int32 &port, EServerSocketConnectionProtocol &connectionProtocol, FString& serverID);
void serverPluginGetSocketSessionInfoNonStatic(const FString sessionID, bool& sessionFound, FString& IP, int32& port, EServerSocketConnectionProtocol& connectionProtocol, FString& serverID);
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static void serverPluginGetSocketSessionInfoByServerID(const FString serverID, const FString sessionID, bool& sessionFound, FString& IP, int32& port, EServerSocketConnectionProtocol& connectionProtocol);
void serverPluginGetSocketSessionInfoByServerIDNonStatic(const FString serverID, const FString sessionID, bool& sessionFound, FString& IP, int32& port, EServerSocketConnectionProtocol& connectionProtocol);
/**
*Close a connection and remove the session
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static void removeSessionAndCloseConnection(FString sessionId, FString optionalServerID);
void removeSessionAndCloseConnectionNonStatic(FString sessionId, FString optionalServerID);
//UDP
/**
*Start UDP Server
*@param domainOrIP IP or Domain to listen
*@param port port to listen
*@param multicast This allows several servers to be started on different computers in the LAN with the same IP.
*@param receiveFilter This allows you to decide which data type you want to receive. If you receive files it makes no sense to convert them into a string.
*@param customServerID Optionally you can assign your own ServerID like "myAuthentificationServer" or "fileServer"
*@param maxPacketSize sets the maximum UDP packet size. More than 65507 is not possible.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|UDP", meta = (AdvancedDisplay = 7))
static void startUDPServer(FString& serverID, FString IP = FString("0.0.0.0"), int32 port = 8888, bool multicast = false, EReceiveFilterServer receiveFilter = EReceiveFilterServer::E_SAB, FString customServerID = FString(""), int32 maxPacketSize = 65507);
void startUDPServerNonStatic(FString& serverID, FString IP = FString("0.0.0.0"), int32 port = 8888, bool multicast = false, EReceiveFilterServer receiveFilter = EReceiveFilterServer::E_SAB, FString customServerID = FString(""), int32 maxPacketSize = 65507);
/**
*Stop UDP Server
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|UDP")
static void stopUDPServer(FString optionalServerID);
void stopUDPServerNonStatic(FString optionalServerID);
UFUNCTION(BlueprintCallable, Category = "SocketServer|UDP")
static void stopAllUDPServers();
void stopAllUDPServersNonStatic();
/**
*Sends data back to a client.
*@param clientSessionIDs
*@param message
*@param byteArray
*@param addLineBreak
*@param socketType Some Thirdparty software expects the data to be sent back over the same socket, others expects a new socket.
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|UDP", meta = (AutoCreateRefTerm = "byteArray"))
static void socketServerSendUDPMessage(TArray<FString> clientSessionIDs, FString message, TArray<uint8> byteArray, bool addLineBreak, bool asynchronous, ESocketServerUDPSocketType socketType, FString optionalServerID);
void socketServerSendUDPMessageNonStatic(TArray<FString> clientSessionIDs, FString message, TArray<uint8> byteArray, bool addLineBreak, bool asynchronous, ESocketServerUDPSocketType socketType, FString optionalServerID);
/**
*Sends data back to a client.
*@param clientSessionIDs
*@param message
*@param byteArray
*@param addLineBreak
*@param socketType Some Thirdparty software expects the data to be sent back over the same socket, others expects a new socket.
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|UDP", meta = (AutoCreateRefTerm = "byteArray"))
static void socketServerSendUDPMessageToClient(FString clientSessionID, FString message, TArray<uint8> byteArray, bool addLineBreak, bool asynchronous, ESocketServerUDPSocketType socketType, FString optionalServerID);
void socketServerSendUDPMessageToClientNonStatic(FString clientSessionID, FString message, TArray<uint8> byteArray, bool addLineBreak, bool asynchronous, ESocketServerUDPSocketType socketType, FString optionalServerID);
/**
*If you want to send data directly to a specific destination without getting data back.
*@param ip
*@param port
*@param message
*@param byteArray
*@param addLineBreak
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|UDP", meta = (AutoCreateRefTerm = "byteArray"))
static void socketServerSendUDPMessageTo(FString ip, int32 port, FString message, TArray<uint8> byteArray, bool addLineBreak, bool asynchronous, FString optionalServerID);
void socketServerSendUDPMessageToNonStatic(FString ip, int32 port, FString message, TArray<uint8> byteArray, bool addLineBreak, bool asynchronous, FString optionalServerID);
//TCP
/**
* Start TCP Server
* @param domainOrIP IP or Domain to listen
* @param port port to listen
* @param receiveFilter This allows you to decide which data type you want to receive. If you receive files it makes no sense to convert them into a string.
* @param messageSeparator It may be that data packets are split or merged when transmitted over TCP in order to optimize the transmission. For example, if you send "Hello" two times in a row very quickly, it can happen that "HelloHa" and "llo" arrive. To counteract this circumstance there are options to separate the data packets.
* @param customServerID Optionally you can assign your own ServerID like "myAuthentificationServer" or "fileServer"
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void startTCPServer(FString& serverID, FString IP = FString("0.0.0.0"), int32 port = 8888, EReceiveFilterServer receiveFilter = EReceiveFilterServer::E_SAB,
ESocketServerTCPSeparator messageSeparator = ESocketServerTCPSeparator::E_None, FString customServerID = FString(""));
void startTCPServerNontStatic(FString& serverID, FString IP, int32 port, EReceiveFilterServer receiveFilter, ESocketServerTCPSeparator messageSeparator,
FString customServerID, bool isFileServer, FString Aes256bitKey, bool resumeFiles, bool writeHandShakeToLogEditorOnly);
/**
* Starts a file server that can receive and send files in response to requests from clients. The files are streamed and do not consume RAM. To determine what can be uploaded or downloaded and how, the tokens are used.
* @param domainOrIP IP or Domain to listen
* @param port port to listen
* @param receiveFilter This allows you to decide which data type you want to receive. If you receive files it makes no sense to convert them into a string.
* @param customServerID Optionally you can assign your own ServerID like "myAuthentificationServer" or "fileServer"
* @param Aes256bitKey The AES key must consist of 32 ASCII characters. The communication between client and server is encrypted via AES in 256bit. Therefore a key must be entered.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void startTCPFileServer(FString& serverID, FString IP = FString("0.0.0.0"), int32 port = 8899, FString customServerID = FString(""), FString Aes256bitKey = FString(""), bool resumeFiles = false, bool writeHandShakeToLogEditorOnly = false);
/**
* Tokens are used to determine what can be uploaded or downloaded and how. It can be used to specify a directory in which a file is stored or to specify a file that can be sent to a client.
* @param deleteTokenAfterUse If true, the token will be deleted after one use.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void addFileToken(FString token, bool deleteTokenAfterUse, EFileFunctionsSocketServerDirectoryType directoryType, FString filePathOrDirectory);
/**
* Tokens are used to determine what can be uploaded or downloaded and how. It can be used to specify a directory in which a file is stored or to specify a file that can be sent to a client.
* @param fileTokens Key= token, value = file path or directory
* @param deleteTokenAfterUse If true, the token will be deleted after one use.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void addFileTokens(TMap<FString, FString> fileTokens, bool deleteAfterUse, EFileFunctionsSocketServerDirectoryType directoryType);
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void removeFileToken(FString token);
/**
*Stop TCP Server
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void stopTCPServer(FString optionalServerID);
void stopTCPServerNonSTatic(FString optionalServerID);
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void stopAllTCPServers();
void stopAllTCPServersNonStatic();
//UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP", meta = (AutoCreateRefTerm = "userIdsToDirectoriesMap"))
// void startTCPFileserver(FString& serverID, TMap<FString, FString> userIdsToDirectoriesMap, FString IP = FString("0.0.0.0"), int32 port = 9999, FString downloadDirectory = FString("Content/"), EFileFunctionsSocketServerDirectoryType directoryType = EFileFunctionsSocketServerDirectoryType::E_gd, EHTTPSocketServerFileDownloadResumeType ifFileExistThen = EHTTPSocketServerFileDownloadResumeType::E_RESUME, FString customServerID = FString(""), bool onlyWithToken = true);
//UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
// void addFileserverToken(FString token, int32 lifeTimeSeconds,bool reusable);
//UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
// void removeFileserverToken(FString token);
/**
*Send data to clients
*@param clientSessionIDs array with client sessionIDs
*@param message data as string
*@param byteArray data as bytes
*@param addLineBreak add linebreak to message
*@param serverID Id of the server you want to send data from
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP", meta = (AutoCreateRefTerm = "byteArray"))
static void socketServerSendTCPMessage(TArray<FString> clientSessionIDs, FString message, TArray<uint8> byteArray, bool addLineBreak = true, FString optionalServerID = FString(""));
void socketServerSendTCPMessageNonStatic(TArray<FString> clientSessionIDs, FString message, TArray<uint8> byteArray, bool addLineBreak = true, FString optionalServerID = FString(""));
/**
*Send data to client
*@param clientSessionID array with client sessionIDs
*@param message data as string
*@param byteArray data as bytes
*@param addLineBreak add linebreak to message
*@param serverID Id of the server you want to send data from
*@param optionalServerID With one server the field can remain empty. If there are several servers, the ServerID should be entered here or the newest server is automatically taken.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP", meta = (AutoCreateRefTerm = "byteArray"))
static void socketServerSendTCPMessageToClient(FString clientSessionID, FString message, TArray<uint8> byteArray, bool addLineBreak = true, FString optionalServerID = FString(""));
void socketServerSendTCPMessageToClientNonStatic(FString clientSessionID, FString message, TArray<uint8> byteArray, bool addLineBreak = true, FString optionalServerID = FString(""));
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void changeTCPSeparatorStringOnServer(FString separator = "(~{");
void changeTCPSeparatorStringOnServerNonStatic(FString separator);
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP")
static void changeTCPSeparatorByteOnServer(uint8 separator = 0x00);
void changeTCPSeparatorByteOnServerNonStatic(uint8 separator);
/**
*Send file to client
*@param clientSessionID client sessionID
*@param directoryWithFileName
*@param directoryType
*@param serverID Id of the server you want to send file from
*/
/*UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP", meta = (AutoCreateRefTerm = "byteArray"))
void socketServerSendTCPFile(FString clientSessionID, FString directoryWithFileName = FString("Content/image.png"), EFileFunctionsSocketServerDirectoryType directoryType = EFileFunctionsSocketServerDirectoryType::E_gd, int64 resumeFileSize =0,FString serverID = FString(""));*/
/**
*Creates a unique ID
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static FString generateUniqueID();
/**
*Resolve Domain. Only Domains. Hostnames do not work.
*@param domain
*@param useDNSCache Domain and IP are stored in RAM. Starting from the second time the IP is taken from the RAM.
*@param dnsIP
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static UDNSClientSocketServer* resolveDomain(FString domain, bool useDNSCache = true, FString dnsIP = FString("8.8.8.8"));
UDNSClientSocketServer* resolveDomainNonStatic(FString domain, bool useDNSCache = true, FString dnsIP = FString("8.8.8.8"));
/**
*UE4 uses different socket connections. When Steam is active, Steam Sockets are used for all connections. This leads to problems if you want to use Steam but not Steam Sockets. Therefore you can change the sockets.
*@param platform Auto = UE4 decides. Thirdparty platforms (Steam, Playstore etc.) will be used if configured. System = UE4 determines the OS and selects a socket type accordingly.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static void changeSocketPlatform(ESocketPlatformServer platform);
/**
* Checks if a server can listen on IP x and port y.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static bool checkPort(EServerSocketConnectionCheckPortType type, FString ip = FString("0.0.0.0"), int32 port = 8888);
bool checkPortNonStatic(EServerSocketConnectionCheckPortType type, FString ip = FString("0.0.0.0"), int32 port = 8888);
/**
* Returns a random port on which the server can listen.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer")
static int32 getRandomPort(EServerSocketConnectionCheckPortType type, FString ip = FString("0.0.0.0"));
int32 getRandomPortNonStatic(EServerSocketConnectionCheckPortType type, FString ip = FString("0.0.0.0"));
UFUNCTION(BlueprintCallable, Category = "SocketServer|Register")
static void registerClientEvent(FString sessionID, UEventBean*& event);
void registerClientEventNonStatic(FString sessionID, UEventBean*& event);
UFUNCTION(BlueprintCallable, Category = "SocketServer|Register")
static void unregisterClientEvent(FString sessionID);
void unregisterClientEventNonStatic(FString sessionID);
UEventBean* getResiteredClientEvent(FString sessionID);
//RCON
/**
* Turns a TCP server into an RCON server.
* @param passwordType If the type "As String Parameter" has been selected, the password can simply be entered as a parameter. Otherwise please select a directory incl. file.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP|RCON")
static void registerRCONServer(FString serverID, ERCONPasswordType passwordType, FString passwordOrFile, bool& success, FString& errorMessage);
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP|RCON")
static void unregiserRCONServer(FString serverID);
/**
* Sends an RCON response to an RCON client. sessionID, serverID and requestID must be specified.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|TCP|RCON")
static void sendRCONResponse(FString sessionID, FString serverID, int32 requestID, FString response);
/**
*The cleaner thread is a thread that runs endlessly and deletes data remnants from closed/broken connections from RAM.
*@param showLogs Writes to the logs when data remnants are deleted.
*@param minLiveTimeInSeconds When a connection is closed it is passed to the cleaner thread. The thread ignores the connection for "minLiveTimeInSeconds" until it clears the data. This is necessary because sometimes connections need some time to be closed completely.
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions")
static void changeCleanerThreadSettingsOnServer(bool showLogs, int32 minLiveTimeInSeconds = 10);
//number stuff
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToFloat(TArray<uint8> bytes, float& value);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToInteger(TArray<uint8> bytes, int32& value);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToInteger64(TArray<uint8> bytes, int64& value);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToFloatPure(TArray<uint8> bytes, float& value);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToIntegerPure(TArray<uint8> bytes, int32& value);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToInteger64Pure(TArray<uint8> bytes, int64& value);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToFloatEndian(TArray<uint8> bytes, float& littleEndian, float& bigEndian);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToIntegerEndian(TArray<uint8> bytes, int32& littleEndian, int32& bigEndian);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number")
static void parseBytesToInteger64Endian(TArray<uint8> bytes, int64& littleEndian, int64& bigEndian);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseFloatToBytes(TArray<uint8>& byteArray, float value, bool switchByteOrder = false);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseIntegerToBytes(TArray<uint8>& byteArray, int32 value, bool switchByteOrder = false);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseInteger64ToBytes(TArray<uint8>& byteArray, int64 value, bool switchByteOrder = false);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseFloatToBytesPure(TArray<uint8>& byteArray, float value, bool switchByteOrder = false);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseIntegerToBytesPure(TArray<uint8>& byteArray, int32 value, bool switchByteOrder = false);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseInteger64ToBytesPure(TArray<uint8>& byteArray, int64 value, bool switchByteOrder = false);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "value"))
static void parseBytesToFloatArrayPure(TArray<float>& value, TArray<uint8> bytes);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "value"))
static void parseBytesToIntegerArrayPure(TArray<int32>& value, TArray<uint8> bytes);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "value"))
static void parseBytesToInteger64ArrayPure(TArray<int64>& value, TArray<uint8> bytes);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseFloatArrayToBytesPure(TArray<uint8>& byteArray, TArray<float> value);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseIntegerArrayToBytesPure(TArray<uint8>& byteArray, TArray<int32> value);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Number", meta = (AutoCreateRefTerm = "byteArray"))
static void parseInteger64ArrayToBytesPure(TArray<uint8>& byteArray, TArray<int64> value);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Hex")
static TArray<uint8> parseHexToBytes(FString hex);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Hex")
static FString parseHexToString(FString hex);
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Hex")
static FString parseBytesToHex(TArray<uint8> bytes);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Hex")
static TArray<uint8> parseHexToBytesPure(FString hex);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Hex")
static FString parseHexToStringPure(FString hex);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SocketServer|SpecialFunctions|Hex")
static FString parseBytesToHexPure(TArray<uint8> bytes);
/**
*With this function you can start a UE game server at runtime. To close the server, you can simply start the server again on the same port. However, the default map will then be loaded.
*@param Protocol i.e. "unreal" or "http"
*@param Host Optional hostname, i.e. "204.157.115.40" or "unreal.epicgames.com", blank if local.
*@param Port Optional host port
*@param Map
* @param name, i.e. "SkyCity", default is "Entry".
* @param RedirectURL Optional place to download Map if client does not possess it
* @param Portal Portal to enter through, default is ""
*/
UFUNCTION(BlueprintCallable, Category = "SocketServer|SpecialFunctions|Multiplayer", meta = (WorldContext = worldContextObject, AutoCreateRefTerm = "Options"))
static bool startUEGameHost(UObject* worldContextObject, FString Protocol, FString Host, FString Map, FString RedirectURL, TArray<FString> Options, FString Portal, int32 Port = 7777);
static ISocketSubsystem* getSocketSubSystem();
//void addClientSession(FString key, FClientSocketSession& session);
//void removeClientSession(FString key);
//TMap<FString, FClientSocketSession> getClientSessions();
//void startTCPClientHandler(FClientSocketSession& session, EReceiveFilterServer receiveFilter);
//bool isTCPServerRun();
//void setTCPServerRun(bool run);
void getTcpSeparator(uint8& byteSeparator, FString& stringSeparator);
void cleanConnection(FSocketServerPluginSession& session);
TMap<FString, USocketServerTCP*> getTcpServerMap();
TMap<FString, FSocketServerToken> fileTokenMap;
FSocketServerCleanerThread* socketServerPluginCleanerThread = nullptr;
private:
ESocketPlatformServer systemSocketPlatform;
TMap<FString, UEventBean*> messageEvents;
TMap<FString, USocketServerUDP*> udpServers;
TMap<FString, USocketServerTCP*> tcpServers;
TMap<FString, URCONServer*> rconServers;
FString lastUDPServerID;
FString lastTCPServerID;
FString tcpStringSeparator = "(~{";
uint8 tcpByteSeparator = 0x00;
FIPandPortStruct checkIpAndPort(FString IP, int32 port);
};
@@ -0,0 +1,25 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
class SOCKETSERVER_API FSocketServerCleanerThread : public FRunnable {
public:
FSocketServerCleanerThread();
virtual uint32 Run() override;
void addSession(FSocketServerPluginSession& session);
void changeSettings(bool showLogs, int32 minLiveTimeInSeconds);
private:
bool showLogs = false;
int32 minLiveTimeInSeconds = 10;
FRunnableThread* thread = nullptr;
TQueue<FSocketServerPluginSession> sessionQueue;
};
@@ -0,0 +1,80 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
#include "SocketServerTCPThread.h"
#include "SocketServerTCPClientSendDataThread.h"
#include "SocketServerTCPClientReceiveDataThread.h"
#include "SocketServerTCPFileHandlerThread.h"
#include "SocketServerTCP.generated.h"
class USocketServerBPLibrary;
UCLASS(Blueprintable, BlueprintType)
class SOCKETSERVER_API USocketServerTCP : public UObject
{
GENERATED_UCLASS_BODY()
public:
void startTCPServer(FIPandPortStruct ipStructP,FString IP, int32 port, EReceiveFilterServer receiveFilter, ESocketServerTCPSeparator messageWrapping,
FString serverID, bool isFileServer, FString Aes256bitKey, bool resumeFiles, bool writeHandShakeToLogEditorOnly);
void stopTCPServer();
void sendTCPMessage(TArray<FString> clientSessionIDs, FString message, TArray<uint8> byteArray, bool addLineBreak);
void sendTCPMessageToClient(FString clientSessionID, FString message, TArray<uint8> byteArray, bool addLineBreak);
FIPandPortStruct getServerIpAndPortStruct();
FString getIP();
int32 getPort();
FString getServerID();
bool hasResume();
void initTCPClientThreads(FSocketServerPluginSession& session, EReceiveFilterServer receiveFilter);
void addClientSession(FSocketServerPluginSession& session);
FSocketServerPluginSession getClientSession(FString key);
void removeClientSession(FString key);
TMap<FString, FSocketServerPluginSession> getClientSessions();
//EHTTPSocketServerFileDownloadResumeType getifFileExistThen();
FString encryptMessage(FString message);
FString decryptMessage(FString message);
struct FSocketServerToken getTokenStruct(FString token);
void removeTokenFromStruct(FString token);
FString getCleanDir(EFileFunctionsSocketServerDirectoryType directoryType, FString fileDirectory);
void getMD5FromFile(FString filePathP, bool& success, FString& MD5);
void deleteFile(FString filePathP);
int64 fileSize(FString filePathP);
FString int64ToString(int64 num);
void getTcpSeparator(FString& stringSeparator, uint8& byteSeparator, ESocketServerTCPSeparator& messageWrapping);
bool isRun();
void readDataLength(TArray<uint8>& byteDataArray, int32& byteLenght);
TMap<FString, FSocketServerPluginSession> clientSessions;
private:
FSocketServerTCPThread* socketServerTCPThread = nullptr;
FSocketServerTCPFileHandlerThread* tcpFileHandlerThread = nullptr;
bool run = true;
FString serverID = FString();
FIPandPortStruct ipAndPortStruct;
FString serverIP = FString();
int32 serverPort = -1;
EReceiveFilterServer receiveFilter;
bool fileServer = false;
FString aesKey = FString();
bool resumeFiles = false;
bool writeHandShakeToLogEditorOnly = false;
//FString downloadDir;
ESocketServerTCPSeparator messageWrapping;
FString tcpStringSeparator = "(~{";
uint8 tcpByteSeparator = 0x00;
};
@@ -0,0 +1,29 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
class SOCKETSERVER_API FSocketServerTCPClientReceiveDataThread : public FRunnable {
public:
FSocketServerTCPClientReceiveDataThread(USocketServerTCP* tcpServerP,
FSocketServerPluginSession& sessionP,
EReceiveFilterServer receiveFilterP);
~FSocketServerTCPClientReceiveDataThread();
virtual uint32 Run() override;
void triggerMessageEvent(TArray<uint8>& byteDataArray, FString& sessionID, FString& serverID, bool addNullTerminator = true);
void stopThread();
private:
USocketServerTCP* tcpServer = nullptr;
FSocketServerPluginSession session;
EReceiveFilterServer receiveFilter;
FRunnableThread* thread = nullptr;
bool run = true;
bool deathConnection = false;
};
@@ -0,0 +1,34 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
class SOCKETSERVER_API FSocketServerTCPClientSendDataThread : public FRunnable {
public:
FSocketServerTCPClientSendDataThread(USocketServerTCP* tcpServerP, FSocketServerPluginSession& sessionP);
~FSocketServerTCPClientSendDataThread();
virtual uint32 Run() override;
FRunnableThread* getThread();
void setThread(FRunnableThread* threadP);
void stopThread();
bool isRun();
void setMessage(FString messageP, TArray<uint8> byteArrayP);
void sendMessage(FString messageP, TArray<uint8> byteArrayP);
void pauseThread(bool pause);
private:
USocketServerTCP* tcpServer = nullptr;
FSocketServerPluginSession session;
FRunnableThread* thread = nullptr;
FSocket* socket = nullptr;
bool run = true;
bool paused;
bool waitForInit = true;
TQueue<FString> messageQueue;
TQueue<TArray<uint8>> byteArrayQueue;
};
@@ -0,0 +1,48 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
//#include "SocketServerTCPFileHandlerThread.generated.h"
class SOCKETSERVER_API FSocketServerTCPFileHandlerThread : public FRunnable {
public:
FSocketServerTCPFileHandlerThread(USocketServerTCP* tcpServerP, FSocketServerPluginSession& sessionP);
~FSocketServerTCPFileHandlerThread();
virtual uint32 Run() override;
void doRequestFileFromServer(FSocketServerToken tokenStruct);
void doSendFileToClient(FString message);
void triggerFileOverTCPProgress(FString sessionIDP, FString filePathP, float percentP, float mbitP, int64 bytesReceivedP, int64 fileSizeP);
void triggerFileTransferOverTCPInfoEvent(FString messageP, FString sessionIDP, FString filePathP, bool successP);
void sendEndMessage(FString fullFilePathP, FString tokenP, FString md5ClientP);
FString readMessageFromClient();
void sendMessageToClient(FString message);
void stopThread();
private:
USocketServerTCP* tcpServer = nullptr;
FSocketServerPluginSession session;
bool fileServer = false;
FRunnableThread* thread = nullptr;
bool run = true;
//int32 commandProgress = 0;
int64 fileSize = 0;
double WaitForRead = 30;
//FSocketServerTCPClientSendFileToThread* sendFileThread = nullptr;
};
//UCLASS(Blueprintable, BlueprintType)
//class USocketServerTCPFileHandler : public UObject
//{
// GENERATED_UCLASS_BODY()
//
// UFUNCTION()
// void receiveData(const FString sessionID, const FString message, const TArray<uint8>& byteArray, const FString serverID);
//};
@@ -0,0 +1,23 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
class SOCKETSERVER_API FSocketServerTCPThread : public FRunnable {
public:
FSocketServerTCPThread(USocketServerTCP* tcpServerP, EReceiveFilterServer receiveFilterP, bool& runP);
~FSocketServerTCPThread();
virtual uint32 Run() override;
void stopThread();
private:
USocketServerTCP* tcpServer;
EReceiveFilterServer receiveFilter;
bool& run;
FRunnableThread* thread = nullptr;
};
@@ -0,0 +1,59 @@
// Copyright 2017 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
#include "SocketServerUDPThread.h"
#include "SocketServerUDPClientSendDataThread.h"
#include "SocketServerUDP.generated.h"
class USocketServerBPLibrary;
class FSocketServerUDPThread;
class FSocketServerUDPClientSendDataThread;
UCLASS(Blueprintable, BlueprintType)
class SOCKETSERVER_API USocketServerUDP : public UObject
{
GENERATED_UCLASS_BODY()
public:
void startUDPServer(FIPandPortStruct ipStruct,FString IP, int32 port, bool multicast, EReceiveFilterServer receiveFilter, FString serverID, int32 maxPacketSize);
void stopUDPServer();
void sendUDPMessage(TArray<FString> clientSessionIDs, FString message, TArray<uint8> byteArray, bool asynchronous, ESocketServerUDPSocketType socketType);
void sendUDPMessageToClient(FString clientSessionID, FString message, TArray<uint8> byteArray, bool asynchronous, ESocketServerUDPSocketType socketType);
void sendUDPMessageTo(FString ip, int32 port, FString message, TArray<uint8> byteArray, bool asynchronous);
//do not work with ipv6
//void UDPReceiver(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt);
void UDPReceiverSocketServerPlugin(FArrayReaderPtr& ArrayReaderPtr, TSharedRef<FInternetAddr> remoteAddress);
FIPandPortStruct getServerIpAndPortStruct();
FString getIP();
int32 getPort();
void setSocketReceiver(FUdpSocketReceiver* socketReceiverP, FSocket* socket);
FUdpSocketReceiver* getSocketReceiver();
FSocket* getSocket();
FString getServerID();
void addClientSession(FSocketServerPluginSession& session);
FSocketServerPluginSession* getClientSession(FString key);
void removeClientSession(FString key);
TMap<FString, FSocketServerPluginSession> getClientSessions();
void sendBytes(FSocket*& socket, TArray<uint8>& bytes, int32& sent, TSharedRef<FInternetAddr>& addr);
private:
FString serverID;
FIPandPortStruct ipAndPortStruct;
FString serverIP;
int32 serverPort = -1;
int32 maxPacketSize = 65507;
FSocket* socket= nullptr;
FUdpSocketReceiver* socketReceiver = nullptr;
EReceiveFilterServer receiveFilter;
FSocketServerUDPThread* serverThread = nullptr;
FSocketServerUDPClientSendDataThread* sendThread = nullptr;
TMap<FString, FSocketServerPluginSession> clientSessions;
};
@@ -0,0 +1,30 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
class SOCKETSERVER_API FSocketServerUDPClientSendDataThread : public FRunnable {
public:
FSocketServerUDPClientSendDataThread(USocketServerUDP* udpServerP);
~FSocketServerUDPClientSendDataThread();
virtual uint32 Run() override;
FRunnableThread* getThread();
void setThread(FRunnableThread* threadP);
void stopThread();
bool isRun();
void sendMessage(FString ip, int32 port, FString message, TArray<uint8> bytes, FSocket* socketUDP);
void pauseThread(bool pause);
private:
USocketServerUDP* udpServer;
FRunnableThread* thread = nullptr;
bool run = true;
bool paused;
TQueue<FSendUDPMessageStruct> messageQueue;
};
@@ -0,0 +1,28 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#pragma once
#include "SocketServer.h"
class SOCKETSERVER_API FSocketServerUDPThread : public FRunnable {
public:
FSocketServerUDPThread(USocketServerUDP* udpServerP, bool multicastP);
~FSocketServerUDPThread();
virtual uint32 Run() override;
void stopThread();
private:
FString message;
USocketServerUDP* udpServer;
bool multicast;
FRunnableThread* thread = nullptr;
bool run = true;
};
@@ -0,0 +1,57 @@
// Copyright 2017-2020 David Romanski (Socke). All Rights Reserved.
using UnrealBuildTool;
public class SocketServer : ModuleRules
{
public SocketServer(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Sockets",
"Networking",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Sockets",
"Networking",
"Slate",
"SlateCore"
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}