介绍
在阅读了罗培羽著作的Unity3D网络游戏实战一书后,博主综合自己的开发经验与考虑进行部分修改和调整,将通用的客户端网络模块和通用的服务端框架进行提取,形成专栏,介绍Socket网络编程,希望对其他人有所帮助。目录如下,链接为对应的CSDN博客地址:
一、通用服务端框架
(一)、定义套接字和多路复用
https://blog.csdn.net/qq_42139931/article/details/124051945?spm=1001.2014.3001.5501
(二)、客户端信息类和通用缓冲区结构
https://blog.csdn.net/qq_42139931/article/details/124053571?spm=1001.2014.3001.5502
(三)、Protobuf 通信协议
https://blog.csdn.net/qq_42139931/article/details/124054972?spm=1001.2014.3001.5501
(四)、数据处理和关闭连接
https://blog.csdn.net/qq_42139931/article/details/124055227?spm=1001.2014.3001.5501
(五)、Messenger 事件发布、订阅系统
https://blog.csdn.net/qq_42139931/article/details/124055392?spm=1001.2014.3001.5501
(六)、单点发送和广播数据
https://blog.csdn.net/qq_42139931/article/details/124055482?spm=1001.2014.3001.5501
(七)、时间戳和心跳机制
https://blog.csdn.net/qq_42139931/article/details/124055856?spm=1001.2014.3001.5501
二、通用客户端网络模块
(一)、Connect 连接服务端
https://blog.csdn.net/qq_42139931/article/details/124091349?spm=1001.2014.3001.5502
(二)、Receive 接收并处理数据
https://blog.csdn.net/qq_42139931/article/details/124092588?spm=1001.2014.3001.5502
(三)、Send 发送数据
https://blog.csdn.net/qq_42139931/article/details/124094323?spm=1001.2014.3001.5502
(四)、Close 关闭连接
https://blog.csdn.net/qq_42139931/article/details/124094895?spm=1001.2014.3001.5502
本篇内容:
单点发送:
定义写入队列:
代码语言:javascript复制//写入队列
private readonly static Queue<ByteArray> writeQueue = new Queue<ByteArray>();
发送方法Send接受两个参数:第一个参数为客户端信息对象,代表要将协议发送给哪个客户端;第二个参数proto代表要发送的协议对象。发送之前先进行状态判断,确保客户端连接有效。代码如下:
代码语言:javascript复制/// <summary>
/// 向客户端发送协议(单点发送)
/// </summary>
/// <param name="client">客户端</param>
/// <param name="proto">协议</param>
public static void Send(Client client, IExtensible proto)
{
//状态判断
if (client == null || !client.socket.Connected) return;
//数据编码
byte[] nameBytes = ProtoUtility.EncodeName(proto);
byte[] bodyBytes = ProtoUtility.Encode(proto);
int length = nameBytes.Length bodyBytes.Length;
byte[] sendBytes = new byte[2 length];
//组装长度
sendBytes[0] = (byte)(length % 256);
sendBytes[1] = (byte)(length / 256);
//组装名字
Array.Copy(nameBytes, 0, sendBytes, 2, nameBytes.Length);
//组装消息体
Array.Copy(bodyBytes, 0, sendBytes, 2 nameBytes.Length, bodyBytes.Length);
//写入队列
ByteArray byteArray = new ByteArray(sendBytes);
lock (writeQueue)
{
writeQueue.Enqueue(byteArray);
}
if (writeQueue.Count == 1)
{
//发送
client.socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, SendCallback, client.socket);
}
}
Send回调:
代码语言:javascript复制//发送回调
private static void SendCallback(IAsyncResult ar)
{
Socket socket = ar.AsyncState as Socket;
//状态判断
if (socket == null || !socket.Connected) return;
//结束发送
int length = socket.EndSend(ar);
ByteArray? byteArray;
lock (writeQueue)
{
byteArray = writeQueue.First();
}
//完整发送
byteArray.readIdx = length;
if (byteArray.length == 0)
{
lock (writeQueue)
{
writeQueue.Dequeue();
byteArray = writeQueue.Count > 0 ? writeQueue.First() : null;
}
}
//继续发送
if (byteArray != null)
{
socket.BeginSend(byteArray.bytes, byteArray.readIdx, byteArray.remain, 0, SendCallback, socket);
}
}
广播数据:
向所有客户端发送数据:
代码语言:javascript复制/// <summary>
/// 向所有客户端发送协议(广播)
/// </summary>
/// <param name="proto">协议</param>
public static void Send(IExtensible proto)
{
foreach (Client client in clients.Values)
{
Send(client, proto);
}
}
参考资料:《Unity3D网络游戏实战》(第2版)罗培羽 著