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

546 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "AddressInfoTypes.h"
#include "Containers/Array.h"
#include "Containers/Map.h"
#include "Containers/StringConv.h"
#include "Containers/UnrealString.h"
#include "CoreMinimal.h"
#include "CoreTypes.h"
#include "HAL/CriticalSection.h"
#include "IPAddress.h"
#include "Logging/LogMacros.h"
#include "Net/Common/Sockets/SocketErrors.h"
#include "SocketTypes.h"
#include "Templates/SharedPointer.h"
#include "Templates/UniquePtr.h"
#include "UObject/NameTypes.h"
#include "UObject/UnrealNames.h"
class Error;
class FInternetAddr;
class FOutputDevice;
class FSocket;
struct FPacketTimestamp;
SOCKETS_API DECLARE_LOG_CATEGORY_EXTERN(LogSockets, Log, All);
// Need to guarantee the "default" socket subsystem on these platforms
// as other subsystems (ie Steam) might override the default
#ifndef PLATFORM_SOCKETSUBSYSTEM
#if PLATFORM_WINDOWS
#define PLATFORM_SOCKETSUBSYSTEM FName(TEXT("WINDOWS"))
#elif PLATFORM_MAC
#define PLATFORM_SOCKETSUBSYSTEM FName(TEXT("MAC"))
#elif PLATFORM_IOS
#define PLATFORM_SOCKETSUBSYSTEM FName(TEXT("IOS"))
#elif PLATFORM_UNIX
#define PLATFORM_SOCKETSUBSYSTEM FName(TEXT("UNIX"))
#elif PLATFORM_ANDROID
#define PLATFORM_SOCKETSUBSYSTEM FName(TEXT("ANDROID"))
#else
#define PLATFORM_SOCKETSUBSYSTEM FName(TEXT(""))
#endif
#endif
/** A unique pointer that will automatically call ISocketSubsystem::DestroySocket on its owned socket. */
class FSocketDeleter;
using FUniqueSocket = TUniquePtr<FSocket, FSocketDeleter>;
/**
* This is the base interface to abstract platform specific sockets API
* differences.
*/
class ISocketSubsystem
{
public:
/**
* Get the singleton socket subsystem for the given named subsystem
*/
static SOCKETS_API ISocketSubsystem* Get(const FName& SubsystemName=NAME_None);
/**
* Shutdown all registered subsystems
*/
static SOCKETS_API void ShutdownAllSystems();
SOCKETS_API ISocketSubsystem();
SOCKETS_API virtual ~ISocketSubsystem();
/**
* Does per platform initialization of the sockets library
*
* @param Error a string that is filled with error information
*
* @return true if initialized ok, false otherwise
*/
virtual bool Init(FString& Error) = 0;
/**
* Performs platform specific socket clean up
*/
virtual void Shutdown() = 0;
/**
* Creates a socket
*
* @Param SocketType type of socket to create (DGram, Stream, etc)
* @param SocketDescription debug description
* @param bForceUDP overrides any platform specific protocol with UDP instead
*
* @return the new socket or NULL if failed
*/
virtual FSocket* CreateSocket(const FName& SocketType, const FString& SocketDescription, bool bForceUDP = false)
{
const FName NoProtocolTypeName(NAME_None);
return CreateSocket(SocketType, SocketDescription, NoProtocolTypeName);
}
/**
* Creates a socket
*
* @Param SocketType type of socket to create (DGram, Stream, etc)
* @param SocketDescription debug description
* @param ProtocolType the socket protocol to be used. Each subsystem must handle the None case and output a valid socket regardless.
*
* @return the new socket or NULL if failed
*/
UE_DEPRECATED(4.23, "Use the CreateSocket with the FName parameter for support for multiple protocol types.")
virtual FSocket* CreateSocket(const FName& SocketType, const FString& SocketDescription, ESocketProtocolFamily ProtocolType)
{
return CreateSocket(SocketType, SocketDescription, GetProtocolNameFromFamily(ProtocolType));
}
/**
* Creates a socket using the given protocol name.
*
* @Param SocketType type of socket to create (DGram, Stream, etc)
* @param SocketDescription debug description
* @param ProtocolName the name of the internet protocol to use for this socket. None should be handled.
*
* @return the new socket or NULL if failed
*/
virtual FSocket* CreateSocket(const FName& SocketType, const FString& SocketDescription, const FName& ProtocolName) = 0;
/**
* Creates a socket wrapped in a unique pointer that will call DestroySocket automatically - do not call it explicitly!
* This SocketSubsystem must also outlive the sockets it creates.
*
* @Param SocketType type of socket to create (DGram, Stream, etc)
* @param SocketDescription debug description
* @param bForceUDP overrides any platform specific protocol with UDP instead
*
* @return the new socket or NULL if failed
*/
SOCKETS_API FUniqueSocket CreateUniqueSocket(const FName& SocketType, const FString& SocketDescription, bool bForceUDP = false);
/**
* Creates a socket using the given protocol name, wrapped in a unique pointer that will call DestroySocket automatically - do not call it explicitly!
* This SocketSubsystem must also outlive the sockets it creates.
*
* @Param SocketType type of socket to create (DGram, Stream, etc)
* @param SocketDescription debug description
* @param ProtocolName the name of the internet protocol to use for this socket. None should be handled.
*
* @return the new socket or NULL if failed
*/
SOCKETS_API FUniqueSocket CreateUniqueSocket(const FName& SocketType, const FString& SocketDescription, const FName& ProtocolName);
/**
* Creates a resolve info cached struct to hold the resolved address
*
* @Param Addr address to resolve for the socket subsystem
*
* @return the new resolved address or NULL if failed
*/
SOCKETS_API virtual class FResolveInfoCached* CreateResolveInfoCached(TSharedPtr<FInternetAddr> Addr) const;
/**
* Cleans up a socket class
*
* @param Socket the socket object to destroy
*/
virtual void DestroySocket(FSocket* Socket) = 0;
/**
* Gets the address information of the given hostname and outputs it into an array of resolvable addresses.
* It is up to the caller to determine which one is valid for their environment.
*
* @param HostName string version of the queryable hostname or ip address
* @param ServiceName string version of a service name ("http") or a port number ("80")
* @param QueryFlags What flags are used in making the getaddrinfo call. Several flags can be used at once by
* bitwise OR-ing the flags together.
* Platforms are required to translate this value into a the correct flag representation.
* @param ProtocolType Used to limit results from the call. Specifying None will search all valid protocols.
* Callers will find they rarely have to specify this flag.
* @param SocketType What socket type should the results be formatted for. This typically does not change any
* formatting results and can be safely left to the default value.
*
* @return the results structure containing the array of address results to this query
*/
UE_DEPRECATED(4.23, "Migrate to GetAddressInfo that takes an FName as the protocol specification.")
virtual FAddressInfoResult GetAddressInfo(const TCHAR* HostName, const TCHAR* ServiceName = nullptr,
EAddressInfoFlags QueryFlags = EAddressInfoFlags::Default,
ESocketProtocolFamily ProtocolType = ESocketProtocolFamily::None,
ESocketType SocketType = ESocketType::SOCKTYPE_Unknown)
{
return GetAddressInfo(HostName, ServiceName, QueryFlags, GetProtocolNameFromFamily(ProtocolType), SocketType);
}
/**
* Gets the address information of the given hostname and outputs it into an array of resolvable addresses.
* It is up to the caller to determine which one is valid for their environment.
*
* This function allows for specifying FNames for the protocol type, allowing for support of other
* platform protocols
*
* @param HostName string version of the queryable hostname or ip address
* @param ServiceName string version of a service name ("http") or a port number ("80")
* @param QueryFlags What flags are used in making the getaddrinfo call. Several flags can be used at once by
* bitwise OR-ing the flags together.
* Platforms are required to translate this value into a the correct flag representation.
* @param ProtocolTypeName Used to limit results from the call. Specifying None will search all valid protocols.
* Callers will find they rarely have to specify this flag.
* @param SocketType What socket type should the results be formatted for. This typically does not change any
* formatting results and can be safely left to the default value.
*
* @return the results structure containing the array of address results to this query
*/
virtual FAddressInfoResult GetAddressInfo(const TCHAR* HostName, const TCHAR* ServiceName = nullptr,
EAddressInfoFlags QueryFlags = EAddressInfoFlags::Default,
const FName ProtocolTypeName = NAME_None,
ESocketType SocketType = ESocketType::SOCKTYPE_Unknown) = 0;
/**
* Async variant of GetAddressInfo that fetches the data from the above function in an
* asynchronous task executed on an available background thread.
*
* On Completion, this fires a callback function that will be executed on the same thread as
* the task's execution. The caller is responsible for either dispatching the result to the thread of their choosing
* or to allow the result callback execute on the task's thread.
*
* This function allows for specifying FNames for the protocol type, allowing for support of other
* platform protocols
*
* @param Callback the callback function to fire when this query completes. Contains the FAddressInfoResult structure.
* @param HostName string version of the queryable hostname or ip address
* @param ServiceName string version of a service name ("http") or a port number ("80")
* @param QueryFlags What flags are used in making the getaddrinfo call. Several flags can be used at once by
* bitwise OR-ing the flags together.
* Platforms are required to translate this value into a the correct flag representation.
* @param ProtocolTypeName Used to limit results from the call. Specifying None will search all valid protocols.
* Callers will find they rarely have to specify this flag.
* @param SocketType What socket type should the results be formatted for. This typically does not change any
* formatting results and can be safely left to the default value.
*
*/
SOCKETS_API virtual void GetAddressInfoAsync(FAsyncGetAddressInfoCallback Callback, const TCHAR* HostName,
const TCHAR* ServiceName = nullptr, EAddressInfoFlags QueryFlags = EAddressInfoFlags::Default,
const FName ProtocolTypeName = NAME_None,
ESocketType SocketType = ESocketType::SOCKTYPE_Unknown);
/**
* Serializes a string that only contains an address.
*
* This is a what you see is what you get, there is no DNS resolution of the input string,
* so only use this if you know you already have a valid address and will not need to convert.
* Otherwise, feed the address to GetAddressInfo for guaranteed results.
*
* @param InAddress the address to serialize
*
* @return The FInternetAddr of the given string address. This will point to nullptr on failure.
*/
virtual TSharedPtr<FInternetAddr> GetAddressFromString(const FString& InAddress) = 0;
/**
* Does a DNS look up of a host name
* This code assumes a lot, and as such, it's not guaranteed that the results provided by it are correct.
*
* @param HostName the name of the host to look up
* @param Addr the address to copy the IP address to
*/
UE_DEPRECATED(4.23, "Please use GetAddressInfo to query hostnames")
virtual ESocketErrors GetHostByName(const ANSICHAR* HostName, FInternetAddr& OutAddr)
{
FAddressInfoResult GAIResult = GetAddressInfo(ANSI_TO_TCHAR(HostName), nullptr, EAddressInfoFlags::Default, NAME_None);
if (GAIResult.Results.Num() > 0)
{
OutAddr.SetRawIp(GAIResult.Results[0].Address->GetRawIp());
return SE_NO_ERROR;
}
return SE_HOST_NOT_FOUND;
}
/**
* Creates a platform specific async hostname resolution object
*
* @param HostName the name of the host to look up
*
* @return the resolve info to query for the address
*/
SOCKETS_API virtual class FResolveInfo* GetHostByName(const ANSICHAR* HostName);
/**
* Some platforms require chat data (voice, text, etc.) to be placed into
* packets in a special way. This function tells the net connection
* whether this is required for this platform
*/
virtual bool RequiresChatDataBeSeparate() = 0;
/**
* Some platforms require packets be encrypted. This function tells the
* net connection whether this is required for this platform
*/
virtual bool RequiresEncryptedPackets() = 0;
/**
* Determines the name of the local machine
*
* @param HostName the string that receives the data
*
* @return true if successful, false otherwise
*/
virtual bool GetHostName(FString& HostName) = 0;
/**
* Create a proper FInternetAddr representation
* @param Address host address
* @param Port host port
*/
UE_DEPRECATED(4.23, "To support different address sizes, use CreateInternetAddr with no arguments and call SetIp/SetRawIp and SetPort on the returned object")
virtual TSharedRef<FInternetAddr> CreateInternetAddr(uint32 Address, uint32 Port = 0)
{
TSharedRef<FInternetAddr> ReturnAddr = CreateInternetAddr();
PRAGMA_DISABLE_DEPRECATION_WARNINGS
ReturnAddr->SetIp(Address);
ReturnAddr->SetPort(Port);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
return ReturnAddr;
}
/**
* Create a proper FInternetAddr representation
*/
virtual TSharedRef<FInternetAddr> CreateInternetAddr() = 0;
/**
* Create a FInternetAddr of the desired protocol
*/
virtual TSharedRef<FInternetAddr> CreateInternetAddr(const FName ProtocolType)
{
// If not implemented, returns the base version
return CreateInternetAddr();
}
/**
* Create a platform specific FRecvMulti representation
*
* @param MaxNumPackets The maximum number of packet receives supported
* @param MaxPacketSize The maximum supported packet size
* @param Flags Flags for specifying how FRecvMulti should be initialized (for e.g. retrieving timestamps)
* @return Returns the platform specific FRecvMulti instance
*/
SOCKETS_API virtual TUniquePtr<FRecvMulti> CreateRecvMulti(int32 MaxNumPackets, int32 MaxPacketSize,
ERecvMultiFlags Flags=ERecvMultiFlags::None);
/**
* @return Whether the machine has a properly configured network device or not
*/
virtual bool HasNetworkDevice() = 0;
/**
* Get the name of the socket subsystem
* @return a string naming this subsystem
*/
virtual const TCHAR* GetSocketAPIName() const = 0;
/**
* Returns the last error that has happened
*/
virtual ESocketErrors GetLastErrorCode() = 0;
/**
* Translates the platform error code to a ESocketErrors enum
*/
virtual ESocketErrors TranslateErrorCode(int32 Code) = 0;
// The following functions are not expected to be overridden
/**
* Returns a human readable string from an error code
*
* @param Code the error code to check
*/
SOCKETS_API const TCHAR* GetSocketError(ESocketErrors Code = SE_GET_LAST_ERROR_CODE);
/**
* Gets the list of addresses associated with the adapters on the local computer.
* Unlike GetLocalHostAddr, this function does not give preferential treatment to multihome options in results.
* It's encouraged that users check for multihome before using the results of this function.
*
* @param OutAddresses - Will hold the address list.
*
* @return true on success, false otherwise.
*/
SOCKETS_API virtual bool GetLocalAdapterAddresses(TArray<TSharedPtr<FInternetAddr>>& OutAddresses);
/**
* Get a local IP to bind to.
*
* Typically, it is better to use GetLocalBindAddresses as it better supports hybrid network functionality
* and less chances for connections to fail due to mismatched protocols.
*/
SOCKETS_API virtual TSharedRef<FInternetAddr> GetLocalBindAddr(FOutputDevice& Out);
/**
* Get bindable addresses that this machine can use as reported by GetAddressInfo with the BindableAddress flag.
* This will return the various any address for usage. If multihome has been specified, only the multihome address
* will be returned in the array.
*
* @return If GetAddressInfo succeeded or multihome is specified, an array of addresses that can be binded on. Failure returns an empty array.
*/
SOCKETS_API virtual TArray<TSharedRef<FInternetAddr>> GetLocalBindAddresses();
/**
* Bind to next available port.
*
* @param Socket The socket that that will bind to the port
* @param Addr The local address and port that is being bound to (usually the result of GetLocalBindAddr()). This addresses port number will be modified in place
* @param PortCount How many ports to try
* @param PortIncrement The amount to increase the port number by on each attempt
*
* @return The bound port number, or 0 on failure
*/
SOCKETS_API int32 BindNextPort(FSocket* Socket, FInternetAddr& Addr, int32 PortCount, int32 PortIncrement);
/**
* Uses the platform specific look up to determine the host address
*
* To better support multiple network interfaces and remove ambiguity between address protocols,
* it is encouraged to use GetLocalAdapterAddresses to determine machine addresses.
* Be sure to check GetMultihomeAddress ahead of time.
*
* @param Out the output device to log messages to
* @param bCanBindAll true if all can be bound (no primarynet), false otherwise
*
* @return The local host address
*/
SOCKETS_API virtual TSharedRef<FInternetAddr> GetLocalHostAddr(FOutputDevice& Out, bool& bCanBindAll);
/**
* Returns the multihome address if the flag is present and valid. For ease of use,
* this function will check validity of the address for the caller.
*
* @param Addr the address structure which will have the Multihome address in it if set.
*
* @return If the multihome address was set and valid
*/
SOCKETS_API virtual bool GetMultihomeAddress(TSharedRef<FInternetAddr>& Addr);
/**
* Checks the host name cache for an existing entry (faster than resolving again)
*
* @param HostName the host name to search for
* @param Addr the out param that the IP will be copied to
*
* @return true if the host was found, false otherwise
*/
SOCKETS_API bool GetHostByNameFromCache(const ANSICHAR* HostName, TSharedPtr<class FInternetAddr>& Addr);
/**
* Stores the ip address with the matching host name
*
* @param HostName the host name to search for
* @param Addr the IP that will be copied from
*/
SOCKETS_API void AddHostNameToCache(const ANSICHAR* HostName, TSharedPtr<class FInternetAddr> Addr);
/**
* Removes the host name to ip mapping from the cache
*
* @param HostName the host name to search for
*/
SOCKETS_API void RemoveHostNameFromCache(const ANSICHAR* HostName);
/**
* Returns true if FSocket::RecvMulti is supported by this socket subsystem
*/
SOCKETS_API virtual bool IsSocketRecvMultiSupported() const;
/**
* Returns true if FSocket::Wait is supported by this socket subsystem.
*/
virtual bool IsSocketWaitSupported() const = 0;
/**
* Converts a platform packet timestamp, into a local timestamp, or into a time delta etc.
*
* @param Timestamp The timestamp to translate
* @param Translation The type of translation to perform on the timestamp (time delta is usually faster than local timestamp)
* @return Returns the translated timestamp or delta
*/
SOCKETS_API virtual double TranslatePacketTimestamp(const FPacketTimestamp& Timestamp,
ETimestampTranslation Translation=ETimestampTranslation::LocalTimestamp);
/**
* Returns true if FSocket::RecvFromWithPktInfo is supported by this socket subsystem.
*/
SOCKETS_API virtual bool IsRecvFromWithPktInfoSupported() const;
protected:
/**
* Conversion functions from the SocketProtocolFamily enum to the new FName system.
* For now, both are supported, but it's better to use the FName when possible.
*/
SOCKETS_API virtual ESocketProtocolFamily GetProtocolFamilyFromName(const FName& InProtocolName) const;
SOCKETS_API virtual FName GetProtocolNameFromFamily(ESocketProtocolFamily InProtocolFamily) const;
private:
/** Used to provide threadsafe access to the host name cache */
FCriticalSection HostNameCacheSync;
/** Stores a resolved IP address for a given host name */
TMap<FString, TSharedPtr<FInternetAddr> > HostNameCache;
};
/** Deleter object that can be used with unique & shared pointers that store FSockets. The SocketSubsystem must be valid when the call operator is invoked! */
class FSocketDeleter
{
public:
FSocketDeleter()
: Subsystem(nullptr)
{
}
FSocketDeleter(ISocketSubsystem* InSubsystem)
: Subsystem(InSubsystem)
{
}
void operator()(FSocket* Socket) const
{
if (Subsystem && Socket)
{
Subsystem->DestroySocket(Socket);
}
}
private:
ISocketSubsystem* Subsystem;
};
typedef TSharedPtr<ISocketSubsystem> IOnlineSocketPtr;