Version: 2019.4
多人游戏大厅
NetworkReader 和 NetworkWriter 序列化程序

使用传输层 API

除了高级网络 API (HLAPI) 之外,Unity 还允许访问称为传输层的低级网络 API。传输层允许您构建自己的网络系统,满足在游戏网络方面更具体或更高级的要求。

传输层是在操作系统基于套接字的网络之上运行的一个薄。传输层可以发送和接收以字节数组表示的消息,并提供许多不同的“服务质量”选项来满足不同的需求。该层专注于灵活性和性能,并在 NetworkTransport 类中公开一个 API。

传输层支持网络通信的基本服务。这些基本服务包括:

  • 建立连接

  • 使用各种“服务质量”进行通信

  • 流量控制

  • 基本统计信息

  • 其他服务,例如通过中继服务器 (Relay Server) 或本地发现进行通信

传输层可以使用两种协议:用于一般通信的 UDP 和用于 WebGL 的 WebSocket。要直接使用传输层,一般工作流程如下:

  1. 初始化网络传输层

  2. 配置网络拓扑

  3. 创建主机

  4. 开始通信(处理连接和发送/接收消息)

  5. 使用之后关闭库

请参阅下面的相应部分以了解每个部分的技术细节。每个部分都提供了一个可添加到网络脚本中的代码片段。

步骤 1:初始化网络传输层

初始化网络传输层时,可在下面代码示例中演示的默认初始化(没有参数)之间进行选择,或者也可提供其他参数(例如最大数据包大小和线程超时限制)来控制网络层的整体行为。

要使用默认设置来初始化传输层,请调用 Init()


    // 在不使用任何参数的情况下初始化传输层(默认设置)
        NetworkTransport.Init();

    要使用您自己的配置来初始化传输层,只需将配置作为参数添加到 Init,如下所示。

        // 使用自定义设置来初始化传输层的示例
        GlobalConfig gConfig = new GlobalConfig();
        gConfig.MaxPacketSize = 500;
        NetworkTransport.Init(gConfig);

仅当具有特殊的网络环境并且熟悉所需的特定设置时,才应使用自定义 Init 值。根据经验,如果要开发基于互联网的典型多人游戏,默认的 Init 设置就足够了。

步骤 2:配置网络拓扑

下一步是配置对等方之间的连接。网络拓扑定义了允许的连接数和要使用的连接配置。如果游戏需要发送不同重要程度的网络消息(例如,偶然声音效果的重要性较低,玩家是否得分的声音效果的重要性较高),您可能需要定义几个通信通道,根据要发送的具体消息类型以及消息在游戏中的相对重要性,为每个通道指定不同的服务质量级别。


    ConnectionConfig config = new ConnectionConfig();
        int myReliableChannelId  = config.AddChannel(QosType.Reliable);
        int myUnreliableChannelId = config.AddChannel(QosType.Unreliable);

上面的示例定义了具有不同服务质量值的两个通信通道。QosType.Reliable 传递消息并确保消息传递成功,而 QosType.Unreliable 以更快速度发送消息,但不会进行任何检查来确保消息传递成功。

还可以调整 ConnectionConfig 上的属性来指定每个连接的配置设置。但是,在从一个客户端连接到另一个客户端时,两个连接对等方的设置应该相同,否则连接会因 CRCMismatch 错误而失败。

网络配置的最后一步是拓扑定义。


HostTopology topology = new HostTopology(config, 10);

此示例将主机拓扑定义为最多允许 10 个连接。这些连接是在步骤 1 中配置的连接。

步骤 3:创建主机

执行前两个初步设置步骤之后,现在需要创建一个主机(打开套接字):


int hostId = NetworkTransport.AddHost(topology, 8888);

此代码示例在端口 8888 和任何 IP 地址上添加新主机。主机最多支持 10 个连接(在步骤 2 中配置)。这些连接是在步骤 1 中配置的连接。

步骤 4:开始通信

要开始通信,必须建立与其他主机的连接。为此,请调用 Connect()。这样将在您和远程主机之间建立连接。此过程会收到一个事件以指示连接是否成功。

首先,使用端口 8888 连接到地址为 192.168.1.42 的远程主机。此情况下会返回分配的 connectionId


connectionId = NetworkTransport.Connect(hostId, "192.168.1.42", 8888, 0, out error);

连接完成后,将收到 ConnectEvent。现在可以开始发送数据了。


NetworkTransport.Send(hostId, connectionId, myReliableChannelId, buffer, bufferLength,  out error);

完成连接后,请调用 Disconnect() 以断开主机连接。

NetworkTransport.Disconnect(hostId, connectionId, out error);

要检查函数调用是否成功,可将 out error 转换为 NetworkErrorNetworkEror.Ok 表示未遇到任何错误。

要检查主机状态,可使用两个函数:

要轮询内部事件队列中的事件,可调用

NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

或 NetworkTransport.ReceiveFromHost(recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

这两个函数都从队列中返回事件;第一个函数将从任何主机返回事件,为 recHostId 变量分配作为消息来源的主机的 ID,而第二个函数仅从提供的 recHostId 所指定的主机返回事件。

一种从 Receive 轮询数据的方式是在 Update 函数中调用它:


void Update()
{

    int recHostId; 
    int connectionId; 
    int channelId; 
    byte[] recBuffer = new byte[1024]; 
    int bufferSize = 1024;
    int dataSize;
    byte error;
    NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
    switch (recData)
    {
        case NetworkEventType.Nothing:                     break;
        case NetworkEventType.ConnectEvent:                break;
        case NetworkEventType.DataEvent:                   break;
        case NetworkEventType.DisconnectEvent:            break;

  case NetworkEventType.BroadcastEvent:

       break;
    }
}

可收到 5 种类型的事件。


case NetworkEventType.ConnectEvent: 
    if(myConnectionId == connectionId)
        //我的连接请求已被批准
    else
        //其他人向我发送了连接请求
    break;

  • NetworkEventType.DataEvent:您收到了一个数据事件。当有一些数据准备好接收时,您会收到数据事件。如果 recBuffer 大到足以容纳数据,则数据将复制到缓冲区中。如果不够大,该事件包含 MessageToLong 网络错误。如果发生这种情况,需要为缓冲区重新分配更大的大小并再次调用 DataEvent 函数。

  • NetworkEventType.DisconnectEvent:建立的连接已断开连接,或者您的连接请求已失败。请检查错误代码以找出发生这种情况的原因。


case NetworkEventType.DisconnectEvent: 
    if(myConnectionId == connectionId)
        //由于某种原因无法连接,请参阅错误
    else
        //其中一个建立的连接已断开连接
    break;

WebGL 支持

可在 WebGL 上使用 WebSocket,但 Web 客户端只能连接到主机,本身不能是主机。这意味着主机必须是单机玩家(仅限 Win、Mac 或 Linux)。对于客户端配置,上述所有步骤(包括拓扑和配置)都是相同的。在服务器上,调用以下函数:


NetworkTransport.AddWebsocketHost(topology, port, ip);

上面的 IP 地址应该是需要监听的具体地址,或者如果希望主机监听所有网络接口,则可以传递 null 作为 IP 地址。

服务器一次只能支持一个 WebSocket 主机,但可以同时处理其他通用主机:


NetworkTransport.AddWebsocketHost(topology, 8887, null);
NetworkTransport.AddHost(topology, 8888);

多人游戏大厅
NetworkReader 和 NetworkWriter 序列化程序