# 网络连接

# UE4中TCP

下面是UE4中C++实现的TCP连接:

# TCP客户端

ATCPClient.h文件:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SocketRSThread.h"
#include "TCPClient.generated.h"

UCLASS(BlueprintType, Blueprintable)
class TESTNETWORK_API ATCPClient : public AActor
{
    GENERATED_BODY()

public:
    // Sets default values for this actor's properties
    ATCPClient();

    UFUNCTION(BlueprintCallable, Category = Network)
        void CreateClientAndConnect(FString ServerIP, int32 Port, int32 ReceiveSize = 1024, int32 SendSize = 1024);

    UFUNCTION(Category = Network)
        bool ConnectServer(FString ServerIP, int32 Port);

    UFUNCTION(BlueprintCallable, Category = Network)
        void SendToServer(FString Message);

    UPROPERTY(BlueprintAssignable, VisibleAnywhere, Category = Network)
        FReceiveSocketDataDelegate ReceiveSocketDataDelegate;

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;


    UFUNCTION()
        void OnServerDisconnect(class USocketRSThread* pThread);

    class FSocket* ClientSocket;
    int32 SendDataSize;
    int32 RecDataDize;
    TArray<class USocketRSThread*> RecThreads;
public:
    // Called every frame
    virtual void Tick(float DeltaTime) override;

};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

ATCPClient.cpp:

#include "TCPClient.h"

// Sets default values
ATCPClient::ATCPClient()
{
    // 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;

}

// Called when the game starts or when spawned
void ATCPClient::BeginPlay()
{
    Super::BeginPlay();

}

// Called every frame
void ATCPClient::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

void ATCPClient::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    if (ClientSocket)
    {
        for (auto RecThreald : RecThreads)
        {
            RecThreald->Stop();
        }


    }
}


void ATCPClient::OnServerDisconnect(USocketRSThread* pThread)
{
    UE_LOG(LogTemp, Warning, TEXT("Server lost"));
    RecThreads.Remove(pThread);
}

void ATCPClient::CreateClientAndConnect(FString ServerIP, int32 Port, int32 ReceiveSize, int32 SendSize)
{
    this->SendDataSize = SendSize;
    this->RecDataDize = ReceiveSize;

    ClientSocket = FTcpSocketBuilder(TEXT("Client Socket"))
        .AsReusable()
        .AsBlocking()
        .WithReceiveBufferSize(ReceiveSize)
        .WithSendBufferSize(SendSize);

    if (!ClientSocket)
    {
        UE_LOG(LogTemp, Error, TEXT("Create Client Socket Error!"));
    }
    else
    {
        ConnectServer(ServerIP, Port);
    }
}

bool ATCPClient::ConnectServer(FString ServerIP, int32 Port)
{
    FIPv4Endpoint ServerEndpoint;
    FIPv4Endpoint::Parse(ServerIP, ServerEndpoint);
    TSharedPtr<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
    bool Success = true;
    addr->SetIp(*ServerIP, Success);
    if (!Success)
    {
        return false;
    }
    addr->SetPort(Port);

    if (ClientSocket->Connect(*addr))
    {
        USocketRSThread* RSThread = NewObject<USocketRSThread>();
        RecThreads.Add(RSThread);
        RSThread->ReceiveSocketDataDelegate = ReceiveSocketDataDelegate;
        RSThread->LostConnectionDelegate.AddDynamic(this, &ATCPClient::OnServerDisconnect);
        RSThread->Start(ClientSocket, SendDataSize, RecDataDize);
        UE_LOG(LogTemp, Warning, TEXT("Client Connect Success"));
        return true;
    }
    else
    {
        ESocketErrors LastErr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode();

        UE_LOG(LogTemp, Warning, TEXT("Connect failed with error code (%d) error (%s)"), LastErr, ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetSocketError(LastErr));
        ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ClientSocket);
    }
    return false;
}

