Files
UnrealEngine/Engine/Source/Runtime/Sockets/Private/SocketSubsystem.cpp
2025-05-18 13:04:45 +08:00

793 lines
24 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SocketSubsystem.h"
#include "Misc/CommandLine.h"
#include "Misc/ScopeLock.h"
#include "Misc/CoreMisc.h"
#include "SocketSubsystemModule.h"
#include "Modules/ModuleManager.h"
#include "SocketTypes.h"
#include "IPAddress.h"
#include "IPAddressAsyncResolve.h"
#include "Sockets.h"
#include "Templates/UniquePtr.h"
DEFINE_LOG_CATEGORY(LogSockets);
IMPLEMENT_MODULE( FSocketSubsystemModule, Sockets );
/** Values for the Protocol Typenames */
namespace FNetworkProtocolTypes
{
const FLazyName IPv4(TEXT("IPv4"));
const FLazyName IPv6(TEXT("IPv6"));
}
/** Each platform will implement these functions to construct/destroy socket implementations */
extern FName CreateSocketSubsystem(FSocketSubsystemModule& SocketSubsystemModule);
extern void DestroySocketSubsystem(FSocketSubsystemModule& SocketSubsystemModule);
/** Helper function to turn the friendly subsystem name into the module name */
static inline FName GetSocketModuleName(const FString& SubsystemName)
{
FName ModuleName;
FString SocketBaseName(TEXT("Sockets"));
if (!SubsystemName.StartsWith(SocketBaseName, ESearchCase::CaseSensitive))
{
ModuleName = FName(*(SocketBaseName + SubsystemName));
}
else
{
ModuleName = FName(*SubsystemName);
}
return ModuleName;
}
/**
* Helper function that loads a given platform service module if it isn't already loaded
*
* @param SubsystemName Name of the requested platform service to load
* @return The module interface of the requested platform service, NULL if the service doesn't exist
*/
static IModuleInterface* LoadSubsystemModule(const FString& SubsystemName)
{
#if !UE_BUILD_SHIPPING && !UE_BUILD_SHIPPING_WITH_EDITOR
// Early out if we are overriding the module load
bool bAttemptLoadModule = !FParse::Param(FCommandLine::Get(), *FString::Printf(TEXT("no%s"), *SubsystemName));
if (bAttemptLoadModule)
#endif
{
FName ModuleName;
FModuleManager& ModuleManager = FModuleManager::Get();
ModuleName = GetSocketModuleName(SubsystemName);
if (!ModuleManager.IsModuleLoaded(ModuleName))
{
// Attempt to load the module
ModuleManager.LoadModule(ModuleName);
}
return ModuleManager.GetModule(ModuleName);
}
#if !UE_BUILD_SHIPPING && !UE_BUILD_SHIPPING_WITH_EDITOR
return nullptr;
#endif
}
ISocketSubsystem::ISocketSubsystem() = default;
ISocketSubsystem::~ISocketSubsystem() = default;
FUniqueSocket ISocketSubsystem::CreateUniqueSocket(const FName& SocketType, const FString& SocketDescription, bool bForceUDP)
{
return FUniqueSocket(CreateSocket(SocketType, SocketDescription, bForceUDP), FSocketDeleter(this));
}
FUniqueSocket ISocketSubsystem::CreateUniqueSocket(const FName& SocketType, const FString& SocketDescription, const FName& ProtocolName)
{
return FUniqueSocket(CreateSocket(SocketType, SocketDescription, ProtocolName), FSocketDeleter(this));
}
/**
* Shutdown all registered subsystems
*/
void ISocketSubsystem::ShutdownAllSystems()
{
if (IsInGameThread() && FModuleManager::Get().IsModuleLoaded(TEXT("Sockets")) == true)
{
// Unloading the Sockets module will call FSocketSubsystemModule::ShutdownSocketSubsystem()
const bool bIsShutdown = true;
FModuleManager::Get().UnloadModule(TEXT("Sockets"), bIsShutdown);
}
}
/**
* Called right after the module DLL has been loaded and the module object has been created
* Overloaded to allow the default subsystem a chance to load
*/
void FSocketSubsystemModule::StartupModule()
{
FString InterfaceString;
// Initialize the platform defined socket subsystem first
DefaultSocketSubsystem = CreateSocketSubsystem( *this );
}
/**
* Called before the module is unloaded, right before the module object is destroyed.
* Overloaded to shut down all loaded online subsystems
*/
void FSocketSubsystemModule::ShutdownModule()
{
ShutdownSocketSubsystem();
}
void FSocketSubsystemModule::ShutdownSocketSubsystem()
{
// Destroy the platform defined socket subsystem first
DestroySocketSubsystem( *this );
FModuleManager& ModuleManager = FModuleManager::Get();
// Unload all the supporting factories
for (TMap<FName, ISocketSubsystem*>::TIterator It(SocketSubsystems); It; ++It)
{
It.Value()->Shutdown();
// Unloading the module will do proper cleanup
FName ModuleName = GetSocketModuleName(It.Key().ToString());
const bool bIsShutdown = true;
ModuleManager.UnloadModule(ModuleName, bIsShutdown);
}
//ensure(SocketSubsystems.Num() == 0);
}
/**
* Register a new socket subsystem interface with the base level factory provider
*
* @param FactoryName - name of subsystem as referenced by consumers
* @param Factory - instantiation of the socket subsystem interface, this will take ownership
* @param bMakeDefault - make this subsystem the default
*/
void FSocketSubsystemModule::RegisterSocketSubsystem(const FName FactoryName, ISocketSubsystem* Factory, bool bMakeDefault)
{
if (!SocketSubsystems.Contains(FactoryName))
{
SocketSubsystems.Add(FactoryName, Factory);
}
if (bMakeDefault)
{
DefaultSocketSubsystem = FactoryName;
}
}
/**
* Unregister an existing online subsystem interface from the base level factory provider
* @param FactoryName - name of subsystem as referenced by consumers
*/
void FSocketSubsystemModule::UnregisterSocketSubsystem(const FName FactoryName)
{
if (SocketSubsystems.Contains(FactoryName))
{
SocketSubsystems.Remove(FactoryName);
}
}
/**
* Main entry point for accessing a socket subsystem by name
* Will load the appropriate module if the subsystem isn't currently loaded
* It's possible that the subsystem doesn't exist and therefore can return NULL
*
* @param SubsystemName - name of subsystem as referenced by consumers
* @return Requested socket subsystem, or NULL if that subsystem was unable to load or doesn't exist
*/
ISocketSubsystem* FSocketSubsystemModule::GetSocketSubsystem(const FName InSubsystemName)
{
FName SubsystemName = InSubsystemName;
if (SubsystemName == NAME_None)
{
SubsystemName = DefaultSocketSubsystem;
}
ISocketSubsystem** SocketSubsystemFactory = SocketSubsystems.Find(SubsystemName);
if (SocketSubsystemFactory == nullptr)
{
// Attempt to load the requested factory
IModuleInterface* NewModule = LoadSubsystemModule(SubsystemName.ToString());
if (NewModule)
{
// If the module loaded successfully this should be non-NULL;
SocketSubsystemFactory = SocketSubsystems.Find(SubsystemName);
}
if (SocketSubsystemFactory == nullptr)
{
UE_LOG(LogSockets, Warning, TEXT("Unable to load SocketSubsystem module %s"), *InSubsystemName.ToString());
}
}
return (SocketSubsystemFactory == nullptr) ? nullptr : *SocketSubsystemFactory;
}
//////////////////////////////////
// ISocketSubsystem
/////////////////////////////////
ISocketSubsystem* ISocketSubsystem::Get(const FName& SubsystemName)
{
// wrap the platform file with a logger
// static TUniquePtr<FLoggedPlatformFile> AutoDestroyLog;
// AutoDestroyLog = MakeUnique<FLoggedPlatformFile>(*CurrentPlatformFile);
struct FStatic
{
FSocketSubsystemModule& SSSModule;
FStatic()
: SSSModule( FModuleManager::LoadModuleChecked<FSocketSubsystemModule>("Sockets") )
{}
~FStatic()
{
ISocketSubsystem::ShutdownAllSystems();
}
};
static FStatic StaticSockets;
return StaticSockets.SSSModule.GetSocketSubsystem(SubsystemName);
}
int32 ISocketSubsystem::BindNextPort(FSocket* Socket, FInternetAddr& Addr, int32 PortCount, int32 PortIncrement)
{
// go until we reach the limit (or we succeed)
for (int32 Index = 0; Index < PortCount; Index++)
{
// try to bind to the current port
if (Socket->Bind(Addr) == true)
{
// if it succeeded, return the port
if (Addr.GetPort() != 0)
{
return Addr.GetPort();
}
else
{
return Socket->GetPortNo();
}
}
// if the address had no port, we are done
if( Addr.GetPort() == 0 )
{
break;
}
// increment to the next port, and loop!
Addr.SetPort(Addr.GetPort() + PortIncrement);
}
return 0;
}
/** Async task support for GetAddressInfo */
class FGetAddressInfoTask : public FNonAbandonableTask
{
public:
friend class FAutoDeleteAsyncTask<FGetAddressInfoTask>;
FGetAddressInfoTask(class ISocketSubsystem* InSocketSubsystem, const FString& InQueryHost,
const FString& InQueryService, EAddressInfoFlags InQueryFlags, const FName& InQueryProtocol,
ESocketType InQuerySocketType, FAsyncGetAddressInfoCallback InCallbackFunction) :
SocketSubsystem(InSocketSubsystem),
QueryHost(InQueryHost),
QueryService(InQueryService),
QueryFlags(InQueryFlags),
QueryProtocol(InQueryProtocol),
QuerySocketType(InQuerySocketType),
CallbackFunction(InCallbackFunction)
{
}
void DoWork()
{
CallbackFunction(SocketSubsystem->GetAddressInfo(*QueryHost, *QueryService, QueryFlags, QueryProtocol, QuerySocketType));
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FGetAddressInfoTask, STATGROUP_ThreadPoolAsyncTasks);
}
private:
ISocketSubsystem* SocketSubsystem;
const FString QueryHost;
const FString QueryService;
EAddressInfoFlags QueryFlags;
const FName QueryProtocol;
ESocketType QuerySocketType;
FAsyncGetAddressInfoCallback CallbackFunction;
};
void ISocketSubsystem::GetAddressInfoAsync(FAsyncGetAddressInfoCallback Callback, const TCHAR* HostName, const TCHAR* ServiceName,
EAddressInfoFlags QueryFlags, const FName ProtocolTypeName, ESocketType SocketType)
{
(new FAutoDeleteAsyncTask<FGetAddressInfoTask>(this, HostName, ServiceName, QueryFlags, ProtocolTypeName, SocketType, Callback))->StartBackgroundTask();
}
TArray<TSharedRef<FInternetAddr>> ISocketSubsystem::GetLocalBindAddresses()
{
TArray<TSharedRef<FInternetAddr>> BindingAddresses;
// Multihome addresses should be the only thing in the array if it exists.
TSharedRef<FInternetAddr> MultihomeAddr = CreateInternetAddr();
if (GetMultihomeAddress(MultihomeAddr))
{
BindingAddresses.Add(MultihomeAddr);
return BindingAddresses;
}
// Next, we want to grab the bindable addresses, which we ask GAI for.
// Because of how GAI works, we need to push in a service flag otherwise the query will fail.
//
// Since these addresses will be initialized to 0 anyways due to the nature of C++, set our service to be the number 0, which will
// cause no issues in terms of functionality.
FAddressInfoResult BindableAddresses = GetAddressInfo(nullptr, TEXT("0"),
EAddressInfoFlags::AllResultsWithMapping | EAddressInfoFlags::BindableAddress | EAddressInfoFlags::OnlyUsableAddresses, NAME_None);
if (BindableAddresses.ReturnCode == SE_NO_ERROR)
{
// Push in all the bindable addresses.
for (const FAddressInfoResultData& BindAddresses : BindableAddresses.Results)
{
// GetAddressInfo can return both TCP and UDP bindings for the same address - which is redundant when returning only addresses
if (!BindingAddresses.ContainsByPredicate([&](const TSharedRef<FInternetAddr>& A) { return *A == *BindAddresses.Address; }))
{
BindingAddresses.Add(BindAddresses.Address);
}
}
}
return BindingAddresses;
}
bool ISocketSubsystem::GetLocalAdapterAddresses(TArray<TSharedPtr<FInternetAddr>>& OutAddresses)
{
FString HostName;
UE_LOG(LogSockets, Warning, TEXT("Falling back to generic GetLocalAdapterAddresses implementation. Consider implementing a Platform specific version."));
// Attempt to get a hostname so that we can look it up in order to get an idea of adapters that we might have
// (or the addresses that are tied to us). This is a fallback implementation for platforms that do not have this implemented.
// Platforms are encouraged to implement this themselves.
if (GetHostName(HostName))
{
// We want usable addresses, and if possible get every protocol and map it. Some platforms will do this query twice to make sure
// we have all protocols. Others will only use the protocol specified.
FAddressInfoResult GAIRequest = GetAddressInfo(*HostName, nullptr,
EAddressInfoFlags::AllResultsWithMapping | EAddressInfoFlags::OnlyUsableAddresses,
NAME_None);
// Start packing the addresses we got to the results.
if (GAIRequest.ReturnCode == SE_NO_ERROR)
{
// Push all results into the output array.
for (const auto& AddressResult : GAIRequest.Results)
{
OutAddresses.Add(AddressResult.Address);
}
}
return true;
}
return false;
}
FResolveInfo* ISocketSubsystem::GetHostByName(const ANSICHAR* HostName)
{
FResolveInfo* Result = NULL;
TSharedPtr<FInternetAddr> Addr;
// See if we have it cached or not
if (GetHostByNameFromCache(HostName, Addr))
{
Result = CreateResolveInfoCached(Addr);
}
else
{
// Create an async resolve info
FResolveInfoAsync* AsyncResolve = new FResolveInfoAsync(HostName);
AsyncResolve->StartAsyncTask();
Result = AsyncResolve;
}
return Result;
}
TSharedRef<FInternetAddr> ISocketSubsystem::GetLocalHostAddr(FOutputDevice& Out, bool& bCanBindAll)
{
TSharedRef<FInternetAddr> HostAddr = CreateInternetAddr();
bCanBindAll = false;
if (!GetMultihomeAddress(HostAddr))
{
bCanBindAll = true;
TArray<TSharedPtr<FInternetAddr>> AdapterAddresses;
if (!GetLocalAdapterAddresses(AdapterAddresses) || (AdapterAddresses.Num() == 0))
{
Out.Logf(TEXT("Could not fetch the local adapter addresses"));
HostAddr->SetAnyAddress();
}
else
{
if (AdapterAddresses.Num() > 0)
{
HostAddr = AdapterAddresses[0]->Clone();
}
}
}
return HostAddr;
}
TSharedRef<FInternetAddr> ISocketSubsystem::GetLocalBindAddr(FOutputDevice& Out)
{
TArray<TSharedRef<FInternetAddr>> BindingAddresses = GetLocalBindAddresses();
if (BindingAddresses.Num() == 0)
{
TSharedRef<FInternetAddr> AnyAddress = CreateInternetAddr();
AnyAddress->SetAnyAddress();
return AnyAddress;
}
return BindingAddresses[0];
}
bool ISocketSubsystem::GetMultihomeAddress(TSharedRef<FInternetAddr>& Addr)
{
TCHAR Home[256] = {};
if (FParse::Value(FCommandLine::Get(), TEXT("MULTIHOME="), Home, UE_ARRAY_COUNT(Home)))
{
TSharedPtr<FInternetAddr> MultiHomeQuery = GetAddressFromString(Home);
if (Home == NULL || !MultiHomeQuery.IsValid())
{
UE_LOG(LogSockets, Log, TEXT("Invalid multihome IP address %s"), Home);
return false;
}
else
{
Addr = MultiHomeQuery.ToSharedRef();
}
return true;
}
return false;
}
bool ISocketSubsystem::GetHostByNameFromCache(const ANSICHAR* HostName, TSharedPtr<FInternetAddr>& Addr)
{
// Lock for thread safety
FScopeLock sl(&HostNameCacheSync);
// Now search for the entry
TSharedPtr<FInternetAddr>* FoundAddr = HostNameCache.Find(FString(HostName));
if (FoundAddr)
{
Addr = *FoundAddr;
}
return FoundAddr != NULL;
}
void ISocketSubsystem::AddHostNameToCache(const ANSICHAR* HostName, TSharedPtr<FInternetAddr> Addr)
{
// Lock for thread safety
FScopeLock sl(&HostNameCacheSync);
HostNameCache.Add(FString(HostName), Addr);
}
void ISocketSubsystem::RemoveHostNameFromCache(const ANSICHAR* HostName)
{
// Lock for thread safety
FScopeLock sl(&HostNameCacheSync);
HostNameCache.Remove(FString(HostName));
}
ESocketProtocolFamily ISocketSubsystem::GetProtocolFamilyFromName(const FName& InProtocolName) const
{
if (InProtocolName == FNetworkProtocolTypes::IPv6)
{
return ESocketProtocolFamily::IPv6;
}
else if (InProtocolName == FNetworkProtocolTypes::IPv4)
{
return ESocketProtocolFamily::IPv4;
}
return ESocketProtocolFamily::None;
}
FName ISocketSubsystem::GetProtocolNameFromFamily(ESocketProtocolFamily InProtocolFamily) const
{
switch (InProtocolFamily)
{
default:
return NAME_None;
case ESocketProtocolFamily::IPv4:
return FNetworkProtocolTypes::IPv4;
case ESocketProtocolFamily::IPv6:
return FNetworkProtocolTypes::IPv6;
}
}
FResolveInfoCached* ISocketSubsystem::CreateResolveInfoCached(TSharedPtr<FInternetAddr> Addr) const
{
return new FResolveInfoCached(*Addr);
}
/**
* Returns a human readable string from an error code
*
* @param Code the error code to check
*/
const TCHAR* ISocketSubsystem::GetSocketError(ESocketErrors Code)
{
if (Code == SE_GET_LAST_ERROR_CODE)
{
Code = GetLastErrorCode();
}
switch (Code)
{
case SE_NO_ERROR: return TEXT("SE_NO_ERROR");
case SE_EINTR: return TEXT("SE_EINTR");
case SE_EBADF: return TEXT("SE_EBADF");
case SE_EACCES: return TEXT("SE_EACCES");
case SE_EFAULT: return TEXT("SE_EFAULT");
case SE_EINVAL: return TEXT("SE_EINVAL");
case SE_EMFILE: return TEXT("SE_EMFILE");
case SE_EWOULDBLOCK: return TEXT("SE_EWOULDBLOCK");
case SE_EINPROGRESS: return TEXT("SE_EINPROGRESS");
case SE_EALREADY: return TEXT("SE_EALREADY");
case SE_ENOTSOCK: return TEXT("SE_ENOTSOCK");
case SE_EDESTADDRREQ: return TEXT("SE_EDESTADDRREQ");
case SE_EMSGSIZE: return TEXT("SE_EMSGSIZE");
case SE_EPROTOTYPE: return TEXT("SE_EPROTOTYPE");
case SE_ENOPROTOOPT: return TEXT("SE_ENOPROTOOPT");
case SE_EPROTONOSUPPORT: return TEXT("SE_EPROTONOSUPPORT");
case SE_ESOCKTNOSUPPORT: return TEXT("SE_ESOCKTNOSUPPORT");
case SE_EOPNOTSUPP: return TEXT("SE_EOPNOTSUPP");
case SE_EPFNOSUPPORT: return TEXT("SE_EPFNOSUPPORT");
case SE_EAFNOSUPPORT: return TEXT("SE_EAFNOSUPPORT");
case SE_EADDRINUSE: return TEXT("SE_EADDRINUSE");
case SE_EADDRNOTAVAIL: return TEXT("SE_EADDRNOTAVAIL");
case SE_ENETDOWN: return TEXT("SE_ENETDOWN");
case SE_ENETUNREACH: return TEXT("SE_ENETUNREACH");
case SE_ENETRESET: return TEXT("SE_ENETRESET");
case SE_ECONNABORTED: return TEXT("SE_ECONNABORTED");
case SE_ECONNRESET: return TEXT("SE_ECONNRESET");
case SE_ENOBUFS: return TEXT("SE_ENOBUFS");
case SE_EISCONN: return TEXT("SE_EISCONN");
case SE_ENOTCONN: return TEXT("SE_ENOTCONN");
case SE_ESHUTDOWN: return TEXT("SE_ESHUTDOWN");
case SE_ETOOMANYREFS: return TEXT("SE_ETOOMANYREFS");
case SE_ETIMEDOUT: return TEXT("SE_ETIMEDOUT");
case SE_ECONNREFUSED: return TEXT("SE_ECONNREFUSED");
case SE_ELOOP: return TEXT("SE_ELOOP");
case SE_ENAMETOOLONG: return TEXT("SE_ENAMETOOLONG");
case SE_EHOSTDOWN: return TEXT("SE_EHOSTDOWN");
case SE_EHOSTUNREACH: return TEXT("SE_EHOSTUNREACH");
case SE_ENOTEMPTY: return TEXT("SE_ENOTEMPTY");
case SE_EPROCLIM: return TEXT("SE_EPROCLIM");
case SE_EUSERS: return TEXT("SE_EUSERS");
case SE_EDQUOT: return TEXT("SE_EDQUOT");
case SE_ESTALE: return TEXT("SE_ESTALE");
case SE_EREMOTE: return TEXT("SE_EREMOTE");
case SE_EDISCON: return TEXT("SE_EDISCON");
case SE_SYSNOTREADY: return TEXT("SE_SYSNOTREADY");
case SE_VERNOTSUPPORTED: return TEXT("SE_VERNOTSUPPORTED");
case SE_NOTINITIALISED: return TEXT("SE_NOTINITIALISED");
case SE_HOST_NOT_FOUND: return TEXT("SE_HOST_NOT_FOUND");
case SE_TRY_AGAIN: return TEXT("SE_TRY_AGAIN");
case SE_NO_RECOVERY: return TEXT("SE_NO_RECOVERY");
case SE_NO_DATA: return TEXT("SE_NO_DATA");
case SE_UDP_ERR_PORT_UNREACH: return TEXT("SE_UDP_ERR_PORT_UNREACH");
case SE_ADDRFAMILY: return TEXT("SE_ADDRFAMILY");
case SE_SYSTEM: return TEXT("SE_SYSTEM");
case SE_NODEV: return TEXT("SE_NODEV");
default: return TEXT("Unknown Error");
};
}
TUniquePtr<FRecvMulti> ISocketSubsystem::CreateRecvMulti(int32 MaxNumPackets, int32 MaxPacketSize,
ERecvMultiFlags Flags/*=ERecvMultiFlags::None*/)
{
UE_LOG(LogSockets, Warning, TEXT("RecvMulti is not supported by current socket subsystem."));
return nullptr;
}
bool ISocketSubsystem::IsSocketRecvMultiSupported() const
{
return false;
}
double ISocketSubsystem::TranslatePacketTimestamp(const FPacketTimestamp& Timestamp,
ETimestampTranslation Translation/*=ETimestampTranslation::LocalTimestamp*/)
{
UE_LOG(LogSockets, Warning, TEXT("TranslatePacketTimestamp is not supported by current socket subsystem."));
return 0.0;
}
bool ISocketSubsystem::IsRecvFromWithPktInfoSupported() const
{
return false;
}
//////////////////////////////////
// SocketSubsystem Testing
/////////////////////////////////
#if WITH_DEV_AUTOMATION_TESTS && !UE_BUILD_SHIPPING
static void DebugPrintGaiResults(ISocketSubsystem* SocketSub, const FAddressInfoResult& GAIResult)
{
UE_LOG(LogSockets, Log, TEXT("Got %d GAI Results for hostname %s. Error Code: %s [%d]"), GAIResult.Results.Num(), *GAIResult.QueryHostName,
SocketSub->GetSocketError(GAIResult.ReturnCode), GAIResult.ReturnCode);
for (int i = 0; i < GAIResult.Results.Num(); ++i)
{
UE_LOG(LogSockets, Log, TEXT("Result #%d Address: %s Type: %s"), i, *GAIResult.Results[i].Address->ToString(false),
*GAIResult.Results[i].Address->GetProtocolType().ToString());
}
}
static void RunGAIQuery(const FString& HostStr)
{
if (HostStr.IsEmpty())
{
UE_LOG(LogSockets, Warning, TEXT("SOCKETSUB GAI requires an input string to test with."));
return;
}
ISocketSubsystem* SocketSub = ISocketSubsystem::Get();
if (SocketSub)
{
FAddressInfoResult GAIResult = SocketSub->GetAddressInfo(*HostStr, nullptr, EAddressInfoFlags::AllResultsWithMapping | EAddressInfoFlags::OnlyUsableAddresses, NAME_None);
if (GAIResult.Results.Num() > 0)
{
DebugPrintGaiResults(SocketSub, GAIResult);
}
else
{
UE_LOG(LogSockets, Warning, TEXT("Did not get results!"));
}
return;
}
UE_LOG(LogSockets, Warning, TEXT("Failed to get socket subsystem!"));
}
static void RunAsyncGAIQuery(const FString& HostStr)
{
if (HostStr.IsEmpty())
{
UE_LOG(LogSockets, Warning, TEXT("SOCKETSUB ASYNCGAI requires an input string to test with."));
return;
}
ISocketSubsystem* SocketSub = ISocketSubsystem::Get();
if (SocketSub)
{
double AsyncRequestStartTime = FPlatformTime::Seconds();
FAsyncGetAddressInfoCallback CallbackFunc = [SocketSub, AsyncRequestStartTime](FAddressInfoResult Results)
{
UE_LOG(LogSockets, Log, TEXT("Async GAI Request returned after %f seconds, started at %f"), FPlatformTime::Seconds() - AsyncRequestStartTime, AsyncRequestStartTime);
DebugPrintGaiResults(SocketSub, Results);
};
SocketSub->GetAddressInfoAsync(CallbackFunc, *HostStr, nullptr, EAddressInfoFlags::Default, NAME_None);
return;
}
UE_LOG(LogSockets, Warning, TEXT("Failed to get socket subsystem!"));
}
static void RunAddressSerialize(const FString& InputStr)
{
if (InputStr.IsEmpty())
{
UE_LOG(LogSockets, Warning, TEXT("SOCKETSUB Serialize requires an ip address to test with."));
return;
}
ISocketSubsystem* SocketSub = ISocketSubsystem::Get();
if (SocketSub)
{
TSharedPtr<FInternetAddr> NewAddr = SocketSub->GetAddressFromString(InputStr);
if (NewAddr.IsValid())
{
UE_LOG(LogSockets, Log, TEXT("Result Address: %s Type: %s"), *NewAddr->ToString(false), *NewAddr->GetProtocolType().ToString());
}
else
{
UE_LOG(LogSockets, Warning, TEXT("Did not get results!"));
}
return;
}
UE_LOG(LogSockets, Warning, TEXT("Failed to get socket subsystem!"));
}
static bool SocketSubsystemCommandHandler(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
if (FParse::Command(&Cmd, TEXT("SOCKETSUB")))
{
if (FParse::Command(&Cmd, TEXT("GAI")))
{
RunGAIQuery(FParse::Token(Cmd, true));
}
else if (FParse::Command(&Cmd, TEXT("SERIALIZE")))
{
RunAddressSerialize(FParse::Token(Cmd, true));
}
else if (FParse::Command(&Cmd, TEXT("ASYNCGAI")))
{
RunAsyncGAIQuery(FParse::Token(Cmd, true));
}
else
{
ISocketSubsystem* SocketSub = ISocketSubsystem::Get();
check(SocketSub);
if (FParse::Command(&Cmd, TEXT("GETBINDADDRESSES")))
{
TArray<TSharedRef<FInternetAddr>> BindAddresses = SocketSub->GetLocalBindAddresses();
UE_LOG(LogSockets, Log, TEXT("Got Binding Addresses:"));
for (const auto& BindAddress : BindAddresses)
{
UE_LOG(LogSockets, Log, TEXT("# Bind Address: %s"), *BindAddress->ToString(false));
}
}
else if (FParse::Command(&Cmd, TEXT("GETADAPTERS")))
{
TArray<TSharedPtr<FInternetAddr>> AdapterAddresses;
if (SocketSub->GetLocalAdapterAddresses(AdapterAddresses))
{
UE_LOG(LogSockets, Log, TEXT("Got Local Adapter Addresses:"));
for (const auto& AdapterAddress : AdapterAddresses)
{
UE_LOG(LogSockets, Log, TEXT("# Adapter Address: %s"), *AdapterAddress->ToString(false));
}
}
else
{
UE_LOG(LogSockets, Warning, TEXT("Could not get the local adapter addresses!"));
}
}
else if (FParse::Command(&Cmd, TEXT("GETLOCALHOST")))
{
bool bCanBindAll = false;
TSharedRef<FInternetAddr> LocalHostAddr = SocketSub->GetLocalHostAddr(Ar, bCanBindAll);
UE_LOG(LogSockets, Log, TEXT("LocalHostAddr is %s (this function auto adds multihome) Bind all: %d"), *LocalHostAddr->ToString(false), bCanBindAll);
}
else if (FParse::Command(&Cmd, TEXT("GETBINDADDR")))
{
TSharedRef<FInternetAddr> BindAddr = SocketSub->GetLocalBindAddr(Ar);
UE_LOG(LogSockets, Log, TEXT("The Local binding addr is %s"), *BindAddr->ToString(false));
}
}
return true;
}
return false;
}
FStaticSelfRegisteringExec FSocketSubsystemExecs(SocketSubsystemCommandHandler);
#endif // WITH_DEV_AUTOMATION_TESTS && !UE_BUILD_SHIPPING