c# SuperWebSocket服務(wù)端、html WebSocket客戶端、c# WebSocket4Net客戶端的使用
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
c# superwebsocket服務(wù)端可以寄宿在控制臺(tái)程序、窗體程序、Windows服務(wù) c# websocket客戶端可以是控制臺(tái)程序、窗體程序、Windows服務(wù)、html、手機(jī),能連上websocket的就行了 服務(wù)端可以開啟wss安全鏈接 如果想做即時(shí)通訊,雙工通訊,或者因?yàn)轭l繁發(fā)送http請(qǐng)求導(dǎo)致的web服務(wù)器承受不住,都可以轉(zhuǎn)用websocket websocket 有良好的交互體驗(yàn),快速的數(shù)據(jù)傳輸 websocket 開發(fā)簡(jiǎn)單,部署方便,上手沒難度 我這里展示了,把websocket服務(wù)端寄宿在Windows服務(wù)里,客戶端則用控制臺(tái)訪問和用web訪問,調(diào)試階段,為了避免頻繁停啟Windows服務(wù),所以我用控制臺(tái)啟動(dòng)了服務(wù)端,生產(chǎn)環(huán)境就可以直接把exe執(zhí)行文件注冊(cè)成服務(wù)了 底部有源代碼百度盤下載,Windows服務(wù)安裝方法 1.服務(wù)端 把websocket服務(wù)端類,拷貝到項(xiàng)目里 using SuperSocket.SocketBase; using SuperSocket.SocketBase.Config; using SuperSocket.SocketEngine; using SuperWebSocket; using System; using System.Threading; using System.Threading.Tasks; //install-package SuperWebSocket //install-package nlog //install-package nlog.config namespace WebSocketService { public class WSocketServer:IDisposable { public static NLog.Logger _Logger = NLog.LogManager.GetCurrentClassLogger(); #region 向外傳遞數(shù)據(jù)事件 public event Action<WebSocketSession, string> MessageReceived; public event Action<WebSocketSession> NewConnected; public event Action<WebSocketSession> Closed; #endregion public WebSocketServer WebSocket; Thread _thread; bool _isRunning = true; public WSocketServer() { } #region WebSockertServer /// <summary> /// 開啟服務(wù)端 /// </summary> /// <param name="port"></param> /// <param name="serverName"></param> /// <param name="isUseCertificate"></param> /// <param name="serverStoreName"></param> /// <param name="serverSecurity"></param> /// <param name="serverThumbprint"></param> /// <returns></returns> public bool Open(int port, string serverName, bool isUseCertificate = false, string serverStoreName = "", string serverSecurity = "", string serverThumbprint = "") { bool isSetuped = false; try { this.WebSocket = new WebSocketServer(); var serverConfig = new ServerConfig { Name = serverName, MaxConnectionNumber = 10000, //最大允許的客戶端連接數(shù)目,默認(rèn)為100。 Mode = SocketMode.Tcp, Port = port, //服務(wù)器監(jiān)聽的端口。 ClearIdleSession = false, //true或者false, 是否清除空閑會(huì)話,默認(rèn)為false。 ClearIdleSessionInterval = 120,//清除空閑會(huì)話的時(shí)間間隔,默認(rèn)為120,單位為秒。 ListenBacklog = 10, ReceiveBufferSize = 64 * 1024, //用于接收數(shù)據(jù)的緩沖區(qū)大小,默認(rèn)為2048。 SendBufferSize = 64 * 1024, //用戶發(fā)送數(shù)據(jù)的緩沖區(qū)大小,默認(rèn)為2048。 KeepAliveInterval = 1, //keep alive消息發(fā)送時(shí)間間隔。單位為秒。 KeepAliveTime = 60, //keep alive失敗重試的時(shí)間間隔。單位為秒。 SyncSend = false }; SocketServerFactory socketServerFactory = null; //開啟wss 使用證書 if (isUseCertificate) { serverConfig.Security = serverSecurity; serverConfig.Certificate = new SuperSocket.SocketBase.Config.CertificateConfig { StoreName = serverStoreName, StoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine, Thumbprint = serverThumbprint }; socketServerFactory = new SocketServerFactory(); } isSetuped = this.WebSocket.Setup(new RootConfig(),serverConfig, socketServerFactory); if (isSetuped) { _Logger.Info("Setup Success..."); } else { _Logger.Error("Failed to setup!"); } this.WebSocket.NewSessionConnected += NewSessionConnected; this.WebSocket.NewMessageReceived += NewMessageReceived; this.WebSocket.SessionClosed += SessionClosed; isSetuped = this.WebSocket.Start(); if (isSetuped) { _Logger.Info("Start Success..."); _Logger.Info("Server Listen at " + this.WebSocket.Listeners[0].EndPoint.Port.ToString()); this._isRunning = true; this._thread = new Thread(new ThreadStart(ProcessMaintainance)); this._thread.Start(); } else { _Logger.Error("Failed to start!"); } } catch (Exception ex) { _Logger.Error(ex.ToString()); } return isSetuped; } /// <summary> /// 消息觸發(fā)事件 /// </summary> /// <param name="session"></param> /// <param name="value"></param> void NewMessageReceived(WebSocketSession session, string value) { try { _Logger.Info("Receive:" + value.ToString() + " ClientIP:" + session.RemoteEndPoint); if(value.ToString().Equals("IsHere**"))//客戶端定時(shí)發(fā)送心跳,維持鏈接 { return; } else { MessageReceived?.Invoke(session, value.ToString()); } } catch (Exception e) { _Logger.Error(e.ToString()); } } /// <summary> /// 新鏈接觸發(fā)事件 /// </summary> /// <param name="session"></param> void NewSessionConnected(WebSocketSession session) { try { string message = string.Format("New Session Connected:{0}, Path:{1}, Host:{2}, IP:{3}", session.SessionID.ToString(), session.Path, session.Host, session.RemoteEndPoint); _Logger.Info(message); NewConnected?.Invoke(session); } catch (Exception e) { _Logger.Error(e.ToString()); } } /// <summary> /// 客戶端鏈接關(guān)閉觸發(fā)事件 /// </summary> /// <param name="session"></param> /// <param name="value"></param> void SessionClosed(WebSocketSession session, CloseReason value) { string message = string.Format("Session Close:{0}, Path:{1}, IP:{2}", value.ToString(), session.Path,session.RemoteEndPoint); _Logger.Info(message); Closed?.Invoke(session); } #endregion /// <summary> /// 關(guān)閉服務(wù)端觸發(fā)事件 /// </summary> public void Dispose() { this._isRunning = false; foreach (WebSocketSession session in this.WebSocket.GetAllSessions()) { session.Close(); } try { this.WebSocket.Stop(); } catch { } } /// <summary> /// 輸出實(shí)時(shí)連接線程 /// </summary> void ProcessMaintainance() { do { try { _Logger.Debug("Display Session Info:" + this.WebSocket.SessionCount); foreach (WebSocketSession session in this.WebSocket.GetAllSessions()) { string message = string.Format("ID:{0}, Remote:{1}, Path:{2}, LastActiveTime:{3}, StartTime:{4}", session.SessionID, session.RemoteEndPoint, session.Path , session.LastActiveTime, session.StartTime); _Logger.Debug(message); } } catch (Exception e) { _Logger.Error(e.ToString()); } System.Threading.Thread.Sleep(5 * 60000); } while (this._isRunning); } /// <summary> /// 發(fā)送消息 /// </summary> /// <param name="session">客戶端連接</param> /// <param name="message">消息內(nèi)容</param> public void SendMessage(WebSocketSession session, string message) { Task.Factory.StartNew(() =>{if (session != null && session.Connected) session.Send(message);}); } } } 打開控制臺(tái) 按順序安裝這幾個(gè)包 install-package SuperWebSocket install-package nlog install-package nlog.config 然后寫個(gè)業(yè)務(wù)邏輯類,操作websocket服務(wù)端類 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WebSocketService { public class BLL { WSocketServer _server = null; bool _isRunning = false; public BLL() { try { _server = new WSocketServer(); _server.MessageReceived += Server_MessageReceived; _server.NewConnected += Server_NewConnected; _server.Closed += _server_Closed; } catch (Exception ex) { WSocketServer._Logger.Error(ex.ToString()); } } private void _server_Closed(SuperWebSocket.WebSocketSession obj) { Console.WriteLine($"Closed {System.Web.HttpUtility.UrlDecode(obj.Path, System.Text.Encoding.UTF8)}"); } private void Server_NewConnected(SuperWebSocket.WebSocketSession obj) { //對(duì)新鏈接做處理,驗(yàn)證鏈接是否合法等等,不合法則關(guān)閉該鏈接 //新鏈接進(jìn)行數(shù)據(jù)初始化 Console.WriteLine($"NewConnected {System.Web.HttpUtility.UrlDecode(obj.Path, System.Text.Encoding.UTF8)}"); } private void Server_MessageReceived(SuperWebSocket.WebSocketSession arg1, string arg2) { //接收到客戶端鏈接發(fā)送的東西 Console.WriteLine($"from {System.Web.HttpUtility.UrlDecode(arg1.Path, System.Text.Encoding.UTF8)} => {arg2}"); } public bool Start() { _isRunning = true; //設(shè)置監(jiān)聽端口 var result = _server.Open(1234, "MySocket"); //模擬 服務(wù)端主動(dòng)推送信息給客戶端 if (result) { Task.Factory.StartNew(() => { while (_isRunning) { foreach (var item in _server.WebSocket.GetAllSessions()) _server.SendMessage(item,"服務(wù)器時(shí)間:"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); System.Threading.Thread.Sleep(1000); } }); } return result; } public void Stop() { _isRunning = false; _server.Dispose(); } } } 然后就是在Windows服務(wù)停啟時(shí)操作業(yè)務(wù)邏輯 好了 一個(gè)c# websocket 服務(wù)端搭建完成了 因?yàn)檫@里是通過控制臺(tái)來調(diào)用調(diào)試這個(gè)服務(wù),所以要再建一個(gè)控制臺(tái)項(xiàng)目 然后選中控制臺(tái)項(xiàng)目,ctrl+f5,啟動(dòng)項(xiàng)目,如果沒什么問題,那么就搞定了,注意websocket監(jiān)聽的端口是可用的,我這里用了1234 2.客戶端-控制臺(tái) 接下來就建個(gè)控制臺(tái)項(xiàng)目作為客戶端,去連接服務(wù)端 新建控制臺(tái)項(xiàng)目,把websocket客戶端類拷進(jìn)去 using SuperSocket.ClientEngine; using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; using WebSocket4Net; //install-package WebSocket4Net //install-package nlog //install-package nlog.config namespace WebSocketClient { public class WSocketClient:IDisposable { //日志管理 public static NLog.Logger _Logger = NLog.LogManager.GetCurrentClassLogger(); #region 向外傳遞數(shù)據(jù)事件 public event Action<string> MessageReceived; #endregion WebSocket4Net.WebSocket _webSocket; /// <summary> /// 檢查重連線程 /// </summary> Thread _thread; bool _isRunning = true; /// <summary> /// WebSocket連接地址 /// </summary> public string ServerPath { get; set; } public WSocketClient(string url) { ServerPath = url; this._webSocket = new WebSocket4Net.WebSocket(url); this._webSocket.Opened += WebSocket_Opened; this._webSocket.Error += WebSocket_Error; this._webSocket.Closed += WebSocket_Closed; this._webSocket.MessageReceived += WebSocket_MessageReceived; } #region "web socket " /// <summary> /// 連接方法 /// <returns></returns> public bool Start() { bool result = true; try { this._webSocket.Open(); this._isRunning = true; this._thread = new Thread(new ThreadStart(CheckConnection)); this._thread.Start(); } catch (Exception ex) { _Logger.Error(ex.ToString()); result = false; } return result; } /// <summary> /// 消息收到事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void WebSocket_MessageReceived(object sender, MessageReceivedEventArgs e) { _Logger.Info(" Received:" +e.Message); MessageReceived?.Invoke(e.Message); } /// <summary> /// Socket關(guān)閉事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void WebSocket_Closed(object sender, EventArgs e) { _Logger.Info("websocket_Closed"); } /// <summary> /// Socket報(bào)錯(cuò)事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void WebSocket_Error(object sender, ErrorEventArgs e) { _Logger.Info("websocket_Error:" + e.Exception.ToString()); } /// <summary> /// Socket打開事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void WebSocket_Opened(object sender, EventArgs e) { _Logger.Info(" websocket_Opened"); } /// <summary> /// 檢查重連線程 /// </summary> private void CheckConnection() { do { try { if (this._webSocket.State != WebSocket4Net.WebSocketState.Open && this._webSocket.State != WebSocket4Net.WebSocketState.Connecting) { _Logger.Info(" Reconnect websocket WebSocketState:" + this._webSocket.State); this._webSocket.Close(); this._webSocket.Open(); Console.WriteLine("正在重連"); } } catch (Exception ex) { _Logger.Error(ex.ToString()); } System.Threading.Thread.Sleep(5000); } while (this._isRunning); } #endregion /// <summary> /// 發(fā)送消息 /// </summary> /// <param name="Message"></param> public void SendMessage(string Message) { Task.Factory.StartNew(() => { if (_webSocket != null && _webSocket.State == WebSocket4Net.WebSocketState.Open) { this._webSocket.Send(Message); } }); } public void Dispose() { this._isRunning = false; try { _thread.Abort(); } catch { } this._webSocket.Close(); this._webSocket.Dispose(); this._webSocket = null; } } } 同樣打開包管理控制臺(tái),順序安裝這幾個(gè)必要的包,注意選擇安裝的項(xiàng)目名字 install-package WebSocket4Net install-package nlog install-package nlog.config 裝完就可以開始連接服務(wù)端了 3.客戶端-網(wǎng)頁 <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title></title> </head> <body> <div id="onMessage" style="width:100%;height:200px;overflow-y:scroll;"> </div> <input type="text" id="input" /><input id="sendMessage" type="button" value="發(fā)送" /> </body> </html> <script> //websocket 類 var myWS = function () { this.ws = null; this.url = null; this.onmessage = function () { }; this.onopen = function () { }; this.onclose = function () { }; this.onerror = function () { }; }; myWS.prototype.sendMessage = function (message) {//客戶端發(fā)送消息到服務(wù)器方法 if (this.ws != null && this.ws.readyState == 1) { this.ws.send(message); } }; myWS.prototype.start = function () {//啟動(dòng)鏈接 this.connect(); this.conncheckstatus(); this.heartbeat(); }; //心跳 myWS.prototype.heartbeat = function () { var obj = this; setTimeout(function () { if (obj.ws != null && obj.ws.readyState == 1) { var message = "IsHere**"; obj.ws.send(message); } setTimeout(arguments.callee, 300000); }, 10); }; myWS.prototype.conncheckstatus = function () { var obj = this; setTimeout(function () { if (obj.ws != null && obj.ws.readyState != 0 && obj.ws.readyState != 1) { obj.connect(); } setTimeout(arguments.callee, 5000); }, 15); }; myWS.prototype.connect = function () { this.disconnect(); //WebSocket地址 if (this.url != null && this.url != "") { try { if ("WebSocket" in window) { this.ws = new WebSocket(this.url); } else if ("MozWebSocket" in window) { this.ws = new MozWebSocket(this.url); } else { alert("browser not support websocket"); } if (this.ws != null) { var that = this; this.ws.onopen = function (event) { that.onopen(event); }; this.ws.onmessage = function (event) { that.onmessage(event); }; this.ws.onclose = function (event) { that.onclose(event); }; this.ws.onerror = function (event) { that.onerror(event); }; } } catch (ex) { console.log("connect:" + ex); } } }; myWS.prototype.disconnect = function () { if (this.ws != null) { try { this.ws.close(); this.ws = null; } catch (ex) { this.ws = null; } } }; </script> <script> var _ws = new myWS(); _ws.url = ''ws://192.168.1.13:1234/lcj網(wǎng)頁''; //注冊(cè)接收服務(wù)端消息的方法 服務(wù)端數(shù)據(jù)位于event.data字段 _ws.onmessage = (event) => { document.getElementById(''onMessage'').innerHTML += event.data + ''<br/>''; document.getElementById(''onMessage'').scrollTop = document.getElementById(''onMessage'').scrollHeight; }; //啟動(dòng)鏈接 _ws.start(); document.getElementById(''sendMessage'').addEventListener(''click'', () => { //客戶端發(fā)送消息到服務(wù)端 _ws.sendMessage(document.getElementById(''input'').value); document.getElementById(''input'').value = ''''; }); </script> 完成… Windows服務(wù)安裝方法 1.把項(xiàng)目模式改成發(fā)布模式Release 2.右鍵項(xiàng)目生成或者重新生成 3.打開項(xiàng)目位置,打開bin文件夾,打開Release文件夾 4.把服務(wù)必備的一些組件都復(fù)制走 安裝完成,在計(jì)算機(jī)管理-服務(wù)里面可以看到,你的服務(wù)名字,還沒啟動(dòng),需要手動(dòng)開啟,或者電腦重啟后自動(dòng)開啟 右鍵服務(wù)-選擇第一個(gè)“啟動(dòng)” 卸載服務(wù),先打開服務(wù)管理,右鍵,停止,然后復(fù)制刪除命令到cmd執(zhí)行 刷新一下服務(wù)列表,服務(wù)已經(jīng)刪除 如果想要更新服務(wù)的話,要先停止服務(wù),然后把生成后的新dll啊或者exe,覆蓋掉舊的就可以了,修改配置文件的話也需要重新啟動(dòng)服務(wù)才能生效,停止服務(wù)需要一點(diǎn)時(shí)間,不能一點(diǎn)停止就立即覆蓋,系統(tǒng)會(huì)報(bào)文件占用 所有步驟都完成了 這里是安裝服務(wù)的工具和命令 鏈接:下載 WSS 開啟介紹 各參數(shù)說明,這里可以有兩種方式使用證書 1.是填證書安裝后的存儲(chǔ)區(qū)名字和證書的指紋 2.是直接填證書的所在路徑,和密碼 目前啊 我只成功地用過路徑,和密碼的方式開啟, 關(guān)于怎么用指紋,我還是不知道,如有哪位知道的,望相告. 另外開了wss后,需要用域名來訪問,ip不行 (以上結(jié)論來于,阿里云免費(fèi)試用服務(wù)器個(gè)人版 win server 2008 r2 和免費(fèi)國(guó)外域名 和 阿里云上申請(qǐng)的免費(fèi) ssl證書…) /// <summary> /// 開啟服務(wù)端 /// </summary> /// <param name="port">123</param> /// <param name="serverName">test</param> /// <param name="isUseCertificate">true 如果開啟wss需要用域名訪問(wss:\\abc.com:123\)</param> /// <param name="certificateStoreName">My -證書安裝后的存儲(chǔ)位置(一般是“個(gè)人”)</param> /// <param name="security">ssl/tls</param> /// <param name="certificateThumbprint">7384a6027fa8004585c0958c7fcbcb8fd9cd27fb 證書指紋 如果指紋填空,則使用路徑加載證書,否則使用指紋</param> /// <param name="certificatePath">D:\1774183_xxx_iis\1774183_xxx.pfx 證書所在路徑</param> /// <param name="certificatePwd">ABC 證書密碼</param> /// <returns></returns> public bool Open(int port, string serverName, bool isUseCertificate = false, string certificateStoreName = "", string security = "", string certificateThumbprint = "",string certificatePath="",string certificatePwd="") { bool isSetuped = false; try { this.WebSocket = new WebSocketServer(); var serverConfig = new ServerConfig { Name = serverName, MaxConnectionNumber = 10000, //最大允許的客戶端連接數(shù)目,默認(rèn)為100。 Mode = SocketMode.Tcp, Port = port, //服務(wù)器監(jiān)聽的端口。 ClearIdleSession = false, //true或者false, 是否清除空閑會(huì)話,默認(rèn)為false。 ClearIdleSessionInterval = 120,//清除空閑會(huì)話的時(shí)間間隔,默認(rèn)為120,單位為秒。 ListenBacklog = 10, ReceiveBufferSize = 64 * 1024, //用于接收數(shù)據(jù)的緩沖區(qū)大小,默認(rèn)為2048。 SendBufferSize = 64 * 1024, //用戶發(fā)送數(shù)據(jù)的緩沖區(qū)大小,默認(rèn)為2048。 KeepAliveInterval = 1, //keep alive消息發(fā)送時(shí)間間隔。單位為秒。 KeepAliveTime = 60, //keep alive失敗重試的時(shí)間間隔。單位為秒。 SyncSend = false }; SocketServerFactory socketServerFactory = null; //開啟wss 使用證書 if (isUseCertificate) { serverConfig.Security = security; //指紋不為空 則使用指紋,否則使用路徑訪問證書 if (certificateThumbprint != string.Empty) { serverConfig.Certificate = new SuperSocket.SocketBase.Config.CertificateConfig { StoreName = certificateStoreName, StoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine, Thumbprint = certificateThumbprint }; } else { serverConfig.Certificate = new SuperSocket.SocketBase.Config.CertificateConfig { FilePath = certificatePath, Password = certificatePwd }; } socketServerFactory = new SocketServerFactory(); } isSetuped = this.WebSocket.Setup(new RootConfig(),serverConfig, socketServerFactory); if (isSetuped) { _Logger.Info("Setup Success..."); } else { _Logger.Error("Failed to setup!"); } this.WebSocket.NewSessionConnected += NewSessionConnected; this.WebSocket.NewMessageReceived += NewMessageReceived; this.WebSocket.SessionClosed += SessionClosed; isSetuped = this.WebSocket.Start(); if (isSetuped) { _Logger.Info("Start Success..."); _Logger.Info("Server Listen at " + this.WebSocket.Listeners[0].EndPoint.Port.ToString()); this._isRunning = true; this._thread = new Thread(new ThreadStart(ProcessMaintainance)); this._thread.Start(); } else { _Logger.Error("Failed to start!"); } } catch (Exception ex) { _Logger.Error(ex.ToString()); } return isSetuped; } 這里我使用配置文件 源碼地址鏈接:下載
該文章在 2019/11/12 17:33:27 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |