Contents

TCP异步通信

异步方法和同步方法的区别

同步方法:

方法中逻辑执行完毕后,再继续执行后面的方法

异步方法:

方法中逻辑可能还没有执行完毕,就继续执行后面的内容

注意:Unity中的协同程序中的某些异步方法,有的使用的是多线程有的使用的是迭代器分步执行


举例说明异步方法原理

我们以一个异步倒计时方法举例

1.线程回调

CountDownAsync(5, ()=> 
               {
                    print("倒计时结束");
               });
print("异步执行后的逻辑");

2.async和await 会等待线程执行完毕 继续执行后面的逻辑

//相对第一种方式 可以让函数分步执行
CountDownAsync(5);
print("异步执行后的逻辑2");

TCP通信中的异步方法(begin,end)

服务器监听

Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.TCP);
socketTcp.BeginAccept(AcceptCallBack,socketTcp);
private void AcceptCallBack(IAsyncResult result)
{
    try
    {        
        //获取传入的参数
        Socket s = result.AsyncState as Socket;
        //得到连入的客户端
        Socket clientSocket = s.EndAccept(result); 
        //继续监听
        s.BeginAccept(AcceptCallBack, s);
    }
    catch(SocketException e)
    {
        print(e.SocketErrorCode);
    }
}

客户端连接

IPEndPoint ipAddress = new IPEndPoint(IPAddress.Parse("127.0.0.1"),8080);
socketTcp.BeginConnect(ipPoint,(result)=>
                       {
                           try
                           {
                               socketTcp.EndConnect(result);
                           }
                           catch(SocketException e)
                           {
                               print($"连接出错{e.SocketErrorCode}{e.Message}");
                           }
                       }, scoketTcp);

接收消息

socketTcp.BeginReceive(resultBytes, 0, resultBytes.Length, SocketFlags.None, (result)=>
                       {
                           try
                           {
                               Socket s = result.AsyncState as Socket;
                               //返回值是接受了多少字节
                               int size = s.EndReceive(result);
                               print(Encoding.UTF8.GetString(resultBytes));
                           }
                           catch(SocketException e)
                           {
                               print($"接受消息错误{e.SocketErrorCode}{e.Message}");
                           }
                       }, socketTcp);

发送消息

byte[] bytes = Encoding.UTF8.GetBytes("你好");
socketTcp.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, (result)=>
                    {
                        try
                        {
                            Socket s = result.AsyncState as Socket;
                            s.EndSend(result);
                        }
                        catch(SocketException e)
                        {
                             print($"接受发送错误{e.SocketErrorCode}{e.Message}");
                        }    
                    }, socktTcp);

TCP通信中的异步方法(SocketAsyncEventArgs)

SocketAsyncEventArgs它会作为Async异步方法的传入值

我们需要通过它进行一些关键参数的赋值

AcceptAsync接收客户端连入

SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += (socket, args) =>
{
    //首先判断是否成功
    if (args.SocketError == SocketError.Success)
    {
        //获取连入的客户端socket
        Socket clientSocket = args.AcceptSocket;

        (socket as Socket).AcceptAsync(args);
    }
    else
    {
        print("连入客户端失败" + args.SocketError);
    }
};
socketTcp.AcceptAsync(e);

ConnectAsync客户端连接服务端

SocketAsyncEventArgs e2 = new SocketAsyncEventArgs();
e2.Completed += (socket, args) =>
{
    if (args.SocketError == SocketError.Success)
    {
        //连接成功
    }
    else
    {
        //连接失败
        print(args.SocketError);
    }
};
socketTcp.ConnectAsync(e2);

SendAsync发送消息

SocketAsyncEventArgs e3 = new SocketAsyncEventArgs();
byte[] bytes2 = Encoding.UTF8.GetBytes("123123的就是拉法基萨克两地分居");
e3.SetBuffer(bytes2, 0, bytes2.Length);
e3.Completed += (socket, args) =>
{
    if (args.SocketError == SocketError.Success)
    {
        print("发送成功");
    }
    else
    {

    }
};
socketTcp.SendAsync(e3);

ReceiveAsync接收消息

SocketAsyncEventArgs e4 = new SocketAsyncEventArgs();
//设置接受数据的容器,偏移位置,容量
e4.SetBuffer(new byte[1024 * 1024], 0, 1024 * 1024);
e4.Completed += (socket, args) =>
{
    if(args.SocketError == SocketError.Success)
    {
        //收取存储在容器当中的字节
        //Buffer是容器
        //BytesTransferred是收取了多少个字节
        Encoding.UTF8.GetString(args.Buffer, 0, args.BytesTransferred);

        args.SetBuffer(0, args.Buffer.Length);
        //接收完消息 再接收下一条
        (socket as Socket).ReceiveAsync(args);
    }
    else
    {

    }
};
socketTcp.ReceiveAsync(e4);

服务端实现(begin,end)

