前言
在UE项目开发中,最常用的网络通讯协议主要是 UDP、TCP、WebSocket 这三种。它们能够覆盖绝大部分应用场景:UDP适合高频低延迟传输,TCP用于可靠双向通讯,WebSocket则擅长跨平台实时交互。
本文将展示这三种协议在UE中的基础实现方式,帮助你掌握UE网络编程的核心技术。
注: UE原生已提供HTTP的蓝图支持(
Send Http Request
节点),本文不再赘述。其他协议如MQTT、gRPC等可根据实际需求引入第三方库实现。
一、UDP通讯:高频低延迟场景
1.1 适用场景
- 传感器数据采集(LiDAR、Camera、GPS)
- 仿真器对接(CarSim、PreScan)
- 局域网广播/组播
- 实时控制指令
1.2 基础实现
创建UDP Socket并绑定端口:
#include "Sockets.h"
#include "SocketSubsystem.h"// 获取Socket子系统
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);// 创建UDP Socket
FSocket* RecvSocket = SocketSubsystem->CreateSocket(NAME_DGram, TEXT("UDP Receiver"), false);// 创建地址并绑定
TSharedRef<FInternetAddr> Addr = SocketSubsystem->CreateInternetAddr();
Addr->SetPort(8888);
Addr->SetAnyAddress();if (RecvSocket->Bind(*Addr))
{UE_LOG(LogTemp, Log, TEXT("UDP Socket绑定成功,端口: 8888"));
}// 设置非阻塞模式
RecvSocket->SetNonBlocking(true);
接收UDP数据:
void ReceiveUDPData(FSocket* RecvSocket)
{ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);TArray<uint8> RecvData;RecvData.SetNumUninitialized(65507); // UDP最大包大小int32 BytesRead = 0;TSharedRef<FInternetAddr> SenderAddr = SocketSubsystem->CreateInternetAddr();if (RecvSocket->RecvFrom(RecvData.GetData(), RecvData.Num(), BytesRead, *SenderAddr)){RecvData.SetNum(BytesRead);FString SenderIP = SenderAddr->ToString(false);int32 SenderPort = SenderAddr->GetPort();UE_LOG(LogTemp, Log, TEXT("收到来自 %s:%d 的数据,大小: %d"),*SenderIP, SenderPort, BytesRead);// 处理接收到的数据ProcessReceivedData(RecvData);}
}
发送UDP数据:
void SendUDPData(const FString& TargetIP, int32 TargetPort, const TArray<uint8>& Data)
{ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);FSocket* SendSocket = SocketSubsystem->CreateSocket(NAME_DGram, TEXT("UDP Sender"), false);TSharedRef<FInternetAddr> RemoteAddr = SocketSubsystem->CreateInternetAddr();bool bIsValid;RemoteAddr->SetIp(*TargetIP, bIsValid);RemoteAddr->SetPort(TargetPort);if (bIsValid){int32 BytesSent = 0;SendSocket->SendTo(Data.GetData(), Data.Num(), BytesSent, *RemoteAddr);UE_LOG(LogTemp, Log, TEXT("发送 %d 字节到 %s:%d"), BytesSent, *TargetIP, TargetPort);}SendSocket->Close();ISocketSubsystem::Get()->DestroySocket(SendSocket);
}
二、TCP通讯:可靠双向传输
2.1 适用场景
- 连接外部服务器(Python/C#工具链)
- 可靠指令传输(不允许丢包)
- 文件传输、资源下载
- 需要双向通讯的场景
2.2 TCP Client基础实现
连接服务器:
bool ConnectToTCPServer(const FString& IP, int32 Port)
{ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);// 创建TCP SocketFSocket* ClientSocket = SocketSubsystem->CreateSocket(NAME_Stream, TEXT("TCP Client"), false);// 设置目标地址TSharedRef<FInternetAddr> Addr = SocketSubsystem->CreateInternetAddr();bool bIsValid;Addr->SetIp(*IP, bIsValid);Addr->SetPort(Port);if (!bIsValid){UE_LOG(LogTemp, Error, TEXT("无效的IP地址: %s"), *IP);return false;}// 连接if (ClientSocket->Connect(*Addr)){UE_LOG(LogTemp, Log, TEXT("TCP连接成功: %s:%d"), *IP, Port);return true;}else{UE_LOG(LogTemp, Error, TEXT("TCP连接失败"));return false;}
}
发送和接收数据:
// 发送
bool SendTCPData(FSocket* Socket, const TArray<uint8>& Data)
{int32 BytesSent = 0;return Socket->Send(Data.GetData(), Data.Num(), BytesSent);
}// 接收
bool ReceiveTCPData(FSocket* Socket, TArray<uint8>& OutData)
{uint32 PendingDataSize = 0;if (Socket->HasPendingData(PendingDataSize)){OutData.SetNumUninitialized(PendingDataSize);int32 BytesRead = 0;if (Socket->Recv(OutData.GetData(), OutData.Num(), BytesRead)){OutData.SetNum(BytesRead);return true;}}return false;
}
2.3 TCP Server基础实现
监听端口:
bool StartTCPServer(int32 Port)
{ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);// 创建监听SocketFSocket* ListenSocket = SocketSubsystem->CreateSocket(NAME_Stream, TEXT("TCP Server"), false);// 绑定端口TSharedRef<FInternetAddr> Addr = SocketSubsystem->CreateInternetAddr();Addr->SetPort(Port);Addr->SetAnyAddress();if (!ListenSocket->Bind(*Addr)){UE_LOG(LogTemp, Error, TEXT("绑定端口失败: %d"), Port);return false;}// 开始监听(最大8个待连接队列)if (!ListenSocket->Listen(8)){UE_LOG(LogTemp, Error, TEXT("监听失败"));return false;}UE_LOG(LogTemp, Log, TEXT("TCP服务器启动,端口: %d"), Port);return true;
}
接受客户端连接:
void AcceptClients(FSocket* ListenSocket)
{ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);TSharedRef<FInternetAddr> ClientAddr = SocketSubsystem->CreateInternetAddr();FSocket* ClientSocket = ListenSocket->Accept(*ClientAddr, TEXT("TCP Client"));if (ClientSocket){FString ClientIP = ClientAddr->ToString(false);int32 ClientPort = ClientAddr->GetPort();UE_LOG(LogTemp, Log, TEXT("客户端连接: %s:%d"), *ClientIP, ClientPort);// 存储客户端Socket用于后续通讯}
}
三、WebSocket:现代化跨平台通讯
3.1 适用场景
- Web前端控制面板
- 云服务实时推送
- 与浏览器/移动端通讯
- 需要跨语言、跨平台的统一协议
3.2 UE内置WebSocket模块
UE提供了WebSockets
模块,需要在.Build.cs
中添加:
PublicDependencyModuleNames.AddRange(new string[] {"Core","WebSockets","Http"
});
3.3 基础实现
连接WebSocket:
#include "IWebSocket.h"
#include "WebSocketsModule.h"void ConnectWebSocket(const FString& URL)
{// 创建WebSocket实例TSharedPtr<IWebSocket> WebSocket = FWebSocketsModule::Get().CreateWebSocket(URL);// 绑定连接成功事件WebSocket->OnConnected().AddLambda([](){UE_LOG(LogTemp, Log, TEXT("WebSocket连接成功"));});// 绑定消息接收事件WebSocket->OnMessage().AddLambda([](const FString& Message){UE_LOG(LogTemp, Log, TEXT("收到消息: %s"), *Message);});// 绑定连接关闭事件WebSocket->OnClosed().AddLambda([](int32 StatusCode, const FString& Reason, bool bWasClean){UE_LOG(LogTemp, Warning, TEXT("WebSocket关闭: %s"), *Reason);});// 发起连接WebSocket->Connect();
}
发送消息:
void SendWebSocketMessage(TSharedPtr<IWebSocket> WebSocket, const FString& Message)
{if (WebSocket.IsValid() && WebSocket->IsConnected()){WebSocket->Send(Message);}
}
注意事项:
- 打包后使用
127.0.0.1
而非localhost
(避免域名解析问题) - UE的WebSocket模块仅支持
ws://
,不支持wss://
加密 - 连接URL格式:
ws://127.0.0.1:8080
四、实际项目中的完善方案
以上代码展示了网络通讯的基础实现,但实际项目中往往需要更完善的功能架构:
- 多通道管理 — 不同端口处理不同业务逻辑
- 异步线程处理 — 避免阻塞游戏主线程
- 自动重连机制 — TCP客户端断线自动重试
- 会话管理 — TCP服务端区分不同客户端
- IP过滤 — 白名单/黑名单机制(支持CIDR)
- 蓝图支持 — 完整的蓝图节点封装
- 数据序列化 — 结构体与字节数组的双向转换
如果需要开箱即用的完整解决方案,我已经将这些功能封装为以下插件,每个功能独立拆分,方便按需选择:
网络通讯插件
SimpleUDP | Fab — 简单UDP通讯方案
- 多通道收发管理
- 异步接收线程
- IP过滤(白名单/黑名单/CIDR)
- 广播地址支持
- 运行时动态配置
- 完整蓝图接口
SimpleTCPClient | Fab — 简单TCP客户端方案
- 多通道独立管理
- 自动重连(可配置间隔)
- 异步IO处理
- 连接状态事件委托
- 蓝图完整支持
SimpleTCPServer | Fab — 简单TCP服务端方案
- 多客户端会话管理(SessionKey:
IP:Port
) - 为每个客户端独立线程
- 连接/断开事件通知
- 运行时通道配置
- 蓝图完整支持
SimpleWebSocket | Fab — 简单WebSocket方案
- 命名连接管理
- 项目启动自动连接
- 消息委托绑定
- 完整蓝图封装
- 打包环境验证
数据处理插件
网络通讯的数据需要序列化为字节数组才能传输,但UE原生功能存在诸多限制:
- UE的Byte转换节点只能转换为单个Byte,无法转换为Byte数组,会导致数据丢失
- UE默认的JSON序列化会强制转换字段名为驼峰式,与原始命名不符
- 浮点数精度不足,序列化后数据失真
- UE没有提供直接将Struct转换为字节数组的蓝图方法
SimpleByteConversion | Fab — 解决UE原生痛点的数据序列化方案
- 基础类型双向转换为字节数组(int/float/bool/string/int64/double)
- 任意结构体的二进制序列化(基于反射,支持复杂嵌套)
- 结构体JSON序列化(保留原始字段名 + 高精度浮点数)
- 确保网络传输中数据格式的一致性
- CustomThunk实现蓝图泛型支持,真正的开箱即用
- 零依赖,纯UE原生API实现
所有插件均支持UE 5.2+,跨平台兼容,经过多个生产项目验证。
总结
UE网络通讯的核心就是:
- 选对协议 — UDP(高频低延迟) / TCP(可靠传输) / WebSocket(跨平台)
- 掌握基础 — 理解Socket创建、收发数据的底层逻辑
- 完善架构 — 实际项目需要异步处理、多通道管理、蓝图支持等
本文展示了基础实现代码,帮助你理解UE网络通讯的原理。
对于会C++的朋友:
原理讲明白之后,完全可以尝试自己动手实现一套完整的网络通讯方案,这也是很好的学习机会。
对于不会C++的朋友:
可以考虑直接使用我发售在Fab上的插件。价格相对于同类产品来说非常便宜,性价比极高,买一个备用,在以后的职业生涯中大概率能直接用上。
这几个插件其实算是我的练手作品,也成功帮我打通了Fab上架插件的完整流程。虽然功能比较基础,但都经过了实际项目的验证。感兴趣的朋友可以关注一波,后期我还会上架更多实用、性价比高的插件。
如果大家在使用中遇到任何问题,欢迎在评论区留言或通过邮箱联系我,我看到后会积极回应。也请大家多多支持,感谢!
📧 技术交流: mengzhishanghun@outlook.com
本文所有代码均基于UE5.2+实测有效。