Cowboy 开源 WebSocket 网络库
Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Protocol) 协议标准,并部分实现了 RFC 7692 (Compression Extensions for WebSocket) 协议标准。
WebSocket 可理解为建立在 TCP 连接通道上的更进一步的握手,并确定了消息封装格式。

通过定义控制帧 (Control Frame) 和数据帧 (Data Frame) 来控制通道内的通信和数据传输,下图用使用 ABNF 格式描述了帧头部的格式。

Cowboy.WebSockets 中对于 WebSocket 的 Client/Server 分别做了实现,分别对应代码中的:
Cowboy.WebSockets 的内部实现是基于 Cowboy.Sockets 中的 TAP 模式的 AsyncTcpSocketServer 和 AsyncTcpSocketClient 。关于 Cowboy.Sockets 可以参考文章《C#高性能TCP服务的多种实现方式》。
可通过 NuGet 查找 Cowboy 来获取 nuget 包。

WebSocket 服务端应用
实现 AsyncWebSocketServerModule 抽象类,其中 ModulePath 对应着 "ws://host:port/path" 中的 path 部分。可以实现多个 Module,将多个 Module 注入到 AsyncWebSocketServerModuleCatalog 中,或者采用反射机制等自动发现 Module。
public class TestWebSocketModule : AsyncWebSocketServerModule
{
public TestWebSocketModule()
: base(@"/test")
{
} public override async Task OnSessionStarted(AsyncWebSocketSession session)
{
Console.WriteLine(string.Format("WebSocket session [{0}] has connected.", session.RemoteEndPoint));
await Task.CompletedTask;
} public override async Task OnSessionTextReceived(AsyncWebSocketSession session, string text)
{
Console.Write(string.Format("WebSocket session [{0}] received Text --> ", session.RemoteEndPoint));
Console.WriteLine(string.Format("{0}", text)); await session.SendTextAsync(text);
} public override async Task OnSessionBinaryReceived(AsyncWebSocketSession session, byte[] data, int offset, int count)
{
var text = Encoding.UTF8.GetString(data, offset, count);
Console.Write(string.Format("WebSocket session [{0}] received Binary --> ", session.RemoteEndPoint));
Console.WriteLine(string.Format("{0}", text)); await session.SendBinaryAsync(Encoding.UTF8.GetBytes(text));
} public override async Task OnSessionClosed(AsyncWebSocketSession session)
{
Console.WriteLine(string.Format("WebSocket session [{0}] has disconnected.", session.RemoteEndPoint));
await Task.CompletedTask;
}
}
实例化 AsyncWebSocketServer,并将 AsyncWebSocketServerModuleCatalog 实例注入,即可启动 WebSocket 的服务端监听。
class Program
{
static AsyncWebSocketServer _server; static void Main(string[] args)
{
NLogLogger.Use(); try
{
var catalog = new AsyncWebSocketServerModuleCatalog();
catalog.RegisterModule(new TestWebSocketModule()); var config = new AsyncWebSocketServerConfiguration();
//config.SslEnabled = true;
//config.SslServerCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(@"D:\\Cowboy.pfx", "Cowboy");
//config.SslPolicyErrorsBypassed = true; _server = new AsyncWebSocketServer(, catalog, config);
_server.Listen(); Console.WriteLine("WebSocket server has been started on [{0}].", _server.ListenedEndPoint);
Console.WriteLine("Type something to send to clients...");
while (true)
{
try
{
string text = Console.ReadLine();
if (text == "quit")
break;
Task.Run(async () =>
{
//await _server.BroadcastText(text);
//Console.WriteLine("WebSocket server [{0}] broadcasts text -> [{1}].", _server.ListenedEndPoint, text);
await _server.BroadcastBinaryAsync(Encoding.UTF8.GetBytes(text));
Console.WriteLine("WebSocket server [{0}] broadcasts binary -> [{1}].", _server.ListenedEndPoint, text);
});
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
} _server.Shutdown();
Console.WriteLine("WebSocket server has been stopped on [{0}].", _server.ListenedEndPoint);
}
catch (Exception ex)
{
Logger.Get<Program>().Error(ex.Message, ex);
} Console.ReadKey();
}
}
WebSocket 客户端应用
客户端侧在实例化 AsyncWebSocketClient 时有两种方式:
- 实现 IAsyncWebSocketClientMessageDispatcher 接口;
- 直接构造函数注入接受各种事件的 Func<> 实现;
public interface IAsyncWebSocketClientMessageDispatcher
{
Task OnServerConnected(AsyncWebSocketClient client);
Task OnServerTextReceived(AsyncWebSocketClient client, string text);
Task OnServerBinaryReceived(AsyncWebSocketClient client, byte[] data, int offset, int count);
Task OnServerDisconnected(AsyncWebSocketClient client); Task OnServerFragmentationStreamOpened(AsyncWebSocketClient client, byte[] data, int offset, int count);
Task OnServerFragmentationStreamContinued(AsyncWebSocketClient client, byte[] data, int offset, int count);
Task OnServerFragmentationStreamClosed(AsyncWebSocketClient client, byte[] data, int offset, int count);
}
下面的 DEMO 采用了方式二。
class Program
{
static AsyncWebSocketClient _client; static void Main(string[] args)
{
NLogLogger.Use(); Task.Run(async () =>
{
try
{
var config = new AsyncWebSocketClientConfiguration();
//config.SslTargetHost = "Cowboy";
//config.SslClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate2(@"D:\\Cowboy.cer"));
//config.SslPolicyErrorsBypassed = true; //var uri = new Uri("ws://echo.websocket.org/");
//var uri = new Uri("wss://127.0.0.1:22222/test");
var uri = new Uri("ws://127.0.0.1:22222/test");
_client = new AsyncWebSocketClient(uri,
OnServerTextReceived,
OnServerBinaryReceived,
OnServerConnected,
OnServerDisconnected,
config);
await _client.Connect(); Console.WriteLine("WebSocket client has connected to server [{0}].", uri);
Console.WriteLine("Type something to send to server...");
while (_client.State == WebSocketState.Open)
{
try
{
string text = Console.ReadLine();
if (text == "quit")
break;
Task.Run(async () =>
{
//await _client.SendText(text);
//Console.WriteLine("Client [{0}] send text -> [{1}].", _client.LocalEndPoint, text);
await _client.SendBinaryAsync(Encoding.UTF8.GetBytes(text));
Console.WriteLine("Client [{0}] send binary -> [{1}].", _client.LocalEndPoint, text);
}).Forget();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
} await _client.Close(WebSocketCloseCode.NormalClosure);
Console.WriteLine("WebSocket client has disconnected from server [{0}].", uri);
}
catch (Exception ex)
{
Logger.Get<Program>().Error(ex.Message, ex);
}
}).Wait(); Console.ReadKey();
} private static async Task OnServerConnected(AsyncWebSocketClient client)
{
Console.WriteLine(string.Format("WebSocket server [{0}] has connected.", client.RemoteEndPoint));
await Task.CompletedTask;
} private static async Task OnServerTextReceived(AsyncWebSocketClient client, string text)
{
Console.Write(string.Format("WebSocket server [{0}] received Text --> ", client.RemoteEndPoint));
Console.WriteLine(string.Format("{0}", text)); await Task.CompletedTask;
} private static async Task OnServerBinaryReceived(AsyncWebSocketClient client, byte[] data, int offset, int count)
{
var text = Encoding.UTF8.GetString(data, offset, count);
Console.Write(string.Format("WebSocket server [{0}] received Binary --> ", client.RemoteEndPoint));
Console.WriteLine(string.Format("{0}", text)); await Task.CompletedTask;
} private static async Task OnServerDisconnected(AsyncWebSocketClient client)
{
Console.WriteLine(string.Format("WebSocket server [{0}] has disconnected.", client.RemoteEndPoint));
await Task.CompletedTask;
}
}
相关资料
- RFC6455: The WebSocket Protocol
- RFC7692: Compression Extensions for WebSocket
- IANA: WebSocket Opcode Registry
- LZ77 and LZ78
- LZ77 Compression Algorithm
- DeflateStream Class
- zlib 1.2.8 Manual
- WebSocket Extensions in Tyrus
本篇文章《Cowboy 开源 WebSocket 网络库》由 Dennis Gao 发表自博客园个人博客,未经作者本人同意禁止以任何的形式转载,任何自动的或人为的爬虫转载行为均为耍流氓。
Cowboy 开源 WebSocket 网络库的更多相关文章
- 【转】Cowboy 开源 WebSocket 网络库
原文链接: http://www.cnblogs.com/gaochundong/p/cowboy_websockets.html
- 我对开源C++网络库简单应用总结
网上有篇文章<开源免费的C/C++网络库(c/c++ sockets library) 七剑下天山>,看了之后觉得每个库都不错,这里我具体下载这些库看一下,简单总结一下: 顺便添加一些我找 ...
- [开源] gnet: 一个轻量级且高性能的 Golang 网络库
Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...
- go网络库cellent实现socket聊天功能
一 .介绍 cellnet是一个组件化.高扩展性.高性能的开源服务器网络库 git地址:https://github.com/davyxu/cellnet 主要使用领域: 游戏服务器 方便定制私有协议 ...
- boost.ASIO-可能是下一代C++标准的网络库
曾几何时,Boost中有一个Socket库,但后来没有了下文,C++社区一直在翘首盼望一个标准网络库的出现,网络上开源的网络库也有不少,例如Apache Portable Runtime就是比较著名的 ...
- muduo 网络库学习之路(一)
前提介绍: 本人是一名大三学生,主要使用C++开发,兴趣是高性能的服务器方面. 网络开发离不开网络库,所以今天开始学一个新的网络库,陈老师的muduo库 我参考的书籍就是陈老师自己关于muduo而编著 ...
- MTNET 自用ios网络库开源
短短两天就在https://git.oschina.net/gangwang/MTNET这里收获15个星 github 5星, 值得收藏! MTNET 自用ios网络库开源, 自用很久了,在数歀上架的 ...
- 开源免费的C/C++网络库(c/c++ sockets library)
(1)ACE 庞大.复杂,适合大型项目.开源.免费,不依赖第三方库,支持跨平台. http://www.cs.wustl.edu/~schmidt/ACE.html (2)Asio Asio基于Boo ...
- 【转】开源C/C++网络库比较
在开源的C/C++网络库中, 常用的就那么几个, 在业界知名度最高的, 应该是ACE了, 不过是个重量级的大家伙, 轻量级的有libevent, libev, 还有 Boost的ASIO. ACE是一 ...
随机推荐
- NodeJs在Linux下使用的各种问题
环境:ubuntu16.04 ubuntu中安装NodeJs 通过apt-get命令安装后发现只能使用nodejs,而没有node命令 如果想避免这种情况请看下面连接的这种安装方式: 拓展见:Linu ...
- java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- ASP.NET Core 之 Identity 入门(三)
前言 在上一篇文章中,我们学习了 CookieAuthentication 中间件,本篇的话主要看一下 Identity 本身. 最早2005年 ASP.NET 2.0 的时候开始, Web 应用程序 ...
- Electron使用与学习--(基本使用与菜单操作)
对于electron是个新手,下面纯属个人理解.如有错误,欢迎指出. 一.安装 如果你本地按照github上的 # Install the `electron` command globally ...
- iOS的ATS配置 - 2017年前ATS规定的适配
苹果规定 从2017年1月1日起,新提交的 app 不允许使用NSAllowsArbitraryLoads来绕过ATS(全称:App Transport Security)的限制. 以前为了能兼容ht ...
- python黑魔法 -- 内置方法使用
很多pythonic的代码都会用到内置方法,根据自己的经验,罗列一下自己知道的内置方法. __getitem__ __setitem__ __delitem__ 这三个方法是字典类的内置方法,分别对应 ...
- nginx源码分析之模块初始化
在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...
- MAC下 mysql不能插入中文和中文乱码的问题总结
MAC下 mysql不能插入中文和中文乱码的问题总结 前言 本文中所提到的问题解决方案,都是基于mac环境下的,但其他环境,比如windows应该也适用. 问题描述 本文解决下边两个问题: 往mysq ...
- 怎样两个月完成Udacity Data Analyst Nanodegree
在迷恋数据科学很久后,我决定要在MOOC网站上拿到一份Data Science的证书.美国三个MOOC网站,Udacity上的课程已经被分成了数个nanodegree,每个nanodegree都是目前 ...
- mono for android学习过程系列教程(4)
今天要讲的事情是构建安卓程序的UI界面. 首先给大家上点小点心,如图: 上面就是我们界面的设计模块,仔细看中间大块的下方,有一个Source,这就类似webform里面的设计和源代码界面. 在这个页面 ...