Unity中使用WebSocket (ws://)的方法

news2024/11/27 0:19:32

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket与http

其实从历史上来讲,websocket是为了克服http无法双向通信而引入的,在通常的使用中,可以复用http的端口与功能,除此外,他们没有其他的联系,而是完全是独立的协议,通常情况下,http是单向的web 服务,而websocket是全双工的,服务器和客户端可以实时的传输信息,在引用时他们可以在http服务器上同时部署,特别是在NodeJs中。

WebSocket与Socket 

那么websocket和socket是什么关系呢? 其实可以理解为websocket是在socket的基础上实现的,其基于消息帧和TCP协议,而socket更通用,在编程中,可以选在tcp,udp,也需要自己控制数据流格式,每次的数据的长度都需要自己控制与读取。

下边记录两种Unity客户端使用WebSocket的方法。

1.不使用插件的客户端

引入System.Net.WebSockets;命名空间。

在使用过程中发现这种方法打包WebGl的的时候是存在问题的。

具体使用方法如下:

WebSocket 类 (System.Net.WebSockets) | Microsoft Learn

 Unity客户端代码:

using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;

public class test4 : MonoBehaviour
{
    private void Start()
    {
        WebSocket();
    }

    public async void WebSocket()
    {
        try
        {
            ClientWebSocket ws = new ClientWebSocket();
            CancellationToken ct = new CancellationToken();
            //添加header
            //ws.Options.SetRequestHeader("X-Token", "eyJhbGciOiJIUzI1N");
            Uri url = new Uri("ws://xxx.xxx.xxx.xx:18x/xxx/xxx");
            await ws.ConnectAsync(url, ct);
            await ws.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("hello")), WebSocketMessageType.Binary, true, ct); //发送数据

            while (true)
            {
                var result = new byte[1024];
                await ws.ReceiveAsync(new ArraySegment<byte>(result), new CancellationToken());//接受数据
                var str = Encoding.UTF8.GetString(result, 0, result.Length);
                Debug.Log(str);
            }
          
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

}

2.使用Best HTTP插件

这款插件不但支持WebSockets,还支持HTTP,Sockets等通信方式是一款不错的插件。也支持打包Webgl

插件地址:

https://download.csdn.net/download/f402455894/87597949?spm=1001.2014.3001.5501

客户端代码:

using UnityEngine;
using System;
using BestHTTP.WebSocket;
using Newtonsoft.Json;

public class GaoYaGuHuaLu : MonoBehaviour
{

     string address = "ws://10.xxx.xx.193:1880/xxx";

    WebSocket webSocket;

    public GaoYaGuHuaLuEntity gaoYaGuHuaLu = new GaoYaGuHuaLuEntity();

    private void Awake()
    {
       
    }
    private void Start()
    {
        Init();
    }
    public void Init()
    {
        if (webSocket == null)
        {
            webSocket = new WebSocket(new Uri(address));

#if !UNITY_WEBGL
            webSocket.StartPingThread = true;
#endif

            // Subscribe to the WS events
            webSocket.OnOpen += OnOpen;
            webSocket.OnMessage += OnMessageRecv;
            webSocket.OnBinary += OnBinaryRecv;
            webSocket.OnClosed += OnClosed;
            webSocket.OnError += OnError;

            // Start connecting to the server
            webSocket.Open();
        }
    }


    public void Destroy()
    {
        if (webSocket != null)
        {
            webSocket.Close();
            webSocket = null;
        }
    }

    void OnOpen(WebSocket ws)
    {
        Debug.Log("OnOpen: ");
      //  webSocket.Send("我来啦");
    }


    void OnMessageRecv(WebSocket ws, string message)
    {
        Debug.LogFormat("OnMessageRecv: msg={0}", message);

    }

    void OnBinaryRecv(WebSocket ws, byte[] data)
    {
        Debug.LogFormat("OnBinaryRecv: len={0}", data.Length);
    }

    void OnClosed(WebSocket ws, UInt16 code, string message)
    {
        Debug.LogFormat("OnClosed: code={0}, msg={1}", code, message);
        webSocket = null;
    }

    void OnError(WebSocket ws, string ex)
    {
        string errorMsg = string.Empty;
#if !UNITY_WEBGL || UNITY_EDITOR
        if (ws.InternalRequest.Response != null)
        {
            errorMsg = string.Format("Status Code from Server: {0} and Message: {1}", ws.InternalRequest.Response.StatusCode, ws.InternalRequest.Response.Message);
        }
#endif
        Debug.LogFormat("OnError: error occured: {0}\n", (ex != null ? ex : "Unknown Error " + errorMsg));
        webSocket = null;
    }

    public void OnClose()
    {
        // 关闭连接
        webSocket.Close(1000, "Bye!");
    }
}

加上心跳检测和断线重连

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using BestHTTP;
using BestHTTP.WebSocket;
using BestHTTP.Examples;


public class ZGAndroidcCient : MonoBehaviour
{
    // w ws://XXXX:7799
    public string address = "ws://XXXX:7799";


    WebSocket webSocket;
    private bool lockReconnect = false;
    private Coroutine _pingCor, _clientPing, _serverPing;

    private void Start()
    {
        CreateWebSocket();
    }

    void CreateWebSocket()
    {
        try
        {
            webSocket = new WebSocket(new Uri(address));
#if !UNITY_WEBGL
            webSocket.StartPingThread = true;
#endif
            InitHandle();
            webSocket.Open();
        }
        catch (Exception e)
        {
            Debug.Log("websocket连接异常:" + e.Message);
            ReConnect();
        }

    }

    private void Update()
    {
        Debug.Log(lockReconnect);
    }

    void InitHandle()
    {
        RemoveHandle();
        webSocket.OnOpen += OnOpen;
        webSocket.OnMessage += OnMessageReceived;
        webSocket.OnClosed += OnClosed;
        webSocket.OnError += OnError;
    }

    void RemoveHandle()
    {
        webSocket.OnOpen -= OnOpen;
        webSocket.OnMessage -= OnMessageReceived;
        webSocket.OnClosed -= OnClosed;
        webSocket.OnError -= OnError;
    }

    void OnOpen(WebSocket ws)
    {
        Debug.Log("websocket连接成功");
        webSocket.Send("一个客户端连过来了");
        if (_pingCor != null)
        {
            StopCoroutine(_pingCor);
            _pingCor = null;
        }
        _pingCor = StartCoroutine(HeartPing());
        // 心跳检测重置
        HeartCheck();
    }

    void OnMessageReceived(WebSocket ws, string message)
    {
        // 如果获取到消息,心跳检测重置
        // 拿到任何消息都说明当前连接是正常的
        HeartCheck();
        Debug.Log(message);
    }

    void OnClosed(WebSocket ws, UInt16 code, string message)
    {
        Debug.LogFormat("OnClosed: code={0}, msg={1}", code, message);
        webSocket = null;
        ReConnect();
    }

    void OnError(WebSocket ws, string ex)
    {
        string errorMsg = string.Empty;
#if !UNITY_WEBGL || UNITY_EDITOR
        if (ws.InternalRequest.Response != null)
        {
            errorMsg = string.Format("Status Code from Server: {0} and Message: {1}", ws.InternalRequest.Response.StatusCode, ws.InternalRequest.Response.Message);
        }
#endif
        Debug.LogFormat("OnError: error occured: {0}\n", (ex != null ? ex : "Unknown Error " + errorMsg));
        webSocket = null;
        ReConnect();

    }

    void ReConnect()
    {
        if (this.lockReconnect)
            return;
        this.lockReconnect = true;
        StartCoroutine(SetReConnect());
    }

    private IEnumerator SetReConnect()
    {
        Debug.Log("正在重连websocket");
        yield return new WaitForSeconds(5);
        CreateWebSocket();
        lockReconnect = false;
    }

    //心跳检测
    private void HeartCheck()
    {
        if (_clientPing != null)
        {
            StopCoroutine(_clientPing);
            _clientPing = null;
        }
        if (_serverPing != null)
        {
            StopCoroutine(_serverPing);
            _serverPing = null;
        }
        _clientPing = StartCoroutine(ClientPing());
    }

    // 这里发送一个心跳,后端收到后,返回一个心跳消息
    // onmessage拿到返回的心跳就说明连接正常
    private IEnumerator ClientPing()
    {
        yield return new WaitForSeconds(5);
        WebSend("heartbeat");
        _serverPing = StartCoroutine(ServerPing());
    }
    // 如果超过一定时间还没重置,说明后端主动断开了
    // 如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
    private IEnumerator ServerPing()
    {
        yield return new WaitForSeconds(5);
        if (webSocket != null)
        {
            webSocket.Close();
        }
      
    }

    //发送心跳
    private IEnumerator HeartPing()
    {
        while (true)
        {
            yield return new WaitForSeconds(5);
            this.WebSend("Heartbeat");
        }
    }
    //向服务端发送信息
    public void WebSend(string msg)
    {
        if (webSocket == null || string.IsNullOrEmpty(msg)) return;
        if (webSocket.IsOpen)
        {
            webSocket.Send(msg);
        }
    }

    public void OnClose()
    {
        // 关闭连接
        webSocket.Close(1000, "Bye!");
    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1448866.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第10集《佛说四十二章经》

请大家打开讲议第十一面&#xff0c;第十九章、假真并观。 前面一章念等本空&#xff0c;说明大乘佛法的修学&#xff0c;身口意应安住在非空非有的中道实相。本章对中道实相的修学&#xff0c;再做明确的说明。修中道实相观要有空观与假观的观照&#xff0c;从空观中远离有相…

站在C/C++的肩膀速通Java面向对象

默认学过C或C&#xff0c;对变量、表达式、选择、循环都会。 运行特征 解释型语言&#xff08;JavaScript、Python等&#xff09; 源文件-(平台专属解释器)->解释器中执行编译型语言&#xff08;C、Go等&#xff09; 源文件-(平台编译器)->平台可执行文件Java 源文件-(…

腾讯云4核8G服务器3年600元?

腾讯云4核8G服务器3年600元&#xff1f;目前的价格是轻量应用服务器4核8G12M带宽一年446元、646元15个月&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;标准型SA2服务器1444.8元一年&#xff0c;在txy.wiki可以查询详细…

4核16G服务器阿里云是怎么收费的?贵吗?

阿里云4核16G服务器优惠价格ECS云服务器经济型e实例26元1个月、149元半年、79元3个月&#xff0c;4核16G通用算力u1服务器、通用型g7、通用型g8i、AMD通用型g8a、性能增强通用型g8ae、高主频通用型hfg8i、AMD通用型g7a、内存型r7p等均提供4核16G配置。阿里云服务器网aliyunfuwu…

第一篇【传奇开心果系列】Python的pyttsx3库技术点案例示例:文本转换语言

传奇开心果短博文系列 系列短博文目录Python的pyttsx3库技术点案例示例系列 短博文目录前言一、pyttsx3主要特点和功能介绍二、pyttsx3文字转语音操作步骤介绍三、多平台支持介绍和示例代码四、多语言支持介绍和示例代码五、自定义语言引擎介绍和示例代码六、调整语速和音量介绍…

DNS出现问题了,怎么处理?-提供完整解决方案

DNS作用 将域名(网址)解析为IP地址 DNS的作用是将域名(网址)解析为IP地址,方便用户访问互联网。通过DNS,用户可以轻松地通过域名来获取对应的IP地址,无需记住复杂的数字串。 负载均衡 负载均衡是DNS的一种功能,它能够将访问请求转发到不同的服务器,从而实现负载均衡。…

OpenAI ChatGPT 记忆功能怎么实现?

你的聊天助手现在能“记住”你的对话了&#xff01; 2月14日凌晨&#xff0c;OpenAI宣布正在测试ChatGPT的新功能——记住用户提问内容&#xff0c;并自由控制内存。这意味着&#xff0c;ChatGPT能帮你记住那些重要的聊天内容&#xff0c;让你的对话更流畅、更自然。 想象一下…

一周学会Django5 Python Web开发-项目配置settings.py文件-基本配置

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计17条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

心法利器[107] onnx和tensorRT的bert加速方案记录

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2023年新一版的文章合集已经发布&#xff0c;获取方式看这里&#xff1a;又添十万字-CS的陋室2…

智慧园区的可视化大屏,比你见过的更漂亮。

智慧园区云平台的建设旨在建立统一的工作流程&#xff0c;协同、调度和共享机制&#xff0c;以云平台为枢纽&#xff0c;形成一个紧密联系的整体&#xff0c;获得高效、协同、互动、整体的效益。

算法学习——LeetCode力扣贪心篇2

算法学习——LeetCode力扣贪心篇2 45. 跳跃游戏 II 45. 跳跃游戏 II - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 num…

奇异递归模板模式应用1-对象计数

需求&#xff1a;有时遇到某些类特征相似而又没有共同的父类&#xff0c;希望能够知道这些类的创建数量之和。 思路&#xff1a;将这些类继承自同一个计数类&#xff0c;共享计数变量s_createCount信息&#xff0c;实现如下&#xff1a; class Counter { public:Counter() {s_…

幻兽帕鲁在腾讯云服务器中怎么修改配置?游戏难度、经验倍率等等

幻兽帕鲁的游戏配置文件应该是PalWorldSettings 找到这个文件&#xff0c;就可以修改里面的参数。 如果你是用腾讯云一键部署的幻兽帕鲁&#xff0c;则可以到轻量应用服务器管理界面&#xff0c;找到“应用管理”&#xff0c;里面有个可视化修改游戏参数的面板设置&#xff0…

Shell 学习笔记(三)-shell变量

Shell 语言是一种动态类型和弱类型语言, 因此,在Shell中无需显示地声明变量, 且变量的类型会根据不同的操作符而发生变化. 静态类型语言: 在程序编译期间就确定变量类型的语言, 如java, C等 动态类型语言: 在程序运行期间才确定变量类型的语言, 如PHP, Python等. 一 shell变量…

vue学习106-120

创建项目p106 router&#xff0c;store和app.vue不用删 清一下router里的路由配置 vant组件库p107 目标&#xff1a;认识第三方vue组件库vant-ui&#xff08;cv战士&#xff09; 封装好了的组件整合在一起就是组件库 http://vant-contrib.gitee.io/vant/v2/#/zh-CN/ vue2用va…

第四篇:数据库安装(命令行)

数据库命令行界面安装 mysql官网&#xff0c;下载解压 https://dev.mysql.com/downloads/mysql/ 在安装之前先去检查一下,本地计算机的用户合组 winr(输入lusrmgr.msc) -点击组-双击administrator 如果只有这两个,那么就添加一下,提高网络服务的权限(避免出现mysql启动失败) …

MATLAB知识点:randperm函数(★★★★★)将一个数字序列进行随机打乱

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第…

十四、java 异常

文章目录 异常5.1 初识异常5.1.1 NullPointerException5.1.2 NumberFormatException 5.2 异常类5.2.1 Throwable5.2.2 异常类体系5.2.3 自定义异常 5.3 异常处理5.3.1 try/catch匹配5.3.2 重新抛出异常5.3.3 finally5.3.4 try-with-resources5.3.5 throws5.3.6 对比受检和未受检…

五、Mybatis复杂映射开发

1.一对一查询 1.1 一对一查询的模型 用户表和订单表的关系为&#xff0c;一个用户有多个订单&#xff0c;一个订单只从属于一个用户一对一查询的需求&#xff1a;查询一个订单&#xff0c;与此同时查询出该订单所属的用户SQL: DROP TABLE IF EXISTS user; CREATE TABLE user …

free pascal:fpwebview 组件通过JSBridge调用本机TTS

从 https://github.com/PierceNg/fpwebview 下载 fpwebview-master.zip 简单易用。 先请看 \fpwebview-master\README.md cd \lazarus\projects\fpwebview-master\demo\js_bidir 学习 js_bidir.lpr &#xff0c;编写 js_bind_speak.lpr 如下&#xff0c;通过JSBridge调用本机…