void ATCPClient::SendToServer(FString Message)
{

    for (auto SocketThread : RecThreads)
    {
        SocketThread->Send(Message);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

# TCP服务端

ATCPServer.h文件:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SocketRSThread.h"
#include "TCPServer.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FServerSocketCreateDelegate, bool, bSuccess);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FConnectReceiveDelegate, FString, RemoteIP, int32, RemotePort);


UCLASS(BlueprintType, Blueprintable)
class TESTNETWORK_API ATCPServer : public AActor
{
    GENERATED_BODY()

public:
    // Sets default values for this actor's properties
    ATCPServer();




protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

public:
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    /**
     * 创建服务器Socket
     * @ServerIP-服务器的IP地址
     * @ServerPort-服务器的端口号
     * @ReceiveBufferSize-接受数据大小
     * @SendBufferSize-发送数据大小
     */
    UFUNCTION(BlueprintCallable, Category = Network)
        void CreateServer(const FString& ServerIP, int32 ServerPort, int32 ReceiveBufferSize = 1024, int32 SendBufferSize = 1024);

    /** 关闭Server */
    UFUNCTION(BlueprintCallable, Category = Network)
        void CloseServer();
    /** 检测是否有客户端连入 */
    void ConnectCheck();

    UFUNCTION(BlueprintCallable, Category = Network)
        void SendToClient(FString Message);

    UFUNCTION(Category = Network)
        void OnClientDisconnect(class USocketRSThread* pThread);
protected:

    int32 SendDataSize;
    int32 RecDataDize;

    class FSocket* serverSocket;
    UPROPERTY(BlueprintAssignable, VisibleAnywhere, Category = Network)
        FServerSocketCreateDelegate SocketCreateDelegate;
    UPROPERTY(BlueprintAssignable, VisibleAnywhere, Category = Network)
        FConnectReceiveDelegate ConnectReceiveDelegate;
    UPROPERTY(BlueprintAssignable, VisibleAnywhere, Category = Network)
        FReceiveSocketDataDelegate ReceiveSocketDataDelegate;

    FTimerHandle ConnectCheckHandler;

    TArray<USocketRSThread*> RecThreads;

};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

ATCPServer.cpp文件:

#include "TCPServer.h"


// Sets default values
ATCPServer::ATCPServer()
{
    // 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;

}


// Called when the game starts or when spawned
void ATCPServer::BeginPlay()
{
    Super::BeginPlay();

}


void ATCPServer::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    CloseServer();
    Super::EndPlay(EndPlayReason);
}

// Called every frame
void ATCPServer::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

void ATCPServer::CreateServer(const FString& ServerIP, int32 ServerPort, int32 ReceiveBufferSize, int32 SendBufferSize)
{
    this->RecDataDize = ReceiveBufferSize;
    this->SendDataSize = SendBufferSize;
    FIPv4Address ServerAddr;
    if (!FIPv4Address::Parse(ServerIP, ServerAddr))
    {
        UE_LOG(LogTemp, Error, TEXT("Server Ip %s is illegal"), *ServerIP);
    }
    serverSocket = FTcpSocketBuilder(TEXT("Socket Listener"))
        .AsReusable()
        .AsBlocking()
        .BoundToAddress(ServerAddr)
        .BoundToPort(ServerPort)
        .Listening(8)
        .WithReceiveBufferSize(ReceiveBufferSize)
        .WithSendBufferSize(SendBufferSize);
    if (serverSocket)
    {
        UE_LOG(LogTemp, Warning, TEXT("Server Create Success!"), *ServerIP);
        SocketCreateDelegate.Broadcast(true);
        GetWorld()->GetTimerManager().SetTimer(ConnectCheckHandler, this, &ATCPServer::ConnectCheck, 1, true);
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Server Create Failed!"));
        SocketCreateDelegate.Broadcast(false);
    }
}

void ATCPServer::CloseServer()
{
    if (serverSocket)
    {
        serverSocket->Close();
        for (auto RecThreald : RecThreads)
        {
            RecThreald->Stop();
        }
        ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(serverSocket);

    }
}

void ATCPServer::ConnectCheck()
{
    bool bPending = false;
    if (serverSocket->HasPendingConnection(bPending) && bPending)
    {
        //有新的socket连接进来
        TSharedRef<FInternetAddr> RemoteAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
        FSocket* RecSocket = serverSocket->Accept(*RemoteAddress, TEXT("Receive Socket"));
        USocketRSThread* RSThread = NewObject<USocketRSThread>();
        RecThreads.Add(RSThread);
        RSThread->ReceiveSocketDataDelegate = ReceiveSocketDataDelegate;
        RSThread->LostConnectionDelegate.AddDynamic(this, &ATCPServer::OnClientDisconnect);
        RSThread->Start(RecSocket, SendDataSize, RecDataDize);
        ConnectReceiveDelegate.Broadcast(RemoteAddress->ToString(false), RemoteAddress->GetPort());
    }
}

void ATCPServer::SendToClient(FString Message)
{



    for (auto SocketThread : RecThreads)
    {
        SocketThread->Send(Message);
    }

}

void ATCPServer::OnClientDisconnect(USocketRSThread* pThread)
{
    UE_LOG(LogTemp, Warning, TEXT("Client lost"));
    RecThreads.Remove(pThread);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

# 收发线程

下面是收发线程类实现。

USocketRSThread.h:

#pragma once

#include "CoreMinimal.h"
#include "Sockets.h"
#include "HAL/Runnable.h"
#include "Networking/Public/Common/TcpSocketBuilder.h"
#include "SocketRSThread.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FReceiveSocketDataDelegate, FString, Data);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLostConnectionDelegate, USocketRSThread*, Thread);
/**
 * Socket数据收 发线程
 */
UCLASS()
class TESTNETWORK_API USocketRSThread : public UObject, public FRunnable
{
    GENERATED_BODY()
public:

    // 线程接口
    virtual bool Init() override { return true; }
    virtual uint32 Run() override;
    virtual void Stop() override;
    virtual void Exit() override {}
    // End FRunnable Interface

    void Start(FSocket* Socket, uint32 SendDataSize, uint32 RecDataSize);
    void Send(FString Message);
    FReceiveSocketDataDelegate ReceiveSocketDataDelegate;
    FLostConnectionDelegate	LostConnectionDelegate;
protected:
    FSocket* ConnectSocket;
    uint32 SendDataSize;
    uint32 RecDataSize;
    TArray<uint8> ReceiveData;
    /** 线程相关 */
    FRunnableThread* pThread;
    bool bThreadStop;

    //socket默认使用utf8格式传输数据,如果传输的是中文的话,那么1个中文占用3个字节,发送的个数就需要针对性就行修改,不能简单的使用FString::Len()进行个数计算,这里给出一个计算个数的函数
    int32 CalcUtf8NumFromString(const FString& Str);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

USocketRSThread.cpp:

#include "SocketRSThread.h"
#include "SocketSubsystem.h"


uint32 USocketRSThread::Run()
{
    while (!bThreadStop)
    {
        //这个地方是之前将socket设置为阻塞模式 在这里5s内判断是否断开连接
        uint32 Size;
        bool LostConnect = false;
        ConnectSocket->HasPendingConnection(LostConnect);
        ConnectSocket->Wait(ESocketWaitConditions::WaitForReadOrWrite, FTimespan(0, 0, 5));
        if (LostConnect)
        {
            //UE_LOG(LogTemp, Warning, TEXT(" doesn't Connect "));
            Stop();
            LostConnectionDelegate.Broadcast(this);
            continue;
        }

        /** 处理接收数据 */
        if (ConnectSocket && ConnectSocket->HasPendingData(Size))
        {
            ReceiveData.Init(0, FMath::Min(Size, RecDataSize));
            int32 Readed;
            ConnectSocket->Recv(ReceiveData.GetData(), RecDataSize, Readed);
            FString ReceivedString = FString((UTF8_TO_TCHAR(ReceiveData.GetData())));

            //FString ReceivedString = FString(ANSI_TO_TCHAR(reinterpret_cast<const char*>(ReceiveData.GetData())));
            ReceiveSocketDataDelegate.Broadcast(ReceivedString);
        }
    }
    return 0;
}

void USocketRSThread::Start(FSocket* Socket, uint32 SendDataSizeP, uint32 RecDataSizeP)
{
    this->ConnectSocket = Socket;
    this->SendDataSize = SendDataSizeP;
    this->RecDataSize = RecDataSizeP;
    FRunnableThread::Create(this, TEXT("Receive Threald"));
}

void USocketRSThread::Stop()
{
    bThreadStop = true;
    ConnectSocket->Close();
    ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ConnectSocket);
    ConnectSocket = nullptr;
}



void USocketRSThread::Send(FString Message)
{
    ///** 处理发送数据 */
    TCHAR* SendMessage = Message.GetCharArray().GetData();
    //int32 size = FCString::Strlen(SendMessage) + 1;
    int32 size = CalcUtf8NumFromString(Message);
    //int32 size = sizeof(SendMessage);
    int32 sent = 0;
    if (size >= (int32)SendDataSize)
    {
        UE_LOG(LogTemp, Error, TEXT("Send Data Size is Larger than Max Size for set"));
    }
    else
    {
        if (ConnectSocket && ConnectSocket->Send((uint8*)TCHAR_TO_UTF8(SendMessage), size, sent))
        {
            UE_LOG(LogTemp, Warning, TEXT("___Send Succeed!"));

        }
        else
        {
            UE_LOG(LogTemp, Error, TEXT("___Send Failed!"));
        }
    }


}

int32 USocketRSThread::CalcUtf8NumFromString(const FString& Str)
{
    int32 result = 0;


    for (int i = 0; i < Str.Len(); i++)
    {

        if (Str[i] <= 0x7f)
            result = result + 1;
        else if (Str[i] > 0x7f && Str[i] <= 0x07ff)
            result = result + 2;
        else if (Str[i] > 0x07ff && Str[i] <= 0xffff)
            result = result + 3;
        else
            result = result + 4;
    }

    return result + 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102