用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
  1  public class ValueWebSocket
  2     {
  3         // WebSocket服务端
  4         private ValueServer server;
  5         // 解析协议
  6         private ValueProtocol valueProtocol;
  7         // 管理在线用户
  8         private SessionManager sessionManager;
  9 
 10         public ValueWebSocket(String ipAddress, Int32 port)
 11         {
 12             valueProtocol = new ValueProtocol();
 13             sessionManager = new SessionManager();
 14 
 15             server = new ValueServer(ipAddress, port, Encoding.UTF8);
 16             server.OnReceive += new ValueHelper.ValueSocket.Infrastructure.ReceiveHandler(server_OnReceive);
 17         }
 18 
 19         private void server_OnReceive(ValueHelper.ValueSocket.SocketEvents.ReceiveEventArgs e)
 20         {
 21             // 分析用户是否已存在
 22             if (sessionManager.CheckSessionExist(e.Socket))
 23             {
 24                 Message message = valueProtocol.Decode(e.Data);
 25                 if (message.header.Opcode == OperType.Close)
 26                 {
 27                     removeUser(e.Socket);
 28                 }
 29                 if (message.header.Opcode == OperType.Text)
 30                 {
 31                     String msg = message.Data.ToString();
 32                     execMsg(e.Socket, msg);
 33                 }
 34             }
 35             else
 36             {
 37                 // 用户不存在则添加用户
 38                 // 并发送握手信息与客户端建立连接
 39                 String request = Encoding.UTF8.GetString(e.Data);
 40                 Byte[] response = valueProtocol.GetResponse(request);
 41                 server.Send(e.Socket, response);
 42                 sessionManager.AddSession(e.Socket, request);
 43             }
 44         }
 45 
 46         // 对消息进行的处理
 47         private void execMsg(Socket socket, String message)
 48         {
 49             String name = String.Empty;
 50             foreach (ValueSession session in SessionManager.Sessions)
 51             {
 52                 Socket sk = session.Socket;
 53                 if (sk.Connected)
 54                 {
 55                     if (sk.RemoteEndPoint == socket.RemoteEndPoint)
 56                     {
 57                         name = session.Cookies["name"];
 58                         break;
 59                     }
 60                 }
 61             }
 62 
 63             // 判断私聊还是公共
 64             String[] separator = message.Split(new String[] { "<separator>" }, StringSplitOptions.None);
 65             String msg = String.Concat(name, ": ", separator[1]);
 66             if (separator[0] == "All")
 67                 SendToAll(msg);
 68             else
 69             {
 70                 foreach (ValueSession session in SessionManager.Sessions)
 71                 {
 72                     if (session.Cookies["name"] == separator[0])
 73                     {
 74                         sendTo(session.Socket, msg);
 75                         break;
 76                     }
 77                 }
 78             }
 79         }
 80 
 81         private void removeUser(Socket socket)
 82         {
 83             sessionManager.RemoveSession(socket);
 84         }
 85 
 86         private void SendToAll(String msg)
 87         {
 88             foreach (ValueSession session in SessionManager.Sessions)
 89             {
 90                 sendTo(session.Socket, msg);
 91             }
 92         }
 93 
 94         private Boolean sendTo(Socket socket, String msg)
 95         {
 96             Byte[] data = valueProtocol.Encode(msg);
 97             return server.Send(socket, data);
 98         }
 99 
100         public void Start()
101         {
102             server.Start();
103         }
104 
105         public void Dispose()
106         {
107             sessionManager.Dispose();
108             server.Dispose();
109         }
110     }

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

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

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

