上一篇文章里介绍,在接收协议时会出现粘包情况,同样的,在协议发送时也会出现此问题。
前面的文章介绍发送协议和接收协议的时候都是从字符串直接转换为byte数组,这在发送协议和解析协议时都不规范。
public class MsgBase {
public string protoName = "";
public static byte[] Encode(MsgBase msg)
{
string sendStr = JsonUtility.ToJson(msg);
sendStr = msg.protoName + "|"+ sendStr;
return System.Text.Encoding.Default.GetBytes(sendStr);
}
public static MsgBase Decode(string sendStr)
{
string[] split = sendStr.Split('|');
string msgName = split[0];
string msgBody = split[1];
MsgBase msgBase = (MsgBase)JsonUtility.FromJson(msgBody, Type.GetType(msgName));
return msgBase;
}
}
public class MsgHeartbeat : MsgBase
{
public MsgHeartbeat()
{
protoName = "MsgHeartbeat";
}
}
public class MsgRotate : MsgBase
{
public MsgRotate()
{
protoName = "MsgRotate";
}
public Vector2 speed;
}
按照上述方式,定义协议基类MsgBase,在其中定义协议名和协议体,Encode代表发送协议前的协议编码,Decode为接收到协议后的解码。
定义ByteArray记录发送位置
public class ByteArray
{
public byte[] bytes;
public int readIndex;
private int writeIndex;
public int length { get { return writeIndex - readIndex; } }
public ByteArray(byte[] defaultByte)
{
bytes = defaultByte;
readIndex = 0;
writeIndex = defaultByte.Length;
}
}
异步发送协议,从队列中取出一条发送,单条没有全部发送成功,则在新的数据位置继续发送
public static void Send(MsgBase msg)
{
if (socket == null) return;
if (!socket.Connected) return;
byte[] bodyBytes = MsgBase.Encode(msg);//协议编码
Int16 bodyLenth = (Int16)bodyBytes.Length;//计算协议长度
byte[] lengthByte = BitConverter.GetBytes(bodyLenth);//将协议长度信息转换为字节数组
if (!BitConverter.IsLittleEndian)//判断大小端
{
Debug.Log("Reverse lengthByte");
lengthByte.Reverse();
}
byte[] sendBytes = lengthByte.Concat(bodyBytes).ToArray();//将协议长度信息和协议信息拼接在一起,其中协议长度信息占两个字节
ByteArray ba = new ByteArray(sendBytes);
lock (writeQueue)
{
writeQueue.Enqueue(ba);
}
if (writeQueue.Count == 1)
{
socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, SendCallBack, socket);
}
}
private static void SendCallBack(IAsyncResult result)//异步发送协议回调
{
Socket socket = (Socket)result.AsyncState;
int count=socket.EndSend(result);
ByteArray ba;
lock (writeQueue)
{
ba = writeQueue.First();
}
ba.readIndex += count;//记录发送数量
if (ba.length==0)//发送完成则取下一条
{
writeQueue.Dequeue();
ba = writeQueue.First();
}
if (ba != null)//没发送完成则记录发送
{
socket.BeginSend(ba.bytes,ba.readIndex,ba.length,0,SendCallBack,socket);
}
}