隐藏

C# WebSocket(Fleck) 客户端:html Winfrom

发布:2023/11/12 13:12:09作者:管理员 来源:本站 浏览次数:495

目录


一、简介


二、服务端


1.新建项目


2.WebSocketHelper


3.Program


三、客户端


1.html


2.Winform


结束


一、简介


WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。


WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。


Websocket 用法和 TCP 协议差不多,在这里我用 C# 写服务器端, html 和 winform 作为客户端。


二、服务端

1.新建项目


新建一个控制台项目


在 NuGet 引入  Fleck 插件


2.WebSocketHelper


新建 WebSocketHelper.cs


代码


   using Fleck;

   

   namespace WebSocket

   {

       internal class WebSocketHelper

       {

           //客户端url以及其对应的Socket对象字典

           IDictionary<string, IWebSocketConnection> dic_Sockets = new Dictionary<string, IWebSocketConnection>();

           //创建一个 websocket ,0.0.0.0 为监听所有的的地址

           WebSocketServer server = new WebSocketServer("ws://0.0.0.0:30000");

   

           //打开连接委托

           public delegate void _OnOpen(string ip);

           public event _OnOpen OnOpen;

           //关闭连接委托

           public delegate void _OnClose(string ip);

           public event _OnClose OnClose;

           //当收到消息

           public delegate void _OnMessage(string ip, string msg);

           public event _OnMessage OnMessage;

   

           /// <summary>

           /// 初始化

           /// </summary>

           private void Init()

           {

               //出错后进行重启

               server.RestartAfterListenError = true;

   

               //开始监听

               server.Start(socket =>

               {

                   //连接建立事件

                   socket.OnOpen = () =>

                   {

                       //获取客户端网页的url

                       string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort;

                       dic_Sockets.Add(clientUrl, socket);

                       if (OnOpen != null) OnOpen(clientUrl);

                       Console.WriteLine(DateTime.Now.ToString() + " | 服务器:和客户端:" + clientUrl + " 建立WebSock连接!");

                   };

   

                   //连接关闭事件

                   socket.OnClose = () =>

                   {

                       string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort;

                       //如果存在这个客户端,那么对这个socket进行移除

                       if (dic_Sockets.ContainsKey(clientUrl))

                       {

                           dic_Sockets.Remove(clientUrl);

                           if (OnClose != null) OnClose(clientUrl);

                       }

                       Console.WriteLine(DateTime.Now.ToString() + " | 服务器:和客户端:" + clientUrl + " 断开WebSock连接!");

                   };

   

                   //接受客户端网页消息事件

                   socket.OnMessage = message =>

                   {

                       string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort;

                       Receive(clientUrl, message);

                       if (OnMessage != null)

                           OnMessage(clientUrl, message);

                   };

               });

           }

   

           /// <summary>

           /// 向客户端发送消息

           /// </summary>

           /// <param name="webSocketConnection">客户端实例</param>

           /// <param name="message">消息内容</param>

           public void Send(string clientUrl, string message)

           {

               IWebSocketConnection webSocketConnection = GetUserSocketInstance(clientUrl);

               if (webSocketConnection != null)

               {

                   if (webSocketConnection.IsAvailable)

                   {

                       webSocketConnection.Send(message);

                   }

               }

           }

   

           /// <summary>

           /// 接收消息

           /// </summary>

           /// <param name="clientUrl"></param>

           /// <param name="message"></param>

           private void Receive(string clientUrl, string message)

           {

               Console.WriteLine(DateTime.Now.ToString() + " | 服务器:【收到】来客户端:" + clientUrl + "的信息:\n" + message);

           }

   

           /// <summary>

           /// 获取用户实例

           /// </summary>

           /// <param name="clientUrl">用户的地址</param>

           public IWebSocketConnection GetUserSocketInstance(string clientUrl)

           {

               if (dic_Sockets.ContainsKey(clientUrl))

                   return dic_Sockets[clientUrl];

               else

                   return null;

           }

   

           /// <summary>

           /// 关闭某一个用户的连接

           /// </summary>

           /// <param name="clientUrl"></param>

           public void CloseUserConnect(string clientUrl)

           {

               IWebSocketConnection webSocketConnection = GetUserSocketInstance(clientUrl);

               if (webSocketConnection != null)

                   webSocketConnection.Close();

           }

   

           /// <summary>

           /// 关闭与客户端的所有的连接

           /// </summary>

           public void CloseAllConnect()

           {

               foreach (var item in dic_Sockets.Values)

               {

                   if (item != null)

                   {

                       item.Close();

                   }

               }

           }

   

           public WebSocketHelper()

           {

               Init();

           }

       }

   }


