用C# ASP.NET MVC 实现WebSocket ,对于WebSocket想必都很了解了,不多说.

东西做的很粗糙 只能实现基本的聊天功能,不过基本的通信实现了,那么后序的扩展应该也不难(个人这么认为...)

先看下效果

可同时支持群聊和私聊 源码下载地址

http://download.csdn.net/detail/formularz/4668280

首先介绍下ValueWebSocket.cs 这个文件 主要是对与客户端的通信进行集中控制

1.ValueServer: Socket服务端

2.ValueProtocol:对WebSocket通信的数据加以解析

3.SessionManager: 集中管理在线用户

ValueWebSocket.cs
public class ValueWebSocket
{
// WebSocket服务端
private ValueServer server;
// 解析协议
private ValueProtocol valueProtocol;
// 管理在线用户
private SessionManager sessionManager;

public ValueWebSocket(String ipAddress, Int32 port)
{
valueProtocol = new ValueProtocol();
sessionManager = new SessionManager();

server = new ValueServer(ipAddress, port, Encoding.UTF8);
server.OnReceive += new ValueHelper.ValueSocket.Infrastructure.ReceiveHandler(server_OnReceive);
}

private void server_OnReceive(ValueHelper.ValueSocket.SocketEvents.ReceiveEventArgs e)
{
// 分析用户是否已存在
if (sessionManager.CheckSessionExist(e.Socket))
{
Message message = valueProtocol.Decode(e.Data);
if (message.header.Opcode == OperType.Close)
{
removeUser(e.Socket);
}
if (message.header.Opcode == OperType.Text)
{
String msg = message.Data.ToString();
execMsg(e.Socket, msg);
}
}
else
{
// 用户不存在则添加用户
// 并发送握手信息与客户端建立连接
String request = Encoding.UTF8.GetString(e.Data);
Byte[] response = valueProtocol.GetResponse(request);
server.Send(e.Socket, response);
sessionManager.AddSession(e.Socket, request);
}
}

// 对消息进行的处理
private void execMsg(Socket socket, String message)
{
String name = String.Empty;
foreach (ValueSession session in SessionManager.Sessions)
{
Socket sk = session.Socket;
if (sk.Connected)
{
if (sk.RemoteEndPoint == socket.RemoteEndPoint)
{
name = session.Cookies["name"];
break;
}
}
}

// 判断私聊还是公共
String[] separator = message.Split(new String[] { "<separator>" }, StringSplitOptions.None);
String msg = String.Concat(name, ": ", separator[1]);
if (separator[0] == "All")
SendToAll(msg);
else
{
foreach (ValueSession session in SessionManager.Sessions)
{
if (session.Cookies["name"] == separator[0])
{
sendTo(session.Socket, msg);
break;
}
}
}
}

private void removeUser(Socket socket)
{
sessionManager.RemoveSession(socket);
}

private void SendToAll(String msg)
{
foreach (ValueSession session in SessionManager.Sessions)
{
sendTo(session.Socket, msg);
}
}

private Boolean sendTo(Socket socket, String msg)
{
Byte[] data = valueProtocol.Encode(msg);
return server.Send(socket, data);
}

public void Start()
{
server.Start();
}

public void Dispose()
{
sessionManager.Dispose();
server.Dispose();
}
}

SessionManager: 该类就不多说了,集中管理用户类.详情查看源码.

ValueProtocol: 这个类其实只是一个接口类,

