最近在维护公司旧项目,偶然发现使用Fleck实现的WebSocket主动推送功能,(由于前端页面关闭时WebSocket Server中执行了多次OnClose事件回调并且打印了大量的关闭日志,),后来我特地看了源码,这里做一些分享
github: https://github.com/statianzo/Fleck
在源码中,作者在 Samples 项目中贴心的准备了Server端和 前端Html文件供调试
打开后,我们稍微改动一下Server.cs类,模拟实际场景向客户端主动推送消息。
运行ConsoleApp项目,随后在浏览器中打开client.html,我们就可以看到客户端接收到很多主动推送的消息
关闭客户端后,此时我们会发现控制台上打印了好多“Close!”,并且抛出了异常,异常是从System.Net.Sockets.NetworkStream 抛出,说我们访问了已释放的对象。
大概猜测的是程序并发太高了,Socket已经关闭释放的同时,我们任然在向流写入byte字节发送消息。
接下来我们读一下源码
首先是 WebSocketServer.cs,构造方法中创建了 System.Net.Sockets.Socket类,并传递给 Fleck.SocketWrapper,后续和Socket相关的操作都是由SocketWrapper实例进行执行。SupportDualStack为True时表示启用IPV6
随后我们看一下 WebSocketServer.Start() 方法,方法的入参是一个Action,IWebSocketConnection 中我们定义了OnOpen、OnClose、OnMessage等方法。
Start()方法中给SocketWrapper(或System.Net.Sockets.Socket类)类绑定了侦听地址和端口。
ListenForClients() 方法为开始接收连接(Accept),如果有连接,则调用OnClientConnect()方法,OnClientConnect()方法调用中如果出现异常,则执行重启Socket工作。
我们再来看看OnClientConnect()方法做了什么,ListenForClients() 方法是继续侦听客户端连接,随后创建了WebSocketConnection类对象,然后开始connection.StartReceiving() 也就是读取消息内容。
我们可以简单的看一下Fleck.SocketWrapper类的实现,特别是Accept()方法和Receive()方法,其实就是Task执行BeginAccept()、EndAccept()、BeginRead() 和 EndRead() 方法,如果调用时出现异常,则执行Action<Exception>()方法。
接下来我们看一下WebSocketConnection类,首先是构造方法,
其次是Read(List<byte>, byte[]) 方法。就是调用SocketWrapper(或System.Net.Sockets.Socket类)的 Receive()方法读取byte[]并交给Handle对象进行处理,如果byte[]长度为0,则表示关闭断开
最后是SendBytes(byte[], Action)方法,就是调用SocketWrapper(或System.Net.Sockets.Socket类)的 Send()方法,如果发送不成功,则会调用CloseSocket()方法关闭Socket(这就是为什么连接只有一个但多次触发OnClose事件原因)
Fleck 的核心功能已经讲解完了,其他类还有 WebSocketConnectionInfo、QueuedStream、SubProtocolNegotiator等类基本也是比较简单,这里就不展开讲解了。
Fleck 源码 用了装饰者模式、工厂模式等设计模式,还很优雅的处理方法执行失败的异常方式,对委托的使用也是值得我们学习的。