Update: ISocketSubsystem::GetHostByName() is deprecated in Unreal 5.0 and should be replaced by a call to ISocketSubsystem::GetAddressInfoAsync(). Because this is an asynchronous call you’ll need to specify a delegate to be called when it returns a value, and because that delegate is likely to run on another thread than the game thread, it’ll be a good idea to use an AsyncTask call to relay it to the game thread. Here’s an example:
static void OnAsyncAddressInfoReceived(ISocketSubsystem* SocketSub, UMyGameGameInstance* GameInstance, const FString& HostStr, const FAddressInfoResult& GAIResult)
{
UE_LOG(LogMyGame, 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)
{
const FAddressInfoResultData& Result{ GAIResult.Results[i] };
const FString AddressString{ Result.Address->ToString(false) };
const FString ProtocolTypeString{ Result.Address->GetProtocolType().ToString() };
UE_LOG(LogMyGame, Log, TEXT("Result #%d Address: %s Type: %s"), i, *AddressString, *ProtocolTypeString);
GameInstance->BlueprintOnAsyncAddressInfoReceived(HostStr, AddressString, ProtocolTypeString);
GameInstance->OnAddressInfoReceivedDelegate.Broadcast(HostStr, AddressString, ProtocolTypeString);
}
}
void UMyGameGameInstance::GetAddressInfoAsync(const FString& HostStr)
{
if (HostStr.IsEmpty())
{
UE_LOG(LogMyGame, Warning, TEXT("UMyGameGameInstance::GetAddressInfoAsync requires a valid host string."));
return;
}
ISocketSubsystem* SocketSub{ ISocketSubsystem::Get() };
if (ensure(SocketSub))
{
const double AsyncRequestStartTime{ FPlatformTime::Seconds() };
UMyGameGameInstance* GameInstance{ this };
FAsyncGetAddressInfoCallback CallbackFunc = [SocketSub, GameInstance, HostStr, AsyncRequestStartTime](FAddressInfoResult Results)
{
UE_LOG(LogMyGame, Log, TEXT("Async GAI Request returned after %f seconds, started at %f"), FPlatformTime::Seconds() - AsyncRequestStartTime, AsyncRequestStartTime);
if (IsInGameThread())
{
OnAsyncAddressInfoReceived(SocketSub, GameInstance, HostStr, Results);
}
else
{
AsyncTask(ENamedThreads::GameThread, [SocketSub, GameInstance, HostStr, Results] {
OnAsyncAddressInfoReceived(SocketSub, GameInstance, HostStr, Results);
});
}
};
SocketSub->GetAddressInfoAsync(CallbackFunc, *HostStr, nullptr, EAddressInfoFlags::Default, NAME_None);
return;
}
UE_LOG(LogMyGame, Warning, TEXT("Failed to get socket subsystem!"));
}
In the example above, I’m handling the call inside the gameinstance, and using an inline lambda to define the callback function. The callback function captures info we’re planning to use inside it, and then uses AsyncTask() to make sure the static function it calls runs on the game thread.
Asynchronous calls are more complicated to use than their synchronous counterparts but they’re much safer in a case like this where we can’t be sure how long it would take for the DNS to resolve the IP address or even whether it would ever succeed at all.
Also be sure to accommodate in your code the possibility that multiple async calls could be kicked off, and decide how you want to make sure you’re pairing the right response with the right call. In my case I just stash the URL I’m looking for and accept the response if the HostStr argument I carry along with it matches the stashed value.