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 时有两种方式:

  1. 实现 IAsyncWebSocketClientMessageDispatcher 接口;
  2. 直接构造函数注入接受各种事件的 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;
}
}

相关资料

本篇文章《Cowboy 开源 WebSocket 网络库》由 Dennis Gao 发表自博客园个人博客,未经作者本人同意禁止以任何的形式转载,任何自动的或人为的爬虫转载行为均为耍流氓。

Cowboy 开源 WebSocket 网络库的更多相关文章

  1. 【转】Cowboy 开源 WebSocket 网络库

    原文链接: http://www.cnblogs.com/gaochundong/p/cowboy_websockets.html

  2. 我对开源C++网络库简单应用总结

    网上有篇文章<开源免费的C/C++网络库(c/c++ sockets library) 七剑下天山>,看了之后觉得每个库都不错,这里我具体下载这些库看一下,简单总结一下: 顺便添加一些我找 ...

  3. [开源] gnet: 一个轻量级且高性能的 Golang 网络库

    Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...

  4. go网络库cellent实现socket聊天功能

    一 .介绍 cellnet是一个组件化.高扩展性.高性能的开源服务器网络库 git地址:https://github.com/davyxu/cellnet 主要使用领域: 游戏服务器 方便定制私有协议 ...

  5. boost.ASIO-可能是下一代C++标准的网络库

    曾几何时,Boost中有一个Socket库,但后来没有了下文,C++社区一直在翘首盼望一个标准网络库的出现,网络上开源的网络库也有不少,例如Apache Portable Runtime就是比较著名的 ...

  6. muduo 网络库学习之路(一)

    前提介绍: 本人是一名大三学生,主要使用C++开发,兴趣是高性能的服务器方面. 网络开发离不开网络库,所以今天开始学一个新的网络库,陈老师的muduo库 我参考的书籍就是陈老师自己关于muduo而编著 ...

  7. MTNET 自用ios网络库开源

    短短两天就在https://git.oschina.net/gangwang/MTNET这里收获15个星 github 5星, 值得收藏! MTNET 自用ios网络库开源, 自用很久了,在数歀上架的 ...

  8. 开源免费的C/C++网络库(c/c++ sockets library)

    (1)ACE 庞大.复杂,适合大型项目.开源.免费,不依赖第三方库,支持跨平台. http://www.cs.wustl.edu/~schmidt/ACE.html (2)Asio Asio基于Boo ...

  9. 【转】开源C/C++网络库比较

    在开源的C/C++网络库中, 常用的就那么几个, 在业界知名度最高的, 应该是ACE了, 不过是个重量级的大家伙, 轻量级的有libevent, libev, 还有 Boost的ASIO. ACE是一 ...

随机推荐

  1. Python中的多进程与多线程(一)

    一.背景 最近在Azkaban的测试工作中,需要在测试环境下模拟线上的调度场景进行稳定性测试.故而重操python旧业,通过python编写脚本来构造类似线上的调度场景.在脚本编写过程中,碰到这样一个 ...

  2. Angular2入门系列教程4-服务

    上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...

  3. 简谈百度坐标反转至WGS84的三种思路

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 基于百度地图进行数据展示是目前项目中常见场景,但是因为百度地图 ...

  4. 背后的故事之 - 快乐的Lambda表达式(一)

    快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...

  5. ASP.NET MVC5+EF6+EasyUI 后台管理系统(67)-MVC与ECharts

    系列目录 ECharts 特性介绍 ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Fire ...

  6. 【开源】分享2011-2015年全国城市历史天气数据库【Sqlite+C#访问程序】

    由于个人研究需要,需要采集天气历史数据,前一篇文章:C#+HtmlAgilityPack+XPath带你采集数据(以采集天气数据为例子),介绍了基本的采集思路和核心代码,经过1个星期的采集,历史数据库 ...

  7. iOS UITableView 与 UITableViewController

    很多应用都会在界面中使用某种列表控件:用户可以选中.删除或重新排列列表中的项目.这些控件其实都是UITableView 对象,可以用来显示一组对象,例如,用户地址薄中的一组人名.项目地址. UITab ...

  8. BPM配置故事之案例7-公式计算

    行政主管发来邮件.要求物资明细表增加"单价""总价"."单价"由其审批时填写,"总价"根据"单价"与 ...

  9. Android Retrofit 2.0 使用-补充篇

    推荐阅读,猛戳: 1.Android MVP 实例 2.Android Retrofit 2.0使用 3.RxJava 4.RxBus 5.Android MVP+Retrofit+RxJava实践小 ...

  10. Android local.properties 文件读取

    转载请标明出处:http://www.cnblogs.com/zhaoyanjun/p/6202369.html 本文出自[赵彦军的博客] 在Android Studio项目里面有个local.pro ...