[转载]实战 HTML5 WebSocket 聊天室.net实现

[转载]实战 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 });
        }

      
    }
赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