真正对数据进行解析的是ProtocolDraft10类(按草案10中介绍的规则对数据进行解析,注:对协议说明有兴趣的同学可以查看这位大牛的文章http://blog.csdn.net/fenglibing/article/details/6852497)

ProtocolDraft10.cs
public class ProtocolDraft10 : IProtocol
{
private const String WebSocketKeyPattern = @"Sec\-WebSocket\-Key:\s+(?<key>.*)\r\n";
private const String MagicKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private const Char charOne = '1';
private const Char charZero = '0';

#region Handshake

// 发送回复信息完成握手
public Byte[] ProduceResponse(string request)
{
String webSocketKey = Common.GetRegexValue(request, WebSocketKeyPattern)[0].Groups["key"].Value;
String acceptKey = produceAcceptKey(webSocketKey);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(String.Concat("HTTP/1.1 101 Web Socket Protocol Handshake", Environment.NewLine));
stringBuilder.Append(String.Concat("Upgrade: WebSocket", Environment.NewLine));
stringBuilder.Append(String.Concat("Connection: Upgrade", Environment.NewLine));
stringBuilder.Append(String.Concat("Sec-WebSocket-Accept: ", acceptKey, Environment.NewLine, Environment.NewLine));
String asd = stringBuilder.ToString();
return Encoding.UTF8.GetBytes(stringBuilder.ToString());
}
// 根据Sec-WebSocket-Key和MagicKey生成AcceptKey
private String produceAcceptKey(String webSocketKey)
{
Byte[] acceptKey = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(webSocketKey + MagicKey));
return Convert.ToBase64String(acceptKey);
}

#endregion

#region Decode
// 对客户端发来的数据进行解析
public Message Decode(Byte[] data)
{
Byte[] buffer = new Byte[14];
if (data.Length >= 14)
Buffer.BlockCopy(data, 0, buffer, 0, 14);
else
Buffer.BlockCopy(data, 0, buffer, 0, data.Length);
MessageHeader header = analyseHead(buffer);
Message msg = new Message();
msg.header = header;

Byte[] payload;
if (header != null)
{
payload = new Byte[data.Length - header.PayloadDataStartIndex];
Buffer.BlockCopy(data, header.PayloadDataStartIndex, payload, 0, payload.Length);
if (header.MASK == charOne)
{
for (int i = 0; i < payload.Length; i++)
{
payload[i] = (Byte)(payload[i] ^ header.Maskey[i % 4]);
}
}
}
else
{
msg.Data = Encoding.UTF8.GetString(data);
return msg;
}

if (header.Opcode == OperType.Text)
msg.Data = Encoding.UTF8.GetString(payload);

return msg;
}
private MessageHeader analyseHead(Byte[] buffer)
{
MessageHeader header = new MessageHeader();
header.FIN = (buffer[0] & 0x80) == 0x80 ? charOne : charZero;
header.RSV1 = (buffer[0] & 0x40) == 0x40 ? charOne : charZero;
header.RSV2 = (buffer[0] & 0x20) == 0x20 ? charOne : charZero;
header.RSV3 = (buffer[0] & 0x10) == 0x10 ? charOne : charZero;

if ((buffer[0] & 0xA) == 0xA)
header.Opcode = OperType.Pong;
else if ((buffer[0] & 0x9) == 0x9)
header.Opcode = OperType.Ping;
else if ((buffer[0] & 0x8) == 0x8)
header.Opcode = OperType.Close;
else if ((buffer[0] & 0x2) == 0x2)
header.Opcode = OperType.Binary;
else if ((buffer[0] & 0x1) == 0x1)
header.Opcode = OperType.Text;
else if ((buffer[0] & 0x0) == 0x0)
header.Opcode = OperType.Row;

header.MASK = (buffer[1] & 0x80) == 0x80 ? charOne : charZero;
Int32 len = buffer[1] & 0x7F;
if (len == 126)
{
header.Payloadlen = (UInt16)(buffer[2] << 8 | buffer[3]);
if (header.MASK == charOne)
{
header.Maskey = new Byte[4];
Buffer.BlockCopy(buffer, 4, header.Maskey, 0, 4);
header.PayloadDataStartIndex = 8;
}
else
header.PayloadDataStartIndex = 4;
}
else if (len == 127)
{
Byte[] byteLen = new Byte[8];
Buffer.BlockCopy(buffer, 4, byteLen, 0, 8);
header.Payloadlen = BitConverter.ToUInt64(byteLen, 0);
if (header.MASK == charOne)
{
header.Maskey = new Byte[4];
Buffer.BlockCopy(buffer, 10, header.Maskey, 0, 4);
header.PayloadDataStartIndex = 14;
}
else
header.PayloadDataStartIndex = 10;
}
else
{
if (header.MASK == charOne)
{
header.Maskey = new Byte[4];
Buffer.BlockCopy(buffer, 2, header.Maskey, 0, 4);
header.PayloadDataStartIndex = 6;
}
else
header.PayloadDataStartIndex = 2;
}
return header;
}

#endregion

#region Encode
// 对要发送的数据进行编码一符合草案10的规则
public Byte[] Encode(String msg)
{
Byte[] data = Encoding.UTF8.GetBytes(msg);
Int32 dataLength = data.Length;

Byte[] head = packetHeader(OperType.Text, dataLength);
for (int i = 0; i < data.Length; i++)
{
data[i] = (Byte)(data[i] ^ maskKey[i % 4]);
}

Byte[] result = new Byte[head.Length + dataLength];
Buffer.BlockCopy(head, 0, result, 0, head.Length);
Buffer.BlockCopy(data, 0, result, head.Length, dataLength);
return result;
}
private const Byte byte80 = 0x80;
private Byte[] maskKey = new Byte[] { 113, 105, 97, 110 };
private Byte[] packetHeader(OperType operType, Int32 length)
{
Byte byteHead = (Byte)(byte80 | (Byte)operType);
Byte[] byteLen;
if (length < 126)
{
byteLen = new Byte[1];
byteLen[0] = (Byte)(byte80 | (Byte)length);
}
else if (length < 65535)
{
byteLen = new Byte[3];
byteLen[0] = (Byte)(byte80 | (Byte)126);
for (int i = 1; i < 3; i++)
byteLen[i] = (Byte)(length >> (8 * (2 - i)));
}
else
{
byteLen = new Byte[9];
byteLen[0] = (Byte)(byte80 | (Byte)127);
for (int i = 1; i < 9; i++)
byteLen[i] = (Byte)(length >> (8 * (8 - i)));
}

Byte[] packet = new Byte[1 + byteLen.Length + maskKey.Length];
packet[0] = byteHead;
Buffer.BlockCopy(byteLen, 0, packet, 1, byteLen.Length);
Buffer.BlockCopy(maskKey, 0, packet, 1 + byteLen.Length, maskKey.Length);
return packet;
}

#endregion
}

