实战 HTML5 WebSocket 聊天室.net实现
2011-03-29 14:17 LoujaDy 阅读(9827) 评论(25) 编辑 收藏 举报WebSocket protocol 是HTML5一种新的协议(protocol)。目前紧测试了三个浏览器支持(Chrome,Firefox4,Safari)
客户端:
var ws = new WebSocket(con);
//与服务器握手成功
ws.onopen = onOpen;
//接收到服务器消息
ws.onmessage = onMessage;
//断开连接消息
ws.onclose = onClose;
//通读错误
进入代码正题服务端
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 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 });
}
}
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 });
}
}
作者:Louja
出处:http://loujady.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此声明,且在文章页面给出原文连接,否则保留追究法律责任的权利。