UE4 Sockets多线程TCP通信
转自:https://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(, ), &wsaData);
if ( != err)
{
cout << "WSAStartup failed!" << endl;
return ;
} //构造监听SOCKET,流式SOCKET
serverSock = socket(AF_INET, SOCK_STREAM, );
//配置监听地址和端口
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(); //本地监听端口: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 -;
} retVal = listen(serverSock, );
if (SOCKET_ERROR == retVal)
{
cout << "listen failed!" << endl;
closesocket(serverSock);
WSACleanup();
return -;
} char IPdot[] = { '\0' };
inet_ntop(AF_INET, (void*)&serverAddr.sin_addr, IPdot, );
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 ()
{
//发送数据
char sendBuff[];
//sprintf_s(sendBuff, "welcome %s to here", inet_ntoa(clientAddr.sin_addr));
char IPdotdec[] = { '\0' };
inet_ntop(AF_INET, (void*)&clientAddr.sin_addr, IPdotdec, );
sprintf_s(sendBuff, "From Server: welcome %s to here", IPdotdec);
send(clientSock, sendBuff, strlen(sendBuff) + , ); //接收数据
char recvBuff[];
recv(clientSock, recvBuff, , );
printf(" %s \n\n", recvBuff); Sleep();
}
//关闭套接字,关闭加载的套接字库
closesocket(serverSock);
WSACleanup();
return ;
}
模块添加
新建项目,在.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" });
}
}
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;
};
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) + ;
int32 sent = ; 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())));
}
收消息线程
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 ;
} TArray<uint8> ReceiveData;
uint8 element = ;
//接收数据包
while (!stopping) //线程计数器控制
{
ReceiveData.Init(element, 1024u);
int32 read = ;
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 ;
} virtual void Stop() override
{
stopping = true; //计数器-1
} private:
FSocket* m_Client; //客户端套接字
bool stopping; //循环控制
FThreadSafeCounter m_StopTaskCounter; //线程引用计数器
};
三、运行
在编辑器中,创建我们之前定义的GameInstance的蓝图类,并在Project Setting中设其为默认GameInstance。创建一个GameMode,在他的EventGraph中添加如下蓝图逻辑:
其中Succe是一个bool类型变量,Index是一个Int类型变量,不改默认值。
之后将这个GameMode绑定到当前场景中,编译运行服务器,然后Play,大功告成!博主运行结果如下:
可以看到,收发消息都成功了。
UE4 Sockets多线程TCP通信的更多相关文章
- JAVASE02-Unit010: 多线程基础 、 TCP通信
多线程基础 . TCP通信 * 当一个方法被synchronized修饰后,那么 * 该方法称为同步方法,即:多个线程不能同时 * 进入到方法内部执行. package day10; /** * 当多 ...
- java基础 UDP通信 user datagram protocol 用户数据豆协议 TCP transmission control protocol 传输控制协议 多线程TCP
无连接通信 UDP 客户端 package com.swift.test; import java.io.IOException; import java.net.DatagramPacket; im ...
- TCP通信 - 服务器开启多线程与read()导致服务器阻塞问题
TCP通信的文件上传案例 本地流:客户端和服务器和本地硬盘进行读写,需要使用自己创建的字节流 网络流:客户端和服务器之间读写,必须使用Socket中提供的字节流对象 客户端工作:读取本地文件,上传到服 ...
- TCP通信---文件上传案例、多线程文件上传
目前大多数服务器都会提供文件上传的功能,由于文件上传需要数据的安全性和完整性,很明显需要使用TCP协议来实现. TCP通信需要创建一个服务器端程序和一个客户端程序,实现客户端向服务器端上传文件 代码实 ...
- 异步tcp通信——APM.ConsoleDemo
APM测试 俗话说麻雀虽小,五脏俱全.apm虽然简单,但是可以实现单机高性能消息推送(可以采用redis.kafka等改造成大型分布式消息推送服务器). 测试demo: using System; u ...
- 异步tcp通信——APM.Core 服务端概述
为什么使用异步 异步线程是由线程池负责管理,而多线程,我们可以自己控制,当然在多线程中我们也可以使用线程池.就拿网络扒虫而言,如果使用异步模式去实现,它使用线程池进行管理.异步操作执行时,会将操作丢给 ...
- 异步tcp通信——APM.Core 解包
TCP通信解包 虽说这是一个老生长谈的问题,不过网上基本很少见完整业务:或多或少都没有写完或者存在bug.接收到的数据包可以简单分成:小包.大包.跨包三种情况,根据这三种情况作相对应的拆包处理,示例如 ...
- java 网络编程之TCP通信和简单的文件上传功能
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- 多线程 TCP 连接
TCP的Java支持 协议相当于相互通信的程序间达成的一种约定,它规定了分组报文的结构.交换方式.包含的意义以及怎样对报文所包含的信息进行解析,TCP/IP协议族有IP协议.TCP协议和UDP协议.现 ...
随机推荐
- linux令普通用户拥有root权限
如题,平时我们在自己电脑上玩linux,基本都是一个root用户就够了(反正我99%时间都是直接用root用户登录系统),可如果在公司里就不行了,有时候需要多个用户对系统具有root权限,类似与一个系 ...
- mysql之主从配置实现
我使用的是两台centos7虚拟机来做实验的,主服务器ip为192.168.2.128,从服务器ip为192.168.2.130 安装mysql就不用说了吧,不对,我们需要安装的是mariadb,命令 ...
- lch 儿童围棋课堂 初级篇1 ( (李昌镐 著))
第1章 常用术语 第2章 吃子 第3章 死活:死活题初步 第4章 劫争 第5章 中盘 第6章 官子 第7章 形势判断 第8章 对杀技巧 第9章 手筋 第1章 常用术语 一 镇 在对方棋子上方隔一路落下 ...
- js 时间戳和日期互转
// 获取当前时间戳(以s为单位) var timestamp = Date.parse(new Date()); timestamp = timestamp / 1000; //当前时间戳为:140 ...
- 谈谈 SOA
为什么要 讨论 SOA 呢 ? 请参考我写的另一篇文章 <论 微服务 和 Entity Framework 对 数据 的 割裂> https://www.cnblogs.com/KS ...
- BTrace学习总结
一.简介: 在生产环境中经常遇到格式各样的问题,如OOM或者莫名其妙的进程死掉.一般情况下是通过修改程序,添加打印日志:然后重新发布程序来完成.然而,这不仅麻烦,而且带来很多不可控的因素.有没有一种方 ...
- navicat外键设置
https://blog.csdn.net/qq_32486599/article/details/73497810
- Python random模块sample、randint、shuffle、choice随机函数
一.random模块简介 Python标准库中的random函数,可以生成随机浮点数.整数.字符串,甚至帮助你随机选择列表序列中的一个元素,打乱一组数据等. 二.random模块重要函数 1 ).ra ...
- hanlp自然语言处理包的基本使用--python
hanlp拥有:中文分词.命名实体识别.摘要关键字.依存句法分析.简繁拼音转换.智能推荐. 这里主要介绍一下hanlp的中文分词.命名实体识别.依存句法分析,这里就不介绍具体的hanlp的安装了,百度 ...
- FFMPEG Qt视频播放器
本文是根据PaintEvent事件处理函数不停在组件中绘制视频帧数据 做过图像界面开发的都知道,任何耗时的操作都不能放在主线程进行,一旦主线程阻塞了,那么体现出来的就是界面卡了. 而我们读取视频和解码 ...