UE4 Sockets多线程TCP通信

来源:互联网 发布:如何自已推广淘宝店铺 编辑:程序博客网 时间:2024/04/27 23:01

本文转自:http://blog.csdn.net/zilisen/article/details/75007447,感谢博主。

一、简介

UE4引擎是提供了Sockets模块和Networking模块的,博主在研究此功能时也是参考的Sockets模块和Networking模块的源码,其中引擎为我们提供了一些实例类很有参考价值,比如Sockets模块中的MultichannelTcpReceiver.h和MultichannelTcpSender.h,Networking模块中的UdpSocketReceiver.h和UdpSocketSender.h。博主的例子就是研究参考了以上代码写出来的。

编译与运行环境

UnrealEngine 4.15 , Visual Studio 2015

实现功能介绍

博主的例子实现的是一个使用Socket多线程TCP通信的客户端。在主线程中发消息,子线程中收消息。当然也能类似的实现两个子线程分别收发消息。Socket相关函数都定义在了GameInstance中,以便我们能在不同场景都能调用。

二、代码实现

服务器代码

此处使用了一个有简单收发功能的服务器,用VS2015新建空项目即可:

//SocketTest.cpp: Defines the entry point for the console application#include <stdio.h>#include <string>#include <iostream>#include <WinSock2.h>#include <WS2tcpip.h>#pragma comment(lib, "WS2_32.lib")using namespace std;int main(){    WSADATA wsaData;    SOCKET serverSock;    SOCKADDR_IN serverAddr;    SOCKET clientSock;    SOCKADDR_IN clientAddr;    cout << "Server Start!" << endl;    //加载套接字库    int err = WSAStartup(MAKEWORD(2, 2), &wsaData);    if (0 != err)    {        cout << "WSAStartup failed!" << endl;        return 1;    }    //构造监听SOCKET,流式SOCKET    serverSock = socket(AF_INET, SOCK_STREAM, 0);    //配置监听地址和端口    serverAddr.sin_family = AF_INET;    serverAddr.sin_port = htons(4399);  //本地监听端口:4399    serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;    //绑定SOCKET    int retVal = bind(serverSock, (SOCKADDR*)&serverAddr, sizeof(serverAddr));    if (SOCKET_ERROR == retVal)    {        cout << "bind failed!" << endl;        closesocket(serverSock);        WSACleanup();        return -1;    }    retVal = listen(serverSock, 5);    if (SOCKET_ERROR == retVal)    {        cout << "listen failed!" << endl;        closesocket(serverSock);        WSACleanup();        return -1;    }    char IPdot[20] = { '\0' };    inet_ntop(AF_INET, (void*)&serverAddr.sin_addr, IPdot, 16);    printf("Welcome,the Host %s is running!Now Wating for someone comes in!\n", IPdot);    int addrClientlen = sizeof(clientAddr);    //等待客户端连接    clientSock = accept(serverSock, (SOCKADDR*)&clientAddr, &addrClientlen);    while (1)    {        //发送数据        char sendBuff[50];        //sprintf_s(sendBuff, "welcome %s to here", inet_ntoa(clientAddr.sin_addr));        char IPdotdec[20] = { '\0' };        inet_ntop(AF_INET, (void*)&clientAddr.sin_addr, IPdotdec, 16);        sprintf_s(sendBuff, "From Server: welcome %s to here", IPdotdec);        send(clientSock, sendBuff, strlen(sendBuff) + 1, 0);        //接收数据        char recvBuff[50];        recv(clientSock, recvBuff, 50, 0);        printf(" %s \n\n", recvBuff);        Sleep(1000);    }    //关闭套接字,关闭加载的套接字库    closesocket(serverSock);    WSACleanup();    return 0;}
  • 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
  • 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

模块添加

新建项目,在.Build.cs文件中添加Sockets模块和Networking模块:

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.using UnrealBuildTool;public class SocketThread : ModuleRules{    public SocketThread(TargetInfo Target)    {        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay","Sockets", "Networking" });    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Socket相关函数定义

STGameInstance.h

//this is the STGameInstance.h#pragma once#include "Engine/GameInstance.h"#include "Networking.h"#include "ReceiveThread.h"#include "STGameInstance.generated.h"UCLASS()class SOCKETTHREAD_API USTGameInstance : public UGameInstance{    GENERATED_BODY()public:    //创建Socket并连接到服务器(主线程)    UFUNCTION(BlueprintCallable, Category = "MySocket")    bool SocketCreate(FString IPStr, int32 port);    //发消息(主线程)    UFUNCTION(BlueprintCallable, Category = "MySocket")    bool SocketSend(FString message);    //收消息(子线程)    UFUNCTION(BlueprintCallable, Category = "MySocket")    bool SocketReceive();    UFUNCTION(BlueprintCallable, Category = "MySocket")    bool ThreadEnd();    FString StringFromBinaryArray(TArray<uint8> BinaryArray);public:    FSocket *Host;    FIPv4Address ip;        FRunnableThread* m_RecvThread;};
  • 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
  • 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

STGameInstance.cpp

//this is the STGameInstance.cpp#include "SocketThread.h"#include "STGameInstance.h"bool USTGameInstance::SocketCreate(FString IPStr, int32 port){    FIPv4Address::Parse(IPStr, ip);     //将传入的IPStr转为IPv4地址    //创建一个addr存放ip地址和端口    TSharedPtr<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();    addr->SetIp(ip.Value);    addr->SetPort(port);    //创建客户端socket    Host = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("default"), false);    //连接成功    if (Host->Connect(*addr))    {        UE_LOG(LogTemp, Warning, TEXT("Connect Succeed!"));        return true;    }    //连接失败    else    {        UE_LOG(LogTemp, Warning, TEXT("Connect Failed!"));        return false;    }}bool USTGameInstance::SocketSend(FString message){    TCHAR *seriallizedChar = message.GetCharArray().GetData();    int32 size = FCString::Strlen(seriallizedChar) + 1;    int32 sent = 0;    if (Host->Send((uint8*)TCHAR_TO_UTF8(seriallizedChar), size, sent))    {        UE_LOG(LogTemp, Warning, TEXT("___Send Succeed!"));        return true;    }    else    {        UE_LOG(LogTemp, Warning, TEXT("___Send Failed!"));        return false;    }}bool USTGameInstance::SocketReceive(){    m_RecvThread = FRunnableThread::Create(new FReceiveThread(Host), TEXT("RecvThread"));    return true;}bool USTGameInstance::ThreadEnd(){    if (m_RecvThread != nullptr)    {        m_RecvThread->Kill(true);        delete m_RecvThread;    }    return true;}FString USTGameInstance::StringFromBinaryArray(TArray<uint8> BinaryArray){    return FString(ANSI_TO_TCHAR(reinterpret_cast<const char*>(BinaryArray.GetData())));}
  • 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
  • 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

收消息线程

ReceiveThread.h

//this is the ReceiveThread.h#pragma once#include "ThreadingBase.h"#include "Networking.h"class SOCKETTHREAD_API FReceiveThread : public FRunnable{public:    FReceiveThread(FSocket* client): m_Client(client)    {}    ~FReceiveThread()    {        stopping = true;    }    virtual bool Init() override    {        stopping = false;        return true;    }    virtual uint32 Run() override    {        if (!m_Client)        {            return 0;        }        TArray<uint8> ReceiveData;        uint8 element = 0;        //接收数据包        while (!stopping)   //线程计数器控制        {            ReceiveData.Init(element, 1024u);            int32 read = 0;            m_Client->Recv(ReceiveData.GetData(), ReceiveData.Num(), read);            const FString ReceivedUE4String = FString(ANSI_TO_TCHAR(reinterpret_cast<const char*>(ReceiveData.GetData())));            FString log = "Server:" + ReceivedUE4String;            UE_LOG(LogTemp, Warning, TEXT("*** %s"), *log);            FPlatformProcess::Sleep(0.01f);        }        return 1;    }    virtual void Stop() override    {        stopping = true;    //计数器-1    }private:    FSocket* m_Client;  //客户端套接字    bool stopping;      //循环控制    FThreadSafeCounter m_StopTaskCounter;   //线程引用计数器};
  • 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
  • 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

三、运行

在编辑器中,创建我们之前定义的GameInstance的蓝图类,并在Project Setting中设其为默认GameInstance。创建一个GameMode,在他的EventGraph中添加如下蓝图逻辑: 
这里写图片描述
其中Succe是一个bool类型变量,Index是一个Int类型变量,不改默认值。 
之后将这个GameMode绑定到当前场景中,编译运行服务器,然后Play,大功告成!博主运行结果如下: 
这里写图片描述
可以看到,收发消息都成功了。



原创粉丝点击