Unity——异步方法

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

        同步方法:

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

        异步方法:

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

        异步方法的本质:

                往往异步方法当中都会使用多线程执行某部分逻辑

                因为我们不需要等待方法中逻辑执行完毕就可以继续执行下面的逻辑了

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

异步倒计时

        1.线程回调

        CountDownAsync(5, () =>
        {
            print("倒计时结束");
        });
        print("这就是异步进行倒计时");

    /// <summary>
    /// 倒计时
    /// </summary>
    /// <param name="second">倒计时的时间</param>
    /// <param name="callBack">倒计时后需要执行的函数</param>
    public void CountDownAsync(int second,UnityAction callBack)
    {
        //每次调用都开一个线程来进行倒计时
        Thread t = new Thread(() =>
        {
            while (true)
            {
                print(second); //输出秒数
                Thread.Sleep(1000); //该线程休眠1s
                --second;

                //如果倒计时到达0之后就跳出循环
                if (second == 0)
                    break;
            }
            callBack?.Invoke(); //callBack不为null就执行callBack方法
        });

        t.Start(); // 启动该线程

        print("开始倒计时");
    }

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

           相对第一种方式 可以让函数分布执行

        CountDownAsync(5);
        print("这是另外一种异步进行的倒计时");

    public async void CountDownAsync(int second)
    {
        print("倒计时开始");

        //await代表要等待该步运行完了之后再运行下面的程序
        await Task.Run(() => {
            while (true)
            {
                print(second);
                Thread.Sleep(1000);
                --second;
                if (second == 0)
                    break;
            }
        });

        print("倒计时结束");
    }

Socket TCP通信中的异步方法(Begin开头的方法)

        回调函数参数IAsyncResult

        AsyncState 调用异步方法时传入的参数 需要转换

        AsyncWaitHandle 用于同步等待

        内部开多线程,通过回调形式返回结果,需要和End相关方法 配合使用

        服务器相关(BeginAccept、EndAccept)    

        //服务器相关
        socketTCP.BeginAccept(AcceptCallBack, socketTCP); //BeginAccept的第二个参数会转化为 IAsyncResult 并作为回调函数的参数使用


    /// <summary>
    /// 连入客户端
    /// </summary>
    /// <param name="result">传入Socket</param>
    private void AcceptCallBack(IAsyncResult result)
    {
        try
        {
            //获取到传入的参数
            Socket s = result.AsyncState as Socket;

            //通过调用EndAccept就可以得到连入的客户端Socket
            Socket clientSocket = s.EndAccept(result);

            //再开一个异步去监听下一个连入的客户端
            s.BeginAccept(AcceptCallBack, s);
        }
        catch(SocketException e)
        {
            print(e.SocketErrorCode);
        }
    }

        客户端相关(BeginConnect、EndConnect)

IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
        socketTcp.BeginConnect(ipPoint, (result) =>
        {
            Socket s = result.AsyncState as Socket;
            try
            {
                s.EndConnect(result);
                print("连接成功");
            }
            catch (SocketException e)
            {
                print("连接出错" + e.SocketErrorCode + e.Message);
            }

        }, socketTcp);

        服务端和客户端通用

                接收消息(BeginReceive、EndReceive)

 //接收消息
        socketTCP.BeginReceive(resultBytes, 0, resultBytes.Length, SocketFlags.None, ReceiveCallBack, socketTCP);


    private void ReceiveCallBack(IAsyncResult result)
    {
        try
        {
            Socket s = result.AsyncState as Socket;

            //这个返回值是你收到了多少个字节
            int num = s.EndReceive(result);

            //进行消息处理
            Encoding.UTF8.GetString(resultBytes, 0, num);

            //回调函数,继续接收下一个消息
            s.BeginReceive(resultBytes, 0, resultBytes.Length, SocketFlags.None, ReceiveCallBack, s);
        }catch(SocketException e)
        {
            print("接受消息出现问题" + e.SocketErrorCode + e.Message);
        }
    }

                发送消息(BeginSend、EndSend)

         //发送消息
        byte[] bytes = Encoding.UTF8.GetBytes("123456789");

        socketTCP.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, (result) =>
        {
            try
            {
                socketTCP.EndSend(result);
                print("发送成功");
            }
            catch (SocketException e)
            {
                print("发送错误" + e.SocketErrorCode + e.Message);
            }
        }, socketTCP);

