1. 程式人生 > >基於websocket的網頁即時通訊(可傳附件圖片塗鴉、最小化狀態通知).NET,winform客戶端、服務端

基於websocket的網頁即時通訊(可傳附件圖片塗鴉、最小化狀態通知).NET,winform客戶端、服務端

公司網站需要即時通訊,就研究了下

主要以下功能:

websocket通訊,網頁端即時通訊,可以傳送表情,可以傳送附件,可以塗鴉,可以實現客服一對多聊天,winform做服務端負責收發,notification提醒,

一番百度下來發現websocket做客戶端+superwebsocket服務端比較靠譜

又想最小化無法發現訊息,加了notification(localhost可以但是域名需要https,呵呵噠)

想著乾脆做個winform客戶端接收提醒?似乎superwebsocket支援的不好

或者存資料庫,獨立winform socket處理?

先上圖?:


下午又完善了聊天視窗,以及用&拆分不太合適,改用“|”分割


今天有空又更新了微笑,winform客戶端上線,BS2CS


20180521更新

優化介面顯示、實現客服單對多會話檢測、建立

客服winform暫時放棄,會話在資料庫儲存、附件在伺服器儲存需要另外指定具體刪除規則(兩日一次之類)

目前為止實驗階段,為達目的不擇手段,程式碼亂是可以想見的,後面會整理


以下思路:

1.SuperWebSocket做服務端監聽並轉發

    ①.NuGet安裝SuperWebSocket

    ②.NuGet安裝log4net

using System;
using System.Configuration;
using SuperWebSocket;
using System.Windows.Forms;
using System.Collections.Generic;
namespace cmd
{

    class socketServer_d
    {
        private const string ip = "192.168.1.72";
        private const int port = 2018;
        private static WebSocketServer ws = null;
        private static TextBox textBox;
        private static Dictionary<string, WebSocketSession> sessions = new Dictionary<string, WebSocketSession>();//記住所有會話
        public socketServer_d(TextBox t){
            textBox = t;
            ws = new WebSocketServer();//例項化WebSocketServer  
            ws.NewSessionConnected += ws_NewSessionConnected;//有新會話握手並連線成功  
            ws.SessionClosed += ws_SessionClosed;//有會話被關閉 可能是服務端關閉 也可能是客戶端關閉  
            ws.NewMessageReceived += ws_NewMessageReceived;//有客戶端傳送新的訊息  
            Start(); 
        }
        static void ws_NewSessionConnected(WebSocketSession session)
        {
            textBox.AppendText(DateTime.Now + "  與客戶端:" + session.RemoteEndPoint.Address + "建立新會話\r\n");
        }

        static void ws_SessionClosed(WebSocketSession session, SuperSocket.SocketBase.CloseReason value)
        {
            foreach (string item in sessions.Keys)
            { 
                if (sessions[item] == session) {
                    sessions.Remove(item);
                }
            }
            textBox.AppendText(DateTime.Now + "  與客戶端:" + session.RemoteEndPoint.Address + "的會話被關閉 原因:" + value + "\r\n");
        }

        static void ws_NewMessageReceived(WebSocketSession session, string value)
        {
            var msg = string.Format("{0:HH:MM:ss} {1}: {2}", DateTime.Now, session.RemoteEndPoint.Address, value)+ "\r\n";
            textBox.AppendText(msg+"\r\n");
            //確認連線是否成功
            if (value.StartsWith("try_connect"))
            {
                string from = value.Split('&')[1].Split('=')[1];
                string to = value.Split('&')[2].Split('=')[1];
                if (from == ""  )
                {
                    session.Send("-1");
                    return;
                }
                sessions.Add(from, session);//記錄會話☆
                session.Send("1");
                return;
            }

            //返回給客戶端  客戶端接收xx.data就可以了  
            string message = "";//0未收到,1確認連線,2訊息傳送
            try
            {
                if (string.IsNullOrEmpty(value))
                { 
                    session.Send("0");
                    return;
                }
                else
                {
                    //解析訊息
                    if (value.StartsWith("msg_send"))
                    {
                        string from = value.Split('&')[1].Split('=')[1];
                        string to = value.Split('&')[2].Split('=')[1];
                        string text = value.Split('&')[3].Substring(5, value.Split('&')[3].Length-5);
                        if (from == "" || to == "")
                        {
                            session.Send("-1");
                            return;
                        }
                        else
                        {
                            WebSocketSession session_rec = sessions[to];
                            if (session_rec==null)
                            {
                                session.Send("-1");
                                return;
                            }
                            else
                            {
                                session_rec.Send("server_conversation&from="+from+"&text="+text);
                            }
                        } 
                        sessions.Add(from, session);//記錄會話☆
                        session.Send("1");
                        return;
                    }
                    message = "2";
                }
            }
            catch (Exception ex)
            {
                message = ex.Message;
            }
            if (!string.IsNullOrEmpty(message))
            {
                //傳送訊息到指定物件(socket會話)
                session.Send(message);
            }
        }

        /// <summary>  
        /// 啟動服務  
        /// </summary>  
        /// <returns></returns>  
        public static void Start()
        {
            if (!ws.Setup(ip, port))
            { 
                textBox.AppendText("WebSocketServer 設定WebSocket服務偵聽地址失敗" + "\r\n");
                return;
            }
            if (!ws.Start())
            {
                textBox.AppendText("WebSocketServer 啟動WebSocket服務偵聽失敗" + "\r\n");
                return;
            }
            textBox.AppendText("WebSocketServer 啟動服務成功" + "\r\n");
        }

