用C# ASP.NET MVC 实现WebSocket
用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
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>转自:http://blog.163.com/da7_1@126/blog/static/10407267820121016103055506/
用C# ASP.NET MVC 实现WebSocket相关推荐
- 在 Asp.NET MVC 中使用 SignalR 实现推送功能
一,简介 Signal 是微软支持的一个运行在 Dot NET 平台上的 html websocket 框架.它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重新发送 ...
- ASP.NET MVC随想录——漫谈OWIN
什么是OWIN OWIN是Open Web Server Interface for .NET的首字母缩写,他的定义如下: OWIN在.NET Web Servers与Web Application之 ...
- ASP.NET MVC SignalR(1):背景
系列目录:ASP.NET MVC SignalR 关键词:HTTP.轮询.WebSocket.Server-Sent Events.长轮询.forever frame. 1. HTTP HTTP(Hy ...
- asp.net(mvc) 框架
1.NFine mvc+ef 2.Grove orm架构 3.NHibernate orm 4.NBear 5.petshop 6.Membership 7.Brnshop 网上商城 8.cms快速开 ...
- 使用 WPF+ ASP.NET MVC 开发 在线客服系统 (一)
近段时间利用业余时间开发了一套在线客服系统,期间遇到过大大小小不少问题,好在都一一解决,最终效果也还可以,打算写一个系列的文章把开发过程详细的记录下来. 希望能够和更多的开发人员互相交流学习,也希望有 ...
- asp.net mvc让api返回json格式
第一种方法 API完整代码 using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Li ...
- ASP.NET MVC 2示例Tailspin Travel
Tailspin Travel 是一个旅游预订的应用程序示例,最新版本采用ASP.NET MVC 2技术构建,主要使用 DataAnnotations 验证, 客户端验证和ViewModels,还展示 ...
- Asp.net MVC中的ViewData与ViewBag
在Asp.net MVC 3 web应用程序中,我们会用到ViewData与ViewBag,对比一下: ViewData ViewBag 它是Key/Value字典集合 它是dynamic类型对像 从 ...
- ASP.NET MVC Identity 兩個多個連接字符串問題解決一例
按照ASP.NET MVC Identity建立了一個用戶權限管理模塊,由于還要加自己已有的數據庫,所以建立了一個實體模型,建立了之后,發現登錄不了: 一直顯示"Login in faile ...
最新文章
- 【ZooKeeper】配置文件详解
- mysql 去掉日期.0_简单介绍MySQL数据库中日期中包含零值的问题
- 在Ring3上实现文件碎甲(解锁)功能
- 科沃斯机器人双十一全渠道成交额超7亿 两大爆款单品携手破亿
- php docker开发环境,使用Docker的PHP开发环境
- 创建型模式:单例模式(懒汉+饿汉+双锁校验+内部类+枚举)
- 基于梅尔频谱的音频信号分类识别(Pytorch)
- Machine Learning List
- 3499元被吐槽太贵!A15加持:新iPhone SE正式发布 还有“苍蝇绿”iPhone 13
- 机器学习--聚类分析(划分方法,层次方法、密度方法)
- oracle主键重复异常捕获,Oracle异常处理异常处
- 操作系统期末总复习(题库)
- 跨境电商开发,源码无加密
- 录音转换成mp3格式
- 堆排序(Heapsort)-全网最详细
- python爬虫数据可视化豆瓣评分top250_Python数据可视化:豆瓣电影TOP250
- Ubuntu 16.04 常用软件安装
- JEB2插件教程之一JEB2AutoRenameByTypeInfo.py
- python项目对接钉钉SDK
- 有关于毕业论文提纲范文
热门文章
- 洛谷P1282 多米诺骨牌 题解
- Adobe Photoshop快捷键_艾孜尔江摘录
- 资源下载--使用Proxy SwitchyOmega+postman下载资源
- 在Windows Mobile手机上运行Android
- 实验八---理解进程调度时机跟踪分析进程调度与进程切换的过程
- java中的LinkedList(链表)与ArrayList(动态数组):(2)尝试简单实现LinkedList
- 山沟沟里的技术脱贫:阿里工程师助平武蜂农物联网养蜂
- 史上最全的PHP常用函数大全,不看看你就out了(还会不断更新哦!)
- 情感分析的一些专业术语
- golang中的gin框架学习