改类主要实现与客户端的握手级数据的解析,至于为什么这么解析,可访问上面那位大牛的文章,有详细说明值得一提的是有掩码的话接

通信时发送的数据都是真实数据与掩码按按异或处理过的.

其他类其实都是一些基础部件, 详情查看源码.

ValueServer: 该类其实就是用套接口编写了一个服务端实现Accept,Receive,Send等相关事件

至于Socket服务端的具体实现请查看这位大牛的文章,有详细说明http://www.cnblogs.com/tianzhiliang/archive/2010/09/08/1821623.html

这里就不赘述了

客户端的实现就比较简单了 只要调用WebSocket的API就行了

具体如下:

WebSocket.js

<script type="text/javascript">
var noSupportMessage = "您的浏览器不支持WebSocket!";
var ws;

function connectSocketServer() {
var messageBoard = $("#messageBoard");

var support = "MozWebSocket" in window ? 'MozWebSocket' : ('WebSocket' in window ? 'WebSocket' : null);

if (support == null) {
alert(noSupportMessage);
messageBoard.append('*' + noSupportMessage + "<br />");
return;
}

messageBoard.append("* Connecting to server..<br />");
try {
ws = new WebSocket('ws://localhost:3000');
} catch (e) {
alert(e.Message);
}

ws.onmessage = function (event) {
messageBoard.append(event.data + "<br />");
}

ws.onopen = function () {
messageBoard.append('* Connection open<br />');
}

ws.onclose = function () {
messageBoard.append('* Connection closed<br />');
}
}

function sendMessage() {
if (ws) {
var mssageBox = document.getElementById("messageBox");
var user = document.getElementById("users");
var msg = user.value + "<separator>" + mssageBox.value;
ws.send(msg);
mssageBox.value = "";
} else {
alert(noSupportMessage);
}
}

window.onload = function () {
connectSocketServer();
}
</script>