3.Program


目前并没有写入太多功能,只是加了控制台输入文字,然后转发给所有连接的用户


代码


   using WebSocket;

   

   namespace WebSocketNet6

   {

       internal class Program

       {

           private static List<string> IPList = new List<string>();

           private static WebSocketHelper WebSocketHelpers = new WebSocketHelper();

   

           static void Main(string[] args)

           {

               WebSocketHelpers.OnOpen += WebSocketHelper_OnOpen;

               WebSocketHelpers.OnClose += WebSocketHelper_OnClose;

               WebSocketHelpers.OnMessage += WebSocketHelper_OnMessage;

   

               while (true)

               {

                   //监听控制台的输入

                   string? contetn = Console.ReadLine();

                   if (contetn != null)

                   {

                       Relay(contetn);

   

                       if (contetn.Equals("q"))

                       {

                           WebSocketHelpers.CloseAllConnect();

                           Console.WriteLine("关闭所有客户端的连接");

                           break;

                       }

                   }

                   Thread.Sleep(10);

               }

           }

   

           #region WebSocket回调

   

           //当收到消息

           private static void WebSocketHelper_OnMessage(string ip, string msg)

           {

               for (int i = 0; i < IPList.Count; i++)

               {

                   if (IPList[i] != ip)

                   {

                       WebSocketHelpers.Send(IPList[i], msg);

                   }

               }

           }

   

           //当客户端断开连接

           private static void WebSocketHelper_OnClose(string ip)

           {

               if (IPList.Contains(ip))

               {

                   IPList.Remove(ip);

               }

           }

   

           //当客户端连接上服务器

           private static void WebSocketHelper_OnOpen(string ip)

           {

               if (!IPList.Contains(ip))

               {

                   IPList.Add(ip);

               }

           }

   

           #endregion

   

           //转发所有客户端

           private static void Relay(string content)

           {

               if (IPList.Count == 0)  return;

   

               for (int i = 0; i < IPList.Count; i++)

               {

                   WebSocketHelpers.Send(IPList[i], content);

               }

           }

       }

   }


三、客户端

1.html


   

   <!DOCTYPE  HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

   <html>

   <head>

       <title>websocket client</title>

       <script type="text/javascript">

           var start = function () {

               var inc = document.getElementById('incomming');

               var wsImpl = window.WebSocket || window.MozWebSocket;

               var form = document.getElementById('sendForm');

               var input = document.getElementById('sendText');

   

               inc.innerHTML += "连接服务器..<br/>";

   

               // 创建一个新的websocket并连接

               window.ws = new wsImpl('ws://localhost:30000/');

   

               // 当数据来自服务器时,将调用此方法

               ws.onmessage = function (evt) {

                   inc.innerHTML += ("[来自服务器的消息] " + evt.data + '<br/>');

                   console.log("[来自服务器的消息] " + evt.data);

               };

   

               // 当建立连接时,将调用此方法

               ws.onopen = function () {

                   inc.innerHTML += '已建立连接.. <br/>';

               };

   

               // 当连接关闭时,将调用此方法

               ws.onclose = function () {

                   inc.innerHTML += '连接已关闭.. <br/>';

               }

   

               form.addEventListener('submit', function (e) {

                   e.preventDefault();

                   var val = input.value;

                   ws.send(val);

                   input.value = "";

               });

           }

           window.onload = start;

       </script>

   </head>

   <body>

       <form id="sendForm">

           <span>输入内容按回车发送消息</span> <br/>

           <input id="sendText" placeholder="Text to send" />

       </form>

       <pre id="incomming"></pre>

   </body>

   </html>


