前言
上一章写了Socket相关的概念并且通过同步方法实现了Socket编程,但是由于同步方法是阻塞的,如果我们在Unity的主线程中使用,在阻塞期间会导致游戏进程直接卡死。
为了让游戏流畅运行同时让服务端异步处理多个客户端请求,我们通常会采用异步的方法进行Socket编程。
同步和异步
同步(Synchronous)和异步(Asynchronous)是指代码执行的方式和模式。
一般来说,同步操作是按顺序执行的,每个操作会阻塞当前线程,直到操作完成才会继续执行后续代码,在同步操作中,调用方会等待被调用方法的返回结果,然后才能继续执行后续代码。而异步操作允许在操作进行的同时执行其他代码,而不会阻塞当前线程,不需要等待操作完成,就可以继续执行后续代码。
相对而言,异步操作能提高应用程序的响应性和并发性,允许更好地利用系统资源,同时也能避免阻塞关键线程。
网络管理类 (异步)
较上一篇文章 只需要更改网络管理类即可 下面是代码示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace vs_Day03_practice
{
internal class NetMgr2:Singleton<NetMgr2>
{
Socket socket;
public List<Client> sockets = new List<Client>(); //所有玩家集合
public void Init()
{
socket=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ip=new IPEndPoint(IPAddress.Any, 9999);
socket.Bind(ip);
socket.Listen(10);
socket.BeginAccept(OnAccept, null);
}
private void OnAccept(IAsyncResult ar)
{
Socket client=socket.EndAccept(ar);
IPEndPoint ip=client.RemoteEndPoint as IPEndPoint;
Console.WriteLine(ip.Address+":"+ip.Port);
Client cli=new Client();
cli.socket=client;
//玩家连接成功 放入集合
sockets.Add(cli);
cli.socket.BeginReceive(cli.data, 0, cli.data.Length,SocketFlags.None,OnReceive,cli);
socket.BeginAccept(OnAccept, null);
}
private void OnReceive(IAsyncResult ar)
{
//取出参数
Client cli=ar.AsyncState as Client;
//获取消息长度
int len=cli.socket.EndReceive(ar);
if (len <= 0)
{
cli.socket.Shutdown(SocketShutdown.Both);
cli.socket.Close();
sockets.Remove(cli); //删除
告诉所有人该玩家下线了
//if (cli.nick != null && cli.nick != "")
//{
// foreach (var item in sockets)
// {
// if (item.nick != null && item.nick != "")
// {
// Send(item.socket, MsgID.S2C_Leave, UTF8Encoding.UTF8.GetBytes(cli.nick));
// }
// }
//}
return;
}
//截取消息
//data=head+body
byte[] data=cli.data.Take(len).ToArray();
//取出id
//head是int转成byte数组,固定是4位
//ToInt32只取4位
int id=BitConverter.ToInt32(data, 0);
//body的长度=len-4
byte[] body=data.Skip(4).ToArray();
//同步传socket异步传Client
MessageCenter.Ins.Broadcast(id, body,cli);
cli.socket.BeginReceive(cli.data, 0, cli.data.Length, SocketFlags.None, OnReceive, cli);
}
public void Send(Socket client,int id, byte[] body)
{
byte[] head=BitConverter.GetBytes(id);
//拼接数组
byte[] data=head.Concat(body).ToArray();
client.BeginSend(data, 0, data.Length, SocketFlags.None, OnSend, client);
}
private void OnSend(IAsyncResult ar)
{
Socket client=ar.AsyncState as Socket;
int len = client.EndSend(ar);
Console.WriteLine("发送成功,发送长度"+len);
}
}
}
internal class Client //用到的自定义类
{
public Socket socket;
public byte[] data=new byte[1024];
public string nick;//昵称 登录时注册的名字
}