C++ 在通过 TCP 发送数据之前发送数据大小

Posted

技术标签:

【中文标题】C++ 在通过 TCP 发送数据之前发送数据大小【英文标题】:C++ send data size before sending data via TCP 【发布时间】:2016-02-02 22:42:40 【问题描述】:

免责声明:我对此真的很陌生,所以我会尽可能清楚。

我有一个可变的“数据包”大小的数据。我知道 TCP 是一种流协议 - 它流的是字节,而不是数据包。

现在,我想告诉我的客户要从流中读取的数据的大小(构成我的“数据包”之一)。

我发送的数据是std::string格式

如果我的字符串被称为outstring,我如何用需要在客户端读取的字符串大小填充UINT8 buff[4]

我的客户端,用 C# 编写,如下所示:

int total = 0;
int read;
byte[] datasize = new byte[4];

read = socket.Receive(datasize, 0, 4, 0);
int size = BitConverter.ToInt32(datasize, 0);
int dataleft = size;
byte[] data = new byte[size];

while (total < size) 
  read = socket.Receive(data, total, dataleft, 0);
  if (read == 0) 
    break;
  
  total += read;
  dataleft -= read;

【问题讨论】:

【参考方案1】:

抱歉,这不是您问题的直接答案,但它可能有用。

使用原始套接字编程很糟糕。要使程序能够稳健地处理它们,还有很多工作要做。例如,在您的代码 sn-p 中,您尝试将 4 个字节接收到数据大小中。不能保证单个 socket.Receive() 操作实际上会读取 4 个字节,它可以读取 1 到 4 个字节之间的任何内容。你必须在循环中调用 socket.Receive() 直到你收到所有 4 个字节,就像你稍后做的那样。

幸运的是,像ZeroMQ 这样的东西几乎可以消除所有的痛苦,并为您提供一个很好的简单消息传递框架,该框架位于套接字、管道、内存等之上。如果您只是想完成某件事,那么它非常重要推荐。

【讨论】:

+1 你需要创建一个 readAll 函数或类似的函数并使用它而不是 read 如果你要假设它会失败或读取你的字节数要求它阅读。 评论,但是对于评论来说太大了,这确实需要指出。赞成 是的,它是经典之一。需要说。 @DavidSchwartz,当然 readAll() 函数会有所帮助。但是,使其变得强大本身就是一项工作。除了循环接收所有被请求的字节之外,还有很多问题需要处理。编写 readAll() 并处理所有可能的问题当然是一种教育!【参考方案2】:
byte[0] = l & 0x000000ff;
byte[1] = (l & 0x0000ff00) >> 8;
byte[2] = (l & 0x00ff0000) >> 16;
byte[3] = (l & 0xff000000) >> 24;

这假设您希望它采用小端格式。如果你想要 bigendian

byte[3] = l & 0x000000ff;
byte[2] = (l & 0x0000ff00) >> 8;
byte[1] = (l & 0x00ff0000) >> 16;
byte[0] = (l & 0xff000000) >> 24;

【讨论】:

附录:Wiki page on Endinanness 以帮助那些想知道“为什么以 Brainy Smurf 眼镜的名义让 pm100 让自己经历这个比特转移地狱?”的读者? pm100 可以使用 htonl 并简单地将 int 转换为 byte* 但这种方式似乎更清晰。也许不是 :-) 而且我懒得去查找 c# bitconverter 想要什么 :-(【参考方案3】:

你的发送代码是用C++编写的,所以你根本不需要字节数组,你可以直接发送整数大小值,例如:

uint32_t size = htonl(outstring.size()); // convert from host byte order to network byte order (big endian)
send(theSocket, (char*)&size, sizeof(size), 0);
send(theSocket, outstring.c_str(), outstring.size(), 0);

在 .NET 客户端,您可以执行以下操作:

bool readSocketFully(Socket s, byte[] buf)

    int idx = 0;
    int dataleft = buf.Length;
    int read;

    while (dataleft > 0) 
        read = s.Receive(buf, idx, dataleft, 0);
        if (read == 0) 
            return false;
        
        idx += read;
        dataleft -= read;
    
    return true;


byte[] readPacket(Socket s)

    byte[] datasize = new byte[4];    

    if (!readSocketFully(s, datasize)) 
        return null;
    

    if (BitConverter.IsLittleEndian) 
        System.Array.Reverse(datasize, 0, 4); // convert from network byte order (big endian) to host byte order (little endian)
    
    int size = BitConverter.ToInt32(datasize, 0);
    byte[] data = new byte[size];

    if (size > 0) 
        if (!readSocketFully(socket, data)) 
            return null;
        
    

    return data;

byte[] data = readPacket(socket);
// use data as needed ...

【讨论】:

以上是关于C++ 在通过 TCP 发送数据之前发送数据大小的主要内容,如果未能解决你的问题,请参考以下文章

TCP重发控制

TCP 拥塞控制

TCP流量控制

UDP和TCP的区别

c++程序员面试题---TCP的“三次握手”和“四次挥手”

TCP/IP详解 卷1 第十七章 TCP:传输控制协议