UE:UE网络编程完全指南之UDP TCP WebSocket实现详解

© mengzhishanghun · 原创文章
首发于 博客园 · 禁止未经授权转载


前言

在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 Socket
    FSocket* 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);

    // 创建监听Socket
    FSocket* 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网络通讯的核心就是:

  1. 选对协议 — UDP(高频低延迟) / TCP(可靠传输) / WebSocket(跨平台)
  2. 掌握基础 — 理解Socket创建、收发数据的底层逻辑
  3. 完善架构 — 实际项目需要异步处理、多通道管理、蓝图支持等

本文展示了基础实现代码,帮助你理解UE网络通讯的原理。

对于会C++的朋友:
原理讲明白之后,完全可以尝试自己动手实现一套完整的网络通讯方案,这也是很好的学习机会。

对于不会C++的朋友:
可以考虑直接使用我发售在Fab上的插件。价格相对于同类产品来说非常便宜,性价比极高,买一个备用,在以后的职业生涯中大概率能直接用上。

这几个插件其实算是我的练手作品,也成功帮我打通了Fab上架插件的完整流程。虽然功能比较基础,但都经过了实际项目的验证。感兴趣的朋友可以关注一波,后期我还会上架更多实用、性价比高的插件。

如果大家在使用中遇到任何问题,欢迎在评论区留言或通过邮箱联系我,我看到后会积极回应。也请大家多多支持,感谢!

📧 技术交流: mengzhishanghun@outlook.com


本文所有代码均基于UE5.2+实测有效。


感谢阅读,欢迎点赞、关注、收藏,有问题可在评论区交流。
如果本文对你有帮助,点击这里捐赠支持作者。

posted @ 2025-10-25 15:44  mengzhishanghun  阅读(141)  评论(0)    收藏  举报