前言 {#%E5%89%8D%E8%A8%80}
最近公司有个项目需要用C/S架构的桌面应用程序与B/S架构的网页程序进行通信做数据的交互功能。在网上查了一下资料,发现 Fleck 实现一个WebSocket服务竟然如此简单明了,于是在此记录和整理了一下 Fleck实现WebSocket服务的简单应用,希望对你有所帮助。
简介 {#%E7%AE%80%E4%BB%8B}
Fleck是一个用C#编写的轻量级WebSocket服务器库,它易于使用且高性能,同时保持代码的简洁性。
特点:
-
无需继承:Fleck不需要你继承任何类,也不需要依赖于容器或额外的引用。
-
无依赖 :Fleck不依赖于
HttpListener
或HTTP.sys
,这意味着它可以在Windows 7和Server 2008主机上工作。 -
跨平台 :由于不依赖于
HttpListener
,Fleck可以在非Windows平台上运行。
使用 {#%E4%BD%BF%E7%94%A8}
安装Fleck {#%E5%AE%89%E8%A3%85fleck}
通过NuGet包管理器安装Fleck库
Install-Package Fleck
创建WebSocket服务器 {#%E5%88%9B%E5%BB%BAwebsocket%E6%9C%8D%E5%8A%A1%E5%99%A8}
以下是一个简单的WebSocket服务器示例:
using Fleck;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace WebSocketServiceDemo
{
public class WebSocketService
{
/// \<summary\> 客户端url以及其对应的Socket对象字典 \</summary\>
public IDictionary\<string, IWebSocketConnection\> dic_Sockets = new Dictionary\<string, IWebSocketConnection\>();
private readonly object _lockObject = new object();
private WebSocketServer _server;
public WebSocketService()
{
//创建WebSocket服务端实例
_server = new WebSocketServer("ws://0.0.0.0:9997");
_server.RestartAfterListenError = true; //出错后进行重启
_server.Start(ws=>
{
ws.OnOpen = () =>
{
WebSocket_OnOpen(ws);
};
ws.OnClose = () =>
{
WebSocket_OnClose(ws);
};
ws.OnMessage = message =>
{
Task.Run(() => { WebSocket_OnMessage(ws, message); });
};
ws.OnError = exp =>
{
WebSocket_OnError(ws, exp);
};
});
}
private void WebSocket_OnOpen(IWebSocketConnection wsConnection)
{
string clientUrl = wsConnection.ConnectionInfo.ClientIpAddress + ":" + wsConnection.ConnectionInfo.ClientPort;
AddSocket(clientUrl, wsConnection);
Debug.WriteLine($"服务器和客户端网页:{clientUrl} 建立WebSock连接!当前连接数量:{dic_Sockets.Count}");
}
private void WebSocket_OnClose(IWebSocketConnection wsConnection)
{
string clientUrl = wsConnection.ConnectionInfo.ClientIpAddress + ":" + wsConnection.ConnectionInfo.ClientPort;
if (dic_WSSockets.ContainsKey(clientUrl))
RemoveSocket(clientUrl);
Debug.WriteLine($"服务器和客户端网页:{clientUrl} 断开WebSock连接!当前连接数量:{dic_Sockets.Count}");
}
private void WebSocket_OnError(IWebSocketConnection wsConnection, Exception exception)
{
string clientUrl = wsConnection.ConnectionInfo.ClientIpAddress + ":" + wsConnection.ConnectionInfo.ClientPort;
if (dic_WSSockets.ContainsKey(clientUrl))
{
RemoveSocket(clientUrl);
Debug.WriteLine($"服务器和客户端网页:{clientUrl} 意外断开WebSock连接!当前连接数量:{dic_Sockets.Count}");
}
}
private void WebSocket_OnMessage(IWebSocketConnection wsConnection, string msg)
{
string clientUrl = wsConnection.ConnectionInfo.ClientIpAddress + ":" + wsConnection.ConnectionInfo.ClientPort;
Debug.WriteLine($"服务器:【收到】来客户端网页:{clientUrl}的信息:\n{msg}");
}
private bool RemoveSocket(string key)
{
lock (_lockObject)
{
return dic_WSSockets.Remove(key);
}
}
private void AddSocket(string key, IWebSocketConnection socket)
{
lock (_lockObject)
{
dic_Sockets.Add(key, socket);
}
}
}
`}
`
安全WebSockets (wss://) {#%E5%AE%89%E5%85%A8websockets-(wss%3A%2F%2F)}
要启用安全连接,需要使用wss
而不是ws
,并指向包含公钥和私钥的x509证书:
var server = new WebSocketServer("wss://0.0.0.0:9997");
server.Certificate = new X509Certificate2("MyCert.pfx");
server.Start(ws =>
{
//...use as normal
});
子协议协商 {#%E5%AD%90%E5%8D%8F%E8%AE%AE%E5%8D%8F%E5%95%86}
Fleck允许你指定支持的子协议,并在WebSocketServer.SupportedSubProtocols
属性上进行协商。如果客户端请求中没有找到支持的子协议,连接将被关闭:
var server = new WebSocketServer("ws://0.0.0.0:9997");
server.SupportedSubProtocols = new []{ "superchat", "chat" };
server.Start(socket =>
{
//socket.ConnectionInfo.NegotiatedSubProtocol 被填充
});
禁用Nagle算法 {#%E7%A6%81%E7%94%A8nagle%E7%AE%97%E6%B3%95}
你可以通过设置WebSocketConnection.ListenerSocket.NoDelay
为true
来禁用Nagle算法:
var server = new WebSocketServer("ws://0.0.0.0:9997");
server.ListenerSocket.NoDelay = true;
server.Start(socket =>
{
//子连接将不使用Nagle算法
});
监听错误后自动重启 {#%E7%9B%91%E5%90%AC%E9%94%99%E8%AF%AF%E5%90%8E%E8%87%AA%E5%8A%A8%E9%87%8D%E5%90%AF}
你可以通过设置WebSocketServer.RestartAfterListenError
为true
来在监听错误后自动重启服务器:
var server = new WebSocketServer("ws://0.0.0.0:9997");
server.RestartAfterListenError = true;
server.Start(socket =>
{
//...正常使用
});
自定义日志记录 {#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%97%A5%E5%BF%97%E8%AE%B0%E5%BD%95}
Fleck可以记录到Log4Net或任何其他第三方日志系统,只需覆盖FleckLog.LogAction
属性:
ILog logger = LogManager.GetLogger(typeof(FleckLog));
FleckLog.LogAction = (level, message, ex) =>
{
switch (level)
{
case LogLevel.Debug:
logger.Debug(message, ex);
break;
case LogLevel.Error:
logger.Error(message, ex);
break;
case LogLevel.Warn:
logger.Warn(message, ex);
break;
default:
logger.Info(message, ex);
break;
}
`};
`