在上一篇文章中,使用同步连接虽然实现了客户端与服务器的连接,但由于通讯方法的阻塞性,例如在接收消息时,当没有消息的情况下会阻塞在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("接收数据失败");
}
}
}
上述代码首先定义了一个新的ClientState类,socket存储连接入的客户端,data存储每个客户端的缓冲消息,服务器采用异步accept的方式,可以允许连入多个客户端且不会卡住程序,并将其存入clients中,同时采用异步方式接收每个客户端发来的信息。
这一章内容使用异步方式解决了在没有收到新消息时程序阻塞在Receivce的问题,同时可以允许多个客户端连接入服务器,但异步方式会引起线程问题。
上述代码还有以下不足:
1.服务器代码并没有处理粘包问题,如果在极短的时间内收到大量客户端发来的消息,会造成信息解析错误。
2.客户端部分的代码没有处理在收到服务器下发协议的处理,由于网络通讯并不在unity主线程中,因此网络线程收到的协议消息并不能操作主线程的UI。
在下一篇文章中将会解决以上问题。