一步一步学WebSocket(二) 使用SuperWebSocket实现自己的服务端
上一篇文章,我们了解了客户端如何与服务器创建WebSocket连接。但是一个巴掌拍不响,既然是通信,就必然最少要有两个端。今天我们来看看c#如何用已有的框架实现一个WebSocket服务端。
在.Net Framework 4.5及以上版本中,微软为我们集成了WebSocket协议的基本实现。微软提供的WebSocket对象位于System.Net.WebSocket命名空间下,使用起来挺繁琐的,所以我选择了SuperWebSocket框架来简化开发的难度。
SuperWebSocket框架可以通过NuGet直接获取并引用到项目中,也可以在http://superwebsocket.codeplex.com/中下载最新的DLL
我们来看SuperWebSocket如何我们快速搭建一个服务端
WebSocketServer wsServer = new WebSocketServer(); if (!wsServer.Setup("127.0.0.1", ))
{
//设置IP 与 端口失败 通常是IP 和端口范围不对引起的 IPV4 IPV6
} if (!wsServer.Start())
{
//开启服务失败 基本上是端口被占用或者被 某杀毒软件拦截造成的
return;
} wsServer.NewSessionConnected += (session) =>
{
//有新的连接
};
wsServer.SessionClosed += (session, reason) =>
{
//有断开的连接
};
wsServer.NewMessageReceived += (session, message) =>
{
//接收到新的文本消息
};
wsServer.NewDataReceived += (session, bytes) =>
{
//接收到新的二进制消息
}; Console.ReadKey(); wsServer.Stop();
这里WebSocketServer对象通过Setup方式对要侦听的IP及端口进行了设置。然后使用Start方法启动侦听。
Setup方法有4种重载,但是我们通常用到的只有设置IP和端口,IP为string类型,如果传入的字符串无法被转换为支持的IP格式,Setup方法会返回false表示设置失败。
WebSocketServer还提供了4个事件用以管理与客户端的连接、断开、和接受消息动作。新版本的WebSocket支持传送的数据格式有 “文本” 和 “二进制”两种,NewMessageReceived事件用于处理文本类型的消息,NewDataReceived事件用于处理二进制类型的消息。
到这里 我们已经成功的搭建了一个实现了WebSocket协议的服务端了。至于服务端的寄宿方式有很多,SuperWebSocket框架支持以 控制台、Winform、IIS以及Windows服务的形式寄宿服务,不过网上很多资料都不建议在IIS中寄宿服务,据说是因为寄宿在IIS中性能比较低。
WebSocket既然是双工通信,那么我们就不能光等着接收来自客户端的消息, 我们也需要从服务端向客户端“推送”消息,现在我们来看如何由服务端向客户端发送消息。
SuperWebSocket框架中,服务端与客户端创建的连接对象为WebSocketSession类型,也就是说它将每一个客户端的实例视为一个会话,在客户端创建连接的时候,产生这个会话,在客户端断开连接的时候,销毁这个会话,而客户端与服务端进行消息通信的时候,也依赖这个会话进行传递。我们要实现服务器端向客户端的广播,就要获取到当前正在活动的所有会话,我们通过代码来看如何获取所有的会话
wsServer.GetAllSessions() //获取所有的会话 已断开的会话不会出现在集合中
很简单吧,在获取到活动的会话之后 我们就可以向客户端发送消息了,这里我们让服务器向客户端定时发送服务器时间
Timer timer = new Timer((data) =>
{
var msg = string.Format("服务器当前时间:{0:HH:MM:ss}", DateTime.Now); //对当前已连接的所有会话进行广播
foreach (var session in wsServer.GetAllSessions())
{
session.Send(msg);
} }, null, , );
这样 所有与服务端保持连接的客户端就都可以接受到来自服务器端的消息了。
在这个例子里 我们看到了所有的消息都是由会话对象发出的,会话对象Send的消息 也支持“文本”与“二进制两种形式,同时会话对象还提供一个SendCloseHandshakeResponse()方法向客户端发送一个强制断开连接的指令。
WebSocketSession对象包含了服务端和客户端的所有信息,以及WebSocketServer对象本身,我们可以利用它做很多事情,下边我们就来实现一个简单的聊天室。至于聊天室的原理 就是一个人将要说的话发送到服务器,再由服务器广播给在这个聊天室里的所有人看到。恩 就这么简单。我们来上代码,多了不解释,相当简单。
public class ChatWebSocket
{
private const string ip = "127.0.0.1";
private const int port = ;
private WebSocketServer ws = null;//SuperWebSocket中的WebSocketServer对象 public ChatWebSocket()
{
ws = new WebSocketServer();//实例化WebSocketServer //添加事件侦听
ws.NewSessionConnected += ws_NewSessionConnected;//有新会话握手并连接成功
ws.SessionClosed += ws_SessionClosed;//有会话被关闭 可能是服务端关闭 也可能是客户端关闭
ws.NewMessageReceived += ws_NewMessageReceived;//有客户端发送新的消息
} void ws_NewSessionConnected(WebSocketSession session)
{
Console.WriteLine("{0:HH:MM:ss} 与客户端:{1}创建新会话", DateTime.Now, GetSessionName(session));
var msg = string.Format("{0:HH:MM:ss} {1} 进入聊天室", DateTime.Now, GetSessionName(session));
SendToAll(session, msg);
} void ws_SessionClosed(WebSocketSession session, SuperSocket.SocketBase.CloseReason value)
{
Console.WriteLine("{0:HH:MM:ss} 与客户端:{1}的会话被关闭 原因:{2}", DateTime.Now, GetSessionName(session), value);
var msg = string.Format("{0:HH:MM:ss} {1} 离开聊天室", DateTime.Now, GetSessionName(session));
SendToAll(session, msg);
}
void ws_NewMessageReceived(WebSocketSession session, string value)
{
var msg = string.Format("{0:HH:MM:ss} {1}说: {2}", DateTime.Now, GetSessionName(session), value); SendToAll(session, msg); }
/// <summary>
/// 启动服务
/// </summary>
/// <returns></returns>
public void Start()
{
if (!ws.Setup(ip, port))
{
Console.WriteLine("ChatWebSocket 设置WebSocket服务侦听地址失败");
return;
} if (!ws.Start())
{
Console.WriteLine("ChatWebSocket 启动WebSocket服务侦听失败");
return;
} Console.WriteLine("ChatWebSocket 启动服务成功"); } /// <summary>
/// 停止侦听服务
/// </summary>
public void Stop()
{ if (ws != null)
{
ws.Stop();
}
} private string GetSessionName(WebSocketSession session)
{
//这里用Path来取Name 不太科学……
return HttpUtility.UrlDecode(session.Path.TrimStart('/'));
} private void SendToAll(WebSocketSession session, string msg)
{
//广播
foreach (var sendSession in session.AppServer.GetAllSessions())
{
sendSession.Send(msg);
}
}
}
关与SuperWebSocket的基本使用就介绍到这里了……顶着老板不时窥屏的压力,可能文章有点语无伦次,希望大家多多体谅,也希望大家提出宝贵意见 共同学习。下一篇我会考虑介绍“子协议”和SuperWebSocket提供的Json字符串类型数据的处理
示例代码在这里 下载
提示:
SuperSocket与SuperWebSocket框架都是Kerry Jiang的项目 SuperSocket最新版本号是1.6 SuperWebSocket最新版本号是0.8 之前一直以为第一个是官网的……后来查了下资料不是……第一个框架BUG海多……这里给大家提个醒……
本系列本月暂停更新………这个框架资料很少……而且有点坑……在死磕探路中……
一步一步学WebSocket(二) 使用SuperWebSocket实现自己的服务端的更多相关文章
- hbase源码系列(十二)Get、Scan在服务端是如何处理
hbase源码系列(十二)Get.Scan在服务端是如何处理? 继上一篇讲了Put和Delete之后,这一篇我们讲Get和Scan, 因为我发现这两个操作几乎是一样的过程,就像之前的Put和Del ...
- WebSocket安卓客户端实现详解(三)–服务端主动通知
WebSocket安卓客户端实现详解(三)–服务端主动通知 本篇依旧是接着上一篇继续扩展,还没看过之前博客的小伙伴,这里附上前几篇地址 WebSocket安卓客户端实现详解(一)–连接建立与重连 We ...
- hbase源码系列(十二)Get、Scan在服务端是如何处理?
继上一篇讲了Put和Delete之后,这一篇我们讲Get和Scan, 因为我发现这两个操作几乎是一样的过程,就像之前的Put和Delete一样,上一篇我本来只打算写Put的,结果发现Delete也可以 ...
- Nacos(二)源码分析Nacos服务端注册示例流程
上回我们讲解了客户端配置好nacos后,是如何进行注册到服务器的,那我们今天来讲解一下服务器端接收到注册实例请求后会做怎么样的处理. 首先还是把博主画的源码分析图例发一下,让大家对整个流程有一个大概的 ...
- 阶段5 3.微服务项目【学成在线】_day01 搭建环境 CMS服务端开发_01-项目概述-功能构架-项目背景
这个就是博学谷下的 在线教育平台
- Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!
随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...
- 打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!
随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...
- 一步一步跟我学DeviceOne开发 - 仿微信应用(一,二,三)
这是一个系列的文档,长期目标是利用DeviceOne开发一些目前使用广泛的优质手机应用,我们会最大化的实现这些应用的每一个功能和细节,不只停留在简单的UI模仿和Demo阶段,而是一个基本可以使用的实际 ...
- usb-host一步一步学(二)安卓在usb-host模式下列出当前连接的usb设备
之前写了一个简单的例子usb-host一步一步学(一)安卓在usb-host模式下列出当前连接的usb设备,下面的这个例子是获取各种usb设备.usb接口以及usb连接点(endpoint) 正如上一 ...
随机推荐
- varnish 4.0.3 域名访问的小问题
1,若端口不是80 端口则匹配的时候必须加端口 if (req.http.host ~ "(?i)^var.test.aa:6081$") {set req.http.host = ...
- 基于Autofac, Castle.DynamicProxy的动态WCF解决方案(原创)
本方案解决了下面3个主要的问题: 1.减少配置,为了避免每次新增service都需要去修改配置文件,包括服务器端跟各个客户端的. 2.能够使用函数重载,泛型函数,以及泛型类. 3.使项目能够快速地在w ...
- 那些年蹚过的坑(c++)
1 main中的参数 尽量用英文双引号括起来,否则可能会出现字符串截断的情况(linux gcc 4.4 不加英文引号的话,遇到英文;会丢弃;后面的部分) 2 包含ipv6地址结构体(in6_addr ...
- pip China
建个文件 ~/.pip/pip.conf, 内容如下 [global] index-url = http://b.pypi.python.org/simple [install] use-mirror ...
- Java基础(二) ---- 继承(Inheritance)
- 基于MFC的单文档,多文档,对话框应用程序
从类的角度区分: 基于对话框(3个类): CAboutDlg 程序名App 程序名Dlg 单文档(5个类): CAboutDlg CMainFrame 程序名App 程序名Doc 程序名View 多文 ...
- php获取当月天数及当月第一天及最后一天、上月第一天及最后一天实现方法
1.获取上个月第一天及最后一天. echo date('Y-m-01', strtotime('-1 month')); echo "<br/>"; ...
- css absolute和float,relative,z-index的同异
大神占楼: 简书作者:张歆琳 http://www.jianshu.com/p/a3da5e27d22b http://www.cnblogs.com/lxblog/p/3152897.html 摘录 ...
- Struts2 XML配置详解
struts官网下载地址:http://struts.apache.org/ 1. 深入Struts2的配置文件 本部分主要介绍struts.xml的常用配置. 1.1. 包配置: S ...
- 解决Debian系统的Crontab执行时间时差问题
首先用 * * * * * date >> /root/log.log 做个测试,发现显示的是UTC的时间,但是直接执行date,得到的是CST的时间.可见在Debian里crontab的 ...