用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>
|
请发表评论