增加多屏显示,视频播放

This commit is contained in:
liuyunhui
2025-09-19 14:40:23 +08:00
parent fded7b9a51
commit 9076a5ea41
112 changed files with 24986 additions and 21 deletions
@@ -0,0 +1,115 @@
// Copyright 2017-2019 David Romanski (Socke). All Rights Reserved.
#include "DNSClientSocketClient.h"
UDNSClientSocketClient::UDNSClientSocketClient(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
}
void UDNSClientSocketClient::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, &UDNSClientSocketClient::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 UDNSClientSocketClient::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;
}
bool UDNSClientSocketClient::isResloving() {
return resolving;
}
FString UDNSClientSocketClient::getIP() {
return ip;
}
@@ -0,0 +1,572 @@
// Copyright 2017-2019 David Romanski (Socke). All Rights Reserved.
#include "FileFunctionsSocketClient.h"
UFileFunctionsSocketClient* UFileFunctionsSocketClient::fileFunctionsSocketClient;
UFileFunctionsSocketClient::UFileFunctionsSocketClient(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {
fileFunctionsSocketClient = this;
}
UFileFunctionsSocketClient* UFileFunctionsSocketClient::getFileFunctionsSocketClientTarget() {
return fileFunctionsSocketClient;
}
FString UFileFunctionsSocketClient::getCleanDirectory(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
if (directoryType == EFileFunctionsSocketClientDirectoryType::E_ad) {
return FPaths::ConvertRelativePathToFull(filePath);
}
else {
FString ProjectDir = FPaths::ProjectDir();
return FPaths::ConvertRelativePathToFull(ProjectDir + filePath);
}
}
void UFileFunctionsSocketClient::writeBytesToFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, TArray<uint8> bytes, bool& success) {
success = FFileHelper::SaveArrayToFile(bytes, *getCleanDirectory(directoryType, filePath));
}
void UFileFunctionsSocketClient::addBytesToFileAndCloseIt(EFileFunctionsSocketClientDirectoryType 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 UFileFunctionsSocketClient::splittFile(EFileFunctionsSocketClientDirectoryType 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> UFileFunctionsSocketClient::readBytesFromFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool& success) {
TArray<uint8> result;
success = FFileHelper::LoadFileToArray(result, *getCleanDirectory(directoryType, filePath));
return result;
}
void UFileFunctionsSocketClient::readStringFromFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool& success, FString& data) {
data.Empty();
success = FFileHelper::LoadFileToString(data, *getCleanDirectory(directoryType, filePath));
}
void UFileFunctionsSocketClient::writeStringToFile(EFileFunctionsSocketClientDirectoryType directoryType, FString data, FString filePath, EFileFunctionsSocketClientEncodingOptions fileEncoding, bool& success) {
success = FFileHelper::SaveStringToFile(data, *getCleanDirectory(directoryType, filePath), (FFileHelper::EEncodingOptions)fileEncoding);
}
void UFileFunctionsSocketClient::getMD5FromFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool& success, FString& MD5) {
getMD5FromFileAbsolutePath(getCleanDirectory(directoryType, filePath), success, MD5);
}
void UFileFunctionsSocketClient::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 UFileFunctionsSocketClient::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 UFileFunctionsSocketClient::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 UFileFunctionsSocketClient::bytesToBase64String(TArray<uint8> bytes, FString& base64String) {
base64String.Empty();
base64String = FBase64::Encode(bytes);
}
TArray<uint8> UFileFunctionsSocketClient::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 UFileFunctionsSocketClient::fileToBase64String(EFileFunctionsSocketClientDirectoryType 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 UFileFunctionsSocketClient::fileExists(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
return FPaths::FileExists(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketClient::fileExistsAbsolutePath(FString filePath) {
return FPaths::FileExists(*filePath);
}
bool UFileFunctionsSocketClient::directoryExists(EFileFunctionsSocketClientDirectoryType directoryType, FString path) {
return FPaths::DirectoryExists(*getCleanDirectory(directoryType, path));
}
int64 UFileFunctionsSocketClient::fileSize(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().FileSize(*getCleanDirectory(directoryType, filePath));
}
int64 UFileFunctionsSocketClient::fileSizeAbsolutePath(FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().FileSize(*filePath);
}
bool UFileFunctionsSocketClient::deleteFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketClient::deleteFileAbsolutePath(FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*filePath);
}
bool UFileFunctionsSocketClient::deleteDirectory(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().DeleteDirectory(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketClient::isReadOnly(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().IsReadOnly(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketClient::moveFile(EFileFunctionsSocketClientDirectoryType directoryTypeTo, FString filePathTo, EFileFunctionsSocketClientDirectoryType directoryTypeFrom, FString filePathFrom) {
return FPlatformFileManager::Get().GetPlatformFile().MoveFile(*getCleanDirectory(directoryTypeTo, filePathTo), *getCleanDirectory(directoryTypeFrom, filePathFrom));
}
bool UFileFunctionsSocketClient::setReadOnly(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool bNewReadOnlyValue) {
return FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*getCleanDirectory(directoryType, filePath), bNewReadOnlyValue);
}
FDateTime UFileFunctionsSocketClient::getTimeStamp(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().GetTimeStamp(*getCleanDirectory(directoryType, filePath));
}
void UFileFunctionsSocketClient::setTimeStamp(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, FDateTime DateTime) {
FPlatformFileManager::Get().GetPlatformFile().SetTimeStamp(*getCleanDirectory(directoryType, filePath), DateTime);
}
FDateTime UFileFunctionsSocketClient::getAccessTimeStamp(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().GetAccessTimeStamp(*getCleanDirectory(directoryType, filePath));
}
FString UFileFunctionsSocketClient::getFilenameOnDisk(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
return FPlatformFileManager::Get().GetPlatformFile().GetFilenameOnDisk(*getCleanDirectory(directoryType, filePath));
}
bool UFileFunctionsSocketClient::createDirectory(EFileFunctionsSocketClientDirectoryType directoryType, FString path) {
return FPlatformFileManager::Get().GetPlatformFile().CreateDirectory(*getCleanDirectory(directoryType, path));
}
void UFileFunctionsSocketClient::getAllFilesFromDirectory(EFileFunctionsSocketClientDirectoryType 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();
}
FFileFunctionsSocketClientOpenFile UFileFunctionsSocketClient::openFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath){
FArchive* writer = IFileManager::Get().CreateFileWriter(*getCleanDirectory(directoryType, filePath), EFileWrite::FILEWRITE_Append);
FFileFunctionsSocketClientOpenFile file;
file.writer = writer;
return file;
}
int64 UFileFunctionsSocketClient::addBytesToFile(FFileFunctionsSocketClientOpenFile 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 UFileFunctionsSocketClient::closeFile(FFileFunctionsSocketClientOpenFile openFile){
if (openFile.writer != nullptr) {
openFile.writer->Close();
openFile.writer = nullptr;
}
}
bool UFileFunctionsSocketClient::encryptFileWithAES(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, FString newFileName, FString key, bool writeEncryptedFileSizeToFile){
if (filePath.IsEmpty() || newFileName.IsEmpty() || key.Len() != 32) {
UE_LOG(LogTemp, Error, TEXT("encryptFileWithAES: FilePath or newFileName empty or wrong key length. The key must consist of 32 ANSI compatible characters."));
return false;
}
FString dir = getCleanDirectory(directoryType, filePath);
FString fileName = FPaths::GetCleanFilename(dir);
int64 fileSize = FPlatformFileManager::Get().GetPlatformFile().FileSize(*dir);
TArray64<uint8> data;
if (!FFileHelper::LoadFileToArray(data, *dir)) {
UE_LOG(LogTemp, Error, TEXT("encryptFileWithAES: File could not be loaded: %s"),*dir);
return false;
}
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));
dir = dir.Replace(*fileName, *newFileName);
if (FPaths::FileExists(*dir)) {
FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*dir);
}
FArchive* writer = IFileManager::Get().CreateFileWriter(*dir, EFileWrite::FILEWRITE_Append);
if (!writer) {
UE_LOG(LogTemp, Error, TEXT("encryptFileWithAES: File could not be saved: %s"), *dir);
data.Empty();
return false;
}
if (writeEncryptedFileSizeToFile) {
TArray<uint8> byteArray;
union {
int64 tmpVal;
uint8 tmpArray[8];
} u;
u.tmpVal = fileSize;
byteArray.AddZeroed(8);
FMemory::Memcpy(byteArray.GetData(), u.tmpArray, 8);
writer->Serialize(byteArray.GetData(), byteArray.Num());
byteArray.Empty();
}
writer->Serialize(data.GetData(), data.Num());
writer->Close();
delete writer;
data.Empty();
return true;
}
bool UFileFunctionsSocketClient::decryptFileWithAES(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, FString newFileName, FString key,
bool hasEncryptedFileSizeInFile, int64 originalFileLength){
if (filePath.IsEmpty() || newFileName.IsEmpty() || key.Len() != 32) {
UE_LOG(LogTemp, Error, TEXT("decryptFileWithAES: FilePath or newFileName empty or wrong key length. The key must consist of 32 ANSI compatible characters."));
return false;
}
FString dir = getCleanDirectory(directoryType, filePath);
FString fileName = FPaths::GetCleanFilename(dir);
int64 fileSize = FPlatformFileManager::Get().GetPlatformFile().FileSize(*dir);
FArchive* reader = IFileManager::Get().CreateFileReader(*dir);
if (!reader) {
UE_LOG(LogTemp, Error, TEXT("decryptFileWithAES: File could not be loaded: %s"), *dir);
return false;
}
if (hasEncryptedFileSizeInFile) {
TArray<uint8> bytes;
bytes.AddZeroed(8);
reader->Serialize(bytes.GetData(), bytes.Num());
FMemory::Memcpy(&originalFileLength, bytes.GetData(), 8);
bytes.Empty();
fileSize = fileSize-8;
}
if (fileSize < originalFileLength) {
originalFileLength = fileSize;
UE_LOG(LogTemp, Warning, TEXT("decryptFileWithAES: The encrypted file size is smaller than the original file size. File may be corrupted: %s"), *dir);
return false;
}
TArray64<uint8> data;
data.AddUninitialized(fileSize);
reader->Serialize(data.GetData(), data.Num());
reader->Close();
delete reader;
const int64 encryptedFileSize = Align(data.Num(), FAES::AESBlockSize);
if (data.Num() < encryptedFileSize) {
UE_LOG(LogTemp, Error, TEXT("decryptFileWithAES: Wrong data length. File could not be decrypted: %s"),*dir);
return false;
}
FAES::DecryptData(data.GetData(), encryptedFileSize, TCHAR_TO_ANSI(*key));
dir = dir.Replace(*fileName, *newFileName);
if (FPaths::FileExists(*dir)) {
FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*dir);
}
FArchive* writer = IFileManager::Get().CreateFileWriter(*dir, EFileWrite::FILEWRITE_Append);
if (!writer) {
UE_LOG(LogTemp, Error, TEXT("encryptFileWithAES: File could not be saved: %s"), *dir);
data.Empty();
return false;
}
writer->Serialize(data.GetData(), originalFileLength);
writer->Close();
delete writer;
data.Empty();
return true;
}
FString UFileFunctionsSocketClient::encryptMessageWithAES(FString message, FString key) {
if (message.IsEmpty() || key.Len() != 32) {
UE_LOG(LogTemp, Error, TEXT("encryptMessageWithAES:Message empty or wrong key length. The key must consist of 32 ANSI compatible characters."));
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);
data.Empty();
return encryptedBase64String;
}
FString UFileFunctionsSocketClient::decryptMessageWithAES(FString message, FString key) {
if (message.IsEmpty() || key.Len() != 32) {
UE_LOG(LogTemp, Error, TEXT("decryptMessageFromAES: Message empty or wrong key length. The key must consist of 32 ANSI compatible characters."));
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. Message could not be decrypted."));
return FString();
}
FAES::DecryptData(data.GetData(), encryptedFileSize, TCHAR_TO_ANSI(*key));
FString s = FString(UTF8_TO_TCHAR((char*)data.GetData()));
data.Empty();
return s;
}
TArray<uint8> UFileFunctionsSocketClient::FStringToByteArray(FString s) {
FTCHARToUTF8 Convert(*s);
TArray<uint8> data;
data.Append((uint8*)Convert.Get(), Convert.Length());
return data;
}
FString UFileFunctionsSocketClient::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 UFileFunctionsSocketClient::readBytesFromFileInPartsAsync(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, int32 bufferSize, float delayBetweenReadsInSeconds) {
UFileFunctionsSocketClient::getFileFunctionsSocketClientTarget()->readBytesFromFileInPartsAsyncInternal(directoryType, filePath, bufferSize, delayBetweenReadsInSeconds);
}
void UFileFunctionsSocketClient::readBytesFromFileInPartsAsyncInternal(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, int32 bufferSize, float delayBetweenReadsInSeconds) {
FString dir = UFileFunctionsSocketClient::getCleanDirectory(directoryType, filePath);
if (readFileInPartsThreads.Find(*dir) != nullptr) {
UE_LOG(LogTemp, Warning, TEXT("ReadBytesFromFileInPartsAsync: %s is being read already. Operation canceled."), *dir);
return;
}
FReadFileInPartsSocketClientThread* readThread = new FReadFileInPartsSocketClientThread(dir, bufferSize, delayBetweenReadsInSeconds);
readFileInPartsThreads.Add(dir, readThread);
}
void UFileFunctionsSocketClient::cancelReadBytesFromFileInParts(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
UFileFunctionsSocketClient::getFileFunctionsSocketClientTarget()->cancelReadBytesFromFileInPartsInternal(directoryType, filePath);
}
void UFileFunctionsSocketClient::cancelReadBytesFromFileInPartsInternal(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) {
FString dir = UFileFunctionsSocketClient::getCleanDirectory(directoryType, filePath);
if (readFileInPartsThreads.Find(*dir) != nullptr) {
(*readFileInPartsThreads.Find(*dir))->stopThread();
}
}
void UFileFunctionsSocketClient::cleanReadBytesFromFileInParts(FString cleanDir) {
if (readFileInPartsThreads.Find(*cleanDir) != nullptr) {
readFileInPartsThreads.Remove(*cleanDir);
}
}
//void UFileFunctionsSocketClient::changeDelayInBytesFromFileInParts(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, float delayBetweenReadsInSeconds) {
// FString dir = UFileFunctionsSocketClient::getCleanDirectory(directoryType, filePath);
//
// if (readFileInPartsThreads.Find(*dir) != nullptr) {
// (*readFileInPartsThreads.Find(*dir))->setDelayBetweenReadsInSeconds(delayBetweenReadsInSeconds);
// }
//}
@@ -0,0 +1,23 @@
// Copyright 2017-2019 David Romanski (Socke). All Rights Reserved.
#include "SocketClient.h"
#define LOCTEXT_NAMESPACE "FSocketClientModule"
bool FSocketClientModule::isShuttingDown;
void FSocketClientModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FSocketClientModule::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.
isShuttingDown = true;
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FSocketClientModule, SocketClient)
@@ -0,0 +1,82 @@
// Copyright 2022 David Romanski(Socke). All Rights Reserved.
#include "SocketClientAsyncNodes.h"
/*--- TCP -------------------------------------------------------------------------------------------------------------*/
UTCPConnectAsyncNode* UTCPConnectAsyncNode::socketClientTCPConnectionAsyncNode(FString domainOrI, ESocketClientIPType ipType, int32 port,
EReceiveFilterClient receiveFilters, ESocketClientTCPSeparator messageWrapping, FString optionalCustomConnectionID, bool disableNaglesAlgorithm) {
UTCPConnectAsyncNode* instance = NewObject<UTCPConnectAsyncNode>();
instance->domainOrIP = domainOrI;
instance->ipType = ipType;
instance->port = port;
instance->receiveFilters = receiveFilters;
instance->messageWrapping = messageWrapping;
instance->optionalCustomConnectionID = optionalCustomConnectionID;
instance->disableNaglesAlgorithm = disableNaglesAlgorithm;
instance->AddToRoot();
//The node is only visible in Blueprints if the function is declared in a UBlueprintAsyncActionBase class.
//Therefore an instance of itself is created here.
return instance;
}
void UTCPConnectAsyncNode::Activate() {
USocketClientBPLibrary::getSocketClientTarget()->connectSocketClientTCPNonStatic(domainOrIP, ipType, port, receiveFilters,
messageWrapping,optionalCustomConnectionID, connectionID, this, disableNaglesAlgorithm);
}
void UTCPConnectAsyncNode::triggerConnectionEvent(bool success, FString clientConnectionID, FString serverMessage){
if (success) {
OnConnect.Broadcast(serverMessage, clientConnectionID, "", TArray<uint8>());
}
else {
OnDisconnect.Broadcast(serverMessage, clientConnectionID, "", TArray<uint8>());
}
}
void UTCPConnectAsyncNode::triggerMessageEvent(TArray<uint8> byteDataArray, FString clientConnectionID, FString serverMessage){
OnServerMessage.Broadcast("",clientConnectionID,serverMessage,byteDataArray);
}
/*--- UDP -------------------------------------------------------------------------------------------------------------*/
UUDPInitAsyncNode* UUDPInitAsyncNode::socketClientInitUDPReceiverAsyncNode(FString domainOrIP,ESocketClientIPType ipType, int32 port,
EReceiveFilterClient receiveFilter, int32 maxPacketSize) {
UUDPInitAsyncNode* instance = NewObject<UUDPInitAsyncNode>();
instance->domainOrIP = domainOrIP;
instance->ipType = ipType;
instance->port = port;
instance->receiveFilter = receiveFilter;
instance->maxPacketSize = maxPacketSize;
instance->AddToRoot();
//The node is only visible in Blueprints if the function is declared in a UBlueprintAsyncActionBase class.
//Therefore an instance of itself is created here.
return instance;
}
void UUDPInitAsyncNode::Activate() {
USocketClientBPLibrary::getSocketClientTarget()->socketClientInitUDPReceiverNonStatic(connectionID, this,domainOrIP, ipType, port,
receiveFilter, maxPacketSize);
}
void UUDPInitAsyncNode::triggerInitEvent(bool success, FString clientConnectionID, FString serverMessage) {
if (success) {
OnSuccess.Broadcast(serverMessage, clientConnectionID,"",0, "", TArray<uint8>());
}
else {
OnFail.Broadcast(serverMessage, clientConnectionID, "",0,"", TArray<uint8>());
}
}
void UUDPInitAsyncNode::triggerMessageEvent(TArray<uint8> byteDataArray, FString clientConnectionID, FString serverMessage, FString peerIP, int32 peerPort) {
OnServerMessage.Broadcast("", clientConnectionID,peerIP, peerPort, serverMessage, byteDataArray);
}
@@ -0,0 +1,734 @@
// Copyright 2017-2019 David Romanski (Socke). All Rights Reserved.
#include "SocketClientBPLibrary.h"
#include "SocketClient.h"
USocketClientBPLibrary* USocketClientBPLibrary::socketClientBPLibrary;
USocketClientBPLibrary::USocketClientBPLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
socketClientBPLibrary = this;
if (socketClientCleanerThread == nullptr)
socketClientCleanerThread = new FSocketClientCleanerThread();
}
/*Delegate functions*/
void USocketClientBPLibrary::socketClientTCPConnectionEventDelegate(const bool success, const FString message, const FString clientConnectionIDP) {}
void USocketClientBPLibrary::receiveTCPMessageEventDelegate(const FString message, const TArray<uint8>& byteArray, const FString clientConnectionIDP) {}
void USocketClientBPLibrary::socketClientUDPConnectionEventDelegate(const bool success, const FString message, const FString clientConnectionID) {}
void USocketClientBPLibrary::receiveUDPMessageEventDelegate(const FString message, const TArray<uint8>& byteArray, const FString IP, const int32 port,const FString clientConnectionID) {}
void USocketClientBPLibrary::transferFileOverTCPProgressEventDelegate(const FString clientConnectionID, const FString filePath, const float percent, const float mbit, const int64 bytesSend, const int64 fileSize){}
void USocketClientBPLibrary::fileTransferOverTCPInfoEventDelegate(const FString message, const FString clientConnectionID, const FString filePath, const bool success){}
void USocketClientBPLibrary::readBytesFromFileInPartsEventDelegate(const int64 fileSize, const int64 position, const bool end, const TArray<uint8>& byteArray){}
USocketClientBPLibrary::~USocketClientBPLibrary() {
}
USocketClientBPLibrary* USocketClientBPLibrary::getSocketClientTarget() {
return socketClientBPLibrary;
}
FString USocketClientBPLibrary::getLocalIP() {
bool canBind = false;
TSharedRef<FInternetAddr> localIp = USocketClientBPLibrary::getSocketClientTarget()->getSocketSubSystem()->GetLocalHostAddr(*GLog, canBind);
if (localIp->IsValid()) {
FString localIP = localIp->ToString(false);
if (localIP.Equals("127.0.0.1")) {
UE_LOG(LogTemp, Error, TEXT("Could not detect the local IP."));
return "0.0.0.0";
}
return localIp->ToString(false);
}
else {
UE_LOG(LogTemp, Error, TEXT("Could not detect the local IP."));
}
return "0.0.0.0";
}
void USocketClientBPLibrary::connectSocketClientTCP(FString domainOrIP, ESocketClientIPType ipType, int32 port, EReceiveFilterClient receiveFilters,
ESocketClientTCPSeparator messageSeparator, FString optionalCustomConnectionID, FString& connectionID, bool disableNaglesAlgorithm){
USocketClientBPLibrary::getSocketClientTarget()->connectSocketClientTCPNonStatic(domainOrIP, ipType, port, receiveFilters,
messageSeparator,optionalCustomConnectionID, connectionID, nullptr,disableNaglesAlgorithm);
}
void USocketClientBPLibrary::connectSocketClientTCPNonStatic(FString domain, ESocketClientIPType ipType, int32 port, EReceiveFilterClient receiveFilter,
ESocketClientTCPSeparator messageSeparator, FString optionalCustomConnectionID, FString& connectionID, UTCPConnectAsyncNode* tcpConnectAsyncNode, bool noPacketDelay) {
USocketClientTCPClient* tcpClient = NewObject<USocketClientTCPClient>(USocketClientTCPClient::StaticClass());
if (optionalCustomConnectionID.IsEmpty()) {
connectionID = FGuid::NewGuid().ToString();
}
else {
connectionID = optionalCustomConnectionID;
if (tcpClients.Find(connectionID) != nullptr) {
FString serverMessage = "An existing connection with this ID was found. Connection establishment was aborted";
UE_LOG(LogTemp, Warning, TEXT("An existing connection with this ID was found. Connection establishment was aborted. %s"), *connectionID);
onsocketClientTCPConnectionEventDelegate.Broadcast(false, serverMessage, connectionID);
tcpClient->onsocketClientTCPConnectionEventDelegate.Broadcast(false, serverMessage, connectionID);
if (tcpConnectAsyncNode != nullptr) {
tcpConnectAsyncNode->triggerConnectionEvent(false, connectionID, serverMessage);
}
return;
}
}
tcpClients.Add(connectionID, tcpClient);
tcpClient->connect(this, domain, ipType, port, receiveFilter, messageSeparator, connectionID,tcpConnectAsyncNode, noPacketDelay, false);
}
void USocketClientBPLibrary::socketClientSendTCP(FString connectionID, FString message, TArray<uint8> byteArray, bool addLineBreak){
USocketClientBPLibrary::getSocketClientTarget()->socketClientSendTCPNonStatic(connectionID, message, byteArray, addLineBreak);
}
void USocketClientBPLibrary::socketClientSendTCPNonStatic(FString connectionID,FString message, TArray<uint8> byteArray, bool addLineBreak) {
if (connectionID.IsEmpty() || tcpClients.Find(connectionID) == nullptr) {
//don't send to many error messages. one second = 10000000 ticks
if (((FDateTime::Now().GetTicks()) - lastErrorMessageTime) >= 10000000) {
UE_LOG(LogTemp, Error, TEXT("Connection not found (socketClientSendTCPMessage). %s"), *connectionID);
lastErrorMessageTime = FDateTime::Now().GetTicks();
}
return;
}
if (message.Len() > 0) {
if (addLineBreak) {
message.Append("\r\n");
}
}
USocketClientTCPClient* tcpClient = *tcpClients.Find(connectionID);
tcpClient->sendMessage(message, byteArray);
}
void USocketClientBPLibrary::socketClientSendFileOverTCP(FString& connectionID, FString domainOrIP, ESocketClientIPType ipType, int32 port,
EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, FString token, FString Aes256bitKey){
USocketClientBPLibrary::getSocketClientTarget()->socketClientSendFileOverTCPNonStatic(connectionID, domainOrIP, ipType,
port, directoryType, filePath, token, Aes256bitKey);
}
void USocketClientBPLibrary::socketClientSendFileOverTCPNonStatic(FString& connectionID, FString domainOrIP, ESocketClientIPType ipType,
int32 port, EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, FString token, FString Aes256bitKey){
USocketClientTCPClient* tcpClient = NewObject<USocketClientTCPClient>(USocketClientTCPClient::StaticClass());
connectionID = FGuid::NewGuid().ToString();
tcpClients.Add(connectionID, tcpClient);
tcpClient->sendFile(this, connectionID, domainOrIP, ipType, port, directoryType, filePath, token, Aes256bitKey);
}
void USocketClientBPLibrary::socketClientRequestFileOverTCP(FString& connectionID, FString domainOrIP, ESocketClientIPType ipType, int32 port,
EFileFunctionsSocketClientDirectoryType directoryType, FString downloadDirectory, bool resume, FString token, FString Aes256bitKey){
USocketClientBPLibrary::getSocketClientTarget()->socketClientRequestFileOverTCPNonStatic(connectionID, domainOrIP, ipType, port,
directoryType, downloadDirectory, resume, token, Aes256bitKey);
}
void USocketClientBPLibrary::socketClientRequestFileOverTCPNonStatic(FString& connectionID, FString domainOrIP, ESocketClientIPType ipType, int32 port,
EFileFunctionsSocketClientDirectoryType directoryType, FString downloadDirectory, bool resume, FString token, FString Aes256bitKey) {
USocketClientTCPClient* tcpClient = NewObject<USocketClientTCPClient>(USocketClientTCPClient::StaticClass());
connectionID = FGuid::NewGuid().ToString();
tcpClients.Add(connectionID, tcpClient);
tcpClient->requestFile(this, connectionID, domainOrIP, ipType, port, directoryType, downloadDirectory, resume,token, Aes256bitKey);
}
void USocketClientBPLibrary::closeSocketClientConnectionTCP(FString connectionID){
USocketClientBPLibrary::getSocketClientTarget()->closeSocketClientConnectionTCPNonStatic(connectionID);
}
void USocketClientBPLibrary::closeSocketClientConnectionTCPNonStatic(FString connectionID){
if (connectionID.IsEmpty()) {
return;
}
if (tcpClients.Find(connectionID) == nullptr) {
UE_LOG(LogTemp, Error, TEXT("Connection not found (closeSocketClientTCPConnection). %s"), *connectionID);
return;
}
USocketClientTCPClient* tcpClient = *tcpClients.Find(connectionID);
if (tcpClient != nullptr) {
if (tcpClient->isRun()) {
tcpClient->closeConnection();
}
}
tcpClients.Remove(connectionID);
tcpClient = nullptr;
}
void USocketClientBPLibrary::closeAllSocketClientConnectionsTCP(){
USocketClientBPLibrary::getSocketClientTarget()->closeAllSocketClientConnectionsTCPNonStatic();
}
void USocketClientBPLibrary::closeAllSocketClientConnectionsTCPNonStatic(){
TArray<FString> tmpArray;
tcpClients.GetKeys(tmpArray);
for (int32 i = 0; i < tmpArray.Num(); i++){
closeSocketClientConnectionTCPNonStatic(tmpArray[i]);
}
}
void USocketClientBPLibrary::getTCPConnectionByConnectionID(FString connectionID, bool& found, USocketClientTCPClient*& connection){
USocketClientBPLibrary::getSocketClientTarget()->getTCPConnectionByConnectionIDNonStatic(connectionID, found, connection);
}
void USocketClientBPLibrary::getTCPConnectionByConnectionIDNonStatic(FString connectionID, bool& found, USocketClientTCPClient* &connection){
if (connectionID.IsEmpty() || tcpClients.Find(connectionID) == nullptr) {
found = false;
connection = nullptr;
return;
}
found = true;
connection = *tcpClients.Find(connectionID);
}
bool USocketClientBPLibrary::isTCPConnected(FString connectionID){
return USocketClientBPLibrary::getSocketClientTarget()->isTCPConnectedNonStatic(connectionID);
}
bool USocketClientBPLibrary::isTCPConnectedNonStatic(FString connectionID){
if (connectionID.IsEmpty() || tcpClients.Find(connectionID) == nullptr) {
return false;
}
USocketClientTCPClient* tcpClient = *tcpClients.Find(connectionID);
if (tcpClient->isRun() && tcpClient->isConnected()) {
return true;
}
return false;
}
void USocketClientBPLibrary::socketClientInitUDPReceiver(FString& connectionID, FString domainOrIP, ESocketClientIPType ipType, int32 port, EReceiveFilterClient receiveFilter, int32 maxPacketSize){
USocketClientBPLibrary::getSocketClientTarget()->socketClientInitUDPReceiverNonStatic(connectionID,nullptr, domainOrIP, ipType, port, receiveFilter, maxPacketSize);
}
void USocketClientBPLibrary::socketClientInitUDPReceiverNonStatic(FString& connectionID, UUDPInitAsyncNode* udpInitAsyncNode, FString domain,
ESocketClientIPType ipType, int32 port, EReceiveFilterClient receiveFilter, int32 maxPacketSize) {
FString key = domain + FString::FromInt(port);
if (udpClients.Find(key) != nullptr) {
USocketClientUDP* udpClient = *udpClients.Find(key);
connectionID = udpClient->getConnectionID();
FString info = "Connection already present:" + domain + ":" + FString::FromInt(port);
onsocketClientUDPConnectionEventDelegate.Broadcast(true,info , connectionID);
if (udpInitAsyncNode != nullptr) {
udpInitAsyncNode->triggerInitEvent(true, connectionID, info);
}
return;
}
USocketClientUDP* udpClient = NewObject<USocketClientUDP>(USocketClientUDP::StaticClass());
connectionID = FGuid::NewGuid().ToString();
udpClients.Add(connectionID, udpClient);
udpClients.Add(key, udpClient);
udpClient->init(this, udpInitAsyncNode, domain, ipType, port, receiveFilter, connectionID, maxPacketSize);
}
void USocketClientBPLibrary::socketClientSendUDP(FString domainOrIP, ESocketClientIPType ipType, int32 port, FString message, TArray<uint8> byteArray, bool addLineBreak, FString connectionID){
USocketClientBPLibrary::getSocketClientTarget()->socketClientSendUDPNonStatic(domainOrIP, ipType, port, message, byteArray, addLineBreak, connectionID);
}
void USocketClientBPLibrary::socketClientSendUDPNonStatic(FString domain, ESocketClientIPType ipType, int32 port, FString message, TArray<uint8> byteArray, bool addLineBreak, FString clientConnectionIDP) {
if (clientConnectionIDP.IsEmpty() || udpClients.Find(clientConnectionIDP) == nullptr) {
//don't send to many error messages. one second = 10000000 ticks
if (((FDateTime::Now().GetTicks()) - lastErrorMessageTime) >= 10000000) {
UE_LOG(LogTemp, Error, TEXT("Connection not found (socketClientSendUDPMessage). %s"), *clientConnectionIDP);
lastErrorMessageTime = FDateTime::Now().GetTicks();
}
return;
}
if (message.Len() > 0 && addLineBreak) {
message.Append("\r\n");
}
USocketClientUDP* udpClient = *udpClients.Find(clientConnectionIDP);
udpClient->sendUDPMessage(domain, ipType, port, message, byteArray);
}
void USocketClientBPLibrary::closeSocketClientConnectionUDP(FString connectionID){
USocketClientBPLibrary::getSocketClientTarget()->closeSocketClientConnectionUDPNonStatic(connectionID);
}
void USocketClientBPLibrary::closeSocketClientConnectionUDPNonStatic(FString connectionID) {
if (connectionID.IsEmpty()) {
return;
}
if (udpClients.Find(connectionID) == nullptr) {
UE_LOG(LogTemp, Warning, TEXT("Connection not found (closeSocketClientUDPConnection). %s"), *connectionID);
return;
}
USocketClientUDP* udpClient = *udpClients.Find(connectionID);
udpClient->closeUDPConnection();
udpClients.Remove(connectionID);
FString key = udpClient->getDomainOrIP() + FString::FromInt(udpClient->getPort());
udpClients.Remove(key);
udpClient = nullptr;
}
void USocketClientBPLibrary::getUDPInitializationByConnectionID(FString connectionID, bool& found, USocketClientUDP*& connection){
USocketClientBPLibrary::getSocketClientTarget()->getUDPInitializationByConnectionIDNonStatic(connectionID, found, connection);
}
void USocketClientBPLibrary::getUDPInitializationByConnectionIDNonStatic(FString connectionID, bool& found, USocketClientUDP*& connection){
if (connectionID.IsEmpty() || udpClients.Find(connectionID) == nullptr) {
found = false;
connection = nullptr;
return;
}
found = true;
connection = *udpClients.Find(connectionID);
}
bool USocketClientBPLibrary::isUDPInitialized(FString connectionID){
return USocketClientBPLibrary::getSocketClientTarget()->isUDPInitializedNonStatic(connectionID);
}
bool USocketClientBPLibrary::isUDPInitializedNonStatic(FString connectionID){
if (connectionID.IsEmpty() || udpClients.Find(connectionID) == nullptr) {
return false;
}
return (*udpClients.Find(connectionID))->isRun();
}
void USocketClientBPLibrary::changeSocketPlatform(ESocketPlatformClient platform) {
USocketClientBPLibrary::getSocketClientTarget()->systemSocketPlatform = platform;
}
FString USocketClientBPLibrary::resolveDomain(FString serverDomainP, ESocketClientIPType ipType) {
FString* cachedDomainPointer = domainCache.Find(serverDomainP);
if (cachedDomainPointer != nullptr) {
return *cachedDomainPointer;
}
//is IPv4?
if (ipType == ESocketClientIPType::E_ipv4) {
TArray<FString> ipNumbers;
int32 lineCount = serverDomainP.ParseIntoArray(ipNumbers, TEXT("."), true);
if (lineCount == 4 && serverDomainP.Len() <= 15 && serverDomainP.Len() >= 7) {
domainCache.Add(serverDomainP, serverDomainP);
return serverDomainP;
}
}
//is IPv6? Just a simple check
if (ipType == ESocketClientIPType::E_ipv6) {
TArray<FString> ipNumbers;
int32 lineCount = serverDomainP.ParseIntoArray(ipNumbers, TEXT(":"), true);
if (lineCount >= 2) {
domainCache.Add(serverDomainP, serverDomainP);
return serverDomainP;
}
}
//resolve Domain
ISocketSubsystem* sSS = USocketClientBPLibrary::getSocketSubSystem();
FResolveInfo* ResolveInfo = sSS->GetHostByName(TCHAR_TO_ANSI(*serverDomainP));
while (!ResolveInfo->IsComplete());
int32 errorCode = ResolveInfo->GetErrorCode();
if (errorCode == 0) {
const FInternetAddr* Addr = &ResolveInfo->GetResolvedAddress();
uint32 OutIP = 0;
FString adr = Addr->ToString(false);
domainCache.Add(serverDomainP, adr);
return adr;
}
else {
if (dnsClient == nullptr)
dnsClient = NewObject<UDNSClientSocketClient>(UDNSClientSocketClient::StaticClass());
dnsClient->resolveDomain(sSS, serverDomainP);
int32 timeout = 1000;
while (dnsClient->isResloving() && timeout > 0) {
timeout -= 10;
FPlatformProcess::Sleep(0.01);
}
FString adr = dnsClient->getIP();
domainCache.Add(serverDomainP, adr);
return adr;
}
return serverDomainP;
}
void USocketClientBPLibrary::cleanConnection(FSocketClientPluginSession& session){
socketClientCleanerThread->addSession(session);
}
void USocketClientBPLibrary::changeCleanerThreadSettingsOnClient(bool showLogs, int32 minLiveTimeInSeconds){
if (USocketClientBPLibrary::getSocketClientTarget()->socketClientCleanerThread != nullptr) {
USocketClientBPLibrary::getSocketClientTarget()->socketClientCleanerThread->changeSettings(showLogs, minLiveTimeInSeconds);
}
}
void USocketClientBPLibrary::getSystemType(ESocketClientSystem& system) {
#if PLATFORM_ANDROID
system = ESocketClientSystem::Android;
return;
#endif
#if PLATFORM_IOS
system = ESocketClientSystem::IOS;
return;
#endif
#if PLATFORM_WINDOWS
system = ESocketClientSystem::Windows;
return;
#endif
#if PLATFORM_LINUX
system = ESocketClientSystem::Linux;
return;
#endif
#if PLATFORM_MAC
system = ESocketClientSystem::Mac;
return;
#endif
}
TArray<uint8> USocketClientBPLibrary::parseHexToBytes(FString hex) {
TArray<uint8> bytes;
if (hex.Contains(" ")) {
hex = hex.Replace(TEXT(" "), TEXT(""));
}
if (hex.Len() % 2 != 0) {
UE_LOG(LogTemp, Error, TEXT("This is not a valid hex string: %s"), *hex);
return bytes;
}
TArray<TCHAR> charArray = hex.GetCharArray();
for (int32 i = 0; i < (charArray.Num() - 1); i++) {
if (CheckTCharIsHex(charArray[i]) == false) {
UE_LOG(LogTemp, Error, TEXT("This is not a valid hex string: %s"), *hex);
return bytes;
}
}
bytes.AddZeroed(hex.Len() / 2);
HexToBytes(hex, bytes.GetData());
return bytes;
}
FString USocketClientBPLibrary::parseHexToString(FString hex) {
TArray<uint8> bytes = parseHexToBytes(hex);
bytes.Add(0x00);// null-terminator
char* Data = (char*)bytes.GetData();
return FString(UTF8_TO_TCHAR(Data));
}
FString USocketClientBPLibrary::parseBytesToHex(TArray<uint8> bytes) {
FString hex;
hex = BytesToHex(bytes.GetData(), bytes.Num());
return hex;
}
TArray<uint8> USocketClientBPLibrary::parseHexToBytesPure(FString hex) {
return parseHexToBytes(hex);
}
FString USocketClientBPLibrary::parseHexToStringPure(FString hex) {
return parseHexToString(hex);
}
FString USocketClientBPLibrary::parseBytesToHexPure(TArray<uint8> bytes) {
return parseBytesToHex(bytes);
}
void USocketClientBPLibrary::parseBytesToFloat(TArray<uint8> bytes, float& value) {
if (bytes.Num() != 4) {
UE_LOG(LogTemp, Error, TEXT("ParseBytesToFloat: Cannot convert bytes to float. Array must contain 4 bytes but has %i bytes."), bytes.Num());
return;
}
FMemory::Memcpy(&value, bytes.GetData(), 4);
}
void USocketClientBPLibrary::parseBytesToInteger(TArray<uint8> bytes, int32& value) {
if (bytes.Num() != 4) {
UE_LOG(LogTemp, Error, TEXT("ParseBytesToInteger: Cannot convert bytes to integer. Array must contain 4 bytes but has %i bytes."), bytes.Num());
return;
}
FMemory::Memcpy(&value, bytes.GetData(), 4);
}
void USocketClientBPLibrary::parseBytesToInteger64(TArray<uint8> bytes, int64& value) {
if (bytes.Num() != 8) {
UE_LOG(LogTemp, Error, TEXT("ParseBytesToInteger64: Cannot convert bytes to integer. Array must contain 8 bytes but has %i bytes."), bytes.Num());
return;
}
FMemory::Memcpy(&value, bytes.GetData(), 8);
}
void USocketClientBPLibrary::parseBytesToFloatPure(TArray<uint8> bytes, float& value){
parseBytesToFloat(bytes, value);
}
void USocketClientBPLibrary::parseBytesToIntegerPure(TArray<uint8> bytes, int32& value){
parseBytesToInteger(bytes, value);
}
void USocketClientBPLibrary::parseBytesToInteger64Pure(TArray<uint8> bytes, int64& value){
parseBytesToInteger64(bytes, value);
}
void USocketClientBPLibrary::parseBytesToFloatEndian(TArray<uint8> bytes, float& littleEndian, float& bigEndian) {
littleEndian = 0.f;
bigEndian = 0.f;
if (bytes.Num() != 4) {
UE_LOG(LogTemp, Error, TEXT("ParseBytesToFloat: Cannot convert bytes to float. Array must contain 4 bytes but has %i bytes."), bytes.Num());
return;
}
uint8 littleEndianChar[] = { bytes[0], bytes[1], bytes[2], bytes[3] };
FMemory::Memcpy(&littleEndian, &littleEndianChar, sizeof(littleEndian));
uint8 bigEndianChar[] = { bytes[3], bytes[2], bytes[1], bytes[0] };
FMemory::Memcpy(&bigEndian, &bigEndianChar, sizeof(bigEndian));
}
void USocketClientBPLibrary::parseBytesToIntegerEndian(TArray<uint8> bytes, int32& littleEndian, int32& bigEndian){
littleEndian = 0;
bigEndian = 0;
if (bytes.Num() != 4){
UE_LOG(LogTemp, Error, TEXT("ParseBytesToInteger: Cannot convert bytes to integer. Array must contain 4 bytes but has %i bytes."), bytes.Num());
return;
}
uint8 littleEndianChar[] = { bytes[0], bytes[1], bytes[2], bytes[3] };
FMemory::Memcpy(&littleEndian, &littleEndianChar, sizeof(littleEndian));
uint8 bigEndianChar[] = { bytes[3], bytes[2], bytes[1], bytes[0] };
FMemory::Memcpy(&bigEndian, &bigEndianChar, sizeof(bigEndian));
}
void USocketClientBPLibrary::parseBytesToInteger64Endian(TArray<uint8> bytes, int64& littleEndian, int64& bigEndian) {
littleEndian = 0;
bigEndian = 0;
if (bytes.Num() != 8) {
UE_LOG(LogTemp, Error, TEXT("ParseBytesToInteger64: Cannot convert bytes to integer. Array must contain 8 bytes but has %i bytes."), bytes.Num());
return;
}return;
uint8 littleEndianChar[] = { bytes[0], bytes[1], bytes[2], bytes[3],bytes[4], bytes[5], bytes[6], bytes[7] };
FMemory::Memcpy(&littleEndian, &littleEndianChar, sizeof(littleEndian));
uint8 bigEndianChar[] = { bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0] };
FMemory::Memcpy(&bigEndian, &bigEndianChar, sizeof(bigEndian));
}
void USocketClientBPLibrary::parseFloatToBytes(TArray<uint8>& byteArray, float value, bool switchByteOrder){
union {
float tmpVal;
uint8 tmpArray[4];
} u;
u.tmpVal = value;
byteArray.AddUninitialized(4);
FMemory::Memcpy(byteArray.GetData(), u.tmpArray, 4);
if (switchByteOrder) {
byteArray.SwapMemory(3, 0);
byteArray.SwapMemory(2, 1);
}
}
void USocketClientBPLibrary::parseIntegerToBytes(TArray<uint8>& byteArray, int32 value, bool switchByteOrder){
union {
int32 tmpVal;
uint8 tmpArray[4];
} u;
u.tmpVal = value;
byteArray.AddUninitialized(4);
FMemory::Memcpy(byteArray.GetData(), u.tmpArray, 4);
if (switchByteOrder) {
byteArray.SwapMemory(3, 0);
byteArray.SwapMemory(2, 1);
}
}
void USocketClientBPLibrary::parseInteger64ToBytes(TArray<uint8>& byteArray, int64 value, bool switchByteOrder){
union {
int64 tmpVal;
uint8 tmpArray[8];
} u;
u.tmpVal = value;
byteArray.AddUninitialized(8);
FMemory::Memcpy(byteArray.GetData(), u.tmpArray, 8);
if (switchByteOrder) {
byteArray.SwapMemory(7, 0);
byteArray.SwapMemory(6, 1);
byteArray.SwapMemory(5, 2);
byteArray.SwapMemory(4, 3);
}
}
void USocketClientBPLibrary::parseFloatToBytesPure(TArray<uint8>& byteArray, float value, bool switchByteOrder){
parseFloatToBytes(byteArray, value, switchByteOrder);
}
void USocketClientBPLibrary::parseIntegerToBytesPure(TArray<uint8>& byteArray, int32 value, bool switchByteOrder){
parseIntegerToBytes(byteArray, value, switchByteOrder);
}
void USocketClientBPLibrary::parseInteger64ToBytesPure(TArray<uint8>& byteArray, int64 value, bool switchByteOrder){
parseInteger64ToBytes(byteArray, value, switchByteOrder);
}
void USocketClientBPLibrary::parseBytesToFloatArrayPure(TArray<float>& value, TArray<uint8> bytes) {
if (bytes.Num() == 0 && bytes.Num() % 4 != 0)
return;
value.Empty();
value.AddUninitialized(bytes.Num() / 4);
FMemory::Memcpy(value.GetData(), bytes.GetData(), bytes.Num());
}
void USocketClientBPLibrary::parseBytesToIntegerArrayPure(TArray<int32>& value, TArray<uint8> bytes) {
if (bytes.Num() == 0 && bytes.Num() % 4 != 0)
return;
value.Empty();
value.AddUninitialized(bytes.Num() / 4);
FMemory::Memcpy(value.GetData(), bytes.GetData(), bytes.Num());
}
void USocketClientBPLibrary::parseBytesToInteger64ArrayPure(TArray<int64>& value, TArray<uint8> bytes) {
if (bytes.Num() == 0 && bytes.Num() % 8 != 0)
return;
value.Empty();
value.AddUninitialized(bytes.Num() / 8);
FMemory::Memcpy(value.GetData(), bytes.GetData(), bytes.Num());
}
void USocketClientBPLibrary::parseFloatArrayToBytesPure(TArray<uint8>& byteArray, TArray<float> value) {
byteArray.Empty();
byteArray.AddUninitialized(value.Num() * 4);
FMemory::Memcpy(byteArray.GetData(), value.GetData(), value.Num() * 4);
}
void USocketClientBPLibrary::parseIntegerArrayToBytesPure(TArray<uint8>& byteArray, TArray<int32> value) {
byteArray.Empty();
byteArray.AddUninitialized(value.Num() * 4);
FMemory::Memcpy(byteArray.GetData(), value.GetData(), value.Num() * 4);
}
void USocketClientBPLibrary::parseInteger64ArrayToBytesPure(TArray<uint8>& byteArray, TArray<int64> value) {
byteArray.Empty();
byteArray.AddUninitialized(value.Num() * 8);
FMemory::Memcpy(byteArray.GetData(), value.GetData(), value.Num() * 8);
}
void USocketClientBPLibrary::changeTCPSeparatorStringOnClient(FString separator) {
USocketClientBPLibrary::getSocketClientTarget()->changeTCPSeparatorStringOnClientNonStatic(separator);
}
void USocketClientBPLibrary::changeTCPSeparatorStringOnClientNonStatic(FString separator) {
tcpStringSeparator = separator;
}
void USocketClientBPLibrary::changeTCPSeparatorByteOnClient(uint8 separator) {
USocketClientBPLibrary::getSocketClientTarget()->changeTCPSeparatorByteOnClientNonStatic(separator);
}
void USocketClientBPLibrary::changeTCPSeparatorByteOnClientNonStatic(uint8 separator) {
tcpByteSeparator = separator;
}
int32 USocketClientBPLibrary::getUniquePlayerID(APlayerController* playerController) {
if (playerController == nullptr || playerController->GetLocalPlayer() == nullptr)
return 0;
return playerController->GetLocalPlayer()->GetUniqueID();
}
FString USocketClientBPLibrary::getRandomID(){
return FGuid::NewGuid().ToString();
}
void USocketClientBPLibrary::getTcpSeparator(uint8& byteSeparator, FString& stringSeparator){
stringSeparator = tcpStringSeparator;
byteSeparator = tcpByteSeparator;
}
ISocketSubsystem* USocketClientBPLibrary::getSocketSubSystem() {
switch (USocketClientBPLibrary::getSocketClientTarget()->systemSocketPlatform)
{
case ESocketPlatformClient::E_SSC_SYSTEM:
return ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
case ESocketPlatformClient::E_SSC_WINDOWS:
return ISocketSubsystem::Get(FName(TEXT("WINDOWS")));
case ESocketPlatformClient::E_SSC_MAC:
return ISocketSubsystem::Get(FName(TEXT("MAC")));
case ESocketPlatformClient::E_SSC_IOS:
return ISocketSubsystem::Get(FName(TEXT("IOS")));
case ESocketPlatformClient::E_SSC_UNIX:
return ISocketSubsystem::Get(FName(TEXT("UNIX")));
case ESocketPlatformClient::E_SSC_ANDROID:
return ISocketSubsystem::Get(FName(TEXT("ANDROID")));
case ESocketPlatformClient::E_SSC_PS4:
return ISocketSubsystem::Get(FName(TEXT("PS4")));
case ESocketPlatformClient::E_SSC_XBOXONE:
return ISocketSubsystem::Get(FName(TEXT("XBOXONE")));
case ESocketPlatformClient::E_SSC_HTML5:
return ISocketSubsystem::Get(FName(TEXT("HTML5")));
case ESocketPlatformClient::E_SSC_SWITCH:
return ISocketSubsystem::Get(FName(TEXT("SWITCH")));
case ESocketPlatformClient::E_SSC_DEFAULT:
return ISocketSubsystem::Get();
default:
return ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
}
}
@@ -0,0 +1,67 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketClientCleanerThread.h"
FSocketClientCleanerThread::FSocketClientCleanerThread() {
FString threadName = "FSocketClientPluginCleanerThread_" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_Lowest);
}
void FSocketClientCleanerThread::addSession(FSocketClientPluginSession& session) {
session.addToCleanerTime = FDateTime::Now().GetTicks();
sessionQueue.Enqueue(session);
}
void FSocketClientCleanerThread::changeSettings(bool showLogsP, int32 minLiveTimeInSecondsP) {
showLogs = showLogsP;
minLiveTimeInSeconds = minLiveTimeInSecondsP;
}
uint32 FSocketClientCleanerThread::Run() {
while (true) {
TArray<FSocketClientPluginSession> tryItAgain;
if (showLogs && sessionQueue.IsEmpty()) {
UE_LOG(LogTemp, Display, TEXT("SocketClient: Cleaner: No connections available to clean up."));
}
while (sessionQueue.IsEmpty() == false) {
FSocketClientPluginSession session;
sessionQueue.Dequeue(session);
//one second = 10000000 ticks
if ((FDateTime::Now().GetTicks() - session.addToCleanerTime) < (10000000 * minLiveTimeInSeconds)) {
tryItAgain.Add(session);
continue;
}
if (showLogs) {
UE_LOG(LogTemp, Display, TEXT("SocketClient: Clean connection: %s"), *session.clientID);
}
delete session.tcpSendThread;
delete session.tcpRecieverThread;
delete session.tcpFileHandlerThread;
delete session.udpSocketReceiver;
delete session.udpSendDataThead;
delete session.udpReceiveDataThread;
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,181 @@
// Copyright 2017-2019 David Romanski (Socke). All Rights Reserved.
#include "SocketClientTCP.h"
USocketClientTCPClient::USocketClientTCPClient(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {
//This prevents the garbage collector from killingand deleting the class from RAM.
this->AddToRoot();
onsocketClientTCPConnectionEventDelegate.AddDynamic(this, &USocketClientTCPClient::connectionEvent);
}
void USocketClientTCPClient::socketClientTCPConnectionEventDelegate(const bool success, const FString message, const FString clientConnectionID) {}
void USocketClientTCPClient::receiveTCPMessageEventDelegate(const FString message, const TArray<uint8>& byteArray, const FString clientConnectionID){}
void USocketClientTCPClient::transferFileOverTCPProgressEventDelegate(const FString clientConnectionID, const FString filePathP, const float percent, const float mbit, const int64 bytesTransferred, const int64 fileSize) {}
void USocketClientTCPClient::fileTransferOverTCPInfoEventDelegate(const FString message, const FString clientConnectionID, const FString filePathP, const bool success) {}
void USocketClientTCPClient::connectionEvent(bool success, FString message, FString clientConnectionID){
connected = success;
}
void USocketClientTCPClient::connect(USocketClientBPLibrary* mainLibP, FString domainOrIP, ESocketClientIPType ipType, int32 port, EReceiveFilterClient receiveFilter,
ESocketClientTCPSeparator messageWrappingP, FString connectionIDP, UTCPConnectAsyncNode* tcpConnectAsyncNodeP, bool noPacketDelay, bool noPacketBlocking){
mainLib = mainLibP;
connectionID = connectionIDP;
messageWrapping = messageWrappingP;
tcpConnectAsyncNode = tcpConnectAsyncNodeP;
USocketClientBPLibrary::socketClientBPLibrary->getTcpSeparator(tcpByteSeparator, tcpStringSeparator);
tcpReceiveDataThread = new FSocketClientTCPReceiveDataThread(mainLib, connectionID, receiveFilter, domainOrIP, ipType, port,this,noPacketDelay,noPacketBlocking);
}
void USocketClientTCPClient::sendMessage(FString message, TArray<uint8> byteArray){
if (run && tcpSendThread != nullptr) {
tcpSendThread->sendMessage(message, byteArray);
}
}
void USocketClientTCPClient::sendFile(USocketClientBPLibrary* mainLibP, FString connectionIDP, FString domainOrIP, ESocketClientIPType ipType, int32 port, EFileFunctionsSocketClientDirectoryType directoryType, FString filePathP, FString token, FString Aes256bitKey){
mainLib = mainLibP;
connectionID = connectionIDP;
aesKey = Aes256bitKey;
fileToken = token;
sendOrReceive = 0;
filePath = UFileFunctionsSocketClient::getCleanDirectory(directoryType, filePathP);
tcpFileConnectionThread = new FSocketClientTCPFileHandlerThread(mainLib, connectionID, domainOrIP, ipType, port, this);
}
void USocketClientTCPClient::requestFile(USocketClientBPLibrary* mainLibP, FString connectionIDP, FString domainOrIP, ESocketClientIPType ipType, int32 port, EFileFunctionsSocketClientDirectoryType directoryType, FString filePathP, bool resumeP, FString token, FString Aes256bitKey) {
mainLib = mainLibP;
connectionID = connectionIDP;
aesKey = Aes256bitKey;
fileToken = token;
sendOrReceive = 1;
resume = resumeP;
filePath = UFileFunctionsSocketClient::getCleanDirectory(directoryType, filePathP);
tcpFileConnectionThread = new FSocketClientTCPFileHandlerThread(mainLib, connectionID, domainOrIP, ipType, port, this);
}
void USocketClientTCPClient::closeConnection(){
setRun(false);
if (tcpFileConnectionThread != nullptr) {
tcpFileConnectionThread->triggerFileTransferOverTCPInfoEvent("Data transfer aborted.", connectionID, filePath, false, mainLib, this);
}
if (tcpSendThread != nullptr) {
tcpSendThread->pauseThread(false);
}
if (mainLib != nullptr) {
FSocketClientPluginSession connectionSession = FSocketClientPluginSession();
connectionSession.tcpRecieverThread = tcpReceiveDataThread;
connectionSession.tcpSendThread = tcpSendThread;
//connectionSession.tcpSendFileThread = fileSendThread;
connectionSession.tcpFileHandlerThread = tcpFileConnectionThread;
connectionSession.socket = socket;
connectionSession.clientID = connectionID;
mainLib->cleanConnection(connectionSession);
}
}
bool USocketClientTCPClient::isRun(){
return run;
}
void USocketClientTCPClient::setRun(bool runP) {
run = runP;
}
FString USocketClientTCPClient::getConnectionID(){
return connectionID;
}
FString USocketClientTCPClient::getAesKey(){
return aesKey;
}
FString USocketClientTCPClient::getFileToken(){
return fileToken;
}
FString USocketClientTCPClient::getFilePath() {
return filePath;
}
void USocketClientTCPClient::setSocket(FSocket* socketP){
socket = socketP;
}
FSocket* USocketClientTCPClient::getSocket(){
return socket;
}
void USocketClientTCPClient::createSendThread(){
tcpSendThread = new FSocketClientTCPSendDataThead(mainLib, this, connectionID);
}
void USocketClientTCPClient::getTcpSeparator(FString& stringSeparator, uint8& byteSeparator, ESocketClientTCPSeparator& messageWrappingP) {
messageWrappingP = messageWrapping;
stringSeparator = tcpStringSeparator;
tcpByteSeparator = byteSeparator;
}
FString USocketClientTCPClient::encryptMessage(FString message) {
return UFileFunctionsSocketClient::encryptMessageWithAES(message, aesKey);
}
FString USocketClientTCPClient::decryptMessage(FString message) {
return UFileFunctionsSocketClient::decryptMessageWithAES(message, aesKey);
}
void USocketClientTCPClient::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);
}
}
bool USocketClientTCPClient::isSendFile(){
return (sendOrReceive == 0);
}
bool USocketClientTCPClient::isReceiveFile(){
return (sendOrReceive == 1);
}
bool USocketClientTCPClient::hasResume(){
return resume;
}
bool USocketClientTCPClient::isConnected()
{
return connected;
}
void USocketClientTCPClient::deleteFile(FString filePathP){
UFileFunctionsSocketClient::deleteFileAbsolutePath(filePathP);
}
void USocketClientTCPClient::getMD5FromFileAbsolutePath(FString filePathP, bool& success, FString& MD5) {
UFileFunctionsSocketClient::getMD5FromFileAbsolutePath(filePathP, success, MD5);
}
int64 USocketClientTCPClient::fileSize(FString filePathP) {
return UFileFunctionsSocketClient::fileSizeAbsolutePath(filePathP);
}
USocketClientBPLibrary* USocketClientTCPClient::getMainLib() {
return mainLib;
}
FString USocketClientTCPClient::int64ToString(int64 num) {
return UFileFunctionsSocketClient::int64ToString(num);
}
@@ -0,0 +1,592 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketClientTCPFileHandlerThread.h"
FSocketClientTCPFileHandlerThread::FSocketClientTCPFileHandlerThread(USocketClientBPLibrary* socketClientP, FString clientConnectionIDP, FString ipOrDomainP, ESocketClientIPType ipTypeP, int32 portP, USocketClientTCPClient* tcpClientP) :
socketClient(socketClientP),
clientConnectionID(clientConnectionIDP),
ipOrDomain(ipOrDomainP),
ipType(ipTypeP),
port(portP),
tcpClient(tcpClientP) {
FString threadName = "FServerFileConnectionThread" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_Normal);
}
FSocketClientTCPFileHandlerThread::~FSocketClientTCPFileHandlerThread() {
delete thread;
}
uint32 FSocketClientTCPFileHandlerThread::Run() {
//UE_LOG(LogTemp, Display, TEXT("DoWork:%s"),*(FDateTime::Now()).ToString());
FString ip = socketClient->resolveDomain(ipOrDomain, ipType);
int32 portGlobal = port;
FString clientConnectionIDGlobal = clientConnectionID;
USocketClientBPLibrary* socketClientGlobal = socketClient;
USocketClientTCPClient* tcpClientGlobal = tcpClient;
//UE_LOG(LogTemp, Warning, TEXT("Tread:%s:%i"),*ip, port);
ISocketSubsystem* sSS = USocketClientBPLibrary::getSocketSubSystem();
if (sSS == nullptr) {
AsyncTask(ENamedThreads::GameThread, [ip, portGlobal, clientConnectionIDGlobal, socketClientGlobal, tcpClientGlobal]() {
if (socketClientGlobal != nullptr)
socketClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection failed(1). SocketSubSystem does not exist." + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
if (tcpClientGlobal != nullptr)
tcpClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection failed(1). SocketSubSystem does not exist." + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
});
return 0;
}
TSharedRef<FInternetAddr> addr = sSS->CreateInternetAddr();
bool bIsValid;
addr->SetIp(*ip, bIsValid);
addr->SetPort(port);
if (bIsValid) {
// create the socket
FSocket* socket = sSS->CreateSocket(NAME_Stream, TEXT("socketClient"), addr->GetProtocolType());
tcpClient->setSocket(socket);
// try to connect to the server
if (socket == nullptr || socket->Connect(*addr) == false) {
const TCHAR* socketErr = sSS->GetSocketError(SE_GET_LAST_ERROR_CODE);
AsyncTask(ENamedThreads::GameThread, [ip, portGlobal, clientConnectionIDGlobal, socketClientGlobal, tcpClientGlobal, socketErr]() {
if (socketClientGlobal != nullptr)
socketClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection failed(2):" + FString(socketErr) + "|" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
if (tcpClientGlobal != nullptr)
tcpClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection failed(2):" + FString(socketErr) + "|" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
});
}
else {
//connected
AsyncTask(ENamedThreads::GameThread, [ip, portGlobal, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal]() {
if (socketClientGlobal != nullptr)
socketClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(true, "Connection successful:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
if (tcpClientGlobal != nullptr)
tcpClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(true, "Connection successful:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
});
tcpClient->setRun(true);
if (tcpClient->isReceiveFile()) {
doRequestFileFromServer(socket);
}
else {
if (tcpClient->isSendFile()) {
doSendFileToServer(socket);
}
}
}
AsyncTask(ENamedThreads::GameThread, [ip, portGlobal, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal]() {
if (socketClientGlobal != nullptr) {
socketClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection close:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
if (tcpClientGlobal != nullptr)
tcpClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection close:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
}
});
USocketClientBPLibrary::getSocketClientTarget()->closeSocketClientConnectionTCPNonStatic(clientConnectionID);
tcpClient->setRun(false);
if (socket != nullptr) {
socket->Close();
}
}
else {
AsyncTask(ENamedThreads::GameThread, [ip, portGlobal, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal]() {
if (socketClientGlobal != nullptr)
socketClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection failed(3). IP not valid:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
if (tcpClientGlobal != nullptr)
tcpClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection failed(3). IP not valid:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal, clientConnectionIDGlobal);
});
}
return 0;
}
void FSocketClientTCPFileHandlerThread::doRequestFileFromServer(FSocket* socket){
FString filePath = tcpClient->getFilePath();
if (!FPaths::DirectoryExists(filePath)) {
triggerFileTransferOverTCPInfoEvent("Data transmission aborted. Directory not found.", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
return;
}
FString message = "REQUEST_FILE_FROM_SERVER_|_" + tcpClient->getFileToken();
sendMessageToServer(message, socket);
message = readMessageFromServer(socket);
if (message.StartsWith("REQUEST_FILE_FROM_SERVER_ACCEPTED_|_" + tcpClient->getFileToken())) {
TArray<FString> lines;
message.ParseIntoArray(lines, TEXT("_|_"), true);
if (lines.Num() == 5) {
if (lines[0].Equals("REQUEST_FILE_FROM_SERVER_ACCEPTED") && lines[1].Len() > 0 && lines[2].Len() > 0 && lines[3].Len() > 0 && lines[4].Len() > 0) {
FArchive* writer = nullptr;
FString md5Server = lines[2];
int64 fileSize = FCString::Atoi64(*lines[3]);
if (filePath.EndsWith("/")) {
filePath += lines[4];
}
else {
filePath += "/" + lines[4];
}
int64 fileSizeOnClient = 0;
if (FPaths::FileExists(filePath)) {
fileSizeOnClient = tcpClient->fileSize(filePath);
if (tcpClient->hasResume()) {
writer = IFileManager::Get().CreateFileWriter(*filePath, EFileWrite::FILEWRITE_Append);
}
else {
writer = IFileManager::Get().CreateFileWriter(*filePath);
}
}
else {
writer = IFileManager::Get().CreateFileWriter(*filePath);
}
if (writer == nullptr) {
triggerFileTransferOverTCPInfoEvent("File could not be created. ", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
return;
}
if (writer->TotalSize() >= fileSize) {
writer->Close();
delete writer;
triggerFileTransferOverTCPInfoEvent("File on the client has the same size or is larger than the file on the server.", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
return;
}
//downloadFile = true;
int64 bytesDownloaded = writer->TotalSize();
message = "REQUEST_FILE_FROM_SERVER_ACCEPTED_|_" + tcpClient->getFileToken() + "_|_" + tcpClient->int64ToString(bytesDownloaded);
sendMessageToServer(message, socket);
int64 lastTimeWithData = 0;
int64 ticks1 = 0;
int64 ticks2 = 0;
int64 ticksDownload = 0;
int64 lastByte = 0;
uint32 dataSize = 0;
TArray<uint8> buffer;
while (socket != nullptr && tcpClient->isRun()) {
ticks1 = FDateTime::Now().GetTicks();
socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(waitForRead));
ticks2 = FDateTime::Now().GetTicks();
bool hasData = socket->HasPendingData(dataSize);
if (!hasData && ticks1 == ticks2) {
triggerFileTransferOverTCPInfoEvent("Connection broken.", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
break;
}
// UE_LOG(LogTemp, Display, TEXT("download %i"),dataSize);
if (hasData) {
lastTimeWithData = FDateTime::Now().GetTicks();
//downlnoad
int32 bytesRead = 0;
buffer.Empty();
buffer.SetNumUninitialized(dataSize);
if (socket->Recv(buffer.GetData(), buffer.Num(), bytesRead)) {
writer->Serialize(buffer.GetData(), buffer.Num());
//show progress each second
if ((ticksDownload + 10000000) <= FDateTime::Now().GetTicks()) {
writer->Flush();
int64 bytesTransferredLastSecond = bytesDownloaded - lastByte;
//float speed = ((float)bytesTransferredLastSecond) / 125000;
float mbit = ((float)bytesTransferredLastSecond) / 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);
}
triggerTransferFileEvent(clientConnectionID, filePath, socketClient, tcpClient, percent, mbit, bytesDownloaded, fileSize);
ticksDownload = FDateTime::Now().GetTicks();
lastByte = bytesDownloaded;
}
bytesDownloaded += bytesRead;
}
if (bytesDownloaded >= fileSize) {
if (writer != nullptr) {
writer->Close();
delete writer;
writer = nullptr;
}
sendEndMessage(filePath, tcpClient->getFileToken(), md5Server, clientConnectionID, socket, socketClient, tcpClient);
//transfer file finish
triggerTransferFileEvent(clientConnectionID, filePath, socketClient, tcpClient, 100, 0, bytesDownloaded, fileSize);
tcpClient->setRun(false);
}
}
//no data time out
else {
//one second = 10000000 ticks
//5 seconds timeout
// UE_LOG(LogTemp, Display, TEXT("timeout %i"),(FDateTime::Now().GetTicks() - lastTimeWithData));
if ((FDateTime::Now().GetTicks() - lastTimeWithData) >= 50000000) {
triggerFileTransferOverTCPInfoEvent("Connection timeout", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
break;
}
}
}
buffer.Empty();
if (writer != nullptr) {
writer->Close();
delete writer;
writer = nullptr;
}
}
}
}
}
void FSocketClientTCPFileHandlerThread::doSendFileToServer(FSocket* socket){
FString filePath = tcpClient->getFilePath();
if (!FPaths::FileExists(filePath)) {
triggerFileTransferOverTCPInfoEvent("Data transmission aborted. File not found.", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
return;
}
int64 fileSize = tcpClient->fileSize(filePath);
bool md5okay = false;
FString md5 = FString();
tcpClient->getMD5FromFileAbsolutePath(filePath, md5okay, md5);
FString fileAuthMessage = "SEND_FILE_TO_SERVER_|_" + tcpClient->getFileToken() + "_|_" + md5 + "_|_" + FPaths::GetCleanFilename(filePath) + "_|_" + tcpClient->int64ToString(fileSize);
sendMessageToServer(fileAuthMessage, socket);
FString message = readMessageFromServer(socket);
if (message.StartsWith("SEND_FILE_TO_SERVER_ACCEPTED_|_" + tcpClient->getFileToken())) {
if (message.RemoveFromStart("SEND_FILE_TO_SERVER_ACCEPTED_|_" + tcpClient->getFileToken() + "_|_")) {
int64 startPosition = FCString::Atoi64(*message);
// upload file
FArchive* reader = IFileManager::Get().CreateFileReader(*filePath);
if (reader == nullptr || reader->TotalSize() == 0) {
if (reader != nullptr) {
reader->Close();
}
tcpClient->setRun(false);
return;
}
fileSize = reader->TotalSize();
/*int64 ticks1 = 0;
int64 ticks2 = 0;*/
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 (tcpClient->isRun() && 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("Send: %i"),buffer.Num());
//ticks1 = FDateTime::Now().GetTicks();
socket->Send(buffer.GetData(), buffer.Num(), dataSendBySocket);
//socket->Wait(ESocketWaitConditions::WaitForWrite, FTimespan::FromSeconds(0.001));
//ticks2 = FDateTime::Now().GetTicks();
if (dataSendBySocket == 0 || dataSendBySocket != buffer.Num()) {
triggerFileTransferOverTCPInfoEvent("Data transmission aborted. Connection possibly broken.", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
return;
}
//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;
triggerTransferFileEvent(clientConnectionID, filePath, socketClient, tcpClient, percent, mbit, lastPosition, fileSize);
}
}
mbit = ((float)lastPosition - (float)bytesSentSinceLastTick) / 1024 / 1024 * 8;
percent = ((float)lastPosition / fileSize) * 100;
triggerTransferFileEvent(clientConnectionID, filePath, socketClient, tcpClient, percent, mbit, lastPosition, fileSize);
buffer.Empty();
if (reader != nullptr) {
reader->Close();
reader = nullptr;
}
message = readMessageFromServer(socket);
if (message.StartsWith("SEND_FILE_TO_SERVER_END_|_" + tcpClient->getFileToken())) {
if (message.RemoveFromStart("SEND_FILE_TO_SERVER_END_|_" + tcpClient->getFileToken() + "_|_")) {
if (message.Equals("OKAY")) {
triggerFileTransferOverTCPInfoEvent("Data transmission successful. ", clientConnectionID, filePath, true, socketClient, tcpClient);
tcpClient->setRun(false);
}
else {
triggerFileTransferOverTCPInfoEvent("There was an error during data transmission. File may be corrupted or incomplete. ", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
}
}
else {
triggerFileTransferOverTCPInfoEvent("Data transmission aborted. Wrong answer from the server (3). ", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
}
}
}
else {
triggerFileTransferOverTCPInfoEvent("Data transmission aborted. Wrong answer from the server (2). ", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
return;
}
}
else {
if (message.StartsWith("SEND_FILE_TO_SERVER_END_|_" + tcpClient->getFileToken())) {
if (message.RemoveFromStart("SEND_FILE_TO_SERVER_END_|_" + tcpClient->getFileToken() + "_|_")) {
if (message.Equals("OKAY")) {
triggerFileTransferOverTCPInfoEvent("Data transmission successful. ", clientConnectionID, filePath, true, socketClient, tcpClient);
tcpClient->setRun(false);
}
else {
triggerFileTransferOverTCPInfoEvent("There was an error during data transmission. File may be corrupted or incomplete. ", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
}
}
else {
triggerFileTransferOverTCPInfoEvent("Data transmission aborted. Wrong answer from the server (3). ", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
}
}
else {
triggerFileTransferOverTCPInfoEvent("Data transmission aborted. Wrong answer from the server (1). ", clientConnectionID, filePath, false, socketClient, tcpClient);
tcpClient->setRun(false);
}
}
}
void FSocketClientTCPFileHandlerThread::sendMessageToServer(FString message, FSocket* socket){
message = tcpClient->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());
socket->Send(byteCache.GetData(), byteCache.Num(), sent);
byteCache.Empty();
}
void FSocketClientTCPFileHandlerThread::triggerFileTransferOverTCPInfoEvent(FString messageP, FString clientConnectionIDP, FString filePathP, bool successP, USocketClientBPLibrary* socketClientP, USocketClientTCPClient* tcpClientP) {
AsyncTask(ENamedThreads::GameThread, [messageP, clientConnectionIDP, filePathP, successP, socketClientP, tcpClientP]() {
if (socketClientP != nullptr)
socketClientP->onfileTransferOverTCPInfoEventDelegate.Broadcast(messageP, clientConnectionIDP, filePathP, successP);
if (tcpClientP != nullptr)
tcpClientP->onfileTransferOverTCPInfoEventDelegate.Broadcast(messageP, clientConnectionIDP, filePathP, successP);
});
}
void FSocketClientTCPFileHandlerThread::triggerTransferFileEvent(FString clientConnectionIDP, FString filePathP, USocketClientBPLibrary* socketClientP, USocketClientTCPClient* tcpClientP, float percentP, float mbitP, int64 transferredP, int64 fileSizeP) {
AsyncTask(ENamedThreads::GameThread, [clientConnectionIDP, socketClientP, filePathP, tcpClientP, percentP, mbitP, transferredP, fileSizeP]() {
if (socketClientP != nullptr)
socketClientP->ontransferFileOverTCPProgressEventDelegate.Broadcast(clientConnectionIDP, filePathP, percentP, mbitP, transferredP, fileSizeP);
if (tcpClientP != nullptr)
tcpClientP->ontransferFileOverTCPProgressEventDelegate.Broadcast(clientConnectionIDP, filePathP, percentP, mbitP, transferredP, fileSizeP);
});
}
void FSocketClientTCPFileHandlerThread::sendEndMessage(FString fullFilePathP, FString tokenP, FString md5ServerP, FString clientConnectionIDP, FSocket* clientSocketP, USocketClientBPLibrary* socketClientP, USocketClientTCPClient* tcpClientP) {
bool md5okay = false;
FString md5Client = FString();
tcpClientP->getMD5FromFileAbsolutePath(fullFilePathP, md5okay, md5Client);
FString response = "REQUEST_FILE_FROM_SERVER_END_|_" + tokenP + "_|_";
if (md5okay && md5ServerP.Equals(md5Client)) {
triggerFileTransferOverTCPInfoEvent("File successfully received.", clientConnectionIDP, fullFilePathP, true, socketClientP, tcpClientP);
response += "OKAY";
}
else {
triggerFileTransferOverTCPInfoEvent("File received but MD5 does not match. Corrupted file will be deleted if resume is not disabled.", clientConnectionIDP, fullFilePathP, false, socketClientP, tcpClientP);
response += "MD5ERROR";
if (tcpClientP->hasResume()) {
tcpClientP->deleteFile(fullFilePathP);
}
}
sendMessageToServer(response, clientSocketP);
}
FString FSocketClientTCPFileHandlerThread::readMessageFromServer(FSocket* socket){
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 (tcpClient->isRun() && socket != nullptr) {
ticks1 = FDateTime::Now().GetTicks();
socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(waitForRead));
ticks2 = FDateTime::Now().GetTicks();
bool hasData = socket->HasPendingData(dataSize);
if (!hasData && ticks1 == ticks2) {
//if (showFTPLogs) {
// UE_LOG(LogTemp, Warning, TEXT("FTP Client: TCP connection broken."));
//}
//fireConnectionEvent(false, 0, "TCP connection broken.");
return "TCP connection broken.";
}
if (!hasData) {
return message;
}
if (hasData) {
TArray<uint8> dataFromSocket;
dataFromSocket.SetNumUninitialized(dataSize);
int32 BytesRead = 0;
if (socket->Recv(dataFromSocket.GetData(), dataFromSocket.Num(), BytesRead)) {
if (lastDataLengthFromHeader == 0 && dataFromSocket.Num() >= 5) {
tcpClient->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) {
tcpClient->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 = tcpClient->decryptMessage(message);
}
return message;
}
}
}
if (message.IsEmpty() == false) {
message = tcpClient->decryptMessage(message);
}
return message;
}
@@ -0,0 +1,288 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketClientTCPReceiveDataThread.h"
FSocketClientTCPReceiveDataThread::FSocketClientTCPReceiveDataThread(USocketClientBPLibrary* socketClientBPLibraryP, FString clientConnectionIDP,
EReceiveFilterClient receiveFilterP, FString ipOrDomainP, ESocketClientIPType ipTypeP, int32 portP,
USocketClientTCPClient* tcpClientP, bool noPacketDelayP, bool noPacketBlockingP) :
socketClientBPLibrary(socketClientBPLibraryP),
clientConnectionID(clientConnectionIDP),
receiveFilter(receiveFilterP),
ipOrDomain(ipOrDomainP),
ipType(ipTypeP),
port(portP),
tcpClient(tcpClientP),
noPacketDelay(noPacketDelayP),
noPacketBlocking(noPacketBlockingP) {
FString threadName = "FServerConnectionThread" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_Normal);
}
FSocketClientTCPReceiveDataThread::~FSocketClientTCPReceiveDataThread() {
if (tcpClient != nullptr && tcpClient->tcpConnectAsyncNode != nullptr) {
tcpClient->tcpConnectAsyncNode = nullptr;
}
delete thread;
}
uint32 FSocketClientTCPReceiveDataThread::Run() {
//UE_LOG(LogTemp, Display, TEXT("DoWork:%s"),*(FDateTime::Now()).ToString());
FString ip = socketClientBPLibrary->resolveDomain(ipOrDomain, ipType);
int32 portGlobal = port;
FString clientConnectionIDGlobal = clientConnectionID;
USocketClientBPLibrary* socketClientGlobal = socketClientBPLibrary;
USocketClientTCPClient* tcpClientGlobal = tcpClient;
//message wrapping
FString stringSeparator = FString();
uint8 byteSeparator = 0x00;
ESocketClientTCPSeparator messageWrapping = ESocketClientTCPSeparator::E_None;
tcpClient->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 == ESocketClientTCPSeparator::E_LengthSeparator && stringSeparatorArray.Num() == 0) {
messageWrapping = ESocketClientTCPSeparator::E_None;
UE_LOG(LogTemp, Warning, TEXT("Socket Client Plugin: Separator mode is set to String but there is no String Separator. Mode changed to none."));
}
//UE_LOG(LogTemp, Warning, TEXT("Tread:%s:%i"),*ip, port);
ISocketSubsystem* sSS = USocketClientBPLibrary::getSocketSubSystem();
if (sSS == nullptr) {
FString info = "Connection failed(1). SocketSubSystem does not exist.:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal;
triggerConnectionEvent(false, clientConnectionID, info, tcpClientGlobal, socketClientGlobal);
return 0;
}
TSharedRef<FInternetAddr> addr = sSS->CreateInternetAddr();
bool bIsValid;
addr->SetIp(*ip, bIsValid);
addr->SetPort(port);
if (bIsValid) {
// create the socket
FSocket* socket = sSS->CreateSocket(NAME_Stream, TEXT("socketClient"), addr->GetProtocolType());
tcpClient->setSocket(socket);
//socket options
if (socket != nullptr) {
socket->SetNoDelay(noPacketDelay);
socket->SetNonBlocking(noPacketBlocking);
tcpClient->setRun(true);
}
// try to connect to the server
if (socket == nullptr || socket->Connect(*addr) == false) {
const TCHAR* socketErr = sSS->GetSocketError(SE_GET_LAST_ERROR_CODE);
FString info = "Connection failed(2):" + FString(socketErr) + "|" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal;
triggerConnectionEvent(false, clientConnectionID, info, tcpClientGlobal, socketClientGlobal);
}
else {
FString info = "Connection successful:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal;
triggerConnectionEvent(true, clientConnectionID, info, tcpClientGlobal, socketClientGlobal);
tcpClient->createSendThread();
int64 ticks1;
int64 ticks2;
TArray<uint8> byteDataArray;
TArray<uint8> byteDataArrayCache;
FString mainMessage;
bool inCollectMessageStatus = false;
int32 lastDataLengthFromHeader = 0;
uint32 dataSize;
while (socket != nullptr && tcpClient->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();
socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(1));
ticks2 = FDateTime::Now().GetTicks();
bool hasData = socket->HasPendingData(dataSize);
if (!hasData && ticks1 == ticks2) {
UE_LOG(LogTemp, Display, TEXT("Socket Client: Connection aborted or closed by the server: %s"),*clientConnectionID);
break;
}
if (hasData) {
TArray<uint8> dataFromSocket;
dataFromSocket.SetNumUninitialized(dataSize);
int32 BytesRead = 0;
if (socket->Recv(dataFromSocket.GetData(), dataFromSocket.Num(), BytesRead)) {
switch (messageWrapping)
{
case ESocketClientTCPSeparator::E_None:
triggerMessageEvent(dataFromSocket, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal);
break;
case ESocketClientTCPSeparator::E_ByteSeparator:
for (int32 i = 0; i < dataFromSocket.Num(); i++) {
byteDataArrayCache.Add(dataFromSocket[i]);
if (dataFromSocket[i] == byteSeparator) {
triggerMessageEvent(byteDataArrayCache, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal, false);
byteDataArrayCache.Empty();
}
}
break;
case ESocketClientTCPSeparator::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, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal, true);
byteDataArrayCache.Empty();
}
}
else {
byteDataArrayCache.Add(dataFromSocket[i]);
}
}
break;
case ESocketClientTCPSeparator::E_LengthSeparator:
if (lastDataLengthFromHeader == 0 && dataFromSocket.Num() >= 5) {
tcpClient->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, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal);
//UE_LOG(LogTemp, Display, TEXT("%s"), *mainMessage);
byteDataArray.Empty();
if (byteDataArrayCache.Num() == 0) {
lastDataLengthFromHeader = 0;
break;
}
if (byteDataArrayCache.Num() > 5) {
tcpClient->readDataLength(byteDataArrayCache, lastDataLengthFromHeader);
byteDataArrayCache.RemoveAt(0, 5, true);
}
}
break;
}
}
mainMessage.Empty();
byteDataArray.Empty();
dataFromSocket.Empty();
}
}
info = "Connection close:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal;
triggerConnectionEvent(false, clientConnectionID, info, tcpClientGlobal, socketClientGlobal);
}
if (socket != nullptr) {
socket->Close();
}
if (tcpClient->isRun()) {
socketClientBPLibrary->closeSocketClientConnectionTCPNonStatic(clientConnectionID);
//tcpClient->closeConnection();
}
}
else {
FString info = "Connection failed(3). IP not valid:" + ip + ":" + FString::FromInt(portGlobal) + "|" + clientConnectionIDGlobal;
triggerConnectionEvent(false, clientConnectionID,info,tcpClientGlobal, socketClientGlobal);
}
return 0;
}
void FSocketClientTCPReceiveDataThread::triggerConnectionEvent(bool succsess, FString clientConnectionIDGlobal, FString serverMessage, USocketClientTCPClient* tcpClientGlobal, USocketClientBPLibrary* socketClientGlobal){
AsyncTask(ENamedThreads::GameThread, [succsess, serverMessage, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal]() {
if (socketClientGlobal != nullptr) {
socketClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(succsess, serverMessage, clientConnectionIDGlobal);
}
if (tcpClientGlobal != nullptr) {
tcpClientGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(succsess, serverMessage, clientConnectionIDGlobal);
if (tcpClientGlobal->tcpConnectAsyncNode != nullptr) {
tcpClientGlobal->tcpConnectAsyncNode->triggerConnectionEvent(succsess, clientConnectionIDGlobal, serverMessage);
}
}
});
}
void FSocketClientTCPReceiveDataThread::triggerMessageEvent(TArray<uint8>& byteDataArray, FString& clientConnectionIDGlobal, USocketClientTCPClient* tcpClientGlobal,
USocketClientBPLibrary* socketClientGlobal, bool addNullTerminator) {
FString mainMessage = FString();
if (receiveFilter == EReceiveFilterClient::E_SAB || receiveFilter == EReceiveFilterClient::E_S) {
if (addNullTerminator)
byteDataArray.Add(0x00);// null-terminator
mainMessage = FString(UTF8_TO_TCHAR((char*)byteDataArray.GetData()));
if (receiveFilter == EReceiveFilterClient::E_S) {
byteDataArray.Empty();
}
}
//switch to gamethread
AsyncTask(ENamedThreads::GameThread, [mainMessage, byteDataArray, clientConnectionIDGlobal, tcpClientGlobal, socketClientGlobal]() {
if (socketClientGlobal != nullptr) {
socketClientGlobal->onreceiveTCPMessageEventDelegate.Broadcast(mainMessage, byteDataArray, clientConnectionIDGlobal);
}
if (tcpClientGlobal != nullptr) {
tcpClientGlobal->onreceiveTCPMessageEventDelegate.Broadcast(mainMessage, byteDataArray, clientConnectionIDGlobal);
if (tcpClientGlobal->tcpConnectAsyncNode != nullptr) {
tcpClientGlobal->tcpConnectAsyncNode->triggerMessageEvent(byteDataArray, clientConnectionIDGlobal,mainMessage);
}
}
});
mainMessage.Empty();
}
@@ -0,0 +1,167 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketClientTCPSendDataThead.h"
FSocketClientTCPSendDataThead::FSocketClientTCPSendDataThead(USocketClientBPLibrary* socketClientLibP, USocketClientTCPClient* tcpClientP, FString clientConnectionIDP) :
socketClientLib(socketClientLibP),
tcpClient(tcpClientP),
clientConnectionID(clientConnectionIDP) {
FString threadName = "FSendDataToServerThread" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_Normal);
}
FSocketClientTCPSendDataThead::~FSocketClientTCPSendDataThead() {
delete thread;
}
uint32 FSocketClientTCPSendDataThead::Run(){
if (tcpClient == nullptr) {
UE_LOG(LogTemp, Error, TEXT("Class is not initialized."));
return 0;
}
/*if (GEngine)
GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Green, TEXT("tcp socket 1"));*/
FString clientConnectionIDGlobal = clientConnectionID;
USocketClientBPLibrary* socketClientLibGlobal = socketClientLib;
//message wrapping
FString stringSeparator = FString();
uint8 byteSeparator = 0x00;
ESocketClientTCPSeparator messageWrapping = ESocketClientTCPSeparator::E_None;
tcpClient->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 == ESocketClientTCPSeparator::E_LengthSeparator && stringSeparatorArray.Num() == 0) {
messageWrapping = ESocketClientTCPSeparator::E_None;
UE_LOG(LogTemp, Warning, TEXT("Socket Client Plugin: Separator mode is set to String but there is no String Separator. Mode changed to none."));
}
// get the socket
FSocket* socket = tcpClient->getSocket();
int32 sent = 0;
while (tcpClient->isRun()) {
// try to connect to the server
if (socket == NULL || socket == nullptr) {
UE_LOG(LogTemp, Error, TEXT("Connection not exist."));
//AsyncTask(ENamedThreads::GameThread, [clientConnectionIDGlobal, socketClientLibGlobal]() {
// if (socketClientLibGlobal != nullptr) {
// socketClientLibGlobal->onsocketClientTCPConnectionEventDelegate.Broadcast(false, "Connection not exist:" + clientConnectionIDGlobal, clientConnectionIDGlobal);
// socketClientLibGlobal->closeSocketClientConnection();
// }
//});
break;
}
if (socket != nullptr && tcpClient->isRun()) {
while (messageQueue.IsEmpty() == false) {
FString m;
messageQueue.Dequeue(m);
FTCHARToUTF8 Convert(*m);
sent = 0;
TArray<uint8> byteCache;
switch (messageWrapping)
{
case ESocketClientTCPSeparator::E_None:
byteCache.Append((uint8*)Convert.Get(), Convert.Length());
break;
case ESocketClientTCPSeparator::E_ByteSeparator:
byteCache.Append((uint8*)Convert.Get(), Convert.Length());
byteCache.Add(byteSeparator);
break;
case ESocketClientTCPSeparator::E_StringSeparator:
{
m += stringSeparator;
FTCHARToUTF8 ConvertWithSeparator(*m);
byteCache.Append((uint8*)ConvertWithSeparator.Get(), ConvertWithSeparator.Length());
}
break;
case ESocketClientTCPSeparator::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 ESocketClientTCPSeparator::E_ByteSeparator:
byteCache.Add(byteSeparator);
break;
case ESocketClientTCPSeparator::E_StringSeparator:
{
FTCHARToUTF8 ConvertWithSeparator(*stringSeparator);
byteCache.Append((uint8*)ConvertWithSeparator.Get(), ConvertWithSeparator.Length());
}
break;
case ESocketClientTCPSeparator::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);
}
}
if (tcpClient->isRun()) {
pauseThread(true);
//workaround. suspend do not work on all platforms. lets sleep
while (paused && tcpClient->isRun()) {
FPlatformProcess::Sleep(0.01);
}
}
}
return 0;
}
void FSocketClientTCPSendDataThead::sendMessage(FString messageP, TArray<uint8> byteArrayP) {
if (messageP.Len() > 0)
messageQueue.Enqueue(messageP);
if (byteArrayP.Num() > 0)
byteArrayQueue.Enqueue(byteArrayP);
pauseThread(false);
}
void FSocketClientTCPSendDataThead::pauseThread(bool pause) {
paused = pause;
if (thread != nullptr)
thread->Suspend(pause);
}
@@ -0,0 +1,148 @@
// Copyright 2017-2019 David Romanski (Socke). All Rights Reserved.
#include "SocketClientUDP.h"
USocketClientUDP::USocketClientUDP(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {
this->AddToRoot();
}
void USocketClientUDP::socketClientUDPConnectionEventDelegate(const bool success, const FString message, const FString clientConnectionIDP){}
void USocketClientUDP::receiveUDPMessageEventDelegate(const FString message, const TArray<uint8>& byteArray, const FString IPP, const int32 portP, const FString clientConnectionIDP) {}
void USocketClientUDP::init(USocketClientBPLibrary* socketClientLibP, UUDPInitAsyncNode* udpInitAsyncNodeP, FString domainOrIPP,
ESocketClientIPType ipType, int32 portP, EReceiveFilterClient receiveFilterP, FString connectionIDP,
int32 maxPacketSizeP) {
socketClientBPLibrary = socketClientLibP;
receiveFilter = receiveFilterP;
connectionID = connectionIDP;
domainOrIP = domainOrIPP;
port = portP;
udpInitAsyncNode = udpInitAsyncNodeP;
maxPacketSize = maxPacketSizeP;
if (maxPacketSize < 1 || maxPacketSize > 65507)
maxPacketSize = 65507;
UDPThread = new FSocketClientUDPReceiveDataThread(this, socketClientLibP, domainOrIPP, portP, ipType);
}
void USocketClientUDP::sendUDPMessage(FString domainOrIPP, ESocketClientIPType ipType, int32 portP, FString message, TArray<uint8> byteArray){
if (UDPSendThread != nullptr) {
UDPSendThread->addData(message, byteArray, domainOrIPP, portP, ipType);
}
}
void USocketClientUDP::closeUDPConnection() {
if (udpSocketReceiver != nullptr) {
udpSocketReceiver->Stop();
}
setRun(false);
if (UDPSendThread != nullptr) {
UDPSendThread->pauseThread(false);
}
if (socketClientBPLibrary != nullptr) {
FSocketClientPluginSession connectionSession = FSocketClientPluginSession();
connectionSession.udpSocketReceiver = udpSocketReceiver;
connectionSession.udpSendDataThead = UDPSendThread;
connectionSession.udpReceiveDataThread = UDPThread;
connectionSession.socket = socket;
connectionSession.clientID = connectionID;
socketClientBPLibrary->cleanConnection(connectionSession);
}
//udpInitAsyncNode = nullptr;
}
void USocketClientUDP::UDPReceiver(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt) {
if (FSocketClientModule::isShuttingDown)
return;
TSharedPtr<FInternetAddr> peerAddr = EndPt.ToInternetAddr();
FString ipGlobal = peerAddr->ToString(false);
int32 portGlobal = peerAddr->GetPort();
TArray<uint8> byteArray;
if (receiveFilter == EReceiveFilterClient::E_SAB || receiveFilter == EReceiveFilterClient::E_B) {
byteArray.Append(ArrayReaderPtr->GetData(), ArrayReaderPtr->Num());
}
FString recvMessage;
if (receiveFilter == EReceiveFilterClient::E_SAB || receiveFilter == EReceiveFilterClient::E_S) {
ArrayReaderPtr->Add(0x00);// null-terminator
char* Data = (char*)ArrayReaderPtr->GetData();
recvMessage = FString(UTF8_TO_TCHAR(Data));
}
//switch to gamethread
USocketClientBPLibrary* socketClientBPLibraryGlobal = socketClientBPLibrary;
USocketClientUDP* udpClientGlobal = this;
FString clientConnectionIDGlobal = connectionID;
UUDPInitAsyncNode* udpInitAsyncNodeGlobal = udpInitAsyncNode;
AsyncTask(ENamedThreads::GameThread, [udpInitAsyncNodeGlobal, udpClientGlobal, recvMessage, byteArray, ipGlobal, portGlobal, socketClientBPLibraryGlobal, clientConnectionIDGlobal]() {
if (FSocketClientModule::isShuttingDown)
return;
socketClientBPLibraryGlobal->onreceiveUDPMessageEventDelegate.Broadcast(recvMessage, byteArray, ipGlobal, portGlobal, clientConnectionIDGlobal);
udpClientGlobal->onreceiveUDPMessageEventDelegate.Broadcast(recvMessage, byteArray, ipGlobal, portGlobal, clientConnectionIDGlobal);
if (udpInitAsyncNodeGlobal != nullptr) {
udpInitAsyncNodeGlobal->triggerMessageEvent(byteArray, clientConnectionIDGlobal, recvMessage, ipGlobal, portGlobal);
}
});
}
bool USocketClientUDP::isRun(){
return run;
}
void USocketClientUDP::setRun(bool runP){
run = runP;
}
FSocket* USocketClientUDP::getSocket(){
return socket;
}
void USocketClientUDP::setSocket(FSocket* socketP){
socket = socketP;
}
void USocketClientUDP::setUDPSocketReceiver(FUdpSocketReceiver* udpSocketReceiverP){
udpSocketReceiver = udpSocketReceiverP;
}
FString USocketClientUDP::getIP(){
return domainOrIP;
}
void USocketClientUDP::setIP(FString ipP){
domainOrIP = ipP;
}
int32 USocketClientUDP::getPort(){
return port;
}
FString USocketClientUDP::getDomainOrIP(){
return domainOrIP;
}
FString USocketClientUDP::getConnectionID(){
return connectionID;
}
void USocketClientUDP::setUDPSendThread(FSocketClientUDPSendDataThead* udpSendThreadP){
UDPSendThread = udpSendThreadP;
}
int32 USocketClientUDP::getMaxPacketSize()
{
return maxPacketSize;
}
@@ -0,0 +1,117 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketClientUDPReceiveDataThread.h"
FSocketClientUDPReceiveDataThread::FSocketClientUDPReceiveDataThread(USocketClientUDP* udpClientP, USocketClientBPLibrary* socketClientP, FString ipP, int32 portP, ESocketClientIPType ipTypeP) :
udpClient(udpClientP),
socketClient(socketClientP),
ipGlobal(ipP),
portGlobal(portP),
ipType(ipTypeP) {
FString threadName = "FServerUDPConnectionThread_" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_Normal);
}
FSocketClientUDPReceiveDataThread::~FSocketClientUDPReceiveDataThread() {
if (udpClient != nullptr && udpClient->udpInitAsyncNode != nullptr) {
udpClient->udpInitAsyncNode = nullptr;
}
delete thread;
}
void FSocketClientUDPReceiveDataThread::triggerInitEvent(bool success, USocketClientUDP* udpClientP, USocketClientBPLibrary* socketClientP,
FString serverMessage, FString connectionID){
AsyncTask(ENamedThreads::GameThread, [success, udpClientP, socketClientP, serverMessage, connectionID]() {
if (socketClientP != nullptr)
socketClientP->onsocketClientUDPConnectionEventDelegate.Broadcast(success, serverMessage, connectionID);
if (udpClientP != nullptr) {
udpClientP->onsocketClientUDPConnectionEventDelegate.Broadcast(success, serverMessage, connectionID);
if (udpClientP->udpInitAsyncNode != nullptr) {
udpClientP->udpInitAsyncNode->triggerInitEvent(success, connectionID, serverMessage);
}
}
});
}
uint32 FSocketClientUDPReceiveDataThread::Run() {
USocketClientUDP* udpClientGlobal = udpClient;
FString ip = socketClient->resolveDomain(ipGlobal, ipType);
udpClient->setIP(ip);
int32 port = portGlobal;
FString connectionID = udpClient->getConnectionID();
if (socket == nullptr || socket == NULL) {
FString endpointAdress = ip + ":" + FString::FromInt(port);
FIPv4Endpoint Endpoint;
// create the socket
FString socketName;
ISocketSubsystem* socketSubsystem = USocketClientBPLibrary::getSocketSubSystem();
TSharedPtr<class FInternetAddr> addr = socketSubsystem->CreateInternetAddr();
bool validIP = true;
addr->SetPort(port);
addr->SetIp(*ip, validIP);
if (!validIP) {
UE_LOG(LogTemp, Error, TEXT("SocketClient UDP. Can't set ip"));
triggerInitEvent(false, udpClient, socketClient, "SocketClient UDP. Can't set ip", connectionID);
return 0;
}
socket = socketSubsystem->CreateSocket(NAME_DGram, *socketName, addr->GetProtocolType());
if (socket == nullptr || socket == NULL) {
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
UE_LOG(LogTemp, Error, TEXT("UE could not init a UDP socket. You can only create listening connections on local IPs. An external IP must be redirected to a local IP in your router. %s:%i. Error: %s"), *ip, port, SocketErr);
triggerInitEvent(false, udpClient, socketClient, "(Error 0) UE could not init a UDP socket. You can only create listening connections on local IPs. An external IP must be redirected to a local IP in your router." + addr->ToString(true) + " Error:" + SocketErr, connectionID);
return 0;
}
if (!socket->SetRecvErr()) {
UE_LOG(LogTemp, Error, TEXT("SocketClient UDP. Can't set recverr"));
}
if (socket == nullptr || socket == NULL || !validIP) {
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
UE_LOG(LogTemp, Error, TEXT("UE could not init a UDP socket. You can only create listening connections on local IPs. An external IP must be redirected to a local IP in your router. %s:%i. Error: %s"), *ip, port, SocketErr);
triggerInitEvent(false, udpClient, socketClient, "(Error 1) UE could not init a UDP socket. You can only create listening connections on local IPs. An external IP must be redirected to a local IP in your router." + addr->ToString(true) + " Error:" + SocketErr, connectionID);
return 0;
}
socket->SetReuseAddr(true);
socket->SetNonBlocking(true);
socket->SetBroadcast(true);
if (!socket->Bind(*addr)) {
const TCHAR* SocketErr = socketSubsystem->GetSocketError(SE_GET_LAST_ERROR_CODE);
UE_LOG(LogTemp, Error, TEXT("UE could not init a UDP socket. You can only create listening connections on local IPs. An external IP must be redirected to a local IP in your router. %s:%i. Error: %s"), *ip, port, SocketErr);
triggerInitEvent(false, udpClient, socketClient, "(Error 2) UE could not init a UDP socket. You can only create listening connections on local IPs. An external IP must be redirected to a local IP in your router." + addr->ToString(true) + " Error:" + SocketErr, connectionID);
return 0;
}
FTimespan ThreadWaitTime = FTimespan::FromMilliseconds(100);
FUdpSocketReceiver* udpSocketReceiver = new FUdpSocketReceiver(socket, ThreadWaitTime, TEXT("SocketClientBPLibUDPReceiverThread"));
udpSocketReceiver->OnDataReceived().BindUObject(udpClient, &USocketClientUDP::UDPReceiver);
udpSocketReceiver->Start();
udpClient->setUDPSocketReceiver(udpSocketReceiver);
udpClient->setSocket(socket);
udpClient->setRun(true);
udpClient->setUDPSendThread(new FSocketClientUDPSendDataThead(udpClient, socketClient, ip, port));
triggerInitEvent(true, udpClient, socketClient, "Init UDP Connection OK. " + addr->ToString(true), connectionID);
}
thread = nullptr;
return 0;
}
@@ -0,0 +1,146 @@
// Copyright 2022 David Romanski (Socke). All Rights Reserved.
#include "SocketClientUDPSendDataThead.h"
FSocketClientUDPSendDataThead::FSocketClientUDPSendDataThead(USocketClientUDP* udpClientP, USocketClientBPLibrary* socketClientP, FString mySocketipP, int32 mySocketportP) :
udpClient(udpClientP),
socketClient(socketClientP),
mySocketip(mySocketipP),
mySocketport(mySocketportP) {
FString threadName = "FServerUDPSendMessageThread_" + FGuid::NewGuid().ToString();
thread = FRunnableThread::Create(this, *threadName, 0, EThreadPriority::TPri_Normal);
}
FSocketClientUDPSendDataThead::~FSocketClientUDPSendDataThead() {
delete thread;
}
uint32 FSocketClientUDPSendDataThead::Run() {
FSocket* socket = udpClient->getSocket();
FString connectionID = udpClient->getConnectionID();
maxPacketSize = udpClient->getMaxPacketSize();
while (udpClient->isRun() && socket != nullptr) {
if (udpClient->isRun() && (messageQueue.IsEmpty() == false || byteArrayQueue.IsEmpty() == false)) {
int32 sent = 0;
TArray<uint8> byteArray;
if (validInternetAdress) {
while (messageQueue.IsEmpty() == false) {
FString m;
messageQueue.Dequeue(m);
FTCHARToUTF8 Convert(*m);
byteArray.Append((uint8*)Convert.Get(), Convert.Length());
sendBytes(socket, byteArray, sent, internetAdress);
}
while (byteArrayQueue.IsEmpty() == false) {
byteArrayQueue.Dequeue(byteArray);
sendBytes(socket, byteArray, sent, internetAdress);
}
}
else {
UE_LOG(LogTemp, Error, TEXT("Can't send to %s:%i . Adress not valid."), *sendToip, sendToport);
}
}
if (udpClient->isRun()) {
pauseThread(true);
//workaround. suspend do not work on all platforms. lets sleep
while (paused && udpClient->isRun()) {
FPlatformProcess::Sleep(0.01);
}
}
}
if (socket != nullptr) {
socket->Close();
socket = nullptr;
udpClient->setSocket(nullptr);
}
USocketClientBPLibrary* socketClientTMP = socketClient;
USocketClientUDP* udpClientGlobal = udpClient;
FString ipGlobal = mySocketip;
int32 portGlobal = mySocketport;
AsyncTask(ENamedThreads::GameThread, [socketClientTMP, udpClientGlobal, ipGlobal, portGlobal, connectionID]() {
if (socketClientTMP != nullptr)
socketClientTMP->onsocketClientUDPConnectionEventDelegate.Broadcast(false, "UDP connection closed. " + ipGlobal + ":" + FString::FromInt(portGlobal), connectionID);
if (udpClientGlobal != nullptr)
udpClientGlobal->onsocketClientUDPConnectionEventDelegate.Broadcast(false, "UDP connection closed. " + ipGlobal + ":" + FString::FromInt(portGlobal), connectionID);
});
thread = nullptr;
return 0;
}
void FSocketClientUDPSendDataThead::addData(FString messageP, TArray<uint8> byteArrayP, FString domainOrIP, int32 port, ESocketClientIPType ipType) {
//new adress?
if (sendToport != port || sendToDomainOrIP.Equals(domainOrIP) == false) {
sendToDomainOrIP = domainOrIP;
sendToip = socketClient->resolveDomain(domainOrIP, ipType);
sendToport = port;
internetAdress->SetIp(*sendToip, validInternetAdress);
internetAdress->SetPort(sendToport);
if (!validInternetAdress) {
//don't send to many error messages. one second = 10000000 ticks
if (((FDateTime::Now().GetTicks()) - lastErrorMessageTime) >= 10000000) {
UE_LOG(LogTemp, Error, TEXT("Can't create Adress %s:%i"), *sendToip, sendToport);
lastErrorMessageTime = FDateTime::Now().GetTicks();
}
return;
}
}
if (!validInternetAdress) {
return;
}
if (messageP.Len() > 0)
messageQueue.Enqueue(messageP);
if (byteArrayP.Num() > 0)
byteArrayQueue.Enqueue(byteArrayP);
pauseThread(false);
}
void FSocketClientUDPSendDataThead::pauseThread(bool pause) {
paused = pause;
if (thread != nullptr)
thread->Suspend(pause);
}
void FSocketClientUDPSendDataThead::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();
}