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)
  • 相关阅读:
    C# 上传辅助方法
    C# 经纬度计算
    C#Excel导出反射数据集
    C# 数据模板导出
    C# 百度经纬度获取地址信息
    【平台开发】— 5.后端:代码分层
    【平台开发】— 4.mysql建库建表
    【平台开发】— 3.前端开发思路
    【平台开发】— 2.前端:vue-element-admin
    【平台开发】— 1.开篇
  • 原文地址:https://www.cnblogs.com/developer-ios/p/8428703.html
Copyright © 2011-2022 走看看