赞
踩
================ 有bug,不推荐使用 ================
WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
Websocket 用法和 TCP 协议差不多,在这里我用 C# 写服务器端, html 和 winform 作为客户端。
新建一个控制台项目
在 NuGet 引入 Fleck 插件
新建 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();
- }
- }
- }
目前并没有写入太多功能,只是加了控制台输入文字,然后转发给所有连接的用户
代码
- 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);
- }
- }
- }
- }
-
- <!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,
然后服务器端就会收到对于的消息了
服务器发送 你好
同样的,网页端也收到了来自服务器的消息
那么这样,就完成了网页端的通信了
新建一个 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 服务器端,再运行客户端,点击连接按钮
服务器端也显示有客户端连接上了。
那么现在用客户端发送消息,输入恭喜发财,然后点击 发送消息 按钮,这时历史消息列表就有对于的记录。
这时,服务器端也同样的收到了来自客户端的消息。
接下来,我们用服务器向客户端发送消息,在控制台输入文字后,按回车,会自动发送
客户端收到了来自服务器的消息
这样,就完成了通讯部分的基本功能了。
源码:点击下载
如果这个帖子对你有用,欢迎 关注 + 点赞 + 留言,谢谢
end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。