Unity3d C# Socket異步發送與接收數據


在網絡游戲開發中,一些游戲需要使用長連接的方式進行網絡通信,即使用Socket建立長連接。那么在Unity3d中,如何使用C#與服務端建立長連接呢?為什么 要說使用異步呢?我們知道,在Unity3d中,每個游戲畫面的播放都是以帖的概念循環播放的。而且只能在UI線程中播放,在其它線程不可以操作UI有關的東西,這都是網絡通信需要解決的問題。

使用Socket創建連接

眾所周知,在游戲客戶端啟動之后,一定有一個時機是創建網絡連接的,比如一般是選游戲大區這后,或用戶點擊進入游戲時,這都是由UI層觸發點擊和創建網絡連接的。但是網絡連接是一個IO阻塞操作,如果直接使用Socket的同步方法,會卡住當前UI線程,導致游戲畫面出現卡頓現象。所以要么使用協程,要么便需要使用異步創建連接。為了方便創建socket連接,這里使用異步連接方式。如下面代碼所示:

    public void ConnectServer()
    {
        this.Close();
        try
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            SocketAsyncEventArgs args = new SocketAsyncEventArgs();//創建連接參數對象
            this.endPoint = new IPEndPoint(IPAddress.Parse(host), port);
            args.RemoteEndPoint = this.endPoint;
            args.Completed += OnConnectedCompleted;//添加連接創建成功監聽
            _socket.ConnectAsync(args); //異步創建連接
        }
        catch(Exception e)
        {
            Debug.Log("服務器連接異常:" + e);
        }
    }

    private void OnConnectedCompleted(object sender,SocketAsyncEventArgs args)
    {
        try
        {   ///連接創建成功監聽處理
            if (args.SocketError != SocketError.Success)
            {
                //通知上層連接失敗
                CompleteConnectServerHandler?.Invoke(ConnectedStatus.ConnectFailed);
            }
            else
            {
                Debug.Log("網絡連接成功線程:" + Thread.CurrentThread.ManagedThreadId.ToString());
                //通知上層連接創建成功
                CompleteConnectServerHandler?.Invoke(ConnectedStatus.ConnectedSucess);
                StartReceiveMessage(); //啟動接收消息

            }
        }
        catch(Exception e)
        {
            Debug.Log("開啟接收數據異常" + e);
        }
        
    }

發送消息

一般來說,消息的發送都是UI層觸發的,比如點擊某個按鈕,執行了某個操作,就會向服務器獲取或保存數據。所以也需要使用協程或異步的方式發送消息,這里使用異步的方式。如下面代碼所示:

    public void SendToServer(byte[] data)
    {
        try
        {
            if (_socket == null || !_socket.Connected)
            {
                Debug.Log("socket未連接,發送消息失敗");
                return;
            }
            //創建發送參數
            SocketAsyncEventArgs sendEventArgs = new SocketAsyncEventArgs();
            sendEventArgs.RemoteEndPoint = endPoint;
            //設置要發送的數據
            sendEventArgs.SetBuffer(data, 0, data.Length);
            //異步發送數據
            _socket.SendAsync(sendEventArgs);
        }
        catch(Exception e)
        {
            Debug.Log("發送數據異常:" + e);
        } 
    }

由於Socket制作是一種非常底層的操作,所以這里直接發送byte[],具體發送什么樣的數據,由應用層決定。

接收數據

由於我們使用的是Socket自帶的異步創建連接,發送數據,所以接收數據也是在socket的異步線程中。如下面代碼所示:

    private void StartReceiveMessage()
    {

        //啟動接收消息
        SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs();
        //設置接收消息的緩存大小,正式項目中可以放在配置 文件中
        byte[] buffer = new byte[1024 * 512];
        //設置接收緩存
        receiveArgs.SetBuffer(buffer, 0, buffer.Length);
        receiveArgs.RemoteEndPoint = this.endPoint;
        receiveArgs.Completed += OnReceiveCompleted; //接收成功
        _socket.ReceiveAsync(receiveArgs);//開始異步接收監聽
    }


    public void OnReceiveCompleted(object sender,SocketAsyncEventArgs args)
    {
        try
        {
            Debug.Log("網絡接收成功線程:" + Thread.CurrentThread.ManagedThreadId.ToString());

            if (args.SocketError == SocketError.Success && args.BytesTransferred > 0)
            {
                //創建讀取數據的緩存
                byte[] bytes = new byte[args.BytesTransferred];
                //將數據復制到緩存中
                Buffer.BlockCopy(args.Buffer, 0, bytes, 0, bytes.Length);
                if(ReceiveSocketDataHandler == null)
                {
                    Debug.Log("沒有處理接收消息的事件 ");
                }
                //接收數據成功,調上層處理接收數據的事件
                ReceiveSocketDataHandler?.Invoke(bytes);
                //再次啟動接收數據監聽,接收下次的數據。
                StartReceiveMessage();
            }
            else
            {
                CompleteConnectServerHandler(ConnectedStatus.Disconnect);
            }
        }
        catch(Exception e)
        {
            Debug.Log("接收數據異常:" + e);
        }
    }

接收數據的方法會在連接創建成功時調一次,它一直監聽服務器的數據到來。當接收到服務器的數據時,我們會調用上層處理數據的事件 ,即ReceiveSocketDataHandler?.Invoke(bytes); ,首先要做的就是對接收的數據進行斷包和粘包處理。這個在下篇文章中再說。

詳細的代碼,可以參考項目源碼:http://www.xinyues.com/h-nd-195.html#_np=2_627
可以關注微信公眾號,及時接收文章推送
歡迎關注微信公號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM