联系方式
Java服务器开发群:66728073
游戏开发者高级群:398808948
Unity3d游戏开发:286114103

Unity3d C# Socket异步发送与接收数据

 二维码 37
发表时间:2019-12-09 00:00

在网络游戏开发中,一些游戏需要使用长连接的方式进行网络通信,即使用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