实现客户端与服务器进行WebSocket通讯(简易)

本文介绍了如何在客户端使用Layabox和TypeScript通过WebSocket与C#控制台应用(使用Fleck库)进行通信,包括SocketTest类的操作、消息处理以及服务端的WebSocketServer接口。
摘要由CSDN通过智能技术生成

实现客户端与服务器进行WebSocket通讯(简易)

简介

  • 客户端使用的是Layabox,语言为TypeScript,利用Laya封装好的webSocket进行网络通信。
  • 服务端使用的是C#简单控制台应用,利用Fleck第三方库封装好的WebSocketServer进行网络通信。

项目效果

初始化

连接服务器
发送消息
发送消息
清空消息
断开连接

客户端代码

  • SocketTest 类,封装LayaSocket ,并且暴露出对应的接口操作。

  • WsTestView 类,对应UI界面的逻辑处理

  • WsTestItem 类,消息队列中对应的文本

  • EventMgr 类,事件管理器。

    import { EventMgr } from "./EventMgr";
    /** Socket辅助类 */
    export default class SocketTest {
        /** 单例 */
        private static instance: SocketTest;
        /** socket实例 */
        private socket: Laya.Socket;
        /** 消息列表 */
        private msgList: { key: number, value: string }[]
        /** 消息列表最大长度 */
        private msgMaxCount: number = 100;
        /** 服务器地址 */
        private host: string;
        /** 服务器端口 */
        private port: number;
        /** 是否连接 */
        private isConnect: boolean = false;
        constructor() {
            // 懒汉式实例
            if (this.socket == null) {
                this.socket = new Laya.Socket();
            }
            if (!this.msgList) {
                this.msgList = [];
            }
            // 监听事件
            this.socket.on(Laya.Event.OPEN, this, this.onOpenHandler);
            this.socket.on(Laya.Event.CLOSE, this, this.onCloseHandler);
            this.socket.on(Laya.Event.MESSAGE, this, this.onMessageHandler);
            this.socket.on(Laya.Event.ERROR, this, this.onErrorHandler);
        }
        
        /**
         * 监听服务器连接成功
         * @param event 事件
         */
        private onOpenHandler(event: any) {
            Laya.LocalStorage.setItem("socket", `ws://${this.host}:${this.port}`);
            this.isConnect = true;
            this.saveMsg("连接服务器成功");
        }
    
        /**
         * 监听服务器连接断开
         */
        private onCloseHandler() {
            this.saveMsg("连接已断开!");
            this.isConnect = false;
        }
    
        /**
         * 监听服务器消息
         * @param msg 消息
         */
        private onMessageHandler(msg) {
            this.saveMsg(msg, 1);
        }
    
        /**
         * 监听服务器错误
         * @param e 错误信息 
         */
        private onErrorHandler(e) {
            this.isConnect = false;
            this.saveMsg("通信错误");
        }
    
        /**
         * 保存消息
         * @param msg 消息
         * @param key 1:服务器 2:客户端 
         */
        private saveMsg(msg: string, key?: number) {
            if (this.msgList) {
                let value = key ? ((key == 2 ? "客户端:" : "服务器:") + msg) : msg;
                var msgItem = { key, value }
                if (this.msgList.length < this.msgMaxCount) {
                    this.msgList.push(msgItem);
                } else {
                    this.msgList.shift();
                    this.msgList.push(msgItem);
                }
                EventMgr.dispatchEvent("updateMsgList", this.msgList);
            }
        }
    
        /**
         * 连接
         * @param url  地址
         */
        public connect(url: string) {
            if (this.validateAddress(url)) {
                const regex = /^wss?:\/\/([^:/\s]+)(?::(\d+))?/;
                const match = url.match(regex);
    
                if (match) {
                    this.host = match[1];
                    this.port = match[2] ? parseInt(match[2]) : 0;
                    this.socket.connect(this.host, this.port);
                }
            } else {
                alert("地址不合法");
            }
        }
    
        /**
         * 关闭
         */
        public close() {
            this.socket.close();
            this.isConnect = false;
        }
    
        /**
         * 发送
         */
        public send(msg: string) {
            if (msg.length > 150) {
                console.error("输入内容不可超过150个字符");
                return;
            }
            if (this.msgList) {
                this.socket.send(msg);
                this.saveMsg(msg, 2);
            }
        }
    
        /**
         * 获取实例
         */
        public static getInstance() {
            if (SocketTest.instance == null) {
                SocketTest.instance = new SocketTest();
            }
            return SocketTest.instance;
    
        }
    
        /**
         * 获取消息列表
         */
        public getMsgList() {
            let arr = Array.from(this.msgList);
            return arr;
        }
    
        /**
         * 验证地址
         */
        public validateAddress(url: string): boolean {
            const regex = /^wss?:\/\/[^\s/$.?#].[^\s]*$/;
            return regex.test(url);
        }
    
        /**
         * 是否连接
         */
        public isConnecting() {
            return this.isConnect;
        }
    
        /**
         * 清空消息
         */
        public clear() {
            this.msgList = [];
        }
    }
    

import { ui } from "../ui/layaMaxUI";
import { EventMgr } from "./EventMgr";
import SocketTest from "./SocketTest";
import WsTestItem from "./WsTestItem";

/** socket测试界面 */
export default class WsTestView extends ui.WsTestViewUI {
    /** socket */
    private socketTest: SocketTest;
    /** 消息列表 */
    private msgList: { key: number, value: string }[]
    constructor() {
        super();
    }
    onAwake(): void {
        this.btn_connection.on(Laya.Event.CLICK, this, this.onConnection);
        this.btn_send.on(Laya.Event.CLICK, this, this.onSend);
        this.btn_close.on(Laya.Event.CLICK, this, this.onClose);
        this.btn_clear.on(Laya.Event.CLICK, this, this.onClear);

        this.list_msgs.selectEnable = true;
        this.list_msgs.vScrollBarSkin = "";
        this.list_msgs.itemRender = WsTestItem;
        this.list_msgs.renderHandler = new Laya.Handler(this, this.updateItem);
        this.list_msgs.array = this.msgList;

        EventMgr.addEvent("updateMsgList", this.updateMsgList, this);
    }

    onEnable(): void {
        let url = Laya.LocalStorage.getItem("socket");
        if (url) {
            this.input_link.text = url;
        }
    }

    /** 连接 */
    private onConnection() {
        this.socketTest = SocketTest.getInstance();
        this.msgList = this.socketTest.getMsgList();
        let url = this.input_link.text;
        this.socketTest.connect(url);

    }

    /** 发送 */
    private onSend() {
        if (!this.socketTest || !this.socketTest.isConnecting) return;
        if (this.input_msg.text.length == 0) return;
        this.socketTest.send(this.input_msg.text);
    }

    /** 关闭 */
    private onClose() {
        if (!this.socketTest.isConnecting) return;
        this.socketTest.close();
        this.clear();
    }

    /** 清空 */
    private onClear() {
        this.clear();
        this.updateMsgList();
    }

    /** 更新消息队列 */
    private updateMsgList(data: { key: number, value: string }[] = []) {
        this.btn_connection.disabled = this.socketTest.isConnecting();
        this.list_msgs.array = data;
        this.list_msgs.scrollTo(data.length - 1);
    }

    /** 刷新数组itemUI */
    private updateItem(cell: WsTestItem, index: number): void {
        cell.setLabel(this.list_msgs.array[index]);
    }
    
    /** 清空 */
    private clear() {
        this.msgList = null;
        this.socketTest.clear();
        this.btn_connection.disabled = this.socketTest.isConnecting();
    }
}
import { ui } from "../ui/layaMaxUI";
/** socket测试item类 */
export default class WsTestItem extends ui.WsTestItemUI {
    constructor() {
        super();
    }

    /** 设置文本 */
    public setLabel(data: { key: number, value: string }) {
        this.lab.text = data.value;
        this.lab.color = this.getColor(data.key);
    }
    
    /** 获取颜色 */
    private getColor(key: number): string {
        if (key) {
            return key == 2 ? "#0000ff" : "#ff0000";
        } else {
            return "#000000";
        }
    }
}
export class EventMgr {

	private static eventList: any = {};

	/**
	 * 添加事件侦听
	 * type 事件ID
	 * callback 事件处理
	 * thisObject 对象
	 */
	public static addEvent(type: number | string, callback: Function, thisObject: Object): void {
		if (this.eventList[type] == null) {
			this.eventList[type] = [];
		}
		// let num = this.eventList[type].length;
		if (!this.hasEvent(type, callback, thisObject)) {
			this.eventList[type].push({ "callback": callback, "thisObject": thisObject });
		}
	}

	/**
	 * 删除静态对象使用 其他对象使用(removeEventByObject)
	 * 【注意】 同一个类多个实例 回调函数值相同 (移除会移除监听列表 位置0的回调) 导致难以发现的bug
	 * 所以只用做静态对象的移除(静态对象 通常只会调用一次监听)
	 * 删除事件侦听
	 */
	public static removeStaticEvent(type: number | string, callback: Function): void {
		let events: { callback: Function, thisObject: Object }[] = this.eventList[type];
		if (events != null) {
			for (let i = events.length - 1; i >= 0; i--) {
				let evt = events[i];
				if (evt["callback"] == callback) {
					events.splice(i, 1);
				}
			}
			if (events.length == 0)
				delete this.eventList[type]
		}
	}

	/**
	 * 删除事件侦听
	 */
	public static removeEventByObject(type: number | string, thisObject: any): void {
		let events: { callback: Function, thisObject: any }[] = this.eventList[type];
		if (events != null) {
			for (let i = events.length - 1; i >= 0; i--) {
				let evt = events[i];
				if (evt["thisObject"] == thisObject) {
					events.splice(i, 1);
				}
			}
			if (events.length == 0)
				delete this.eventList[type]
		}
	}


	/**
	 * 派发事件
	 * type 事件ID
	 * data 自定义数据
	 */
	public static dispatchEvent(type: number | string, data: any = null) {
		let events = this.eventList[type];
		if (events) {
			let num: number = events.length;
			for (let i: number = 0; i < num; i++) {
				let evt = events[i];
				if (evt) {
					let callback: Function = <Function>evt["callback"];
					let obj: Object = evt["thisObject"];
					callback.apply(obj, [data]);
				}
			}
		}
	}

	/**
	 * 判断EventMgr是否有该事件的某个回调函数S
	 * type 事件ID
	 * callback 回调函数
	 */
	public static hasEvent(type: number | string, callback: Function, thisObject: any): boolean {
		let events = this.eventList[type];
		if (events != null) {
			let num: number = events.length;
			for (let i: number = 0; i < num; i++) {
				let evt = events[i];
				let cb: Function = <Function>evt["callback"];
				let obj: any = evt["thisObject"];
				if (cb == callback) {
					if (obj && thisObject && obj == thisObject)
						return true;
				}
			}
		}
		return false;
	}

}

服务端代码

  • WebSocket 类,封装WebSocketServer,同时暴露一些基础接口给外部调用。
  • Program 类,项目主入口。
using Fleck;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace webSocket
{
    public class WebSocket
    {
        /// <summary>
        /// socket连接池
        /// </summary>
        private List<IWebSocketConnection> connectSocketPool;
        /// <summary>
        /// WebSocketServer 对应实例
        /// </summary>
        private WebSocketServer server;
        /// <summary>
        /// 连接地址
        /// </summary>
        private const string wsLink = "ws://127.0.0.1:9999";
        /// <summary>
        /// 消息队列
        /// </summary>
        private List<KeyValuePair<string, string>> msgListPool;
        /// <summary>
        /// 最大消息队列数
        /// </summary>
        private int maxMsgCount;
        /// <summary>
        /// 连接器
        /// </summary>
        private IWebSocketConnection connection;
        public WebSocket()
        {
            connectSocketPool = new List<IWebSocketConnection>();
            server = new WebSocketServer(wsLink);
            msgListPool = new List<KeyValuePair<string, string>>();
            maxMsgCount = 100;
        }

        public void StartSocket()
        {
            if (server == null)
            {
                server = new WebSocketServer(wsLink);
            }
            server.Start(socket =>
            {
                this.connection = socket;
                this.connection.OnOpen = () =>
                {
                    Console.WriteLine("开启服务");

                    connectSocketPool.Add(this.connection);
                };

                this.connection.OnClose = () =>
                {
                    Console.WriteLine("关闭服务");

                    this.connection.Close();
                    connectSocketPool.Remove(this.connection);
                };

                this.connection.OnMessage = msg =>
                {
                    Console.WriteLine(msg);
                    if (msgListPool != null)
                    {
                        var time = new DateTime();

                        if (msgListPool.Count > maxMsgCount)
                            msgListPool.RemoveAt(0);

                        msgListPool.Add(new KeyValuePair<string, string>(time.ToString(), msg));
                    }

                    this.connection.Send($"这是接收到的消息{msg},这是返回的消息{"=========" + msg}");
                };

            });
        }

        public void StopSocket()
        {
            if (server != null && this.connection != null)
            {
                this.connection.Close();
                connectSocketPool.Remove(connection);
                msgListPool = null;
            }
        }
    }
}

using Fleck;

namespace webSocket
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var ws = new WebSocket();
            
            ws.StartSocket();

            Console.WriteLine("服务已开启,如需关闭请按下任意键...");
            Console.ReadLine();

            ws.StopSocket();
        }
    }
}

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值