ProtocolDraft10.cs
  1  public class ProtocolDraft10 : IProtocol
  2     {
  3         private const String WebSocketKeyPattern = @"Sec\-WebSocket\-Key:\s+(?<key>.*)\r\n";
  4         private const String MagicKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  5         private const Char charOne = '1';
  6         private const Char charZero = '0';
  7 
  8         #region Handshake
  9 
 10         // 发送回复信息完成握手
 11         public Byte[] ProduceResponse(string request)
 12         {
 13             String webSocketKey = Common.GetRegexValue(request, WebSocketKeyPattern)[0].Groups["key"].Value;
 14             String acceptKey = produceAcceptKey(webSocketKey);
 15             StringBuilder stringBuilder = new StringBuilder();
 16             stringBuilder.Append(String.Concat("HTTP/1.1 101 Web Socket Protocol Handshake", Environment.NewLine));
 17             stringBuilder.Append(String.Concat("Upgrade: WebSocket", Environment.NewLine));
 18             stringBuilder.Append(String.Concat("Connection: Upgrade", Environment.NewLine));
 19             stringBuilder.Append(String.Concat("Sec-WebSocket-Accept: ", acceptKey, Environment.NewLine, Environment.NewLine));
 20             String asd = stringBuilder.ToString();
 21             return Encoding.UTF8.GetBytes(stringBuilder.ToString());
 22         }
 23         // 根据Sec-WebSocket-Key和MagicKey生成AcceptKey
 24         private String produceAcceptKey(String webSocketKey)
 25         {
 26             Byte[] acceptKey = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(webSocketKey + MagicKey));
 27             return Convert.ToBase64String(acceptKey);
 28         }
 29 
 30         #endregion
 31 
 32         #region Decode
 33         // 对客户端发来的数据进行解析
 34         public Message Decode(Byte[] data)
 35         {
 36             Byte[] buffer = new Byte[14];
 37             if (data.Length >= 14)
 38                 Buffer.BlockCopy(data, 0, buffer, 0, 14);
 39             else
 40                 Buffer.BlockCopy(data, 0, buffer, 0, data.Length);
 41             MessageHeader header = analyseHead(buffer);
 42             Message msg = new Message();
 43             msg.header = header;
 44 
 45             Byte[] payload;
 46             if (header != null)
 47             {
 48                 payload = new Byte[data.Length - header.PayloadDataStartIndex];
 49                 Buffer.BlockCopy(data, header.PayloadDataStartIndex, payload, 0, payload.Length);
 50                 if (header.MASK == charOne)
 51                 {
 52                     for (int i = 0; i < payload.Length; i++)
 53                     {
 54                         payload[i] = (Byte)(payload[i] ^ header.Maskey[i % 4]);
 55                     }
 56                 }
 57             }
 58             else
 59             {
 60                 msg.Data = Encoding.UTF8.GetString(data);
 61                 return msg;
 62             }
 63 
 64             if (header.Opcode == OperType.Text)
 65                 msg.Data = Encoding.UTF8.GetString(payload);
 66 
 67             return msg;
 68         }
 69         private MessageHeader analyseHead(Byte[] buffer)
 70         {
 71             MessageHeader header = new MessageHeader();
 72             header.FIN = (buffer[0] & 0x80) == 0x80 ? charOne : charZero;
 73             header.RSV1 = (buffer[0] & 0x40) == 0x40 ? charOne : charZero;
 74             header.RSV2 = (buffer[0] & 0x20) == 0x20 ? charOne : charZero;
 75             header.RSV3 = (buffer[0] & 0x10) == 0x10 ? charOne : charZero;
 76 
 77             if ((buffer[0] & 0xA) == 0xA)
 78                 header.Opcode = OperType.Pong;
 79             else if ((buffer[0] & 0x9) == 0x9)
 80                 header.Opcode = OperType.Ping;
 81             else if ((buffer[0] & 0x8) == 0x8)
 82                 header.Opcode = OperType.Close;
 83             else if ((buffer[0] & 0x2) == 0x2)
 84                 header.Opcode = OperType.Binary;
 85             else if ((buffer[0] & 0x1) == 0x1)
 86                 header.Opcode = OperType.Text;
 87             else if ((buffer[0] & 0x0) == 0x0)
 88                 header.Opcode = OperType.Row;
 89 
 90             header.MASK = (buffer[1] & 0x80) == 0x80 ? charOne : charZero;
 91             Int32 len = buffer[1] & 0x7F;
 92             if (len == 126)
 93             {
 94                 header.Payloadlen = (UInt16)(buffer[2] << 8 | buffer[3]);
 95                 if (header.MASK == charOne)
 96                 {
 97                     header.Maskey = new Byte[4];
 98                     Buffer.BlockCopy(buffer, 4, header.Maskey, 0, 4);
 99                     header.PayloadDataStartIndex = 8;
100                 }
101                 else
102                     header.PayloadDataStartIndex = 4;
103             }
104             else if (len == 127)
105             {
106                 Byte[] byteLen = new Byte[8];
107                 Buffer.BlockCopy(buffer, 4, byteLen, 0, 8);
108                 header.Payloadlen = BitConverter.ToUInt64(byteLen, 0);
109                 if (header.MASK == charOne)
110                 {
111                     header.Maskey = new Byte[4];
112                     Buffer.BlockCopy(buffer, 10, header.Maskey, 0, 4);
113                     header.PayloadDataStartIndex = 14;
114                 }
115                 else
116                     header.PayloadDataStartIndex = 10;
117             }
118             else
119             {
120                 if (header.MASK == charOne)
121                 {
122                     header.Maskey = new Byte[4];
123                     Buffer.BlockCopy(buffer, 2, header.Maskey, 0, 4);
124                     header.PayloadDataStartIndex = 6;
125                 }
126                 else
127                     header.PayloadDataStartIndex = 2;
128             }
129             return header;
130         }
131 
132         #endregion
133 
134         #region Encode
135         // 对要发送的数据进行编码一符合草案10的规则
136         public Byte[] Encode(String msg)
137         {
138             Byte[] data = Encoding.UTF8.GetBytes(msg);
139             Int32 dataLength = data.Length;
140 
141             Byte[] head = packetHeader(OperType.Text, dataLength);
142             for (int i = 0; i < data.Length; i++)
143             {
144                 data[i] = (Byte)(data[i] ^ maskKey[i % 4]);
145             }
146 
147             Byte[] result = new Byte[head.Length + dataLength];
148             Buffer.BlockCopy(head, 0, result, 0, head.Length);
149             Buffer.BlockCopy(data, 0, result, head.Length, dataLength);
150             return result;
151         }
152         private const Byte byte80 = 0x80;
153         private Byte[] maskKey = new Byte[] { 113, 105, 97, 110 };
154         private Byte[] packetHeader(OperType operType, Int32 length)
155         {
156             Byte byteHead = (Byte)(byte80 | (Byte)operType);
157             Byte[] byteLen;
158             if (length < 126)
159             {
160                 byteLen = new Byte[1];
161                 byteLen[0] = (Byte)(byte80 | (Byte)length);
162             }
163             else if (length < 65535)
164             {
165                 byteLen = new Byte[3];
166                 byteLen[0] = (Byte)(byte80 | (Byte)126);
167                 for (int i = 1; i < 3; i++)
168                     byteLen[i] = (Byte)(length >> (8 * (2 - i)));
169             }
170             else
171             {
172                 byteLen = new Byte[9];
173                 byteLen[0] = (Byte)(byte80 | (Byte)127);
174                 for (int i = 1; i < 9; i++)
175                     byteLen[i] = (Byte)(length >> (8 * (8 - i)));
176             }
177 
178             Byte[] packet = new Byte[1 + byteLen.Length + maskKey.Length];
179             packet[0] = byteHead;
180             Buffer.BlockCopy(byteLen, 0, packet, 1, byteLen.Length);
181             Buffer.BlockCopy(maskKey, 0, packet, 1 + byteLen.Length, maskKey.Length);
182             return packet;
183         }
184 
185         #endregion
186     }

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

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

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

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>


完毕,有不当言论,欢迎指正.