如何获取刚刚从套接字接收到的缓冲区的长度?

Posted

技术标签:

【中文标题】如何获取刚刚从套接字接收到的缓冲区的长度?【英文标题】:How to get length of the buffer which is just received from socket? 【发布时间】:2022-01-15 15:00:51 【问题描述】:

我正在使用与服务器的#include <sys/socket.h>库套接字连接,并使用char类型的vector从套接字连接接收数据,如下所示:

    struct sockaddr_in serv_addr;
    int sock, valread;
    sock = 0;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    
        printf("\n Socket creation error \n");
    

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // Convert IPv4 and IPv6 addresses from text to binary form
    if (inet_pton(AF_INET, "0.0.0.0", &serv_addr.sin_addr) <= 0)
    
        printf("\nInvalid address/ Address not supported \n");
    

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    
        printf("\nConnection Failed \n");
    
    std::vector<char> buffer = 0;
    buffer.reserve(1024);
    read(sock, buffer.data(), 1024);

由于服务器响应大小的长度可变但不超过1024,所以为什么buffer的大小固定为1024。 现在因为我收到可变大小的响应,所以我想知道buffer 的大小。 我尝试了以下操作:

std::cout<<sizeof(buffer)<<" "<<buffer.size();

输出是

 sizeof(buffer) = 32
 buffer.size() = 1

如果我尝试1024 值,它会产生一些垃圾值,如下所示:

for (int i = 0; i < 1024; i++)

    std::cout<<buffer[i];

输出:

[“xmin”:95,“ymin”:147,“ymax”:276,“xmax”:193,“xmin”:42,“ymin”:353,“ymax”:488,“ xmax”:123,“xmin”:85,“ymin”:19,“ymax”:166,“xmax”:145,“xmin”:1,“ymin”:254,“ymax”:327 ,“xmax”:107,“xmin”:393,“ymin”:281,“ymax”:419,“xmax”:463,“xmin”:379,“ymin”:316,“ymax” : 457, "xmax": 442]���������!�)��0�8��?�G��N�V��]�e��l�t�� ���������������������������������������������

那么有什么方法可以得到准确的响应大小?

【问题讨论】:

使用read的返回值。您的程序应该始终检查每个函数调用的返回值,因为这是 C 语言中用来指示错误情况的习惯用法。 buffer.reserve(1024);这只是保留而不是设置向量大小。 @Dai 不太好 :) 这是那个图书馆的习语。在 C++ 中也有例外。所以更笼统地说,阅读任何库的文档,不要跳过它们总是很重要的细节。 @PepijnKramer read 是 POSIX 中的 C 函数,而不是 C++ 函数。检查函数返回值的习惯用法是an ecosystem-wide C idiom,而不仅仅是任何特定库之一。 好的,我现在知道了。问题标记为 C++。那么应该是“C” 【参考方案1】:

始终检查 C 风格 API 中的返回值!

C 风格的 API 可直接从大多数其他编程语言(包括 C++)调用。因为可移植的 C 不支持抛出异常 C 风格的库 API 被设计为通常通过返回值(例如返回 NULL 或负值)来指示错误条件,而 输出数据(例如字节缓冲区, struct pointers 等)通过作为参数传递的指针传递。因为 C 暴露了原始程序内存,这意味着如果您尝试使用无效指针或者您正在使用的库表明它处于无效状态或终端状态(例如 @ 987654324@ 在文件流上)。


POSIX 的read 函数返回实际写入缓冲区的字节数。

https://man7.org/linux/man-pages/man2/read.2.html

成功时,返回读取的字节数(零表示 文件结尾),文件位置提前这个数字。

如果这个数字小于 请求的字节数;这可能会发生,例如因为更少的字节 现在实际上可以使用(可能是因为我们接近 文件结束,或者因为我们正在从管道读取,或者从 终端),或者因为read()被信号中断了。

注意这部分:

这可能会发生,例如因为现在实际可用的字节数较少

...这意味着您需要在循环中调用read,直到它返回零。

像这样:


using std::vector<char>; // Consider using `std::Array` instead as it's a fixed-size buffer.

//

const size_t bufferLength = 1024;

vector<char> buffer(/*n:*/ bufferLength);

char* bufferPtr = buffer.data();
size_t totalRead = 0;
while( totalRead < bufferLength )

    char* bufferPtrOffset = bufferPtr + totalRead;

    ssize_t bytesRead = read( /*fd:*/ sock, /*buffer:*/ bufferPtrOffset, /*count:*/ bufferLength - totalRead );
    if( bytesRead < 0 )
    
        // TODO: Error condition. Throw an exception or something.
    
    else if( bytesRead == 0 )
    
        break;
    
    else
    
        totalRead += bytesRead;
    

【讨论】:

【参考方案2】:

根据@Dai。我应该使用函数read的返回值 再次感谢。

【讨论】:

您还需要在循环中调用read直到它读取为零顺便说一句。 那个 or 循环,直到它提供您期望的所有字节。当您从流套接字中读取数据时,您将获得流中可用的内容,直至请求的字节数。如果您需要 10000 个字节,您可能不得不继续要求更多数据,直到所有 10000 个都到达。永远不要假设你得到了全部信息,因为流不知道你认为什么是信息,坦率地说他们不在乎。 内心深处他们确实在乎;他们只是从未学会如何表达自己的感受。 @user4581301 令我惊讶的是,BSD 套接字没有提供用于正确和安全地处理固定大小和分隔消息的辅助函数。在过去的几十年里,仅此一项就可以使我们免于无数缓冲区溢出漏洞。 @Dai:它们带有标准输入输出——您可以使用fdopen(socket, "r+"),然后使用 fread/fwrite 处理固定大小的数据,或者使用 fgets 处理换行符分隔的文本。或者 fscanf 用于其他分隔符,尽管处理 NUL 可能很棘手。对于某些用途,最好为一个套接字 fdopen 两个 FILE(一个“r”和一个“w”),但如果小心操作,这也可以。

以上是关于如何获取刚刚从套接字接收到的缓冲区的长度?的主要内容,如果未能解决你的问题,请参考以下文章

如何拆分接收到的 boost asio udp 套接字联合数据报

如何从字节数组元素中解包/提取低阶和高阶值

C#中的客户端套接字

如何得知socket的缓存大小,这个缓存是不是有

如何设置 QWebView 的套接字选项?

如何实时播放从服务器接收到的视频流