基於websocket的網頁即時通訊(可傳附件圖片塗鴉、最小化狀態通知).NET,winform客戶端、服務端
阿新 • • 發佈:2019-02-05
公司網站需要即時通訊,就研究了下
主要以下功能:
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埠具體多少範圍我也不研究了