介绍
在阅读了罗培羽著作的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
本篇内容:
客户端信息类Client:
每一个客户端都会包含一个与服务器连接的Socket套接字和字节数据读写缓冲区,定义相关内容如下:
代码语言:javascript复制using System.Net.Sockets;
namespace SK.Framework.Sockets
{
/// <summary>
/// 客户端信息类
/// </summary>
public class Client
{
/// <summary>
/// 套接字
/// </summary>
public Socket socket;
/// <summary>
/// 缓冲区
/// </summary>
public ByteArray readBuff;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="socket">套接字</param>
public Client(Socket socket)
{
this.socket = socket;
readBuff = new ByteArray();
}
}
}
代码语言:javascript复制通用缓冲区结构ByteArray:
作为通用的缓冲区结构,ByteArray支持自动拓展,例如写入的数据长度大于缓冲区剩余长度时,缓冲区的容量会自动扩充。核心变量:readIdx读取位置、writeIdx写入位置、length缓冲区中数据长度、remain缓冲区中剩余空间。代码如下:
代码语言:javascript复制namespace SK.Framework.Sockets
{
public class ByteArray
{
//默认大小
private const int DEFAULT_SIZE = 1024;
//初始大小
private readonly int initSize = 0;
//缓冲区
public byte[] bytes;
//读取位置
public int readIdx = 0;
//写入位置
public int writeIdx = 0;
//容量
private int capacity = 0;
//剩余空间
public int remain { get { return capacity - writeIdx; } }
//数据长度
public int length { get { return writeIdx - readIdx; } }
//构造函数
public ByteArray(int size = DEFAULT_SIZE)
{
bytes = new byte[size];
capacity = size;
initSize = size;
writeIdx = 0;
readIdx = 0;
}
//构造函数
public ByteArray(byte[] defaultBytes)
{
bytes = defaultBytes;
capacity = defaultBytes.Length;
initSize = defaultBytes.Length;
readIdx = 0;
writeIdx = defaultBytes.Length;
}
//重设尺寸
public void ReSize(int size)
{
if (size < length) return;
if (size < initSize) return;
int n = 1;
while (n < size)
{
n *= 2;
}
capacity = n;
byte[] newBytes = new byte[capacity];
Array.Copy(bytes, readIdx, newBytes, 0, writeIdx - readIdx);
bytes = newBytes;
writeIdx = length;
readIdx = 0;
}
//检查并移动数据
public void CheckAndMoveBytes()
{
if (length < 8)
{
MoveBytes();
}
}
//移动数据
public void MoveBytes()
{
if (length > 0)
{
Array.Copy(bytes, readIdx, bytes, 0, length);
}
writeIdx = length;
readIdx = 0;
}
//写入数据
public int Write(byte[] bs, int offset, int count)
{
if (remain < count)
{
ReSize(length count);
}
Array.Copy(bs, offset, bytes, writeIdx, count);
writeIdx = count;
return count;
}
//读取数据
public int Read(byte[] bs, int offset, int count)
{
count = Math.Min(count, length);
Array.Copy(bytes, readIdx, bs, offset, count);
readIdx = count;
CheckAndMoveBytes();
return count;
}
//读取Int16
public Int16 ReadInt16()
{
if (length < 2) return 0;
Int16 ret = (Int16)((bytes[readIdx 1]) << 8 | bytes[readIdx]);
readIdx = 2;
CheckAndMoveBytes();
return ret;
}
//读取Int32
public Int32 ReadInt32()
{
if (length < 4) return 0;
Int32 ret = (Int32)((bytes[readIdx 3] << 24) |
(bytes[readIdx 2] << 16) |
(bytes[readIdx 1] << 8) |
bytes[readIdx 0]);
readIdx = 4;
CheckAndMoveBytes();
return ret;
}
}
}
参考资料:《Unity3D网络游戏实战》(第2版)罗培羽 著