.net/c# 运用websocket做网站的消息通知

.net/c# 运用websocket做网站的消息通知

websocket在这就不多说了之前小渣渣有帖子已经介绍过了,不了解的可以自行百度或者去这个帖子看(.net/c# 模拟websocket客户端连接[源码]
https://www.itsvse.com/thread-3652-1-1.html)。
好了不多说了,web的消息通知之前看了好多人用的ajax轮训去查的有点浪费资源,也有长连接做的,我用了WebSocket+iNotify.js,
iNotify.js 是一个封装好了的网页消息通知,它用到了Notification()这API接口,这个接口可以在你网站最小化的情况下提示窗口也会在右下角弹出,很好用的。
不过在谷歌浏览器里62版本后需要网站是https的才可以,这个到后面在后面会提到。
还有用到了websocket心跳,在设定的时间里会向服务端要你需要的数据。
好了说了这么多废话,下面把代码贴出来,源码后面附上。
客户端:

heartBeat.js 

var ws;//websocket实例

public class WebSocket
{
   private Dictionary SessionPool = new Dictionary();
   private Dictionary MsgPool = new Dictionary();
    #region 启动WebSocket服务
    /// <summary>
    /// 启动WebSocket服务
    /// </summary>  
    public void start(int port)
    {
        Socket SockeServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        SockeServer.Bind(new IPEndPoint(IPAddress.Any, port));
        SockeServer.Listen(20);
        SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer);

    }
    #endregion

    #region 处理客户端连接请求
    /// <summary>
    /// 处理客户端连接请求
    /// </summary>
    /// <param name="result"></param>
    private void Accept(IAsyncResult socket)
    {
        // 还原传入的原始套接字
        Socket SockeServer = (Socket)socket.AsyncState;
        // 在原始套接字上调用EndAccept方法,返回新的套接字
        Socket SockeClient = SockeServer.EndAccept(socket);
        byte[] buffer = new byte[4096];
        try
        {
            //接收客户端的数据
            SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
            //保存登录的客户端
            Session session = new Session();
            session.SockeClient = SockeClient;
            session.IP = SockeClient.RemoteEndPoint.ToString();
            session.buffer = buffer;
            lock (SessionPool)
            {
                if (SessionPool.ContainsKey(session.IP))
                {
                    this.SessionPool.Remove(session.IP);
                }
                this.SessionPool.Add(session.IP, session);
            }
            //准备接受下一个客户端
            SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer);
        }
        catch (Exception ex)
        {

        }
    }
    #endregion

    #region 处理接收的数据
    /// <summary>
    /// 处理接受的数据
    /// </summary>
    /// <param name="socket"></param>
    private void Recieve(IAsyncResult socket)
    {
        Socket SockeClient = (Socket)socket.AsyncState;
        string IP = SockeClient.RemoteEndPoint.ToString();
        if (SockeClient == null || !SessionPool.ContainsKey(IP))
        {
            return;
        }
        try
        {
            int length = SockeClient.EndReceive(socket);
            byte[] buffer = SessionPool[IP].buffer;
            SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
            string msg = Encoding.UTF8.GetString(buffer, 0, length);
            //  websocket建立连接的时候,除了TCP连接的三次握手,websocket协议中客户端与服务器想建立连接需要一次额外的握手动作
            if (msg.Contains("Sec-WebSocket-Key"))
            {
                SockeClient.Send(PackageHandShakeData(buffer, length));
                SessionPool[IP].isWeb = true;
                return;
            }
            if (SessionPool[IP].isWeb)
            {
                msg = AnalyzeClientData(buffer, length);
            }

            byte[] msgBuffer = PackageServerData(msg);
            //foreach (Session se in SessionPool.Values)
            //{
            //    se.SockeClient.Send(msgBuffer, msgBuffer.Length, SocketFlags.None);
            //}
            Session currentClient = SessionPool[IP];
            currentClient.SockeClient.Send(msgBuffer, msgBuffer.Length, SocketFlags.None);
        }
        catch
        {
            //SockeClient.Disconnect(true);
            SessionPool.Remove(IP);
        }
    }
    #endregion

    #region 客户端和服务端的响应
    /*
     * 客户端向服务器发送请求
     * 
     * GET / HTTP/1.1
     * Origin: https://localhost:1416
     * Sec-WebSocket-Key: vDyPp55hT1PphRU5OAe2Wg==
     * Connection: Upgrade
     * Upgrade: Websocket
     *Sec-WebSocket-Version: 13
     * User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
     * Host: localhost:8064
     * DNT: 1
     * Cache-Control: no-cache
     * Cookie: DTRememberName=admin
     * 
     * 服务器给出响应
     * 
     * HTTP/1.1 101 Switching Protocols
     * Upgrade: websocket
     * Connection: Upgrade
     * Sec-WebSocket-Accept: xsOSgr30aKL2GNZKNHKmeT1qYjA=
     * 
     * 在请求中的“Sec-WebSocket-Key”是随机的,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个魔幻字符串
     * “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用 SHA-1 加密,之后进行 BASE-64编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端
     */
    #endregion

    #region 打包请求连接数据
    /// <summary>
    /// 打包请求连接数据
    /// </summary>
    /// <param name="handShakeBytes"></param>
    /// <param name="length"></param>
    /// <returns></returns>
    private byte[] PackageHandShakeData(byte[] handShakeBytes, int length)
    {
        string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, length);
        string key = string.Empty;
        Regex reg = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
        Match m = reg.Match(handShakeText);
        if (m.Value != "")
        {
            key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
        }
        byte[] secKeyBytes = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
        string secKey = Convert.ToBase64String(secKeyBytes);
        var responseBuilder = new StringBuilder();
        responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + "\r\n");
        responseBuilder.Append("Upgrade: websocket" + "\r\n");
        responseBuilder.Append("Connection: Upgrade" + "\r\n");
        responseBuilder.Append("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n");
        return Encoding.UTF8.GetBytes(responseBuilder.ToString());
    }
    #endregion

    #region 处理接收的数据
    /// <summary>
    /// 处理接收的数据
    /// 参考 https://www.cnblogs.com/smark/archive/2012/11/26/2789812.html
    /// </summary>
    /// <param name="recBytes"></param>
    /// <param name="length"></param>
    /// <returns></returns>
    private string AnalyzeClientData(byte[] recBytes, int length)
    {
        int start = 0;
        // 如果有数据则至少包括3位
        if (length < 2) return "";
        // 判断是否为结束针
        bool IsEof = (recBytes[start] >> 7) > 0;
        // 暂不处理超过一帧的数据
        if (!IsEof) return "";
        start++;
        // 是否包含掩码
        bool hasMask = (recBytes[start] >> 7) > 0;
        // 不包含掩码的暂不处理
        if (!hasMask) return "";
        // 获取数据长度
        UInt64 mPackageLength = (UInt64)recBytes[start] &amp; 0x7F;
        start++;
        // 存储4位掩码值
        byte[] Masking_key = new byte[4];
        // 存储数据
        byte[] mDataPackage;
        if (mPackageLength == 126)
        {
            // 等于126 随后的两个字节16位表示数据长度
            mPackageLength = (UInt64)(recBytes[start] << 8 | recBytes[start + 1]);
            start += 2;
        }
        if (mPackageLength == 127)
        {
            // 等于127 随后的八个字节64位表示数据长度
            mPackageLength = (UInt64)(recBytes[start] << (8 * 7) | recBytes[start] << (8 * 6) | recBytes[start] << (8 * 5) | recBytes[start] << (8 * 4) | recBytes[start] << (8 * 3) | recBytes[start] << (8 * 2) | recBytes[start] << 8 | recBytes[start + 1]);
            start += 8;
        }
        mDataPackage = new byte[mPackageLength];
        for (UInt64 i = 0; i < mPackageLength; i++)
        {
            mDataPackage[i] = recBytes[i + (UInt64)start + 4];
        }
        Buffer.BlockCopy(recBytes, start, Masking_key, 0, 4);
        for (UInt64 i = 0; i < mPackageLength; i++)
        {
            mDataPackage[i] = (byte)(mDataPackage[i] ^ Masking_key[i % 4]);
        }
        return Encoding.UTF8.GetString(mDataPackage);
    }
    #endregion

    #region 发送数据
    /// <summary>
    /// 把发送给客户端消息打包处理(拼接上谁什么时候发的什么消息)
    /// </summary>
    /// <returns>The data.</returns>
    /// <param name="message">Message.</param>
    private byte[] PackageServerData(string msg)
    {
        byte[] content = null;
        object oj = new { States = 0, Count = 0 }; // 0:没有通知,1:有通知  
        oj = new { States = 0}; // 0:没有通知,1:有通知  
        if (!string.IsNullOrWhiteSpace(msg))
        {

            oj = new
            {
                States = 1,                 
                NContent = "您有新消息", //消息内容
                PublisherName = "Admin", //发布消息的人
                PublisherImage ="/Upload/Images/20180131/测试.png",  //发布消息人头像
            };
        }
        string json = JsonConvert.SerializeObject(oj);
        byte[] temp = Encoding.UTF8.GetBytes(json);
        if (temp.Length < 126)
        {
            content = new byte[temp.Length + 2];
            content[0] = 0x81;
            content[1] = (byte)temp.Length;
            Buffer.BlockCopy(temp, 0, content, 2, temp.Length);
        }
        else if (temp.Length < 0xFFFF)
        {
            content = new byte[temp.Length + 4];
            content[0] = 0x81;
            content[1] = 126;
            content[2] = (byte)(temp.Length >> 8 &amp; 0xFF);
            content[3] = (byte)(temp.Length &amp; 0xFF);
            Buffer.BlockCopy(temp, 0, content, 4, temp.Length);
        }
        return content;
    }
    #endregion
}

发表评论

邮箱地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据