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
可以关注微信公众号,及时接收文章推送
欢迎关注微信公号

原文地址:https://www.cnblogs.com/wgslucky/p/12008433.html