c# 实现WebSocket的更多相关文章

  1. 漫扯:从polling到Websocket

    Http被设计成了一个单向的通信的协议,即客户端发起一个request,然后服务器回应一个response.这让服务器很为恼火:我特么才是老大,我居然不能给小弟发消息... 轮询 老大发火了,小弟们自 ...

  2. 细说WebSocket - Node篇

    在上一篇提高到了 web 通信的各种方式,包括 轮询.长连接 以及各种 HTML5 中提到的手段.本文将详细描述 WebSocket协议 在 web通讯 中的实现. 一.WebSocket 协议 1. ...

  3. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  4. WebSocket - ( 一.概述 )

    说到 WebSocket,不得不提 HTML5,作为近年来Web技术领域最大的改进与变化,包含CSS3.离线与存储.多媒体.连接性( Connectivity )等一系列领域,而即将介绍的 WebSo ...

  5. php+websocket搭建简易聊天室实践

    1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...

  6. Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!

    随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...

  7. Cowboy 开源 WebSocket 网络库

    Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...

  8. 借助Nodejs探究WebSocket

    文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...

  9. 细说websocket - php篇

    下面我画了一个图演示 client 和 server 之间建立 websocket 连接时握手部分,这个部分在 node 中可以十分轻松的完成,因为 node 提供的 net 模块已经对 socket ...

  10. webSocket and LKDBHelper的使用说明

    socketket与lkdbhelper来处理数据 客户需求: 当我们有需要从自己的后台推送消息给我们的用户时,用户需要实时的接收到来自我们的推送消息.前提是没有使用第三方的推送框架,那么这个使用we ...

随机推荐

  1. UNP学习笔记(第十八章 路由套接字)

    路由套接字上支持3种类型的操作 1). 进程能通过写路由套接字向内核发消息. 2). 进程能通过路由套接字从内核读消息. 3). 进程可以用sysctl函数得到路由表或列出所有已配置的接口. 数据链路 ...

  2. 《Head First 设计模式》学习笔记——观察者模式 + 装饰者模式

    装饰者模式是JDK中还有一个使用较多的设计模式,上一个是观察者模式(在Swing中大量使用),业内好的API设计无一离不开常见的设计模式,通常我们所说要阅读源代码,也是为了学习大牛们的设计思路.--- ...

  3. Rom Modified [Galaxy 3 Tested]

    1,Virtualbox虚拟机设置-数据空间注意这里不要勾选那个自动挂载,不然后面mount总会提示mount.vbox.. invalid argument. 2,进入ubuntu中,在终端下输入 ...

  4. jquery插件函数传参错误

    1.jquery传参通过json,可能的错误是,参数中的结束符写成了;

  5. Dell 刀片服务器CentOS6.5mini开机20~30分钟宕机

    今天查看系统日志发现大量的nf_conntrack: table full, dropping packet. 错误 cat /var/log/messages | moreJun  7 09:52: ...

  6. IPv4(四)子网和子网掩码

    为了完成路由选择,每个数据链路(网络)都必须有一个惟一的地址: 另外,数据链路上的每台主机也必须有一个地址,这个地址不仅标识主机为一个网络成员,还可以把主机与网络上的其他主机区分开来. 粗放使用IPv ...

  7. leetCode 90.Subsets II(子集II) 解题思路和方法

    Given a collection of integers that might contain duplicates, nums, return all possible subsets. Not ...

  8. ASP.NET动态网站制作(4)--css(3)

    前言:这节课主要运用前面所学的知识写三个例子,并且学习浏览器兼容性的解决方法. 内容: 例子1:一个关于列表的例子 html代码: <!DOCTYPE html PUBLIC "-// ...

  9. YII框架学习(一)

    1.安装: windows:将php命令所在的文件夹路径加入到环境变量中,通过cmd命令:进入yii框架中的framework目录,执行: php yiic webapp ../cms linux:类 ...

  10. Java&amp;Xml教程(十)XML作为属性文件使用

    我们一般会将Java应用的配置參数保存在属性文件里.Java应用的属性文件能够是一个正常的基于key-value对,以properties为扩展名的文件.也能够是XML文件. 在本案例中.將会向大家介 ...