Socket TCP通信中的异步(Async结尾方法)

        关键变量类型(SocketAsyncEventArgs)

        它会作为Async异步方法的传入值,我们需要通过它进行一些关键参数的赋值

        内部开多线程,通过回调形式返回结果,依赖SocketAsyncEventArgs对象配合使用

        服务器端(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("yuanshengxiao缘笙箫");

        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);

服务端

        Program

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 异步服务端
{
    class Program
    {
        static void Main(string[] args)
        {
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.Start("127.0.0.1", 8080, 1024);
            Console.WriteLine("开启服务器成功");

            while (true)
            {
                string input = Console.ReadLine();
                if(input.Substring(0,2) == "B:")
                {
                    serverSocket.Broadcast(input.Substring(2));
                }
            }
        }
    }
}

        ServerSocket

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace 异步服务端
{
    class ServerSocket
    {
        private Socket socket;
        private Dictionary<int, ClientSocket> clientDic = new Dictionary<int, ClientSocket>();

        public void Start(string ip,int port,int num)
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);

            try
            {
                socket.Bind(ipPoint);
                socket.Listen(num);

                //通过异步接收客户端连入
                socket.BeginAccept(AcceptCallBack, null);
            }
            catch (Exception e)
            {
                Console.WriteLine("启动服务器失败" + e.Message);
            }
        }

        /// <summary>
        /// 客户端连入的回调函数
        /// </summary>
        /// <param name="result"></param>
        private void AcceptCallBack(IAsyncResult result)
        {
            try
            {
                //获取连入的客户端
                Socket clientSocket = socket.EndAccept(result);
                ClientSocket client = new ClientSocket(clientSocket);

                //记录客户端对象
                clientDic.Add(client.clientID, client);

                //继续让等待别的客户端连入
                socket.BeginAccept(AcceptCallBack, null);
            }
            catch(Exception e)
            {
                Console.WriteLine("客户端连入失败" + e.Message);
            }
        }

        /// <summary>
        /// 广播消息
        /// </summary>
        /// <param name="str">需要广播的消息</param>
        public void Broadcast(string str)
        {
            foreach(ClientSocket client in clientDic.Values)
            {
                client.Send(str);
            }
        }
    }
}

        ClientSocket

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace 异步服务端
{
    class ClientSocket
    {
        public Socket socket;
        public int clientID;
        private static int CLIENT_BEGIN_ID = 1;

        private byte[] cacheBytes = new byte[1024];
        private int cacheNum = 0;

        public ClientSocket(Socket socket)
        {
            this.clientID = CLIENT_BEGIN_ID++; //将ID赋值给该客户端之后就加1方便下一个客户端赋值
            this.socket = socket;

            //开始接收消息
            this.socket.BeginReceive(cacheBytes, cacheNum, cacheBytes.Length, SocketFlags.None, ReceiveCallBack, null);
        }

        /// <summary>
        /// 接收的回调函数
        /// </summary>
        /// <param name="result">因为是在该类里面所以可以直接传this.不用传具体的值</param>
        private void ReceiveCallBack(IAsyncResult result)
        {
            try
            {
                cacheNum = this.socket.EndReceive(result);//Begin和End进行配对

                //通过字符串去解析
                Console.WriteLine(Encoding.UTF8.GetString(cacheBytes, 0, cacheNum));

                //如果是连接状态再继续接收消息
                //因为目前我们是以字符串的形式解析的 所以 解析完 就直接从0开始
                cacheNum = 0;
                if (this.socket.Connected)
                    this.socket.BeginReceive(cacheBytes, cacheNum, cacheBytes.Length, SocketFlags.None, ReceiveCallBack, null);
                else
                {
                    Console.WriteLine("没有连接,不用再收消息了");
                }
            }
            catch(SocketException e)
            {
                Console.WriteLine("接收消息错误" + e.SocketErrorCode + e.Message);
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="str">要发送的消息</param>
        public void Send(string str)
        {
            if (this.socket.Connected)
            {
                byte[] bytes = Encoding.UTF8.GetBytes(str);
                this.socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallBack, null);
            }
            else
            {

            }
        }

        /// <summary>
        /// 发送的回调函数
        /// </summary>
        /// <param name="result"></param>
        private void SendCallBack(IAsyncResult result)
        {
            try
            {
                this.socket.EndSend(result);
            }
            catch(SocketException e)
            {
                Console.WriteLine("发送失败" + e.SocketErrorCode + e.Message);
            }
        }
    }
}

客户端

        NetAsyncMgr

using System.Collections;
using System.Collections.Generic;
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;

    //和服务器进行连接的socket
    private Socket socket;

    //接受消息用的 缓冲容器
    private byte[] cacheBytes = new byte[1024];
    private int cacheNum = 0;

    private void Awake()
    {
        instance = this;

        //过场景不移除
        DontDestroyOnLoad(this.gameObject);
    }

    /// <summary>
    /// 连接服务器
    /// </summary>
    /// <param name="ip">服务器的ip</param>
    /// <param name="port">服务器的端口</param>
    public void Connect(string ip,int port)
    {
        if (socket != null && socket.Connected)
            return;

        IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        SocketAsyncEventArgs args = new SocketAsyncEventArgs(); //回调函数的参数
        args.RemoteEndPoint = ipPoint;
        args.Completed += (socket, args) =>  //该lambda表达式代表在连接完成之后,执行后面的函数
        {
            if(args.SocketError == SocketError.Success)
            {
                print("连接成功");

                //收消息
                SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs();
                receiveArgs.SetBuffer(cacheBytes, 0, cacheBytes.Length); //利用setBufffer设置接受的字节串,第一个是接受的容器,第二个参数是从哪开始存储,第二个是接收的最大长度
                receiveArgs.Completed += ReceiveCallBack; //接收完毕后,调用处理的函数
                this.socket.ReceiveAsync(receiveArgs);
            }
            else
            {
                print("连接失败" + args.SocketError);
            }
        };
        socket.ConnectAsync(args);
    }

    /// <summary>
    /// 收消息完成后的回调函数
    /// </summary>
    public void ReceiveCallBack(object obj,SocketAsyncEventArgs args)
    {
        if (args.SocketError == SocketError.Success)
        {
            //解析消息 目前用的字符串规则   args.Buffer是字节串  args.BytesTransferred是字节长度
            print(Encoding.UTF8.GetString(args.Buffer, 0, args.BytesTransferred));

            //继续接收消息
            args.SetBuffer(0, args.Buffer.Length);

            //继续异步收消息
            if (this.socket != null && this.socket.Connected)
                socket.ReceiveAsync(args);
            else
                Close();
        }
        else
        {
            print("接受消息出错" + args.SocketError);
            //关闭客户端连接
            Close();
        }
    }

    /// <summary>
    /// 关闭该客户端的连接
    /// </summary>
    public void Close()
    {
        if(socket != null)
        {
            socket.Shutdown(SocketShutdown.Both);
            socket.Disconnect(false);
            socket.Close();
            socket = null;
        }
    }

    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="str">发送的消息</param>
    public void Send(string str)
    {
        if (this.socket != null && this.socket.Connected)
        {
            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)
                {
                    print("接受消息出错" + args.SocketError);
                    //关闭客户端连接
                    Close();
                }
            };
            this.socket.SendAsync(args);
        }
        else
        {
            Close();
        }
    }
}

        MainAsync

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MainAsync : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        //如果场景里面没有Instance的函数就添加一个
        if(NetAsyncMgr.Instance == null)
        {
            GameObject obj = new GameObject("NetAsync");
            obj.AddComponent<NetAsyncMgr>();
        }

        NetAsyncMgr.Instance.Connect("127.0.0.1", 8080);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

        Lesson13

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Lesson13 : MonoBehaviour
{
    public Button btnSend;
    public InputField input;

    // Start is called before the first frame update
    void Start()
    {
        btnSend.onClick.AddListener(() =>
        {
            if (input.text != "")
                NetAsyncMgr.Instance.Send(input.text);
        });
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值