在上一篇文章中,使用同步连接虽然实现了客户端与服务器的连接,但由于通讯方法的阻塞性,例如在接收消息时,当没有消息的情况下会阻塞在Receice方法中,下面将会把代码改为异步方式。

客户端异步:

using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System;
public class Client : MonoBehaviour {
    private Socket client;
    private byte[] data = new byte[1024];
	// Use this for initialization
	void Start () {
        
        Thread thread = new Thread(ConnectToServer);
        thread.Start();
    }
    private void ConnectToServer()
    {
        client = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        //ip:"127.0.0.1"连接本地测试, 端口号:8888 服务器客户端端口号需相同
        IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"),8888);
        client.BeginConnect(endPoint, OnConnectCallBack, client);      
    }
    void OnConnectCallBack(IAsyncResult ar)
    {
        Socket socket = ar.AsyncState as Socket;
        socket.EndConnect(ar);
        Debug.Log("Connected sucess");
        socket.BeginReceive(data,0,1024,0, OnReiceiveCallBack, socket);
    }
    void OnReiceiveCallBack(IAsyncResult ar)
    {
        try {
            Socket socket = ar.AsyncState as Socket;
            int count = socket.EndReceive(ar);
            string recStr = Encoding.UTF8.GetString(data,0,count);
            Debug.Log("收到服务器发送消息:"+ recStr);
            //在此处继续接收信息
            socket.BeginReceive(data, 0, 1024, 0, OnReiceiveCallBack, socket);

        } catch (SocketException e)
        {
            Debug.Log("Receive fail");
        }
    }
}

上述代码中客户端采用异步方式连接服务器,连接成功后异步方式获取服务器信息,异步接收的回调函数中继续接收信息。

服务器异步:

using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System;
using System.Collections.Generic;
public class ClientState
{
    public Socket socket;
    public byte[] data = new byte[1024];
}
public class Server : MonoBehaviour {

    private Socket server;
    private Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();
	void Start () {
        Thread thread = new Thread(InitServer);
        thread.Start();
    }
    private void InitServer()
    {
        server = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"),8888);
        server.Bind(endPoint);
        server.Listen(0);//监听客户端数量不限
        Debug.Log("等待客户端连接。。。");
        server.BeginAccept(AcceptCallBack, server);
    }
    void AcceptCallBack(IAsyncResult ar)
    {
        try
        {
            Debug.Log("客户端接入");
            Socket server = ar.AsyncState as Socket;
            Socket client = server.EndAccept(ar);
            ClientState clientState = new ClientState();
            clientState.socket = client;
            //将连接进来的客户端保存起来
            clients.Add(client,clientState);
            //接收此客户端发来的信息
            client.BeginReceive(clientState.data,0,1024,0, ReceiveCallBack,clientState);
            //继续监听新的客户端接入
            server.BeginAccept(AcceptCallBack, server);
        } catch (SocketException e)
        {
            Debug.Log("Accept fail");
        }
    }
    void ReceiveCallBack(IAsyncResult ar)
    {
        try {
            ClientState state = ar.AsyncState as ClientState;
            Socket client = state.socket;
            int count = client.EndReceive(ar);
            if (count == 0)
            {
                client.Close();
                clients.Remove(client);
                Debug.Log("客户端关闭");
                return;
            }
            string msg = Encoding.UTF8.GetString(state.data,0,count);
            Debug.Log("收到客户端消息:"+ msg);
            client.BeginReceive(state.data, 0, 1024, 0, ReceiveCallBack, state);
        } catch (SocketException e)
        {
            Debug.Log("接收数据失败");
        }        
    }
}

unity 服务器与客户端数据同步问题 unity connect to server_服务器

   

上述代码首先定义了一个新的ClientState类,socket存储连接入的客户端,data存储每个客户端的缓冲消息,服务器采用异步accept的方式,可以允许连入多个客户端且不会卡住程序,并将其存入clients中,同时采用异步方式接收每个客户端发来的信息。

这一章内容使用异步方式解决了在没有收到新消息时程序阻塞在Receivce的问题,同时可以允许多个客户端连接入服务器,但异步方式会引起线程问题。

上述代码还有以下不足:

1.服务器代码并没有处理粘包问题,如果在极短的时间内收到大量客户端发来的消息,会造成信息解析错误。

2.客户端部分的代码没有处理在收到服务器下发协议的处理,由于网络通讯并不在unity主线程中,因此网络线程收到的协议消息并不能操作主线程的UI。

在下一篇文章中将会解决以上问题。