一、简介
WebSocket
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
WebSocket与http
其实从历史上来讲,websocket是为了克服http无法双向通信而引入的,在通常的使用中,可以复用http的端口与功能,除此外,他们没有其他的联系,而是完全是独立的协议,通常情况下,http是单向的web 服务,而websocket是全双工的,服务器和客户端可以实时的传输信息,在引用时他们可以在http服务器上同时部署,特别是在NodeJs中。
WebSocket与Socket
那么websocket和socket是什么关系呢? 其实可以理解为websocket是在socket的基础上实现的,其基于消息帧和TCP协议,而socket更通用,在编程中,可以选在tcp,udp,也需要自己控制数据流格式,每次的数据的长度都需要自己控制与读取。
二、使用
Best HTTP插件:这款插件不但支持WebSockets,还支持HTTP,Sockets等通信方式是一款不错的插件。也支持打包Webgl。下边记录两种Unity客户端使用WebSocket的方法。
插件
绑定
将插件包拖入unity然后在代码里引入插件类,最后写代码
代码
using UnityEngine;
using System;
using BestHTTP.WebSocket;
using System.Collections;
public class WebSocketClient : MonoBehaviour
{
public string address = "ws://127.0.0.1:8002";
WebSocket webSocket;
private bool lockReconnect = false;
private Coroutine _pingCor, _clientPing, _serverPing;
private void Start()
{
CreateWebSocket();
}
private void Update()
{
Debug.Log(lockReconnect);
}
/*
* 创建并初始化连接 start -------------------------------------------------------------------------------------------------------------------
*/
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();
}
}
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();
}
//end -------------------------------------------------------------------------------------------------------------------
/*
* 发送信息
*/
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!");
}
/*
* 重新连接
*/
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;
}
/*
* 心跳检测 start -------------------------------------------------------------------------------------------------------------------
*/
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");
}
}
//end -------------------------------------------------------------------------------------------------------------------
}