效果:
一、概述
WebSocketSharp 是一个 C# 实现 websocket 协议客户端和服务端,WebSocketSharp 支持RFC 6455;WebSocket客户端和服务器;消息压缩扩展;安全连接;HTTP身份验证;查询字符串,起始标题和Cookie;通过HTTP代理服务器连接;.NET Framework 3.5或更高版本(包括兼容环境,如Mono)。
github 地址
https://github.com/sta/websocket-sharp
websocket-sharp supports:
- RFC 6455
- WebSocket Client and Server
- Per-message Compression extension
- Secure Connection
- HTTP Authentication
- Query string, Origin header, and Cookies
- Connecting through the HTTP proxy server
- .NET Framework 3.5 or later versions of .NET Framework (includes compatible environment such as Mono)
二、服务器端
服务器端的代码量很少,这里我全部使用 winform 来展示。
新建一个 winform 项目,界面如下
先安装 WebSocketSharp-netstandard 插件,我这里的版本是 1.0.1
新建一个类 ServerManager
using System;
using WebSocketSharp.Server;
internal class ServerManager
{
private static WebSocketServer WebSocketServers = null;
public static bool IsListening
{
get
{
if (WebSocketServers == null)
return false;
return WebSocketServers.IsListening;
}
}
private static void Init()
{
WebSocketServers = new WebSocketServer(8888);
//添加具有指定行为和路径的WebSocket服务
//string 参数 表示要添加的服务的绝对路径
WebSocketServers.AddWebSocketService<Laputa>("/Laputa");
}
public static void Open()
{
WebSocketServers.Start();
Console.WriteLine("开启服务器");
}
public static void Close()
{
WebSocketServers.Stop();
Console.WriteLine("关闭服务器");
}
static ServerManager()
{
Init();
}
private ServerManager() { }
}
新建一个类 Laputa,这个类相当于在 websocket 里面负责单独的模块,同样的,在客户端的访问链接也要配套。
using System;
using WebSocketSharp;
using WebSocketSharp.Server;
public class Laputa : WebSocketBehavior
{
protected override void OnMessage(MessageEventArgs e)
{
Console.WriteLine("[OnMessage]" + e.Data);
var msg = e.Data == "BALUS"
? "Are you kidding?"
: "I'm not available now.";
Send(msg);
}
protected override void OnOpen()
{
Console.WriteLine("[OnOpen]");
}
protected override void OnClose(CloseEventArgs e)
{
Console.WriteLine("[OnClose]" + e.Reason);
}
protected override void OnError(ErrorEventArgs e)
{
Console.WriteLine("[OnError]" + e.Message);
}
}
OnMessage 是指收到客户端的消息
OnOpen 客户端连接到了当前的接口
OnClose 客户端断开了连接
OnError websocket 出现了错误
接下来就是 winform 界面的代码了
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WebSocketSharpServer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (ServerManager.IsListening)
ServerManager.Close();
}
private void Button_Open_Click(object sender, EventArgs e)
{
ServerManager.Open();
}
private void Button_Close_Click(object sender, EventArgs e)
{
ServerManager.Close();
}
}
}
服务器端的代码就这么多,其实还有其他的很多功能,SSL/TLS,Cookies,证书 等,有兴趣的可以参考 github 的文档。
三、客户端
客户端可以使用 html 进行连接,也可以使用其他的客户端,我这里就使用 winform 来写。
winform 界面
新建一个类 NetManager
using System;
using System.Threading.Tasks;
using WebSocketSharp;
/// <summary>
/// Websocket
/// </summary>
internal class NetManager
{
#region 字段
private static WebSocket WebSockets = null;
private static int ConsoleCount = 0;
private static System.Windows.Forms.Timer Timer = null;
/// <summary>
/// 当前的网络是否连接
/// </summary>
public static bool IsConnect
{
get
{
if (WebSockets == null)
return false;
return WebSockets.IsAlive;
}
}
//这里ip地址可以从别的地方取值,因为用的 static 静态构造函数,
//构造函数会调用 Init 方法,这会导致 IPAddress 为 null 就初始化完成了。
/// <summary>
/// websocket 的地址,一般以 ws:// 或者 wss:// 开头
/// </summary>
public static string IPAddress { get; private set; } = "ws://127.0.0.1:8888/Laputa";
/// <summary>
/// 是否显示ping
/// </summary>
public static bool IsShowPingLog { get; set; } = false;
/// <summary>
/// 收到服务器消息的回调
/// </summary>
public static Action<string> OnMessage = null;
/// <summary>
/// 连接成功和断开连接的回调
/// </summary>
public static Action<bool> OnNetStateChange = null;
#endregion
#region 初始化
private static void Init()
{
if (string.IsNullOrEmpty(IPAddress))
return;
WebSockets = new WebSocket(IPAddress);
WebSockets.OnOpen += WebSockets_OnOpen;
WebSockets.OnMessage += WebSockets_OnMessage;
WebSockets.OnClose += WebSockets_OnClose;
WebSockets.OnError += WebSockets_OnError;
Console.WriteLine("[NetManager]初始化成功!");
Timer = new System.Windows.Forms.Timer();//实例化Timer类,设置间隔时间(毫秒);
Timer.Interval = 3000;
Timer.Tick += Timer_Tick;//到达时间的时候执行事件
}
private static void Timer_Tick(object sender, EventArgs e)
{
if (!WebSockets.IsAlive) return;
WebSockets.Send("ping");
if (IsShowPingLog)
{
Console.WriteLine("ping");
ConsoleCount++;
if (ConsoleCount > 100)
{
ConsoleCount = 0;
Console.Clear();
}
}
}
#endregion
#region WebSocket 相关
private static void WebSockets_OnOpen(object sender, EventArgs e)
{
Console.WriteLine("[NetManager][OnOpen] 时间:{0}", DateTime.Now);
if (OnNetStateChange != null)
OnNetStateChange(true);
}
private static void WebSockets_OnError(object sender, ErrorEventArgs e)
{
string message = string.Format("[NetManager][OnError]{0}", e.Message);
Console.WriteLine(message);
}
private static void WebSockets_OnClose(object sender, CloseEventArgs e)
{
Console.WriteLine("[NetManager][OnClose]" + e.Reason);
if (OnNetStateChange != null)
OnNetStateChange(false);
if (!string.IsNullOrEmpty(e.Reason) && !IsClose)
{
DisconnectionReconnects();//断线重连
}
}
private static void WebSockets_OnMessage(object sender, MessageEventArgs e)
{
Console.WriteLine("[NetManager][OnMessage]" + e.Data);
//心跳类型消息
//if (e.IsPing)
//{
// Console.WriteLine("ping");
// return;
//}
//字符串类型的消息
if (e.IsText)
{
if (OnMessage != null)
OnMessage(e.Data);
return;
}
//二进制消息
if(e.IsBinary)
{
//二进制使用RawData
Console.WriteLine(e.RawData);
return;
}
}
#endregion
#region 服务器开关
private static bool IsClose = false;
/// <summary>
/// 连接服务器
/// </summary>
/// <returns></returns>
public static bool Connect()
{
if (WebSockets == null)
return false;
if (WebSockets.IsAlive)
return false;
WebSockets.Connect();
//异步连接服务器
//WebSockets.ConnectAsync();
//是否接收ping
//WebSockets.EmitOnPing = true;
//连接状态可以参考枚举:ReadyState
if (WebSockets.IsAlive)
{
IsClose = false;
Timer.Enabled = true;
return true;
}
return false;
}
/// <summary>
/// 关闭服务器连接
/// </summary>
/// <returns></returns>
public static bool Disconnect()
{
if (WebSockets == null)
return false;
WebSockets.Close();
//异步关闭
//WebSockets.CloseAsync();
Timer.Enabled = false;
IsClose = true;
return WebSockets.IsAlive == false;
}
#endregion
#region 发送消息
/// <summary>
/// 发送消息
/// </summary>
/// <param name="message"></param>
public static void Send(string message)
{
if (string.IsNullOrEmpty(message))
{
Console.WriteLine("[NetManager]发送的消息不能为空");
return;
}
if (WebSockets == null || !WebSockets.IsAlive)
{
Console.WriteLine("[NetManager]请先打开服务器");
return;
}
WebSockets.Send(message);
//异步发送
//completed 是一个委托,发送完成会调用
//WebSockets.SendAsync(data, completed);
Console.WriteLine("[NetManager]发送:" + message);
}
#endregion
#region 断线重连
//是否正在重新连接中
private static bool IsReconnectioning = false;
private static async void DisconnectionReconnects()
{
if (IsReconnectioning) return;
IsReconnectioning = true;
while (true)
{
if (WebSockets.IsAlive)
{
Console.WriteLine("[NetManager]断线重连成功");
IsReconnectioning = false;
break;
}
if (IsClose)
{
Console.WriteLine("[NetManager]关闭连接不必断线重连");
break;
}
Console.WriteLine("[NetManager]断线重连中......");
WebSockets.Connect();
//WebSockets.ConnectAsync();
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
#endregion
#region 构造函数
static NetManager()
{
Init();
}
private NetManager() { }
#endregion
}
form1 界面代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WebSocketSharpClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
NetManager.OnMessage = OnMessage;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
NetManager.Disconnect();
}
private void OnMessage(string msg)
{
Console.WriteLine("收到消息:" + msg);
}
private void Button_Send_Click(object sender, EventArgs e)
{
string msg = TextBox_Content.Text;
NetManager.Send(msg);
}
private void Button_Close_Click(object sender, EventArgs e)
{
NetManager.Disconnect();
}
private void Button_Connect_Click(object sender, EventArgs e)
{
NetManager.Connect();
}
}
}
运行后,效果如下
经过一段时间的测试,目前在 websocket 的各种框架中,运行还算比较稳定的,bug 也少。
源码:点击下载
结束
如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言
end