基于.Net Websocket协议编程

Websocket是一个持久化的协议。

基于.Net服务端:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Security.Cryptography;

namespace WebSocketService
{
    /// <summary>
    /// webSocket服务端,使用C#Socket实现
    /// </summary>
    public class WebSocketServer
    {
        //全局变量集合,字典类<连接字符串,socket客户端>
        public static Dictionary<string, ClientConnectSocket> dic = new Dictionary<string, ClientConnectSocket>();

        Socket socketServer = null;
        /// <summary>
        /// 启动服务
        /// </summary>
        public void StarServer()
        {
            //实例化socket,TCP流
            socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定ip,端口
            socketServer.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9000));
            //同时监听数量,最大为10个
            socketServer.Listen(10);
            //异步接受连接
            socketServer.BeginAccept(new AsyncCallback(Accept), socketServer);
        }
        /// <summary>
        /// 异步接受回调方法
        /// </summary>
        /// <param name="ia"></param>
        private void Accept(IAsyncResult ia)
        {
            //连接socket
            Socket connectSocket = socketServer.EndAccept(ia);
            IPEndPoint clientipe = (IPEndPoint)connectSocket.RemoteEndPoint;
            //实例化连接客户端socket类
            ClientConnectSocket c = new ClientConnectSocket(connectSocket);
            //添加到集合
            dic.Add(clientipe.ToString(), c);
            //循环异步接受连接
            socketServer.BeginAccept(new AsyncCallback(Accept), socketServer);
        }
    }
    /// <summary>
    /// 连接客户端socket类
    /// </summary>
    public class ClientConnectSocket
    {
        int judge = 0;//判断第一次接受消息,因为与websocket连接上就会给我们会发送一个xml的消息来和我们握手,
        Socket socket = null;
        byte[] bys = new byte[1000];
        public ClientConnectSocket(Socket sok)
        {
            socket = sok;
            //异步接受数据
            socket.BeginReceive(bys, 0, bys.Length, SocketFlags.None, new AsyncCallback(Receive), socket);
        }
        private void Receive(IAsyncResult ia)
        {
            try
            {
                int length = socket.EndReceive(ia);
                if (judge == 0)
                {
                    //-----------------------------
                    //如果是websocket刚连接上我们发送的消息,这个消息用于和我们握手,方法解析这个数据,回复一个握手数据
                    socket.Send(PackHandShakeData(GetSecKeyAccetp(bys, length)));
                    judge = 1;

                }
                else
                {
                    //普通接受数据需要用这个方法来解析数据------------------------------
                    string clientMsg = AnalyticData(bys, length);
                    foreach (var item in WebSocketServer.dic)
                    {
                        item.Value.send(clientMsg);
                    }

                }
                socket.BeginReceive(bys, 0, bys.Length, SocketFlags.None, new AsyncCallback(Receive), socket);
            }
            catch (Exception ex)
            {
 
            }
        }
        //发送消息
        public void send(string msg)
        {
            try
            {
                //发送消息需要用下边方法来解析----------------
                socket.Send(PackData(msg));
            }
            catch (Exception ex)
            { }
        }


        #region 和websocket通讯重要的方法
        /// <summary>
        /// 打包握手信息
        /// </summary>
        /// <param name="secKeyAccept">Sec-WebSocket-Accept</param>
        /// <returns>数据包</returns>
        private byte[] PackHandShakeData(string secKeyAccept)
        {
            var responseBuilder = new StringBuilder();
            responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + Environment.NewLine);
            responseBuilder.Append("Upgrade: websocket" + Environment.NewLine);
            responseBuilder.Append("Connection: Upgrade" + Environment.NewLine);
            responseBuilder.Append("Sec-WebSocket-Accept: " + secKeyAccept + Environment.NewLine + Environment.NewLine);
            return Encoding.UTF8.GetBytes(responseBuilder.ToString());
        }

        /// <summary>
        /// 生成Sec-WebSocket-Accept
        /// </summary>
        /// <param name="handShakeText">客户端握手信息</param>
        /// <returns>Sec-WebSocket-Accept</returns>
        private string GetSecKeyAccetp(byte[] handShakeBytes, int bytesLength)
        {
            string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, bytesLength);
            string key = string.Empty;
            Regex r = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
            Match m = r.Match(handShakeText);
            if (m.Groups.Count != 0)
            {
                key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
            }
            byte[] encryptionString = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
            return Convert.ToBase64String(encryptionString);
        }

        /// <summary>
        /// 解析客户端数据包
        /// </summary>
        /// <param name="recBytes">服务器接收的数据包</param>
        /// <param name="recByteLength">有效数据长度</param>
        /// <returns></returns>
        private string AnalyticData(byte[] recBytes, int recByteLength)
        {
            if (recByteLength < 2) { return string.Empty; }

            bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit,1表示最后一帧  
            if (!fin)
            {
                return string.Empty;// 超过一帧暂不处理 
            }

            bool mask_flag = (recBytes[1] & 0x80) == 0x80; // 是否包含掩码  
            if (!mask_flag)
            {
                return string.Empty;// 不包含掩码的暂不处理
            }

扫描二维码关注公众号,回复: 2888968 查看本文章

            int payload_len = recBytes[1] & 0x7F; // 数据长度  

            byte[] masks = new byte[4];
            byte[] payload_data;

            if (payload_len == 126)
            {
                Array.Copy(recBytes, 4, masks, 0, 4);
                payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);
                payload_data = new byte[payload_len];
                Array.Copy(recBytes, 8, payload_data, 0, payload_len);

            }
            else if (payload_len == 127)
            {
                Array.Copy(recBytes, 10, masks, 0, 4);
                byte[] uInt64Bytes = new byte[8];
                for (int i = 0; i < 8; i++)
                {
                    uInt64Bytes[i] = recBytes[9 - i];
                }
                UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);

                payload_data = new byte[len];
                for (UInt64 i = 0; i < len; i++)
                {
                    payload_data[i] = recBytes[i + 14];
                }
            }
            else
            {
                Array.Copy(recBytes, 2, masks, 0, 4);
                payload_data = new byte[payload_len];
                Array.Copy(recBytes, 6, payload_data, 0, payload_len);

            }

            for (var i = 0; i < payload_len; i++)
            {
                payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);
            }

            return Encoding.UTF8.GetString(payload_data);
        }


        /// <summary>
        /// 打包服务器数据
        /// </summary>
        /// <param name="message">数据</param>
        /// <returns>数据包</returns>
        private byte[] PackData(string message)
        {
            byte[] contentBytes = null;
            byte[] temp = Encoding.UTF8.GetBytes(message);

            if (temp.Length < 126)
            {
                contentBytes = new byte[temp.Length + 2];
                contentBytes[0] = 0x81;
                contentBytes[1] = (byte)temp.Length;
                Array.Copy(temp, 0, contentBytes, 2, temp.Length);
            }
            else if (temp.Length < 0xFFFF)
            {
                contentBytes = new byte[temp.Length + 4];
                contentBytes[0] = 0x81;
                contentBytes[1] = 126;
                contentBytes[2] = (byte)(temp.Length & 0xFF);
                contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF);
                Array.Copy(temp, 0, contentBytes, 4, temp.Length);
            }
            else
            {
                // 暂不处理超长内容  
            }

            return contentBytes;
        }
        #endregion
    }
}
 

客户端代码,基于.NETMVC框架
@{
    ViewBag.Title = "About";
}
@Scripts.Render("~/bundles/jquery")
<script type="text/javascript">
    var ws;
    $(
        function () {
            $("#btnConnect").click(function () {
                $("#msg").text("Connection...");
                //alert($(document.getElementById("serverIP").value)); 
                //alert($("#ServerIP").text()); 
                //alert($("#serverIP").val());
                ws = new WebSocket("ws://" + $("#serverIP").val() + ":" + $("#serverPort").val() +"/");// + "/api/WSChat");
                ws.onopen = function () {
                    $("#msg").text("Connected!");
                };
                ws.onmessage = function (result) {
                    $("#msg").text(result.data);
                };
                ws.onerror = function (error) {
                    $("#msg").text(error.data);
                };
                ws.onclose = function () {
                    $("#msg").text("Disconnected!");
                };
            });
            $("#btnSend").click(function () {
                if (ws.readyState == WebSocket.OPEN) {
                    ws.send($("#txtInput").val());
                }
                else {
                    $("#msg").text("Connection is Closed!");
                }
            });
            $("#btnDisconnect").click(function () {
                ws.close();
            });
        }
    );
</script>

<div>
    @*<div>
        <text id="serverIP">127.0.0.1</text>
        <text id="serverPort">9000</text>
    </div>*@
        @*<input type="text" value="127.0.0.1" id="serverIP" placeholder="服务器IP" autofocus="autofocus" />
        <input type="text" value="9000" id="serverPort" placeholder="服务器端口" />*@
    <div>
        @*@Html.TextBox("serverIP", "serverIP", new {Value="127.0.0.1"})
        @Html.TextBox("serverPort", "", new {Value="9000" })*@
        <div>
            <input type="text" id="serverIP" value="127.0.0.1" placeholder="服务器IP" autofocus="autofocus" />
            <input type="text" id="serverPort" value="9000" placeholder="服务器端口" />
       </div>
        <div>
            <input type="button" value="Connect" id="btnConnect" />
            <input type="button" value="DisConnect" id="btnDisConnect" />
        </div>
    </div>
    <div>
        <input type="text" id="txtInput" />
        <input type="button" value="Send" id="btnSend" />
    </div>
    <div>
        <textarea id="msg" cols="80" rows="20" readonly="readonly"></textarea>
    </div>
</div>
 

猜你喜欢

转载自blog.csdn.net/tianhelzl/article/details/81837227
今日推荐