• WebSocket C#服务器端+VUE客户端


    WebSocket C#服务器端

    先定义一个基类

    注:用于和WinSocket融合。当然不用也是可以的

    clsSocket

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace CtiService
    {
        abstract class clsSocket
        {
            //属性
            public string Name;
    
            //方法
            public abstract void m_SendData(string Data);
            public abstract void m_Close();
            public abstract bool m_IsConnected();
            public abstract bool m_IsClientNull();//界面强制登陆的client为null
    
            //事件定义
            //收到消息
            public delegate void LineReceivedEventHandler(clsSocket sender, string Data);
            public LineReceivedEventHandler LineReceivedEvent;
            public abstract event LineReceivedEventHandler LineReceived;
    
            //收消息时发生错误
            public delegate void LineReceivedErrorEventHandler(clsSocket sender, string Data);
            public LineReceivedErrorEventHandler LineReceivedErrorEvent;
            public abstract event LineReceivedErrorEventHandler LineReceivedError;
    
            //客户端断开连接 
            public delegate void DisconnectedEventHandler(clsSocket sender);
            public DisconnectedEventHandler DisconnectedEvent;
            public abstract event DisconnectedEventHandler Disconnected;
    
            //一个新连接
            public delegate void NewConnectionEventHandler();
            public NewConnectionEventHandler NewConnectionEvent;
            public abstract event NewConnectionEventHandler NewConnection;
    
            
     
    
        }
    }

    clsWebSocket

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using System.Net.Sockets;
    using System.Security.Cryptography;
    using WebSocket;
    using System.Net;
    //using System.Collections;
    using myClass;
    
    
    namespace CtiService
    {
        class clsWebSocket : clsSocket
        {
            //private Logger logger;
    
            //private string name;
            //public string Name
            //{
            //    get { return name; }
            //    set { name = value; }
            //}
    
            private Boolean isDataMasked;
            public Boolean IsDataMasked
            {
                get { return isDataMasked; }
                set { isDataMasked = value; }
            }
    
            public Socket ConnectionSocket;
    
            private int MaxBufferSize;
            private string Handshake;
            private string New_Handshake;
    
            public byte[] receivedDataBuffer;
            private byte[] FirstByte;
            private byte[] LastByte;
            private byte[] ServerKey1;
            private byte[] ServerKey2;
    
    
            //public event NewConnectionEventHandler NewConnection;
            //public event DataReceivedEventHandler DataReceived;
            //public event DisconnectedEventHandler Disconnected;
    
            #region 委托,事件
            //收到消息
            public override event LineReceivedEventHandler LineReceived
            {
                add
                {
                    LineReceivedEvent = (LineReceivedEventHandler)System.Delegate.Combine(LineReceivedEvent, value);
                }
                remove
                {
                    LineReceivedEvent = (LineReceivedEventHandler)System.Delegate.Remove(LineReceivedEvent, value);
                }
            }
            ////收消息时发生错误
            public override event LineReceivedErrorEventHandler LineReceivedError
            {
                add
                {
                    LineReceivedErrorEvent = (LineReceivedErrorEventHandler)System.Delegate.Combine(LineReceivedErrorEvent, value);
                }
                remove
                {
                    LineReceivedErrorEvent = (LineReceivedErrorEventHandler)System.Delegate.Remove(LineReceivedErrorEvent, value);
                }
            }
            ////WebSocket中客户端断开连接
            public override event DisconnectedEventHandler Disconnected
            {
                add
                {
                    DisconnectedEvent = (DisconnectedEventHandler)System.Delegate.Combine(DisconnectedEvent, value);
                }
                remove
                {
                    DisconnectedEvent = (DisconnectedEventHandler)System.Delegate.Remove(DisconnectedEvent, value);
                }
            }
            //一个新连接
            public override event NewConnectionEventHandler NewConnection
            {
                add
                {
                    NewConnectionEvent = (NewConnectionEventHandler)System.Delegate.Combine(NewConnectionEvent, value);
                }
                remove
                {
                    NewConnectionEvent = (NewConnectionEventHandler)System.Delegate.Remove(NewConnectionEvent, value);
                }
            }
            #endregion
    
    
            public clsWebSocket()
            {
                //logger = new Logger();
    
                MaxBufferSize = 1024*100;
                receivedDataBuffer = new byte[MaxBufferSize];
                FirstByte = new byte[MaxBufferSize];
                LastByte = new byte[MaxBufferSize];
                FirstByte[0] = 0x00;
                LastByte[0] = 0xFF;
    
                Handshake = "HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine;
                Handshake += "Upgrade: WebSocket" + Environment.NewLine;
                Handshake += "Connection: Upgrade" + Environment.NewLine;
                Handshake += "Sec-WebSocket-Origin: " + "{0}" + Environment.NewLine;
                Handshake += string.Format("Sec-WebSocket-Location: " + "ws://{0}:4141/chat" + Environment.NewLine, this.getLocalmachineIPAddress());
                Handshake += Environment.NewLine;
    
                New_Handshake = "HTTP/1.1 101 Switching Protocols" + Environment.NewLine;
                New_Handshake += "Upgrade: WebSocket" + Environment.NewLine;
                New_Handshake += "Connection: Upgrade" + Environment.NewLine;
                New_Handshake += "Sec-WebSocket-Accept: {0}" + Environment.NewLine;
                New_Handshake += Environment.NewLine;
            }
    
            public override bool m_IsConnected()
            {
                if (this.ConnectionSocket == null)//界面强制登陆的client为null
                { return false; }
                //client.Client.Connected 
                //client.Client.Poll 
                return ConnectionSocket.Connected;
            }
    
            //界面强制登陆的client为null
            public override bool m_IsClientNull()
            {
                if (this.ConnectionSocket == null)//界面强制登陆的client为null
                { return true; }
                else
                { return false; }
            }
    
            /// <summary>
            /// 断开连接
            /// </summary>
            public override void m_Close()
            {
                try
                {
                    ConnectionSocket.Close();
                }
                catch (Exception ex)
                {
                    clsLogHelper.m_CreateErrorLogTxt("WebSocket m_Close: " + Name, "断开连接", ex.Message);
                }
            }
            
            /// <summary>
            /// 发送消息
            /// </summary>
            /// <param name="Data">消息内容</param>
            public override void m_SendData(string strMsg)
            {
                try
                {
                    if (!ConnectionSocket.Connected == true)//如果连接断开则不再发送信息
                    {
                        clsLogHelper.m_CreateErrorLogTxt("WebSocket m_SendData: " + Name, "如果连接断开则不再发送信息","");
                        return;
                    }
                    //两种协议 
                    if (this.IsDataMasked)//是否数据屏蔽
                    {
                        clsDataFrame dr = new clsDataFrame(strMsg);
                        this.ConnectionSocket.Send(dr.GetBytes());
                    }
                    else
                    {
                        this.ConnectionSocket.Send(FirstByte);
                        this.ConnectionSocket.Send(Encoding.UTF8.GetBytes(strMsg));
                        this.ConnectionSocket.Send(LastByte);
                    }
    
                }
                catch (Exception ex)
                {
                    clsLogHelper.m_CreateErrorLogTxt("WebSocket m_SendData: " + Name + "," + strMsg, "发送消息", ex.Message);
                }
            }
    
    
            private void m_Read(IAsyncResult status)
            {
                if (!ConnectionSocket.Connected) return;
                string messageReceived = string.Empty;
                clsDataFrame dr = new clsDataFrame(receivedDataBuffer);
    
                try
                {
                    if (!this.isDataMasked)
                    {
                        // Web Socket protocol: messages are sent with 0x00 and 0xFF as padding bytes
                        System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
                        int startIndex = 0;
                        int endIndex = 0;
    
                        // Search for the start byte
                        while (receivedDataBuffer[startIndex] == FirstByte[0]) startIndex++;
                        // Search for the end byte
                        endIndex = startIndex + 1;
                        while (receivedDataBuffer[endIndex] != LastByte[0] && endIndex != MaxBufferSize - 1) endIndex++;
                        if (endIndex == MaxBufferSize - 1) endIndex = MaxBufferSize;
    
                        // Get the message
                        messageReceived = decoder.GetString(receivedDataBuffer, startIndex, endIndex - startIndex);
                    }
                    else
                    {
                        messageReceived = dr.Text;
                    }
    
                    if ((messageReceived.Length == MaxBufferSize && messageReceived[0] == Convert.ToChar(65533)) ||
                        messageReceived.Length == 0)
                    {
                        //logger.Log("接受到的信息 [\"" + string.Format("logout:{0}",this.name) + "\"]");
                        if (DisconnectedEvent != null)//WebSocket中客户端断开连接
                            DisconnectedEvent(this);
                    }
                    else
                    {
                        //接受到的信息
                        if (this.LineReceivedEvent != null)
                        {
                            //logger.Log("接受到的信息 [\"" + messageReceived + "\"]");
                            this.LineReceivedEvent(this, messageReceived);
                        }
                        Array.Clear(receivedDataBuffer, 0, receivedDataBuffer.Length);
                        ConnectionSocket.BeginReceive(receivedDataBuffer, 0, receivedDataBuffer.Length, 0, new AsyncCallback(m_Read), null);
                    }
                }
                catch(Exception ex)
                {
                    //logger.Log(ex.Message);
                    //logger.Log("Socket连接将会被终止。");
                    if (DisconnectedEvent != null)//Socket连接将会被终止
                        DisconnectedEvent(this);
    
                    myClass.clsLogHelper.m_CreateErrorLogTxt("WebSocket Read: " + Name, "读取异步信息流", ex.Message);
                    //this.m_Close();
                    Thread.CurrentThread.Abort();
                }
            }
    
            private void BuildServerPartialKey(int keyNum, string clientKey)
            {
                string partialServerKey = "";
                byte[] currentKey;
                int spacesNum = 0;
                char[] keyChars = clientKey.ToCharArray();
                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);
                }
            }
    
            private byte[] BuildServerFullKey(byte[] last8Bytes)
            {
                //try
                //{
                    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);
    
                //}
                //catch (Exception ex)
                //{
                //    clsLogHelper.m_CreateErrorLogTxt("WebSocket BuildServerFullKey: ", "", ex.Message);
                //}
            }
    
            public void ManageHandshake(IAsyncResult status)
            {
                try
                {
                    string header = "Sec-WebSocket-Version:";
                    int HandshakeLength = (int)status.AsyncState;
                    byte[] last8Bytes = new byte[8];
    
                    System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
                    String rawClientHandshake = decoder.GetString(receivedDataBuffer, 0, HandshakeLength);
    
                    Array.Copy(receivedDataBuffer, HandshakeLength - 8, last8Bytes, 0, 8);
    
                    //现在使用的是比较新的Websocket协议
                    if (rawClientHandshake.IndexOf(header) != -1)
                    {
                        this.isDataMasked = true;
                        string[] rawClientHandshakeLines = rawClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries);
                        string acceptKey = "";
                        foreach (string Line in rawClientHandshakeLines)
                        {
                            Console.WriteLine(Line);
                            if (Line.Contains("Sec-WebSocket-Key:"))
                            {
                                acceptKey = ComputeWebSocketHandshakeSecurityHash09(Line.Substring(Line.IndexOf(":") + 2));
                            }
                        }
    
                        New_Handshake = string.Format(New_Handshake, acceptKey);
                        byte[] newHandshakeText = Encoding.UTF8.GetBytes(New_Handshake);
                        ConnectionSocket.BeginSend(newHandshakeText, 0, newHandshakeText.Length, 0, HandshakeFinished, null);
                        return;
                    }
    
                    string ClientHandshake = decoder.GetString(receivedDataBuffer, 0, HandshakeLength - 8);
    
                    string[] ClientHandshakeLines = ClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries);
    
                    ///logger.Log("新的连接请求来自" + ConnectionSocket.LocalEndPoint + "。正在准备连接 ...");
    
                    // Welcome the new client
                    foreach (string Line in ClientHandshakeLines)
                    {
                        //logger.Log(Line);
                        if (Line.Contains("Sec-WebSocket-Key1:"))
                            BuildServerPartialKey(1, Line.Substring(Line.IndexOf(":") + 2));
                        if (Line.Contains("Sec-WebSocket-Key2:"))
                            BuildServerPartialKey(2, Line.Substring(Line.IndexOf(":") + 2));
                        if (Line.Contains("Origin:"))
                            try
                            {
                                Handshake = string.Format(Handshake, Line.Substring(Line.IndexOf(":") + 2));
                            }
                            catch
                            {
                                Handshake = string.Format(Handshake, "null");
                            }
                    }
                    // Build the response for the client
                    byte[] HandshakeText = Encoding.UTF8.GetBytes(Handshake);
                    byte[] serverHandshakeResponse = new byte[HandshakeText.Length + 16];
                    byte[] serverKey = BuildServerFullKey(last8Bytes);
                    Array.Copy(HandshakeText, serverHandshakeResponse, HandshakeText.Length);
                    Array.Copy(serverKey, 0, serverHandshakeResponse, HandshakeText.Length, 16);
    
                    //logger.Log("发送握手信息 ...");
                    ConnectionSocket.BeginSend(serverHandshakeResponse, 0, HandshakeText.Length + 16, 0, HandshakeFinished, null);
                    //logger.Log(Handshake);
    
                }
                catch (Exception ex)
                {
                    clsLogHelper.m_CreateErrorLogTxt("WebSocket ManageHandshake: "  + status.ToString(), "", ex.Message);
                }
            }
    
            public static String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey)
            {
                const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
                String secWebSocketAccept = String.Empty;
                // 1. Combine the request Sec-WebSocket-Key with magic key.
                String ret = secWebSocketKey + MagicKEY;
                // 2. Compute the SHA1 hash
                SHA1 sha = new SHA1CryptoServiceProvider();
                byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
                // 3. Base64 encode the hash
                secWebSocketAccept = Convert.ToBase64String(sha1Hash);
                return secWebSocketAccept;
            }
    
            private void HandshakeFinished(IAsyncResult status)
            {
                ConnectionSocket.EndSend(status);
                ConnectionSocket.BeginReceive(receivedDataBuffer, 0, receivedDataBuffer.Length, 0, new AsyncCallback(m_Read), null);
                if (this.NewConnectionEvent != null)
                    this.NewConnectionEvent();
            }
    
            private IPAddress getLocalmachineIPAddress()
            {
                string strHostName = Dns.GetHostName();
                IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
    
                foreach (IPAddress ip in ipEntry.AddressList)
                {
                    //IPV4
                    if (ip.AddressFamily == AddressFamily.InterNetwork)
                        return ip;
                }
    
                return ipEntry.AddressList[0];
            }
    
        }
    }

    frmMain 窗体中调用方式

     private int intWebSocketPort = 5011;// WebSocket 监听端口 默认:5011
     private Socket WebSocketListener;
     private System.Threading.Thread WebSocketListenerThread;
     delegate void RecTcpMessage(clsSocket myAgentTcp, string strData);

    在 窗体 Load 事件中 开始监听

    //WebSocket
    this.WebSocketListenerThread = new System.Threading.Thread(new System.Threading.ThreadStart(m_WebSocketDoListen));
    this.WebSocketListenerThread.Start();

    在窗体关闭事件中  调用关闭监听

    private void m_StopDoListen()
    {
         if (this.WebSocketListener != null)
         {
             this.WebSocketListener.Close();
         }   
    }

    开始监听 函数

    注:这句  System.Threading.Thread.Sleep(100);   非常重要

    如果网页不能在iframe中固定,需要刷新时(尤其是VUE这种单页不断刷新整页时)  线程等待100 毫秒 太短了,网页刷新后则不能再正确连接

    解决方法: 将 100  改为 3000  在Vue上亲测没问题

    #region WebSocket 监听
            private void m_WebSocketDoListen()
            {
                try
                {
                    Char char1 = Convert.ToChar(65533);
                    this.WebSocketListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                    this.WebSocketListener.Bind(new IPEndPoint(this.IPV4, this.intWebSocketPort));
                    this.WebSocketListener.Listen(500);//ConnectionsQueueLength
                    while (true)
                    {
                        Socket sc = this.WebSocketListener.Accept();
                        if (sc != null)
                        {
                            //System.Threading.Thread.Sleep(100);
                            System.Threading.Thread.Sleep(3000);
                            clsWebSocket socketConn = new clsWebSocket();
                            socketConn.ConnectionSocket = sc;
                            //socketConn.NewConnection += new NewConnectionEventHandler(socketConn_NewConnection);
                            socketConn.LineReceived += new clsSocket.LineReceivedEventHandler(m_OnLineReceived);//收到消息
                            socketConn.LineReceivedError += new clsSocket.LineReceivedErrorEventHandler(m_OnLineReceivedError);//收到消息时出错
                            socketConn.Disconnected += new clsSocket.DisconnectedEventHandler(m_Disconnected);//WebSocket中客户端断开连接
                            socketConn.ConnectionSocket.BeginReceive(socketConn.receivedDataBuffer,
                                                                     0, socketConn.receivedDataBuffer.Length,
                                                                     0, new AsyncCallback(socketConn.ManageHandshake),
                                                                     socketConn.ConnectionSocket.Available);;
                        }
                    }
                }
                catch (Exception ex)//
                {
                    myClass.clsLogHelper.m_CreateErrorLogTxt("WebScript StartServer", "出错", ex.Message.ToString());
                }
            }
            #endregion

    收到消息 和  连接异常  和 WebSocket中断 回调函数

    注:这里我用的是窗体及多线程,所以当从 WebSocket线程中 回到窗体线程 时需要用到 委托

            //在线用户接收信息时出错
            private void m_OnLineReceivedError(clsSocket myAgentTcp, string strData)
            {
                try
                {
                    RecTcpMessage dh = new RecTcpMessage(m_RecTcpMessageError);
                    this.Invoke(dh, new object[] { myAgentTcp, strData });
                }
                catch (System.Exception ex)
                {
                    clsLogHelper.m_CreateErrorLogTxt("frmMain m_OnLineReceivedError", myAgentTcp.Name.ToString() + "," + strData, ex.Message.ToString());
                }
            }
            private void m_RecTcpMessageError(clsSocket myAgentTcp, string strData)
            {
                  
                
            }
            //事件, 接收信息
            private void m_OnLineReceived(clsSocket myAgentTcp, string strData)
            {
                try
                {
                    RecTcpMessage dh = new RecTcpMessage(m_RecTcpMessage);
                    this.Invoke(dh, new object[] { myAgentTcp, strData });
                }
                catch (System.Exception ex)
                {
                    clsLogHelper.m_CreateErrorLogTxt("frmMain m_OnLineReceived", myAgentTcp.Name.ToString() + "," + strData, ex.Message.ToString());
                }
            }
    
            //WebSocket中客户端主动 断开连接
            private void m_Disconnected(clsSocket myAgentTcp)
            {
              
            }
    
            private void m_RecTcpMessage(clsSocket myAgentTcp, string strData)
            {
                 
            }
            #endregion
    获取IP地址 
    注:现在大多服务器都是 双网卡。所以要用于websocket的Ip需要指定
    #region 获取IP地址
            private IPAddress m_GetIPV4Address()
            {
     
                string strHostName = Dns.GetHostName();
                IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
    
                foreach (IPAddress ip in ipEntry.AddressList)
                {
                    //IPV4
                    if (ip.ToString() == "192.168.214.1")
                    {
                        return ip;
                    }
                    //if (ip.AddressFamily == AddressFamily.InterNetwork)
                    //    return ip;
                }
    
                return ipEntry.AddressList[0];
            }
             
            #endregion

    VUE客户端

    代码量有点大,我只列出用到的地方

    path:"ws://192.168.214.1:5011/chat/",    //WebSocket服务器地址

    websock: null,   //websock 连接对象 页面级

    <script>
    import { mapState } from "vuex";
    export default {
      
      name: '',
      props: {},
      data() {
        return {
          pageContentData: [],
          activeIndex: '0',
          currentPage4: 1,
          path:"ws://192.168.214.1:5011/chat/",
          websock: null,
          total: 20
        }
      },
     created() {
          this.initWebSocket();//初始化 连接websocket
      },
      destroyed() {this.websock.close() //离开路由 关闭网页或刷新 之后断开websocket连接
      },

    具体函数实现

    methods: {
      
      
        initWebSocket(){ //初始化weosocket
    
            const wsuri = "ws://192.168.214.1:5011/chat/";
            console.log("打开一个websocket " +wsuri); 
    
            this.websock = new WebSocket(wsuri);
            this.websock.onmessage = this.websocketonmessage;
            this.websock.onopen = this.websocketonopen;
            this.websock.onerror = this.websocketonerror;
            this.websock.onclose = this.websocketclose;
          },
          websocketonopen(){ //连接建立之后执行send方法发送数据
            //let actions = {"test":"12345"};
            //this.websocketsend(JSON.stringify(actions));
            let strMsg="@&l_login:1005,1005,1005";
            this.websocketsend(strMsg);
            console.log("连接建立成功 发 " +strMsg); 
          },
          websocketonerror(){//连接建立失败重连
            this.initWebSocket();
          },
          websocketonmessage(e){ //数据接收
            //const redata = JSON.parse(e.data);
            console.log("数据接收 " +e.data); 
          },
          websocketsend(Data){//数据发送
            this.websock.send(Data);
          },
          websocketclose(e){  //关闭
            console.log('断开连接',e);
          }, 
    }
  • 相关阅读:
    python之路——进程
    python之路——操作系统的发展史
    python之路——网络编程
    模块学习之re模块
    day11迭代器、生成器
    day10闭包、函数装饰器
    vnc安装和配置
    单例模式
    代理设计模式
    工厂模式例子
  • 原文地址:https://www.cnblogs.com/hailexuexi/p/16854230.html
Copyright © 2020-2023  润新知