[转载]实战 HTML5 WebSocket 聊天室.net实现 – 大月&小年 – 博客园.
WebSocket protocol 是HTML5一种新的协议(protocol)。目前紧测试了三个浏览器支持(Chrome,Firefox4,Safari)
客户端:
var ws = new WebSocket(con);
//与服务器握手成功
ws.onopen = onOpen;
//接收到服务器消息
ws.onmessage = onMessage;
//断开连接消息
ws.onclose = onClose;
//通读错误
ws.onerror = onError;
进入代码正题服务端
using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; using System.Net; using System.Threading; namespace DotNetWebSocket.Engine { //广播事件 public delegate void BroadcastEvent(MessageEntity me); public class WebSocketServer:IDisposable { private Socket serverListener; //回调,用于消息传给上层应用 ICallback callback = null; //广播事件 public BroadcastEvent BroadcastMessage=null; //客户端连接列表 List<ClientSocketInstance> listConnection = new List<ClientSocketInstance>(); public WebSocketServer(ICallback callback) { this.callback = callback; } /// <summary> /// 启动等待连接 /// </summary> public void StartConnection() { serverListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); // Start the socket string[] ip = WebSocketProtocol.GetInstance.ServerId.Split('.'); IPAddress localIp = new IPAddress(new byte[] { Convert.ToByte(ip[0]), Convert.ToByte(ip[1]), Convert.ToByte(ip[2]),Convert.ToByte(ip[3]) }); serverListener.Bind(new IPEndPoint(localIp, WebSocketProtocol.GetInstance.ServerPort)); serverListener.Listen(WebSocketProtocol.GetInstance.ConnectionsCount); while (true) { //等待客户端请求 Socket sc = serverListener.Accept(); if (sc != null) { Thread.Sleep(100); ClientSocketInstance ci = new ClientSocketInstance(); ci.ClientSocket = sc; //初始化三个事件 ci.NewUserConnection += new ClientSocketEvent(Ci_NewUserConnection); ci.ReceiveData += new ClientSocketEvent(Ci_ReceiveData); ci.DisConnection += new ClientSocketEvent(Ci_DisConnection); //开始与客户端握手[握手成功,即可通讯了] ci.ClientSocket.BeginReceive(ci.receivedDataBuffer, 0, ci.receivedDataBuffer.Length, 0, new AsyncCallback(ci.StartHandshake), ci.ClientSocket.Available); listConnection.Add(ci); } } } /// <summary> /// 断开服务端Socket /// </summary> /// <param name="sender"></param> /// <param name="me"></param> private void Ci_DisConnection(object sender, MessageEntity me) { callback.DisConnection(sender as ClientSocketInstance, me); } /// <summary> /// 接收数据 /// </summary> /// <param name="sender"></param> /// <param name="me"></param> private void Ci_ReceiveData(object sender, MessageEntity me) { callback.Read(sender as ClientSocketInstance, me); } /// <summary> /// 握手成功手的连接 /// </summary> /// <param name="sender"></param> /// <param name="me"></param> private void Ci_NewUserConnection(object sender, MessageEntity me) { ClientSocketInstance ci=sender as ClientSocketInstance; BroadcastMessage += new BroadcastEvent(ci.SendMessage); callback.NewUserConnectionJoin(ci, me); } #region IDisposable 成员 public void Dispose() { serverListener = null; } #endregion }
using System.Collections.Generic; using System.Text; using System.Net.Sockets; using Newtonsoft.Json; using DotNetWebSocket.Engine.Message; namespace DotNetWebSocket.Engine { public delegate void ClientSocketEvent(object sender,MessageEntity me); public class ClientSocketInstance { private byte[] ServerKey1; private byte[] ServerKey2; public string name; public string Name { get { return name; } set { name = value; } } public Socket ClientSocket; public byte[] receivedDataBuffer; public event ClientSocketEvent NewUserConnection; public event ClientSocketEvent ReceiveData; public event ClientSocketEvent DisConnection; public ClientSocketInstance() { receivedDataBuffer = new byte[WebSocketProtocol.GetInstance.MaxBufferSize]; ServerKey1 = new byte[4]; ServerKey2 = new byte[4]; } /// <summary> /// 接收数据 /// </summary> /// <param name="result"></param> private void Read(IAsyncResult result) { if (!ClientSocket.Connected) return; try { // Web Socket protocol: 0x00开头,0xFF结尾 System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding(); int startIndex = 0; int endIndex = 0; //查找起启位置 while (receivedDataBuffer[startIndex] == 0x00) startIndex++; // 查找结束位置 endIndex = startIndex + 1; while (receivedDataBuffer[endIndex] != 0xff && endIndex != WebSocketProtocol.GetInstance.MaxBufferSize - 1) endIndex++; if (endIndex == WebSocketProtocol.GetInstance.MaxBufferSize - 1) endIndex = WebSocketProtocol.GetInstance.MaxBufferSize; string messageReceived = decoder.GetString(receivedDataBuffer, startIndex, endIndex - startIndex); MessageEntity me = JsonConvert.DeserializeObject(messageReceived, typeof(MessageEntity)) as MessageEntity; if (!string.IsNullOrEmpty(this.Name)) { ReceiveData(this, me); } else if (me.MessageId.ToLower() == "login") { if (NewUserConnection != null) { this.Name = (Newtonsoft.Json.JsonConvert.DeserializeObject(me.MessageContent,typeof(ChartMessage)) as ChartMessage).Message; NewUserConnection(this, me); } } /* MessageEntity me=new MessageEntity(); me.MessageContent = messageReceived; ReceiveData(this, me);*/ Array.Clear(receivedDataBuffer, 0, receivedDataBuffer.Length); ClientSocket.BeginReceive(receivedDataBuffer, 0, receivedDataBuffer.Length, 0, new AsyncCallback(Read), null); } catch(Exception ex) { DisConnection(this,null); } } /// <summary> /// 发送与客户端握手信息 /// </summary> /// <param name="status"></param> public void StartHandshake(IAsyncResult status) { int ClientHandshakeLength = (int) status.AsyncState; byte[] last8Bytes = new byte[8]; Array.Copy(receivedDataBuffer, ClientHandshakeLength - 8, last8Bytes, 0, 8); ASCIIEncoding decoder = new System.Text.ASCIIEncoding(); string ClientHandshake = decoder.GetString(receivedDataBuffer, 0, ClientHandshakeLength - 8); string[] ClientHandshakeLines = ClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries); /*请求中的Sec-WebSocket-Key1中所有的数字连在一起 * 然后除以空格的个数,得到结果1。 * 然后从Key2同样的得到结果2, * 这两个结果取整后切断为32位整数, * 然后转成大头的网络顺序(Big-Endian), * 这两个结果和请求中最后的8个字节拼在一起, * 然后计算MD5。 这个MD5的16字节结果就是服务器的反馈key*/ //计算16位的服务端Key foreach (string Line in ClientHandshakeLines) { if (Line.Contains("Sec-WebSocket-Key1:")) BuildServerSecKey(1, Line.Substring(Line.IndexOf(":") + 2)); if (Line.Contains("Sec-WebSocket-Key2:")) BuildServerSecKey(2, Line.Substring(Line.IndexOf(":") + 2)); } //握手头信息 byte[] HandshakeText = Encoding.ASCII.GetBytes(WebSocketProtocol.GetInstance.ServerHandshake); byte[] serverHandshakeResponse = new byte[HandshakeText.Length + 16]; byte[] serverKey = BuildFullServerSecKey(last8Bytes); Array.Copy(HandshakeText, serverHandshakeResponse, HandshakeText.Length); Array.Copy(serverKey, 0, serverHandshakeResponse, HandshakeText.Length, 16); ClientSocket.BeginSend(serverHandshakeResponse, 0, HandshakeText.Length + 16, 0, HandshakeSuccess, null); } /// <summary> /// 根据客户端握手Key生成客户端响应给客户端的安全Key /// </summary> /// <param name="keyNum"></param> /// <param name="clientKey"></param> private void BuildServerSecKey(int keyNum, string clientKey) { string partialServerKey = ""; byte[] currentKey; int spacesNum = 0; char[] keyChars = clientKey.ToCharArray(); //根据客户端Key获取得其中的空格数及其中的数字 foreach (char currentChar in keyChars) { if (char.IsDigit(currentChar)) partialServerKey += currentChar; if (char.IsWhiteSpace(currentChar)) spacesNum++; } try { //用获取的数字除于空格数,再转成大头网络数据 currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey) / spacesNum)); if (BitConverter.IsLittleEndian) Array.Reverse(currentKey); if (keyNum == 1) ServerKey1 = currentKey; else ServerKey2 = currentKey; } catch { if (ServerKey1 != null) Array.Clear(ServerKey1, 0, ServerKey1.Length); if (ServerKey2 != null) Array.Clear(ServerKey2, 0, ServerKey2.Length); } } /// <summary> ///生成完整的16位安全Key[将Key1和Key2加在一起再加客户端握手信息的手八位] MD5后返回 /// </summary> /// <returns></returns> private byte[] BuildFullServerSecKey(byte[] last8Bytes) { byte[] concatenatedKeys = new byte[16]; Array.Copy(ServerKey1, 0, concatenatedKeys, 0, 4); Array.Copy(ServerKey2, 0, concatenatedKeys, 4, 4); Array.Copy(last8Bytes, 0, concatenatedKeys, 8, 8); // MD5 Hash System.Security.Cryptography.MD5 MD5Service = System.Security.Cryptography.MD5.Create(); return MD5Service.ComputeHash(concatenatedKeys); } /// <summary> /// 握手成功,此时客户端与服务端建立接连,可进行通讯 /// </summary> /// <param name="result"></param> private void HandshakeSuccess(IAsyncResult result) { ClientSocket.EndSend(result); ClientSocket.BeginReceive(receivedDataBuffer, 0, receivedDataBuffer.Length, 0, new AsyncCallback(Read), null); } /// <summary> /// 发送消息 /// </summary> /// <param name="me"></param> public void SendMessage(MessageEntity me) { ClientSocket.Send(new byte[] {0x00}); ClientSocket.Send(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(me))); ClientSocket.Send(new byte[] { 0xff }); } }