在本地新建一个 文本文档,将名字改为 index.html ,然后将上面的代码复制进去。


先打开上面的 Webscoket 服务器,用浏览器打开 index.html,


然后服务器端就会收到对于的消息了


服务器发送 你好


同样的,网页端也收到了来自服务器的消息


那么这样,就完成了网页端的通信了


2.Winform


新建一个 winform 项目


先安装插件 SuperSocket.ClientEngine


再安装插件 WebSocket4Net


新建脚本 WSocketClient.cs


   using SuperSocket.ClientEngine;

   using System;

   using System.Threading;

   using System.Threading.Tasks;

   using WebSocket4Net;

   

   namespace WebSocketClient

   {

       public class WSocketClient : IDisposable

       {

           //收到消息后的回调

           //public event Action<string> MessageReceived;

           public Action<string> MessageReceived;

   

           private WebSocket4Net.WebSocket _webSocket;

   

           /// <summary>

           /// 检查重连线程

           /// </summary>

           Thread _thread;

           bool _isRunning = false;

   

           /// <summary>

           /// 是否在运行中

           /// </summary>

           public bool IsRunning => _isRunning;

   

           /// <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;

           }

   

           /// <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)

               {

                   Console.WriteLine(ex.ToString());

                   result = false;

                   this._isRunning = false;

               }

               return result;

           }

   

           /// <summary>

           /// 消息收到事件

           /// </summary>

           /// <param name="sender"></param>

           /// <param name="e"></param>

           private void WebSocket_MessageReceived(object sender, MessageReceivedEventArgs e)

           {

               Console.WriteLine("Received:" + e.Message);

   

               if (MessageReceived != null)

                   MessageReceived(e.Message);

           }

   

           /// <summary>

           /// Socket关闭事件

           /// </summary>

           /// <param name="sender"></param>

           /// <param name="e"></param>

           private void WebSocket_Closed(object sender, EventArgs e)

           {

               Console.WriteLine("websocket_Closed");

           }

   

           /// <summary>

           /// Socket报错事件

           /// </summary>

           /// <param name="sender"></param>

           /// <param name="e"></param>

           private void WebSocket_Error(object sender, ErrorEventArgs e)

           {

               Console.WriteLine("websocket_Error:" + e.Exception.ToString());

           }

   

           /// <summary>

           /// Socket打开事件

           /// </summary>

           /// <param name="sender"></param>

           /// <param name="e"></param>

           private void WebSocket_Opened(object sender, EventArgs e)

           {

               Console.WriteLine("websocket_Opened");

           }

   

           /// <summary>

           /// 检查重连线程

           /// </summary>

           private void CheckConnection()

           {

               do

               {

                   try

                   {

                       if (this._webSocket.State != WebSocket4Net.WebSocketState.Open && this._webSocket.State != WebSocket4Net.WebSocketState.Connecting)

                       {

                           Console.WriteLine("Reconnect websocket WebSocketState:" + this._webSocket.State);

   

                           this._webSocket.Close();

                           this._webSocket.Open();

   

                           Console.WriteLine("正在重连");

                       }

                   }

                   catch (Exception ex)

                   {

                       Console.WriteLine(ex.ToString());

                   }

                   System.Threading.Thread.Sleep(5000);

               } while (this._isRunning);

           }

   

           /// <summary>

           /// 发送消息

           /// </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()

           {

               try

               {

                   _thread?.Abort();

               }

               catch (Exception ex)

               {

                   Console.WriteLine(ex.Message);

               }

               this._webSocket.Close();

               this._webSocket.Dispose();

               this._webSocket = null;

               this._isRunning = false;

           }

       }

   }


winfrom 界面如下


