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(Encoding.UTF8.GetBytes("hello")), WebSocketMessageType.Binary, true, ct); //发送数据

while (true)

{

var result = new byte[1024];

await ws.ReceiveAsync(new ArraySegment(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!");

}

}

文章来源

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: