zoukankan      html  css  js  c++  java
  • C# 实现WEBSOCKET聊天应用示例

    C# 实现WEBSOCKET聊天应用示例  

    http://blog.163.com/da7_1@126/blog/static/10407267820121016103055506/

    2012-11-16 22:30:55|  分类: .Net编程 |  标签:websocket  |举报|字号 订阅

     
     

    用C# ASP.NET MVC 实现WebSocket ,对于WebSocket想必都很了解了,不多说.

    东西做的很粗糙 只能实现基本的聊天功能,不过基本的通信实现了,那么后序的扩展应该也不难(个人这么认为...)

    先看下效果

    C 实现WEBSOCKET聊天应用示例 - 空客 - Program  Management

    可同时支持群聊和私聊 源码下载地址

    http://download.csdn.net/detail/formularz/4668280

    首先介绍下ValueWebSocket.cs 这个文件 主要是对与客户端的通信进行集中控制

    1.ValueServer: Socket服务端

    2.ValueProtocol:对WebSocket通信的数据加以解析

    3.SessionManager: 集中管理在线用户

    ValueWebSocket.cs
      

    ValueWebSocket.cs
    public class ValueWebSocket
    {
    // WebSocket服务端
    private ValueServer server;
    // 解析协议
    private ValueProtocol valueProtocol;
    // 管理在线用户
    private SessionManager sessionManager;

    public ValueWebSocket(String ipAddress, Int32 port)
    {
    valueProtocol = new ValueProtocol();
    sessionManager = new SessionManager();

    server = new ValueServer(ipAddress, port, Encoding.UTF8);
    server.OnReceive += new ValueHelper.ValueSocket.Infrastructure.ReceiveHandler(server_OnReceive);
    }

    private void server_OnReceive(ValueHelper.ValueSocket.SocketEvents.ReceiveEventArgs e)
    {
    // 分析用户是否已存在
    if (sessionManager.CheckSessionExist(e.Socket))
    {
    Message message = valueProtocol.Decode(e.Data);
    if (message.header.Opcode == OperType.Close)
    {
    removeUser(e.Socket);
    }
    if (message.header.Opcode == OperType.Text)
    {
    String msg = message.Data.ToString();
    execMsg(e.Socket, msg);
    }
    }
    else
    {
    // 用户不存在则添加用户
    // 并发送握手信息与客户端建立连接
    String request = Encoding.UTF8.GetString(e.Data);
    Byte[] response = valueProtocol.GetResponse(request);
    server.Send(e.Socket, response);
    sessionManager.AddSession(e.Socket, request);
    }
    }

    // 对消息进行的处理
    private void execMsg(Socket socket, String message)
    {
    String name = String.Empty;
    foreach (ValueSession session in SessionManager.Sessions)
    {
    Socket sk = session.Socket;
    if (sk.Connected)
    {
    if (sk.RemoteEndPoint == socket.RemoteEndPoint)
    {
    name = session.Cookies["name"];
    break;
    }
    }
    }

    // 判断私聊还是公共
    String[] separator = message.Split(new String[] { "<separator>" }, StringSplitOptions.None);
    String msg = String.Concat(name, ": ", separator[1]);
    if (separator[0] == "All")
    SendToAll(msg);
    else
    {
    foreach (ValueSession session in SessionManager.Sessions)
    {
    if (session.Cookies["name"] == separator[0])
    {
    sendTo(session.Socket, msg);
    break;
    }
    }
    }
    }

    private void removeUser(Socket socket)
    {
    sessionManager.RemoveSession(socket);
    }

    private voidSendToAll(String msg)
    {
    foreach(ValueSession session inSessionManager.Sessions)
    {
    sendTo(session.Socket, msg);
    }
    }

    privateBoolean sendTo(Socket socket,String msg)
    {
    Byte[] data = valueProtocol.Encode(msg);
    return server.Send(socket, data);
    }

    publicvoidStart()
    {
    server.Start();
    }

    publicvoidDispose()
    {
    sessionManager.Dispose();
    server.Dispose();
    }
    }

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

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

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

     ProtocolDraft10.cs
      

    ProtocolDraft10.cs
    public class ProtocolDraft10 : IProtocol
    {
    private const String WebSocketKeyPattern = @"Sec-WebSocket-Key:s+(?<key>.*) ";
    private const String MagicKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    private const Char charOne = '1';
    private const Char charZero = '0';

    #region Handshake

    // 发送回复信息完成握手
    public Byte[] ProduceResponse(string request)
    {
    String webSocketKey = Common.GetRegexValue(request, WebSocketKeyPattern)[0].Groups["key"].Value;
    String acceptKey = produceAcceptKey(webSocketKey);
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.Append(String.Concat("HTTP/1.1 101 Web Socket Protocol Handshake", Environment.NewLine));
    stringBuilder.Append(String.Concat("Upgrade: WebSocket", Environment.NewLine));
    stringBuilder.Append(String.Concat("Connection: Upgrade", Environment.NewLine));
    stringBuilder.Append(String.Concat("Sec-WebSocket-Accept: ", acceptKey, Environment.NewLine, Environment.NewLine));
    String asd = stringBuilder.ToString();
    return Encoding.UTF8.GetBytes(stringBuilder.ToString());
    }
    // 根据Sec-WebSocket-Key和MagicKey生成AcceptKey
    private String produceAcceptKey(String webSocketKey)
    {
    Byte[] acceptKey = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(webSocketKey + MagicKey));
    return Convert.ToBase64String(acceptKey);
    }

    #endregion

    #region Decode
    // 对客户端发来的数据进行解析
    public Message Decode(Byte[] data)
    {
    Byte[] buffer = new Byte[14];
    if (data.Length >= 14)
    Buffer.BlockCopy(data, 0, buffer, 0, 14);
    else
    Buffer.BlockCopy(data, 0, buffer, 0, data.Length);
    MessageHeader header = analyseHead(buffer);
    Message msg = new Message();
    msg.header = header;

    Byte[] payload;
    if (header != null)
    {
    payload = new Byte[data.Length - header.PayloadDataStartIndex];
    Buffer.BlockCopy(data, header.PayloadDataStartIndex, payload, 0, payload.Length);
    if (header.MASK == charOne)
    {
    for (int i = 0; i < payload.Length; i++)
    {
    payload[i] = (Byte)(payload[i] ^ header.Maskey[i % 4]);
    }
    }
    }
    else
    {
    msg.Data = Encoding.UTF8.GetString(data);
    return msg;
    }

    if(header.Opcode==OperType.Text)
    msg.Data=Encoding.UTF8.GetString(payload);

    return msg;
    }
    privateMessageHeader analyseHead(Byte[] buffer)
    {
    MessageHeader header =newMessageHeader();
    header.FIN =(buffer[0]&0x80)==0x80? charOne : charZero;
    header.RSV1 =(buffer[0]&0x40)==0x40? charOne : charZero;
    header.RSV2 =(buffer[0]&0x20)==0x20? charOne : charZero;
    header.RSV3 =(buffer[0]&0x10)==0x10? charOne : charZero;

    if((buffer[0]&0xA)==0xA)
    header.Opcode=OperType.Pong;
    elseif((buffer[0]&0x9)==0x9)
    header.Opcode=OperType.Ping;
    elseif((buffer[0]&0x8)==0x8)
    header.Opcode=OperType.Close;
    elseif((buffer[0]&0x2)==0x2)
    header.Opcode=OperType.Binary;
    elseif((buffer[0]&0x1)==0x1)
    header.Opcode=OperType.Text;
    elseif((buffer[0]&0x0)==0x0)
    header.Opcode=OperType.Row;

    header.MASK =(buffer[1]&0x80)==0x80? charOne : charZero;
    Int32 len = buffer[1]&0x7F;
    if(len ==126)
    {
    header.Payloadlen=(UInt16)(buffer[2]<<8| buffer[3]);
    if(header.MASK == charOne)
    {
    header.Maskey=newByte[4];
    Buffer.BlockCopy(buffer,4, header.Maskey,0,4);
    header.PayloadDataStartIndex=8;
    }
    else
    header.PayloadDataStartIndex=4;
    }
    elseif(len ==127)
    {
    Byte[] byteLen =newByte[8];
    Buffer.BlockCopy(buffer,4, byteLen,0,8);
    header.Payloadlen=BitConverter.ToUInt64(byteLen,0);
    if(header.MASK == charOne)
    {
    header.Maskey=newByte[4];
    Buffer.BlockCopy(buffer,10, header.Maskey,0,4);
    header.PayloadDataStartIndex=14;
    }
    else
    header.PayloadDataStartIndex=10;
    }
    else
    {
    if(header.MASK == charOne)
    {
    header.Maskey=newByte[4];
    Buffer.BlockCopy(buffer,2, header.Maskey,0,4);
    header.PayloadDataStartIndex=6;
    }
    else
    header.PayloadDataStartIndex=2;
    }
    return header;
    }

    #endregion

    #region Encode
    // 对要发送的数据进行编码一符合草案10的规则
    publicByte[]Encode(String msg)
    {
    Byte[] data =Encoding.UTF8.GetBytes(msg);
    Int32 dataLength = data.Length;

    Byte[] head = packetHeader(OperType.Text, dataLength);
    for(int i =0; i < data.Length; i++)
    {
    data[i]=(Byte)(data[i]^ maskKey[i %4]);
    }

    Byte[] result =newByte[head.Length+ dataLength];
    Buffer.BlockCopy(head,0, result,0, head.Length);
    Buffer.BlockCopy(data,0, result, head.Length, dataLength);
    return result;
    }
    privateconstByte byte80 =0x80;
    privateByte[] maskKey =newByte[]{113,105,97,110};
    privateByte[] packetHeader(OperType operType,Int32 length)
    {
    Byte byteHead =(Byte)(byte80 |(Byte)operType);
    Byte[] byteLen;
    if(length <126)
    {
    byteLen =newByte[1];
    byteLen[0]=(Byte)(byte80 |(Byte)length);
    }
    elseif(length <65535)
    {
    byteLen =newByte[3];
    byteLen[0]=(Byte)(byte80 |(Byte)126);
    for(int i =1; i <3; i++)
    byteLen[i]=(Byte)(length >>(8*(2- i)));
    }
    else
    {
    byteLen =newByte[9];
    byteLen[0]=(Byte)(byte80 |(Byte)127);
    for(int i =1; i <9; i++)
    byteLen[i]=(Byte)(length >>(8*(8- i)));
    }

    Byte[] packet =newByte[1+ byteLen.Length+ maskKey.Length];
    packet[0]= byteHead;
    Buffer.BlockCopy(byteLen,0, packet,1, byteLen.Length);
    Buffer.BlockCopy(maskKey,0, packet,1+ byteLen.Length, maskKey.Length);
    return packet;
    }

    #endregion
    }

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

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

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

    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>

     
     
     
     
    阅读(38849)评论(8)
  • 相关阅读:
    在SQLite中使用索引优化查询速度
    SQLite支持的SQL数据操作
    left (outer) join , right (outer) join, full (outer) join, (inner) join, cross join 区别
    深入理解Android内存管理原理(六)
    Merge Sorted Array
    Sort Colors
    Construct Binary Tree from Preorder and Inorder Traversal
    Binary Tree Postorder Traversal
    Symmetric Tree
    Rotate Image
  • 原文地址:https://www.cnblogs.com/developer-ios/p/8428703.html
Copyright © 2011-2022 走看看