Form1.cs


   using System;

   using System.Windows.Forms;

   using WebSocketClient;

   

   namespace WinFormWebsocket

   {

       public partial class Form1 : Form

       {

           private static string url = "ws://127.0.0.1:30000";

           private WSocketClient client = new WSocketClient(url);

   

           public Form1()

           {

               InitializeComponent();

           }

   

           private void Form1_Load(object sender, EventArgs e)

           {

               Control.CheckForIllegalCrossThreadCalls = false;

               txtServerIP.Text = url;

               client.MessageReceived = MessageReceived;

               this.Text = "客户端";

           }

   

           private void Form1_FormClosing(object sender, FormClosingEventArgs e)

           {

               if(client.IsRunning)

                   client.Dispose();

           }

   

           /// <summary>

           /// 连接服务器

           /// </summary>

           /// <param name="sender"></param>

           /// <param name="e"></param>

           private void btnConnect_Click(object sender, EventArgs e)

           {

               try

               {

                   if(client.IsRunning)

                   {

                       AddOrdinaryLog("已经连接服务器,不能重复执行");

                       return;

                   }

   

                   bool result = client.Start();

                   AddOrdinaryLog("连接是否成功:" + result);

               }

               catch (Exception ex)

               {

                   string err = string.Format("连接失败:{0}", ex.Message);

                   Console.WriteLine(err);

                   throw;

               }

           }

   

           /// <summary>

           /// 关闭服务器

           /// </summary>

           /// <param name="sender"></param>

           /// <param name="e"></param>

           private void btnClose_Click(object sender, EventArgs e)

           {

               if (!client.IsRunning)

               {

                   AddOrdinaryLog("服务器未连接");

                   return;

               }

   

               // 记得释放资源否则会造成堆栈

               client.Dispose();

               AddOrdinaryLog("连接已关闭");

           }

   

           /// <summary>

           /// 发送消息

           /// </summary>

           /// <param name="sender"></param>

           /// <param name="e"></param>

           private void btnSendMsg_Click(object sender, EventArgs e)

           {

               string inputMsg = txtInputMsg.Text;

               if (string.IsNullOrEmpty(inputMsg))

               {

                   MessageBox.Show("输入框不能为空");

                   return;

               }

   

               client.SendMessage(inputMsg);

               AddOrdinaryLog(inputMsg);

               txtInputMsg.Text = string.Empty;

           }

   

           /// <summary>

           /// 服务端返回的消息

           /// </summary>

           private void MessageReceived(string msg)

           {

               AddOrdinaryLog(msg);

           }

   

           /// <summary>

           /// 添加日志

           /// </summary>

           /// <param name="content"></param>

           private void AddOrdinaryLog(string content)

           {

               //读取当前ListBox列表长度

               int len = ListBox_OrdinaryLogList.Items.Count;

               //插入新的一行

               ListBox_OrdinaryLogList.Items.Insert(len, content);

               //列表长度大于30,那么就删除第1行的数据

               if (len > 30)

                   ListBox_OrdinaryLogList.Items.RemoveAt(0);

               //插入新的数据后,将滚动条移动到最下面

               int visibleItems = ListBox_OrdinaryLogList.ClientSize.Height / ListBox_OrdinaryLogList.ItemHeight;

               ListBox_OrdinaryLogList.TopIndex = Math.Max(ListBox_OrdinaryLogList.Items.Count - visibleItems + 1, 0);

           }

       }

   }


到这里,所有的准备工作都做完了,下面就来测试一下效果吧。


首先,运行 WebSocket 服务器端,再运行客户端,点击连接按钮


服务器端也显示有客户端连接上了。


那么现在用客户端发送消息,输入恭喜发财,然后点击 发送消息 按钮,这时历史消息列表就有对于的记录。


这时,服务器端也同样的收到了来自客户端的消息。


接下来,我们用服务器向客户端发送消息,在控制台输入文字后,按回车,会自动发送


客户端收到了来自服务器的消息




这样,就完成了通讯部分的基本功能了。