Socket网络连接
1. 使用C#实现服务器后端
TCP粘包、拆包属于网络底层问题,数据链路层、网络层、传输层都有可能出现。日常的网络应用开发大多数在传输层出现,而UDP是由消息保护边界的,不会发生粘包、拆包问题,只发生在TCP协议中。
产生粘包问题主要是:接收方不知道接收的数据间的界限,不知道接收多长的数据。
网络消息传输过程中由于种种情况不同的数据包往往会粘在一起,粘包的情况一般来说相对较多些,分包出现的情况相对较少,但是同样的都需要去解决,否则最终的实现结果一定和我们的想法大有出入。
2.解决办法:人为定义数据传输过程
出现粘包分包的情况难以避免,但是同样的现在也已经有很多解决办法了,比较通用的办法就是采用包头+包尾的构建方式。
什么意思呢,我们都知道数据在网络传输过程中是转换为二进制数据来进行通信的,而字节是二进制的基本单位,我们每次进行通信的时候只要得到我们传输的数据长度就可以了。
具体就是当我们发送一段消息后,假如发送的是 “this’s a message”,然后不管是通过protobuf还是自带的二进制转换api将string转换为二进制之后,首先我们可以知道本次通信的字节数组的长度,发送这消息的时候,假设数据长度为20,那我们就重新构建一个字节数组,先储存20,然后再把我们要发的消息存进去,最终就是 20+“我们的消息”,读取的时候我们直接读取4个字节得到其长度,然后在根据这个长度读取后面的内容就可以了
3.定义缓冲区域
缓冲区的优势以文件流的写入为例,如果我们不使用缓冲区,那么每次写操作 CPU 都会和低速存储设备也就是磁盘进行交互,那么整个写入文件的速度就会受制于低速的存储设备(磁盘)。但如果使用缓冲区的话,每次写操作会先将数据保存在高速缓冲区内存上,当缓冲区的数据到达某个阈值之后,再将文件一次性写入到磁盘上。因为内存的写入速度远远大于磁盘的写入速度,所以当有了缓冲区之后,文件的写入速度就被大大提升了。
代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace Server
{
internal class EncodeTool
{
/// <summary>
/// 我们不能将收到的信息每次收到的时候就直接读取
/// 为了提升性能需要先存到缓存区
/// 需要先放到缓存区中然后在拿出来
/// </summary>
public static byte[] EncodePacket(byte[] data)
{
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
bw.Write(data.Length);
bw.Write(data);
byte[] byteArray = new byte[(int)ms.Length];
Buffer.BlockCopy(ms.GetBuffer(), 0, byteArray, 0, (int)ms.Length);
return byteArray;
}
}
}
/// <summary>
/// 解析消息体 从缓存里取出一个一个完整的数据包
/// </summary>
/// <returns></returns>
public static byte[] DecodePacket(ref List<byte> dataCache)
{
if (dataCache.Count < 4)
return null;
using (MemoryStream ms = new MemoryStream(dataCache.ToArray()))
{
using (BinaryReader br = new BinaryReader(ms))
{
int length = br.ReadInt32();
int dataRemainLength = (int)(ms.Length - ms.Position);
if (length > dataRemainLength)
return null;
byte[] data = br.ReadBytes(length);
//更新一下数据缓存
dataCache.Clear();
dataCache.AddRange(br.ReadBytes(dataRemainLength));
return data;
}
}
}
}