一、简介
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新建空项目即可:
#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; } serverSock = socket(AF_INET, SOCK_STREAM, 0); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(4399); serverAddr.sin_addr.S_un.S_addr = INADDR_ANY; 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]; 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模块:
using UnrealBuildTool;public class SocketThread : ModuleRules{ public SocketThread(TargetInfo Target) { PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay","Sockets", "Networking" }); }}
Socket相关函数定义
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: 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
#include "SocketThread.h"#include "STGameInstance.h"bool USTGameInstance::SocketCreate(FString IPStr, int32 port){ FIPv4Address::Parse(IPStr, ip); TSharedPtr<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr(); addr->SetIp(ip.Value); addr->SetPort(port); 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
#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; }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,大功告成!博主运行结果如下:
可以看到,收发消息都成功了。