UE4을 사용 중 간단한 소켓 서버가 필요해서 아래의 소스를 찾아서 구현해 보았다.
테스트 : 처음 실행 이후 서버를 재실행하니 데이터 수신이 안된다.
ListenerSocket->HasPendingConnection(Pending)
클라이언트에서 데이터를 보내도 Pending 값이 들어오지 않아서 살펴보니 종료 시 소켓 닫는 부분이 없어서 추가해서 돌려보니 잘 된다.
(빨간 소스 : 추가 부분)
SocketSeverActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include <string>
#include "Networking.h"
#include "GameFramework/Actor.h"
#include "SocketServerActor.generated.h"
UCLASS()
class SOCKETTEST_API ASocketServerActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASocketServerActor();
~ASocketServerActor();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick(float DeltaSeconds) override;
FSocket* ListenerSocket;
FSocket* ConnectionSocket;
FIPv4Endpoint RemoteAddressForConnection;
bool StartTCPReceiver(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort);
FSocket* CreateTCPConnectionListener(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort, const int32 ReceiveBufferSize = 2 * 1024 * 1024);
//Timer functions, could be threads
void TCPConnectionListener(); //can thread this eventually
void TCPSocketListener(); //can thread this eventually
//Format String IP4 to number array
bool FormatIP4ToNumber(const FString& TheIP, uint8(&Out)[4]);
//Rama's StringFromBinaryArray
FString StringFromBinaryArray(const TArray<uint8>& BinaryArray);
void Start();
};
SocketServerActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SocketTest.h"
#include "SocketServerActor.h"
#include <string>
#include "Networking.h"
// Sets default values
ASocketServerActor::ASocketServerActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
ASocketServerActor::~ASocketServerActor()
{
if (ListenerSocket)
{
ListenerSocket->Close();
delete ListenerSocket;
}
if (ConnectionSocket)
delete ConnectionSocket;
}
// Called when the game starts or when spawned
void ASocketServerActor::BeginPlay()
{
Super::BeginPlay();
Start();
}
// Called every frame
void ASocketServerActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ASocketServerActor::Start()
{
if (!StartTCPReceiver("RamaSocketListener", "61.35.84.242", 1111))
{
UE_LOG(LogTemp, Log, TEXT("TCP Socket Listener Created!"));
return;
}
UE_LOG(LogTemp, Log, TEXT("StartTCPReceiver Success!!"));
}
//Rama's Start TCP Receiver
bool ASocketServerActor::StartTCPReceiver(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort)
{
//Rama's CreateTCPConnectionListener
ListenerSocket = CreateTCPConnectionListener(YourChosenSocketName, TheIP, ThePort);
//Not created?
if (!ListenerSocket)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("StartTCPReceiver>> Listen socket could not be created! ~> %s %d"), *TheIP, ThePort));
UE_LOG(LogTemp, Log, TEXT("StartTCPReceiver>> Listen socket could not be created! ~> %s %d"), *TheIP, ThePort);
return false;
}
//Start the Listener! //thread this eventually
//GetWorldTimerManager().SetTimer(this, &AMyActorTest::TCPConnectionListener, 0.01, true);
FTimerHandle time_handler;
GetWorldTimerManager().SetTimer(time_handler, this, &ASocketServerActor::TCPConnectionListener, 0.01, true);
return true;
}
//Format IP String as Number Parts
bool ASocketServerActor::FormatIP4ToNumber(const FString& TheIP, uint8(&Out)[4])
{
//IP Formatting
TheIP.Replace(TEXT(" "), TEXT(""));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// IP 4 Parts
//String Parts
TArray<FString> Parts;
TheIP.ParseIntoArray(Parts, TEXT("."), true);
if (Parts.Num() != 4)
return false;
//String to Number Parts
for (int32 i = 0; i < 4; ++i)
{
Out[i] = FCString::Atoi(*Parts[i]);
}
return true;
}
//Rama's Create TCP Connection Listener
FSocket* ASocketServerActor::CreateTCPConnectionListener(const FString& YourChosenSocketName, const FString& TheIP, const int32 ThePort, const int32 ReceiveBufferSize)
{
uint8 IP4Nums[4];
if (!FormatIP4ToNumber(TheIP, IP4Nums))
{
//VShow("Invalid IP! Expecting 4 parts separated by .");
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Create Socket
FIPv4Endpoint Endpoint(FIPv4Address(IP4Nums[0], IP4Nums[1], IP4Nums[2], IP4Nums[3]), ThePort);
FSocket* ListenSocket = FTcpSocketBuilder(*YourChosenSocketName).AsReusable().BoundToEndpoint(Endpoint).Listening(8);
//Set Buffer Size
int32 NewSize = 0;
ListenSocket->SetReceiveBufferSize(ReceiveBufferSize, NewSize);
//Done!
return ListenSocket;
}
//Rama's TCP Connection Listener
void ASocketServerActor::TCPConnectionListener()
{
//~~~~~~~~~~~~~
if (!ListenerSocket)
{
return;
}
//~~~~~~~~~~~~~
//Remote address
TSharedRef<FInternetAddr> RemoteAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
bool Pending;
// handle incoming connections
if (ListenerSocket->HasPendingConnection(Pending))
{
// UE_LOG(LogTemp, Log, TEXT("TCPConnectionListener Pending = %d"), Pending);
if (Pending)
{
UE_LOG(LogTemp, Log, TEXT("TCPConnectionListener 1"));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Already have a Connection? destroy previous
if (ConnectionSocket)
{
ConnectionSocket->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ConnectionSocket);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//New Connection receive!
ConnectionSocket = ListenerSocket->Accept(*RemoteAddress, TEXT("RamaTCP Received Socket Connection"));
if (ConnectionSocket != NULL)
{
//Global cache of current Remote Address
RemoteAddressForConnection = FIPv4Endpoint(RemoteAddress);
//can thread this too
FTimerHandle time_handler;
GetWorldTimerManager().SetTimer(time_handler, this, &ASocketServerActor::TCPSocketListener, 0.01, true);
}
}
}
}
//Rama's String From Binary Array
//This function requires
// #include <string>
FString ASocketServerActor::StringFromBinaryArray(const TArray<uint8>& BinaryArray)
{
//Create a string from a byte array!
UE_LOG(LogTemp, Log, TEXT("StringFromBinaryArray %d"), BinaryArray.Num());
std::string cstr(reinterpret_cast<const char*>(BinaryArray.GetData()), BinaryArray.Num());
UE_LOG(LogTemp, Log, TEXT("cstr.length %d"), cstr.length());
return FString(cstr.c_str());
}
//Rama's TCP Socket Listener
void ASocketServerActor::TCPSocketListener()
{
//~~~~~~~~~~~~~
if (!ConnectionSocket) return;
//~~~~~~~~~~~~~
//Binary Array!
TArray<uint8> ReceivedData;
uint32 Size;
while (ConnectionSocket->HasPendingData(Size))
{
UE_LOG(LogTemp, Log, TEXT("Size %d"), Size);
ReceivedData.Init(FMath::Min(Size, 4096u));
int32 Read = 0;
ConnectionSocket->Recv(ReceivedData.GetData(), ReceivedData.Num(), Read);
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Data Read! %d"), ReceivedData.Num()));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if (ReceivedData.Num() <= 0)
{
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Rama's String From Binary Array
const FString ReceivedUE4String = StringFromBinaryArray(ReceivedData);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UE_LOG(LogTemp, Log, TEXT("Receive : %s"), *ReceivedUE4String);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("As String Data ~> %s"), *ReceivedUE4String));
}
소스 참조 : https://wiki.unrealengine.com/TCP_Socket_Listener,_Receive_Binary_Data_From_an_IP/Port_Into_UE4,_(Full_Code_Sample)