C# 基于二进制协议的TCP分帧

C# 基于二进制协议的TCP分帧,c#,.net,sockets,C#,.net,Sockets,嘿,我在使用自定义二进制协议分离数据包时遇到了一个问题。 目前,服务器端代码如下所示 public void HandleConnection(object state) { TcpClient client = threadListener.AcceptTcpClient(); NetworkStream stream = client.GetStream(); byte[] data = new byte[4096];

嘿,我在使用自定义二进制协议分离数据包时遇到了一个问题。 目前,服务器端代码如下所示

    public void HandleConnection(object state)
    {
        TcpClient client = threadListener.AcceptTcpClient();
        NetworkStream stream = client.GetStream();
        byte[] data = new byte[4096];

        while (true)
        {
            int recvCount = stream.Read(data, 0, data.Length);
            if (recvCount == 0) break;
            LogManager.Debug(Utility.ToHexDump(data, 0, recvCount));
            //processPacket(new MemoryStream(data, 0, recvCount));
        }
        LogManager.Debug("Client disconnected");
        client.Close();
        Dispose();
    }
我一直在观察数据包的十六进制转储,有时整个数据包都是一次性的,比如说全部20个字节。在其他情况下,它是零碎的,我需要如何缓冲这些数据才能正确地将其传递给processPacket()方法。我试图只使用单字节操作码头,我是否也应该在头中添加(ushort)contentLength之类的内容?我试图使协议尽可能轻量级,并且这个系统不会发送非常大的数据包(<128字节)

我正在测试的客户端代码如下所示

    public void auth(string user, string password)
    {
        using (TcpClient client = new TcpClient())
        {
            client.Connect(IPAddress.Parse("127.0.0.1"), 9032);
            NetworkStream networkStream = client.GetStream();

            using (BinaryWriter writer = new BinaryWriter(networkStream))
            {
                writer.Write((byte)0); //opcode
                writer.Write(user.ToUpper());
                writer.Write(password.ToUpper());
                writer.Write(SanitizationMgr.Verify()); //App hash
                writer.Write(Program.Seed);
            }
        }
    }
我不确定这是否是造成混乱的原因,而且二进制协议在网络上似乎没有太多信息,特别是在涉及C#的地方。任何评论都会有帮助。=)

解决了这个问题,不确定它是否正确,但它似乎给了我的处理者他们所需要的东西

    public void HandleConnection(object state)
    {
        TcpClient client = threadListener.AcceptTcpClient();
        NetworkStream stream = client.GetStream();
        byte[] data = new byte[1024];

        uint contentLength = 0;
        var packet = new MemoryStream();
        while (true)
        {
            int recvCount = stream.Read(data, 0, data.Length);
            if (recvCount == 0) break;

            if (contentLength == 0 && recvCount < headerSize)
            {
                LogManager.Error("Got incomplete header!");
                Dispose();
            }

            if(contentLength == 0) //Get the payload length
                contentLength = BitConverter.ToUInt16(data, 1);

            packet.Write(data, (int) packet.Position, recvCount); //Buffer the data we got into our MemStream
            if (packet.Length < contentLength + headerSize) //if it's not enough, continue trying to read
                continue;

            //We have a full packet, pass it on
            //LogManager.Debug(Utility.ToHexDump(packet));
            processPacket(packet);

            //reset for next packet
            contentLength = 0;
            packet = new MemoryStream();
        }
        LogManager.Debug("Client disconnected");
        client.Close();
        Dispose();
    }
public void HandleConnection(对象状态)
{
TcpClient client=threadListener.AcceptTcpClient();
NetworkStream=client.GetStream();
字节[]数据=新字节[1024];
uint contentLength=0;
var packet=newmemoryStream();
while(true)
{
int recvCount=stream.Read(数据,0,数据.长度);
如果(recvCount==0)中断;
if(contentLength==0&&recvCount
您应该将其视为一个流。不要依赖任何特定的分块行为

您需要的数据量是否始终相同?如果没有,您应该更改协议(如果可以的话)以使用字节长度作为数据逻辑“块”的前缀

在本例中,您在一侧使用
BinaryWriter
,因此将
BinaryReader
附加到
TcpClient.GetStream()
返回的
NetworkStream
似乎是最简单的方法。但是,如果您真的希望一次捕获一个块的所有数据,那么您应该回到我的想法,即在数据前面加上其长度。然后循环,直到你得到所有的数据


(请确保您有足够的数据来读取长度!如果您的长度前缀为4字节,您不希望读取2字节而错过下一个2…

Hmm,我将在标题中添加一个contentLength,谢谢。我在processPacket()中使用了二进制读取器,但当它只被传递一个部分数据包时,显然失败得很惨。@Endian:为什么不直接传递网络流而不是数据包呢?@Jon:packet更像是一个块,我的二进制读取器可以在没有缓冲区不足的情况下读取。根据你的建议,我已经用我现在正在做的事情编辑了这篇原始文章,它似乎起到了作用,谢谢你的帮助。:)