[C#]使用WebSocket進行廣播功能 - 透過WebSocketSharp

  • 9845
  • 0
  • C#
  • 2021-11-14

[C#]使用WebSocket進行廣播功能 - 透過WebSocketSharp

WebSocket 是 HTML5 開始提供的一種瀏覽器與伺服器間進行全雙工通訊的網路技術,通訊協定被 IETF 定為標準 RFC 6455

Client與Server之間只需要做過一次handshark兩端邊建立起一條TCP的通道,兩者之間就能進行資料的傳輸

自己動手實作 RFC 6455其實是累人的一件事情, 在Go專案上如果要自己造輪子實為非常累人,

建議還是要花點時間了解RFC6455

今天要介紹的是使用C#實作一個廣播中心 - 透過WebSocketSharp這一個套件

目錄結構如下


Server 端程式

1. MessageQueueSingleton.cs為單例模式的一個佇列存取類別, 提供廣播訊息的存入與取出

using System;
using System.Collections.Concurrent;

namespace WS.Demo.Controller {
    public class MessageQueueSingleton {
        private static volatile MessageQueueSingleton _instance;
        private static object _lockObj = new object();
        private static ConcurrentQueue<string> _msgQueue;
        private MessageQueueSingleton() { }
        public static MessageQueueSingleton Instance() {
            if (_instance == null) {
                lock (_lockObj) {
                    if (_instance == null) {
                        _instance = new MessageQueueSingleton();
                        _msgQueue = new ConcurrentQueue<string>();
                    }
                }
            }
            return _instance;
        }

        public void AddMsg() {
            try {
                _msgQueue.Enqueue(DateTime.Now.ToString("yyyyMMddHHmmssfff"));
            } catch (Exception ex) {
                //do something
            }
        }

        public void GetMsg(out string msg) {
            msg = null;
            try {
                lock (_lockObj) {
                    if (!_msgQueue.TryDequeue(out msg)) {
                        msg = null;
                    }
                }
            } catch (Exception ex) {
                //do something
            }
        }
        public void FreeQueue() {
            lock (_lockObj) {
                string temp;
                while (_msgQueue.TryDequeue(out temp)) { 
                }
            }
        }
    }
}

2. BroadcastBehavior.cs  WebSocket Server 針對廣播功能的行為定義類別

using System.Text;
using System.Threading;
using WebSocketSharp.Server;

namespace WS.Demo.Controller {
    public class BroadcastBehavior : WebSocketBehavior {
        /// <summary>
        /// 建構子
        /// </summary>
        public BroadcastBehavior() { }

        /// <summary>
        /// WebSocket會話建立後調用Fn
        /// </summary>
        protected override void OnOpen() {

            /***
             * 一直從MessageQueueSingleton取出字串,正確取得就進行廣播
             ***/
            while (true) {
                string msg = null;
                MessageQueueSingleton.Instance().GetMsg(out msg);
                if (msg != null) {
                    Sessions.BroadcastAsync(Encoding.UTF8.GetBytes(msg), null);
                }
                Thread.Sleep(1);
            }
        }
    }
}

3. Program.cs 程式進入點

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using WS.Demo.Controller;
using WebSocketSharp.Server;


namespace WS.Demo {
    class Program {
        static void Main(string[] args) {
            //每250秒加入一個廣播訊息至MessageQueue
            var msgCreater = new Thread(() => {
                while (true) {
                    MessageQueueSingleton.Instance().AddMsg();
                    Thread.Sleep(250);
                }
               });
            msgCreater.Start();

            //WebSocketServer 監聽 PORT 55688
            var wssv = new WebSocketServer(55688);
            wssv.AddWebSocketService<BroadcastBehavior>("/Broadcast");
            wssv.Start();
            if (wssv.IsListening) {
                Console.WriteLine("Listening on port {0}, and providing WebSocket services:", wssv.Port);
                foreach (var path in wssv.WebSocketServices.Paths) {
                    Console.WriteLine("- {0}", path);
                }
            }

            //WebSocketServer 停止行為
            Console.WriteLine("\nPress Enter key to stop the server...");
            Console.ReadLine();
            MessageQueueSingleton.Instance().FreeQueue(); //清除MessageQueue內所有資料
            msgCreater.Abort();// 停用廣播訊息產生執行緒
            wssv.Stop();

        }
    }
}

Client端 程式

<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

</head>
<body>
    <div id="app">
        <input type="button" value="連線" v-on:click="_fnConnect">
        <input type="button" value="關閉" v-on:click="_fnClose"><br />
        Status : {{ status }}<br />
        Revice count : {{ reciveCount }}<br />
        Revice Msg : <br />
        {{ message }}
    </div>

    <script>
        var websocket; //websocket物件
    var app = new Vue({
        el: '#app',
        data: {
            message: '',
            reciveCount: 0,
            status: 'Disconnected',
            WS_Uri:'ws://localhost:55688/Broadcast/'
        }, methods: {
            _fnConnect: function () {
                websocket = new WebSocket(this.WS_Uri);
                websocket.binaryType = "arraybuffer";
                //會話建立後事件
                websocket.onopen = function (evt) {
                    app._data.status = 'Connected';
                };
                //會話結束後事件
                websocket.onclose = function (evt) {
                    app._data.status = 'Disconnected';
                };
                //接收到訊息後事件
                websocket.onmessage = function (evt) {
                    app._data.reciveCount += 1;
                    app._data.message = new TextDecoder("utf-8").decode(evt.data);;
                };
                //發生錯誤後事件
                websocket.onerror = function (evt) {
                    app._data.status = evt.data;
                };
            },
            _fnClose: function () {
                //會話關閉
                websocket.close(); 
            }
        }

    })
    </script>
</body>
</html>

Demo

https://drive.google.com/file/d/1EX1Pifqq86B8S7Rp9eTUeAGL5zIH-U6P/view?usp=sharing

Download

https://drive.google.com/file/d/1DDHI2Y8HQ_z_cgTftiHsbwcT0Yv5l-G6/view?usp=sharing

egan2608@gmail.com