学习《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网络游戏编程学习(三)