代码改变世界

实战 HTML5 WebSocket 聊天室.net实现

2011-03-29 14:17 by LoujaDy, ... 阅读, ... 评论, 收藏, 编辑

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, 0new 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, 0new 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, 08); 
            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 + 160, 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, 04);
            Array.Copy(ServerKey2, 
0, concatenatedKeys, 44);
            Array.Copy(last8Bytes, 
0, concatenatedKeys, 88);

            
// 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, 0new 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 });
        }

      
    }

 



作者:Louja
出处:http://loujady.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此声明,且在文章页面给出原文连接,否则保留追究法律责任的权利。