ServerSocket

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace TcpServerLearnSync
{
    class ServerSocket
    {
        private Socket socket;
        private Dictionary<int, ClientSocket> clientDic = new Dictionary<int, ClientSocket>();
        public ServerSocket(string ip, int port, int num = 1024) 
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ipAddress = new IPEndPoint(IPAddress.Parse(ip),port);
            try
            {
                socket.Bind(ipAddress);
                socket.Listen(num);
                socket.BeginAccept(AcceptCallBack, socket);


            }
            catch (SocketException e)
            {
                Console.WriteLine($"ip绑定错误{e.Message}");
            }
        }
        private void AcceptCallBack(IAsyncResult result) 
        {
            try
            {
                Socket socket = result.AsyncState as Socket;
                
                Socket client = socket.EndAccept(result);
                ClientSocket clientSocket = new ClientSocket(client);
                clientDic.Add(clientSocket.clientID, clientSocket);
                //继续监听
                socket.BeginAccept(AcceptCallBack, socket);
            }
            catch (SocketException e) 
            {
                Console.WriteLine($"客户端连接错误{e.Message}");
            }
        }
        public void Broadcast(string str) 
        {
            foreach (var client in clientDic.Values)
            {
                client.Send(str);
            }
        }
    }
}

ClientSocket

using System;
using System.Net.Sockets;
using System.Text;

namespace TcpServerLearnSync
{
    class ClientSocket
    {
        private readonly Socket socket;
        public readonly int clientID;
        public static int CLIENT_BEGIN_ID = 1;
        private byte[] bytes = new byte[1024];
        public ClientSocket(Socket socket)
        {
            this.socket = socket;
            clientID = CLIENT_BEGIN_ID;
            CLIENT_BEGIN_ID++;
            //开始接受消息
            socket.BeginReceive(bytes, 0, bytes.Length, SocketFlags.None, ReceiveCallBack, socket);
        }
        private void ReceiveCallBack(IAsyncResult result)
        {
            try
            {
                int receiveSize = socket.EndReceive(result);
                if (receiveSize > 0)
                {
                    Console.WriteLine(Encoding.UTF8.GetString(bytes));
                }
                receiveSize = 0;
                if (socket.Connected)
                {
                    socket.BeginReceive(bytes, 0, bytes.Length, SocketFlags.None, ReceiveCallBack, socket);
                }
                else
                {
                    Console.WriteLine("客户端断开连接");
                }
            }
            catch (SocketException e)
            {
                Console.WriteLine($"消息接收错误{e.Message}");
            }
        }
        public void Send(string message)
        {
            if (socket == null) return;
            byte[] msgbytes = Encoding.UTF8.GetBytes(message);
            socket.BeginSend(msgbytes, 0, msgbytes.Length, SocketFlags.None, SendCallBack, socket);
        }
        private void SendCallBack(IAsyncResult result)
        {
            try
            {
                socket.EndSend(result);
            }
            catch (SocketException e)
            {
                Console.WriteLine($"消息发送错误{e.Message}");
            }
        }
    }
}

客户端实现(SocketAsyncEventArgs)

using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

public class NetAsyncMgr : MonoBehaviour
{
    private static NetAsyncMgr _instance;//单例
    public static NetAsyncMgr Instance => _instance;
    private byte[] buffer = new byte[1024 * 1024];
    private Socket socket;

    private void Awake()
    {
        _instance = this;
    }

    public void Connect(string ip, int port)
    {
        if (socket == null)
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        IPEndPoint ipAdd = new IPEndPoint(IPAddress.Parse(ip), port);

        SocketAsyncEventArgs args = new SocketAsyncEventArgs();
        args.RemoteEndPoint = ipAdd;
        args.Completed += (socket, args) =>
         {
             if (args.SocketError == SocketError.Success)
             {
                 //开始接收服务器消息
                 Debug.Log("开始接收服务器消息");
                 SocketAsyncEventArgs args2 = new SocketAsyncEventArgs();
                 args2.SetBuffer(buffer, 0, buffer.Length);
                 args2.Completed += ReceiveCallBack;
                 this.socket.ReceiveAsync(args2);
             }
             else
             {
                 Debug.Log("连接错误" + args.SocketError);
             }
         };
        socket.ConnectAsync(args);
    }

    public void ReceiveCallBack(object socket, SocketAsyncEventArgs args)
    {
        if (args.SocketError == SocketError.Success)
        {
            var msg = Encoding.UTF8.GetString(args.Buffer, 0, args.BytesTransferred);
            Debug.Log(msg);
            if (this.socket != null && this.socket.Connected) 
            {
                this.socket.ReceiveAsync(args);
            }
        }
        else
        {
            Debug.Log("消息接收错误" + args.SocketError);
        }
    }

    public void Send(string str)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(str);
        SocketAsyncEventArgs args = new SocketAsyncEventArgs();
        args.SetBuffer(bytes, 0, bytes.Length);
        args.Completed += (socket, args) =>
        {
            if (args.SocketError != SocketError.Success)
            {
                Debug.Log("消息发送错误" + args.SocketError);
            } 
        };
        socket.SendAsync(args);
    }

    public void Close()
    {
        if (socket != null)
        {
            socket.Shutdown(SocketShutdown.Both);
            socket.Disconnect(false);
            socket.Close();
            socket = null;
        }
    }
}