【Unity/网络】Unity和内网穿透的网络测试 —— 以聊天室为例

这两天在做那个CodeMonky的胡闹厨房的案例,一直困扰我的是关于Lobby和Relay的相关网络服务,需要挂加速器并且延迟不低,所以我一直在寻找一些其他替代方案,想起来之前做一个UEC++的网络枪战时做过一个内网穿透的方法,所以在Unity中也采用这个方案,但中间怎么改IP和端口都没法连接成功,最后就索性单开了一个项目来测试这一块,现在也算是测出来原因了,这里写个笔记记录下过程

文章目录

案例概述

首先先说一下写的聊天室案例

使用MessageManager来作为RPC调用,提供全局添加Message方法的单例,其他类可以订阅对应的委托进行自己的处理,比如UI部分可以根据委托创建消息显示

NetworkManager是Unity提供的网络类,配合UnityTransport实现网络的传输和连接的管理

UI部分分三大部分:

  • 第一部分是输入消息内容并发送的部分
  • 第二部分是提供启动Host和Client的按钮部分
  • 第三部分就是消息的生成显示的部分。

关键代码部分

MessageManager

添加消息的RPC部分,该部分只负责转发委托,并不实际存储消息内容,由于一开始是希望方便测试查看网络相关的回调信息,所以添加了一个LogLevel控制消息的颜色,但后来又想不如加个聊天,再后来就成了个聊天室了()

cs 复制代码
    public void AddMessage(string name, string content, LogLevel level = LogLevel.Normal)
    {
        AddMessageServerRPC(name, content, level);
    }

    [ServerRpc(RequireOwnership = false)]
    private void AddMessageServerRPC(string name, string content, LogLevel level = LogLevel.Normal)
    {
         AddMessageClientRPC(name, content, level);
    }

    [ClientRpc]
    private void AddMessageClientRPC(string name, string content, LogLevel level = LogLevel.Normal)
    {
        onMessageReceived.Invoke(name, content, level);
    }

接下来是在该类中重新在NetworkManager的StartHost和StartClient外面裹一层自己的代码

cs 复制代码
public void StartHost()
{
    //这一句后面详细解释
    NetworkManager.Singleton.GetComponent<UnityTransport>().SetConnectionData("你的公网IP", 7777, "0.0.0.0");
    
    //提示打印玩家的连接通知
    //Namer.GM是一个名称类,拿来代替直接使用"GM"的字符串方式,方便统一名称,统一修改,查找引用等
    NetworkManager.Singleton.OnClientConnectedCallback += (clientId) =>
    {
        Instance.AddMessage(Namer.GM, "player " + clientId + " Connected.", LogLevel.Warning);

        clientIDList.Add(clientId);
    };

    NetworkManager.Singleton.StartHost();
}

public void StartClient()
{
    NetworkManager.Singleton.OnClientDisconnectCallback += (clientId) =>
    {
        onMessageReceived.Invoke(Namer.GM, "未能成功连接到服务器!", LogLevel.Error);
    };

    NetworkManager.Singleton.StartClient();
}

最后是该类中向外部提供的委托

cs 复制代码
public UnityAction<string, string, LogLevel> onMessageReceived;

MessageUI

接下来是消息的显示部分,这一部分订阅单例的消息委托并显示出来,注意textPrefab是一个网络物体,其父级要在初始化时就提供。并且要在NetworkManager的NetworkPrefabList中添加该Prefab

cs 复制代码
public class MessageUI : MonoBehaviour
{
    [SerializeField] private TextBlockUI textPrefab;
    [SerializeField] private Transform textParent;

    private void Start()
    {
        MessageManager.Instance.onMessageReceived += (name, content, level) =>
        {
            TextBlockUI obj = Instantiate(textPrefab, textParent);
            obj.SetText(name, content, level);
        };
    }
}

SendUI

这一部分算是负责发送消息的部分,本质上就是获取消息然后调用写好的函数就行,另外连接Host和Client只需要两个按钮分别调用MessageManager中对应的StartHost和StartClient就行,之后就不写了

cs 复制代码
public class SendUI : MonoBehaviour
{
    [SerializeField] private TMP_InputField inputField;
    [SerializeField] private TMP_Dropdown dropdown;
    [SerializeField] private Button submitBtn;
    [SerializeField] private TMP_InputField nameField;

    // Start is called before the first frame update
    void Start()
    {
        submitBtn.onClick.AddListener(() =>
        {
            string name = nameField.text;
            string content = inputField.text;
            LogLevel level = (LogLevel)dropdown.value;
            MessageManager.Instance.AddMessage(name, content, level);

            inputField.text = "";
        });

        nameField.onEndEdit.AddListener((str) =>
        {
            PlayerPrefs.SetString("PlayerName", str);
        });
    }
}

上述写完后,在本地进行测试,没问题的话就可以准备进行远程连接了

远程连接

远程连接有许多方案,这里说一下我知道的

  1. Lobby + Relay : 在国内无服务器,连接靠玄学

  2. Unity Online Services:国内的网络服务,相关教程和API可在官网找到,可用,但是免费用户只有五十次房间额度,用完就无了

  3. 内网穿透:直接连接到玩家主机上,花费视使用的软件而定,最经济的办法是内网穿透做服务器然后放云上,或者像我一样直接用来Host连接,不额外搞服务器

内网穿透:

我使用的是路由侠,穿透时都会分配到一个公网的域名和对应端口,其映射到的内网端口通常选为127.0.0.1 : 7777,IP输入本机IP,后面的7777端口只要选一个没其他进程用的即可

确保穿透是打开的,域名对应的IP可以直接在CMD窗口中输入"ping 公网域名",即可得到IP地址

公网的IP地址和端口填入Unity中NetworkManager下的UnityTransport的Address和Port中,并且打开运行远程连接

到达这一步会发现哪里都正常,但是客户端就是连不上服务器,最主要的一点是监听的端口问题

先打开服务器,再使用CMD,输入netstat -an | find "端口号"可以得到当前端口的状态,一般是

UDP 0.0.0.0:28907 *:*,这说明该端口当前监听所有UDP协议的IP地址连接,主机是正常监听的,

客户端也是连接的公网IP和端口,那么唯一的可能就是主机监听的端口和客户端不一致,客户端使用公网IP和端口进行连接,但最后端口会被映射到内网端口上,因此主机应当监听的是内网的对应端口,所以在StartHost时需要修改监听的端口值,也就是MessageManager中StartHost中的第一段代码

cs 复制代码
    NetworkManager.Singleton.GetComponent<UnityTransport>().SetConnectionData("你的公网IP", 7777, "0.0.0.0");

自此大功告成,能够通过内网穿透进行通信。

相关推荐
异次元的归来11 分钟前
Unity DOTS中的share component
unity·游戏引擎
fantasy_arch2 小时前
CPU性能优化-磁盘空间和解析时间
网络·性能优化
向宇it3 小时前
【从零开始入门unity游戏开发之——C#篇25】C#面向对象动态多态——virtual、override 和 base 关键字、抽象类和抽象方法
java·开发语言·unity·c#·游戏引擎
是Dream呀3 小时前
Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络
网络·python·神经网络
kaixin_learn_qt_ing4 小时前
了解RPC
网络·网络协议·rpc
_oP_i4 小时前
unity webgl部署到iis报错
unity
Go_Accepted5 小时前
Unity全局雾效
unity
向宇it5 小时前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
安全小王子5 小时前
Kali操作系统简单介绍
网络·web安全
Hacker_LaoYi6 小时前
【漏洞分析】DDOS攻防分析(四)——TCP篇
网络·tcp/ip·ddos