        /// <summary>  
        /// 停止偵聽服務  
        /// </summary>  
        public static void Stop()
        {
            if (ws != null)
            {
                ws.Stop();
            }
        }
    }
}

自己定了個簡單的傳送接收規則(說起來就是協議原形吧微笑),程式碼有點亂,服務端基於收發規則、會話管理其實應該抽出一個管理Class,客戶端亦可,我笨就不搞了,你們來吧。

1.WebSocket客戶端

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>

<html>
<head>
    <meta charset="utf-8">
    <title>WebSocket</title>
    <%-- UeDITOR --%>
    <script type="text/javascript" charset="gbk" src="js/ueditor.config.js"></script>
    <script type="text/javascript" charset="gbk" src="js/ueditor.all.min.js"> </script>
    <!--建議手動加在語言,避免在ie下有時因為載入語言失敗導致編輯器載入失敗-->
    <!--這裡載入的語言檔案會覆蓋你在配置專案裡新增的語言型別,比如你在配置專案裡配置的是英文,這裡載入的中文,那最後就是中文-->
    <script type="text/javascript" charset="gbk" src="js/lang/zh-cn/zh-cn.js"></script>

    



    <script type="text/javascript">
        // 開啟一個 web socket
        var ws ;
        window.onload = function () {
            // 獲得通知許可權
            Notification.requestPermission();
             
            WebSocketTest();
        }
        function WebSocketTest() {
            ws = new WebSocket("ws://192.168.1.72:2018");
            var ifconnected = false;
            if ("WebSocket" in window) {
               //alert("您的瀏覽器支援 WebSocket!");
                 
                ws.onopen = function () {
                    // Web Socket 已連線上,使用 send() 方法傳送資料
                    ws.send("try_connect&from=<%=from%>&to=<%=to%>");
                };
                ws.onmessage = function (evt) {
                    var received_msg = evt.data;
                    if (received_msg=="1") {
                        ifconnected = true;
                        document.getElementById("d_con").style.backgroundColor = "green";
                    } else {
                        if (received_msg.split("&")[0] == "server_conversation") {
                            document.getElementById("d_con").style.backgroundColor = "green";
                            //頁面顯示
                            document.getElementById("d_ul").innerHTML = document.getElementById("d_ul").innerHTML + "<li style='color:red'>" + received_msg.split("&")[1].split("=")[1] + ":" + received_msg.split("&")[2].substring(5) + "</li>"
                            //彈窗
                            NoticeWindow(received_msg.split("&")[1].split("=")[1], received_msg.split("&")[2].split("=")[1]);
                        }
                    }
                }; 
                ws.onclose = function () {
                    // 關閉 websocket
                    ifconnected = false;
                    document.getElementById("d_con").style.backgroundColor = "red";
                };
            }
            else {
                // 瀏覽器不支援 WebSocket
                document.getElementById("d_con").style.backgroundColor = "red";
                ifconnected = false;
            }
        }
        //傳送訊息
        function sendMsg() {
            //取值
            var from = document.getElementById("d_from").value;
            var text = UE.getEditor('d_text').getContent();
            //清空輸入
            UE.getEditor('d_text').setContent('');


            var to = document.getElementById("d_to").value;
            //alert(text)
            document.getElementById("d_ul").innerHTML = document.getElementById("d_ul").innerHTML + "<li style='color:green'>"+from+":" + text + "</li>"
            var tranStr = "msg_send&from=" + from + "&to=" + to + "&text=" + text;
            ws.send(tranStr);
        }
        function NoticeWindow(from,msg) { 
            new Notification(from+":", {
                body: msg+"<div style='width:100px;height:100px;border-radius:50px;background-color:red'></div>",
                    icon: 'https://avatars6.githubusercontent.com/u/496048?v=4&s=460'
                }); 
        }
        function HTMLEncode(html) {
            var temp = document.createElement("div");
            (temp.textContent != null) ? (temp.textContent = html) : (temp.innerText = html);
            var output = temp.innerHTML;
            temp = null;
            return output;
        }
    </script>

</head>
<body>
    <form runat="server">
    <div id="sse">
        連結狀態:<p id="d_con" style="width:10px;height:10px;background-color:gray;border-radius:5px"></p> 
        <table>
            <tr><td>編    號:</td><td><input runat="server" type="text" id="d_from"  /></td></tr>
            <tr>
                <td>
                    內    容:
                </td>
                <td>
                <script id="d_text" type="text/plain" style="width:380px;height:300px;"></script>
                                </td> 
            </tr>
            <tr><td>接受編號:</td><td><input runat="server" type="text" id="d_to" /></td></tr>
            <tr>
                <td colspan="2"><a href="javascript:sendMsg()">傳送</a></td> 
            </tr>
        </table> 
        <ul id="d_ul">
            
        </ul>
    </div> 
        <button id="button">
            彈窗
        </button>
        </form>
</body>
    <script type="text/javascript">

        //例項化編輯器
        //建議使用工廠方法getEditor建立和引用編輯器例項,如果在某個閉包下引用該編輯器,直接呼叫UE.getEditor('editor')就能拿到相關的例項
        var ue = UE.getEditor('d_text');
        </script>
</html>

編輯內容通過UEditor(自帶表情、塗鴉、附件簡直無敵好嗎生氣

注意點:socket不能走80埠具體多少範圍我也不研究了微笑