2015년 12월 30일 수요일

Socket Server 만들기


 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)