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是一 ...
随机推荐
- Git Bash的一些命令和配置
查看git版本号: git --version 如果是第一次使用Git,你需要设置署名和邮箱: $ git config --global user.name "用户名" $ gi ...
- 转:聊聊mavenCenter和JCenter
Gradle支持从maven中央仓库和JCenter上获取构件,那这两者有什么区别呢? maven中央仓库(http://repo1.maven.org/maven2/)是由Sonatype公司提供的 ...
- C# 数组的交集、差集、并集
C# 数组的交集.差集.并集 工作中经常会用这方面的知识来检查那些字段是必须输入的,那些是禁止输入. using System; using System.Collections.Generic; u ...
- Android Studio:Failed to resolve ***
更换电脑后,也更新了所有的SDK的tool,仍然报错:Failed to resolve 各种jar包,出现这种问题主要是因为在Android studio中默认不允许在线更新,修改方法如下:
- javaMail
JavaMail概述: JavaMail是由Sun定义的一套收发电子邮件的API,不同的厂商可以提供自己的实现类.但它并没有包含在JDK中,而是作为JavaEE的一部分. javaMai ...
- NOIP2016纪录[那些我所追求的]
人生第一场正式OI [序] 2016-12-04 见底部 [Day -1] 2016-11-17 期中考试无心插柳柳成荫,考了全市第2班里第1(还不是因为只复习了不到两天考试),马上请了一个周的假准备 ...
- Xamarin.Android多界面
一.准备 开始学习本教程前必须先完成该教程http://www.cnblogs.com/yaozhenfa/p/xamarin_android_quickstart.html 否则将无法继续. 二.界 ...
- what's deviceone
DeviceOne技术介绍 一. DeviceOne是什么 DeviceOne(以下简称Do)是一个移动开发的平台或技术,与之对等的是Android移动开发技术,iOS移动开发技术,Windo ...
- Linux 中的数值计算和符号计算
不知道经常需要做科学计算的朋友们有没有这样的好奇:在 Linux 系统下使用什么工具呢?说到科学计算,首先想到的肯定是 Matlab,如果再说到符号计算,那就非 Mathematica 不可了.可惜, ...
- Entity Framework 6 Recipes 2nd Edition(12-7)译 -> 设定默认值
12-7. 设定默认值 问题 在把一个实体保存到数据库之前,设置该实体属性的默认值 解决方案 假设你有一个如Figure 12-9所示的表, 它保存采购订单(purchase order). 主键Pu ...