前言
本篇来实现WebSocket通信
一、导入UnityWebSocket插件
要在Unity中使用WebSocket我查看了官方文档 我一直觉得Unity是一个把所有东西都为开发者准备好的引擎 然而这次我错了,它摆烂了,Unity并没有为我们提供WebScoket的内容
所以只好使用插件了,这里我使用的是UnityWebSocket 网上也有很多教程用的是BestHttp 我也心动过,但是它好贵,坐等打折好了
言归正传,首先我们先在UnityWebSocket的Release页面下载插件 然后将它导入到我们的项目中 这个插件提供的Demo来供我们参考
二、在TS中使用
这个插件的使用是在C#中的,我们需要封装一层来让它能被TS使用 这里我定义了一个WebSocketClient
public class WebSocketClient
{
private string connectAddress;
protected IWebSocket _socket;
public Action<string> JSOnOpen;
public Action<string> JSOnMessage;
public Action<CloseStatusCode, string> JSOnClose;
public Action<string> JSOnError;
public ushort State
{
get
{
if (_socket == null)
{
return 0;
}
else
{
return (ushort)(_socket.ReadyState + 1);
}
}
}
public void Connect(string address)
{
connectAddress = address;
if (_socket == null)
{
_socket = new WebSocket(address);
_socket.OnOpen += Socket_OnOpen;
_socket.OnMessage += Socket_OnMessage;
_socket.OnClose += Socket_OnClose;
_socket.OnError += Socket_OnError;
_socket.ConnectAsync();
}
}
public void SendStr(string str)
{
try
{
_socket.SendAsync(str);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
public void SendByte(string data)
{
try
{
var bytes = System.Text.Encoding.UTF8.GetBytes(data);
_socket.SendAsync(bytes);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
public void Close()
{
_socket.CloseAsync();
}
private void Socket_OnOpen(object sender, OpenEventArgs e)
{
JSOnOpen?.Invoke(connectAddress);
}
private void Socket_OnMessage(object sender, MessageEventArgs e)
{
try
{
if (e.IsBinary)
{
JSOnMessage?.Invoke(e.Data);
}
else if (e.IsText)
{
JSOnMessage?.Invoke(e.Data);
}
}
catch (Exception err)
{
Debug.LogError(err);
}
}
private void Socket_OnClose(object sender, CloseEventArgs e)
{
JSOnClose?.Invoke(e.StatusCode, e.Reason);
}
private void Socket_OnError(object sender, ErrorEventArgs e)
{
JSOnError?.Invoke(e.Message);
}
}
需要注意的是,我们需要声明使用的Action类型,才能正常使用
_jsEnv.UsingAction<string>();
_jsEnv.UsingAction<CloseStatusCode, string>();
别忘了为它生成一下胶水代码
接下来我们来在TS中继续工作
import { UnityWebSocket, WebSocketClient } from "csharp";
import { TSLog } from "../../CustomLog/TSLog";
import { IBaseMessageData, SocketMessageType, WebSocketState } from "../NetDefine";
export enum WebSocketState {
Null = 0,
Connecting = 1,
Open = 2,
Closing = 3,
Closed = 4
}
export interface IWebSocket {
Connect(address: string): void;
Send(data: IBaseMessageData, droppable?: boolean): void;
Close(): void;
ListenMessage(state: SocketMessageType, fun: Function): void;
RemoveListen(state: SocketMessageType, fun: Function): void;
}
export class JSWebSocket extends WebSocketClient implements IWebSocket {
private _waitSendCash: Array<string> = new Array<string>();
private _messageHandler: Map<SocketMessageType, Array<Function>> = new Map<SocketMessageType, Array<Function>>();
constructor() {
super();
this.JSOnOpen = this.Socket_OnOpen.bind(this);
this.JSOnMessage = this.Socket_OnMessage.bind(this);
this.JSOnClose = this.Socket_OnClose.bind(this);
this.JSOnError = this.Socket_OnError.bind(this);
}
RemoveListen(state: SocketMessageType, fun: Function): void {
if (this._messageHandler.has(state)) {
let array = this._messageHandler.get(state);
let newArray = new Array<Function>();
array.forEach(v => {
if (v != fun) {
newArray.push(v);
}
});
this._messageHandler.set(state, newArray);
array.length = 0;
array = null;
}
}
ListenMessage(state: SocketMessageType, fun: Function): void {
if (!this._messageHandler.has(state)) {
this._messageHandler.set(state, new Array<Function>());
}
this._messageHandler.get(state).push(fun);
}
public Send(data: IBaseMessageData, droppable: boolean = true): void {
let jstr = JSON.stringify(data);
TSLog.Log("stringify:" + jstr)
if (this.State != WebSocketState.Open) {
if (!droppable) {
this._waitSendCash.push(jstr);
}
}
else {
this.SendByte(jstr);
}
}
private Socket_OnOpen(address: string) {
TSLog.Log("OnConnect---->" + address);
if (this._waitSendCash.length > 0) {
TSLog.Log("sendcash")
this._waitSendCash.forEach(jstr => {
this.SendByte(jstr);
});
}
this._waitSendCash.length = 0;
}
private Socket_OnMessage(jstr: string) {
TSLog.Log("OnMessage---->" + jstr);
if (jstr == "echo.websocket.events sponsored by Lob.com") return;
let jsdata: IBaseMessageData = JSON.parse(jstr) as IBaseMessageData;
if (this._messageHandler.has(jsdata.type)) {
let handlers = this._messageHandler.get(jsdata.type);
for (let i = 0; i < handlers.length; i++) {
if (handlers[i] != null) {
try {
handlers[i](jsdata);
}
catch (e) {
TSLog.Error(e);
}
}
}
}
}
private Socket_OnClose(errCode: UnityWebSocket.CloseStatusCode, reason: string) {
TSLog.Log("OnClose---->" + reason);
}
private Socket_OnError(message: string) {
TSLog.Log("Connect---->" + message);
}
}
这样我们在TS中就可以使用WebSocket了
这里我定义了一个全局的mainSocket
export const mainSocket:IWebSocket = new JSWebSocket();
以及一些配置和数据接口
export let mainSocketAddress: string = "wss://echo.websocket.events";
export let httpAddress: string = "";
export interface IBaseMessageData {
type: SocketMessageType;
}
export enum SocketMessageType {
Null = 0,
Test = 1
}
export enum WebSocketState {
Null = 0,
Connecting = 1,
Open = 2,
Closing = 3,
Closed = 4
}
在游戏中我们会有很多个功能使用到Socket 我不想在Socket中去为每个功能写处理 而是在每个功能的Hanlder中去处理 这里我写了一个TestHanlder来测试
export interface Req_Test_MessageData extends IBaseMessageData {
data: {
str: number,
str2: number
}
}
export interface Resp_Test_MessageData extends IBaseMessageData {
data: {
str: number,
str2: number
}
}
export interface ITestHandler{
Connect(): ITestHandler;
ReqTest(n1: number, n2: number): ITestHandler;
Close(time: number): ITestHandler;
}
export class TestHanler extends Singleton<TestHanler> implements ITestHandler {
constructor() {
super();
mainSocket.ListenMessage(SocketMessageType.Test, this.OnRespTest.bind(this))
}
public OnRespTest(resp: Resp_Test_MessageData): void {
TSLog.Log(resp.data.str)
TSLog.Log(resp.data.str2)
}
public ReqTest(n1: number, n2: number): ITestHandler {
let req: Req_Test_MessageData = {
type: SocketMessageType.Test,
data: {
str: n1,
str2: n2
}
}
mainSocket.Send(req, false);
return this;
}
public Connect(): ITestHandler {
mainSocket.Connect(mainSocketAddress);
return this;
}
public Close(time: number = 5000): ITestHandler {
setTimeout(() => {
mainSocket.Close();
}, time)
return this;
}
}
然后我们就可以调用了
new TestHandler()
.Connect()
.ReqTest(5, 1)
.Close(5000);
嗯,挺简洁的,测试了也没有什么问题 具体内容还需要与服务端联调,目前以及实现了功能
|