Unity网络游戏编程学习(二)

Unity网络游戏编程学习(二)


学习《Unity3D网络游戏实战》(第二版)时的学习笔记
上一篇: Unity网络游戏编程学习(一)

异步回调

上一篇中客户端与服务器中所使用的同步API(如Connect、Reveive、Send、Accept),可称为同步Socket方法,都是阻塞方法,这种方式简单容易实现,但却会时不时卡住程序,这也是上一篇中的程序第二次发送数据会报错的原因,这是其致命的缺点,也导致了这种方法写的程序不具备实用性。
这样就用到了异步,异步指的是进程不需要一直等待下去,而是继续往下执行,直到满足条件时才调用回调函数,这样可以提高执行效率。
举个例子理解一下异步,食堂只有一个打饭窗口,有很多人在排队,最前面的一个人需要考虑打什么饭。按照同步方法,打饭阿姨就要一直等待这个人,直到他考虑明白,而后面的人也要等待这个人,这样就阻塞了,效率就低了。按照异步方法,第一个人到一边去想,阿姨给第二个人打饭、第三个人…,若第一个人考虑明白,就直接去窗口前打饭,不需要再去排队了,以此类推。
异步实现依赖的是多线程技术,一个简单示意图:
异步的简单示意图

示例程序

每一个同步API(如Connect)对应着两个异步的API,分别在原名的前面加上Begin和End(如BeginConnect和EndConnect)。各异步API请参考https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.beginaccept?view=netcore-3.1

服务器

服务器程序有两个类
ClientState类是用于保存用户状态信息的,MainClass类是程序的主类

//这个类用于保存客户端的信息
    class ClientState
    {
         //定义套接字
        public Socket socket;
        //接收缓冲区
        public byte[] recvBuffer = new byte[1024];
        public string recvStr = "";

    }
//主类
class MainClass
    {
        //发送缓冲区
        static byte[] sendBuffer = new byte[1014];
        static string sendStr = "";

        //监听Socket
        static Socket listenfd;
        //客户端Socket及状态信息
        static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();
        public static void Main(string[] args)
        {
            //Socket
            listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //Bind;
            IPAddress ipAdr = IPAddress.Parse("127.0.0.1");
            IPEndPoint iPEndPoint = new IPEndPoint(ipAdr, 8888);
            listenfd.Bind(iPEndPoint);
            //Listen
            listenfd.Listen(0);
            Console.WriteLine("[服务器]启动成功");
            //Accept
            listenfd.BeginAccept(AcceptCallBack, listenfd);
            //等待
            Console.ReadLine();
        }

        /// <summary>
        /// Accept回调
        /// </summary>
        /// <param name="ar"></param>
        public static void AcceptCallBack(IAsyncResult ar) 
        {
            try 
            {
                Console.WriteLine("有客户端连接");
                
                Socket listenfd = (Socket)ar.AsyncState;
                Socket clientfd = listenfd.EndAccept(ar);
                //Clients列表
                ClientState state = new ClientState();
                state.socket = clientfd;
                clients.Add(clientfd, state);
                //接收数据BeginReceive
                clientfd.BeginReceive(state.recvBuffer, 0, 1024, 0, ReceiveCallBack, state);
                //继续Accept
                listenfd.BeginAccept(AcceptCallBack, listenfd);
            }catch(SocketException ex) 
            {
                Console.WriteLine("Socket accept failed" + ex.ToString());
            }
        }

        /// <summary>
        /// Receice回调
        /// </summary>
        /// <param name="ar"></param>
        public static void ReceiveCallBack(IAsyncResult ar) 
        {
            try
            {
                ClientState state = (ClientState)ar.AsyncState;
                Socket clientfd = state.socket;
                int count = clientfd.EndReceive(ar);
                //客户端关闭
                if (count == 0)
                {
                    clientfd.Close();
                    clients.Remove(clientfd);
                    Console.WriteLine("Socket Close");
                    return;
                }
                state.recvStr = Encoding.Default.GetString(state.recvBuffer, 0, count);
                Console.WriteLine("客户端发来" + state.recvStr);

                //告诉客户端收到消息
                sendStr = state.recvStr;
                sendBuffer = Encoding.Default.GetBytes("服务器收到" + sendStr);
                clientfd.Send(sendBuffer);

                clientfd.BeginReceive(state.recvBuffer, 0, 1024, 0, ReceiveCallBack, state);
            }
            catch (SocketException ex)
            {
                Console.WriteLine("Socket receive failed" + ex.ToString());
            }
        }
    }

客户端

public class Echo : MonoBehaviour
{
    public InputField sendStrInput;
    public Text textInfo;
    
    //定义套接字
    Socket socket;
    //接收缓冲区
    byte[] recvBuffer = new byte[1024];
    string recvStr = "";
    //发送缓冲区
    byte[] sendBuffer = new byte[1024];
    string sendStr = "";

    /// <summary>
    /// 点击连接按钮
    /// </summary>
    public void Connection() 
    {
        //Socket
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //Connect
        socket.BeginConnect("127.0.0.1", 8888, ConnectCallBack, socket);
    }

    /// <summary>
    /// Connect回调
    /// </summary>
    /// <param name="ar"></param>
    public void ConnectCallBack(IAsyncResult ar) 
    {
        try
        {
            Socket socket = (Socket)ar.AsyncState;
            socket.EndConnect(ar);
            Debug.Log("Socket sonnect successfully");

            socket.BeginReceive(recvBuffer, 0, 1024, 0, ReceiveCallBack, socket);
        }
        catch (SocketException ex)
        {
            Debug.Log("Socket connect failed" + ex.ToString());
        }
    }

    /// <summary>
    /// Receive回调
    /// </summary>
    /// <param name="ar"></param>
    public void ReceiveCallBack(IAsyncResult ar) 
    {
        try
        {
            Socket socket = (Socket)ar.AsyncState;
            int count = socket.EndReceive(ar);
            recvStr = Encoding.Default.GetString(recvBuffer, 0, count);

            socket.BeginReceive(recvBuffer, 0, 1024, 0, ReceiveCallBack, socket);
        }
        catch (SocketException ex) 
        {
            Debug.Log("Socket receive failed" + ex.ToString());
        }
    }

    /// <summary>
    /// 发送按钮
    /// </summary>
    public void Send() 
    {
        //Send
        sendStr = sendStrInput.text;
        sendBuffer = Encoding.Default.GetBytes(sendStr);
        socket.BeginSend(sendBuffer, 0, sendBuffer.Length, 0, SendCallBack, socket);
    }

    /// <summary>
    /// Send回调
    /// </summary>
    /// <param name="ar"></param>
    public void SendCallBack(IAsyncResult ar) 
    {
        try
        {
            Socket socket = (Socket)ar.AsyncState;
            int count = socket.EndSend(ar);
            Debug.Log("Socket send successfully" + count);
        }
        catch (SocketException ex)
        {
            Debug.Log("Socket send failed" + ex.ToString());
        }
    }

   
    public void Update()
    {
         //显示服务器反馈的信息
        textInfo.text = recvStr;
    }
}

运行效果

服务器运行效果
客户端运行效果
下一篇:Unity网络游戏编程学习(三)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值