起因        

一般来讲,微信小游戏与web页面类似。我们在网页中更习惯的是使用websocket进行通信。Websocket本身是基于TCP进行封装的,在确定TCP达不到我们要求的延迟时,我们只能选择使用UDP。

        这边参考微信小游戏的UDP文档来实现一个简单的Demo。

首先,由于UDP是面向无连接的,只是单方向发送报文信息,没有Response,我们怎么才能确认UDP信息呢。我们用Go实现一个简单的UDP Server,收到什么消息就返回什么消息,将客户端发送和接收到的信息进行对比就知道了。

Go UDP Server

package main

import (
	"fmt"
	"net"
)

func main() {
	// 创建一个UDP地址
	addr, err := net.ResolveUDPAddr("udp", ":8101")
	if err != nil {
		fmt.Println("Error resolving UDP address:", err)
		return
	}

	// 创建一个UDP连接
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		fmt.Println("Error listening on UDP port:", err)
		return
	}
	defer conn.Close()

	fmt.Println("UDP Server is listening on :8101")

	buffer := make([]byte, 1024)

	for {
		// 读取数据
		n, clientAddr, err := conn.ReadFromUDP(buffer)
		if err != nil {
			fmt.Println("Error reading from UDP:", err)
			continue
		}

		// 打印接收到的数据
		fmt.Printf("Received message from %s: %s\n", clientAddr, string(buffer[:n]))

		// 发送相同的消息回客户端
		_, err = conn.WriteToUDP(buffer[:n], clientAddr)
		if err != nil {
			fmt.Println("Error sending response to UDP:", err)
			continue
		}

		fmt.Printf("Sent response to %s: %s\n", clientAddr, string(buffer[:n]))
	}
}

Unity UDP Demo

在这个方法里面,对应外面一个叫Addr的InputFiled,用来输入 <ip:port>,然后我们依次调用 Listen, connect, write/send, close四个方法,对应的监听UDP端口,连接到对应的<ip:port>, 发送消息,支持发送string或者byte, 以及关闭端口四个方法。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using LitJson;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using WeChatWASM;

public class TestUDPConnect : MonoBehaviour
{
    private static WXUDPSocket _udpSocket;
    private bool _connected = false;

    // 数据
    private string _stringData = "hello, how are you";
    private byte[] _bufferData = { 66, 117, 102, 102, 101, 114, 32, 68, 97, 116, 97, 32 };
    public TMP_InputField AddrInputField;

    private void Start()
    {
        AddrInputField = GameObject.Find("Addr").GetComponent<TMP_InputField>(); 
    }

    public void Listen()
    {
        AddrInputField = GameObject.Find("Addr").GetComponent<TMP_InputField>(); 
        Debug.Log(AddrInputField.text);
        WX.InitSDK((int code) =>
        {
            Debug.Log("InitSDK code: " + code);
        });
        if (_udpSocket == null)
        {
            _udpSocket = WX.CreateUDPSocket();
            var port = _udpSocket.Bind();
            Debug.Log("port: " + port);
            Debug.Log("udpSocket: " + JsonUtility.ToJson(_udpSocket));

            _udpSocket.OnListening(
                (res) =>
                {
                    Debug.Log("onListening: " + JsonUtility.ToJson(res));
                }
            );

            _udpSocket.OnError(
                (res) =>
                {
                    Debug.Log("onError: " + JsonUtility.ToJson(res));
                }
            );

            _udpSocket.OnClose(
                (res) =>
                {
                    Debug.Log("onClose: " + JsonUtility.ToJson(res));
                }
            );

            _udpSocket.OnMessage(
                (res) =>
                {
                    Debug.Log("onMessage: " + JsonUtility.ToJson(res));
                }
            );
        }
        else
        {
            Debug.LogError("udp实例已初始化");
        }
    }

    public void connect()
    {
        if (_udpSocket != null && !_connected)
        {
            AddrInputField = GameObject.Find("Addr").GetComponent<TMP_InputField>();
            var arr = AddrInputField.text.Split(':');
            _udpSocket.Connect(
                new UDPSocketConnectOption() { address = arr[0], port = Convert.ToInt32(arr[1]) }
            );
            _connected = true;
        }
        else
        {
            Debug.LogError("连接失败:udp实例未初始化或已连接");
        }
    }

    public void writeString()
    {
        write("String");
    }

    public void sendString()
    {
        send("String");
    }
    private void write(string type)
    {
        if (_udpSocket != null && _connected)
        {
            AddrInputField = GameObject.Find("Addr").GetComponent<TMP_InputField>();
            var arr = AddrInputField.text.Split(':');
            Debug.Log(AddrInputField);
            UDPSocketSendOption option = new UDPSocketSendOption()
            {
                address = arr[0],
                port = Convert.ToInt32(arr[1])
            };
            if (type == "String")
            {
                option.message = _stringData;
            }
            else
            {
                option.message = _bufferData;
            }
            _udpSocket.Write(option);
            Debug.Log("Message: " + option.message);
        }
        else
        {
            Debug.LogError("write失败:udp实例未初始化或未连接");
        }
    }

    private void send(string type)
    {
        if (_udpSocket != null)
        {
            AddrInputField = GameObject.Find("Addr").GetComponent<TMP_InputField>();
            var arr = AddrInputField.text.Split(':');
            UDPSocketSendOption option = new UDPSocketSendOption()
            {
                address = arr[0],
                port = Convert.ToInt32(arr[1])
            };
            if (type == "String")
            {
                option.message = _stringData;
            }
            else
            {
                option.message = _bufferData;
            }
            _udpSocket.Send(option);
            Debug.Log("Message: " + option.message);
        }
        else
        {
            Debug.LogError("send失败:udp实例未初始化");
        }
    }

    public void close()
    {
        if (_udpSocket != null && _connected)
        {
            _udpSocket.Close();
            _connected = false;
            _udpSocket = null;
        }
        else
        {
            Debug.LogError("关闭失败:udp实例未初始化或未连接");
        }
    }

}

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