2009-08-12 126 views
1

嗨,我有问题使用自定义二进制协议分组数据包。 目前服务器端代码如下所示。使用二进制协议的TCP帧

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(); 
    } 

回答

3

您应该将其视为流。不要依赖任何特定的分块行为。

您需要的数据量总是相同吗?如果没有,你应该改变协议(如果可以的话),以字节长度为逻辑“数据块”加上数据前缀。

在这种情况下,您在一边使用的是BinaryWriter,因此将BinaryReader附加到由TcpClient.GetStream()返回的NetworkStream似乎是最简单的方法。如果你真的想一次捕获一个块的所有数据,那么你应该回到我的想法是用它的长度来加前缀数据。然后循环,直到获得所有数据。

(请确保你有足够的数据来读,虽然长度!如果你的前缀长度为4个字节,你不想读2个字节,错过了接下来的2 ...)

+0

嗯,我会将contentLength添加到标题,谢谢。我在processPacket()中使用了一个二进制读取器,但显然这只是在传递一个部分数据包时才会失败。 – Endian 2009-08-12 20:02:14

+0

@Endian:你为什么不通过网络流而不是数据包呢? – 2009-08-12 20:03:29

+0

@Jon:数据包更多的是我的二进制读取器可以读取而不会产生缓冲区溢出的块。我已经按照我现在所做的来编辑原文,在你的建议之后,它似乎在工作,感谢您的帮助。 :) – Endian 2009-08-12 20:39:11