功能 setsockopt 似乎不起作用

Posted

技术标签:

【中文标题】功能 setsockopt 似乎不起作用【英文标题】:Function setsockopt seems not working 【发布时间】:2016-08-10 17:32:19 【问题描述】:

我正在使用 WinSock2,我想一次接收我所有的包(它们是 XML 文件)。 现在我正在制作如下,因为 setsockopt 似乎忽略了我的“命令”来设置接收缓冲区的大小,但它总是从执行中返回 OK:

#define IP_BUF_SZ 2000000
//...
std::string sBuffer;
long chrs_read = 0;
int iBufSize = IP_BUF_SZ;
//...
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int)) == SOCKET_ERROR)
    MessageBox("Unable to increase"); //never reachs here

do

    char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000);
    chrs_read = recv(sockfd, buf, IP_BUF_SZ, 0);

    if (chrs_read > 0)
        sBuffer += std::string(buf);

    FREE(buf);
    buf = NULL;

while (chrs_read > 0);

但我想做这样的事情(一次全部):

setsockopt(IP_STATUS[CHAN_EPIC].sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int);
char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000);
chrs_read = recv(IP_STATUS[CHAN_EPIC].sockfd, buf, IP_BUF_SZ, 0); 
sBuffer += std::string(buf);

备注:我的文件小于 IP_BUF_SZ 大小。 提前谢谢你。

【问题讨论】:

您可以通过检查recv 函数的返回值来接收while 循环中的所有数据,并期望读取直到返回值为0 【参考方案1】:

setsockopt(SO_RCVBUF) 不保证您可以在单个recv() 呼叫中收到完整的消息。它甚至不保证您将获得您请求的缓冲区大小。您必须使用getsockopt(SO_RCVBUF) 来检索实际的缓冲区大小。

SO_RCVBUF 只是控制在发送方开始阻塞等待您读取字节之前,套接字可以在其内部缓冲区中保存多少字节。但是recv() 不保证它会返回您请求的所有字节(除非您使用MSG_WAITALL 标志调用它,在这种情况下我不建议这样做,因为您事先不知道消息长度)。如果您要求 recv() 读取 X 字节,并且当前只有 Y 字节可用,其中 Y recv() 将返回 Y 字节,而不是等待返回 X 字节。唯一的保证是recv() 将返回至少 1 个字节并且不超过 X 个字节。因此,无论您将内部缓冲区大小设置为多少,您仍然需要一个读取循环。

SO_RCVBUF 只是用于优化网络 I/O 的建议选项。代码的阅读逻辑实际上不要依赖它。

也就是说,您显示的代码还有其他问题。

您在循环的每次迭代中分配和释放接收缓冲区。不要那样做。分配一次,然后循环读取,完成后释放缓冲区。

除了您从recv() 实际询问的内容之外,无需过度分配您的接收缓冲区。读取时也不需要将缓冲区归零。这些步骤只是浪费了开销。

在将接收缓冲区附加到std::string 时,您也没有考虑recv() 的返回值。您正在使用 std::string 构造函数,该构造函数期望缓冲区以空值终止。您依靠缓冲区归零来提供空终止符,但如果数据包含其自己的任何嵌入的空字符(取决于 XML 的编码),那么这将截断您附加到 std::string 的内容。 recv() 返回它读取的字节数。您需要在std::string 中添加那么多字节,不多不少。

试试这样的:

#define IP_BUF_SZ 2000000

//...

std::string sBuffer;
long chrs_read;

//...

int iBufSize = IP_BUF_SZ;
int iBufVarSize = sizeof(iBufSize);

if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, iBufVarSize) == SOCKET_ERROR)
    MessageBox("Unable to set buffer size");
else if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, &iBufVarSize) == SOCKET_ERROR)

    MessageBox("Unable to get buffer size");
    iBufSize = IP_BUF_SZ;


char* buf = (char*) malloc(iBufSize);
if (!buf)
    MessageBox("Unable to allocate buffer");
else

    do
    
        chrs_read = recv(sockfd, buf, iBufSize, 0);
        if (chrs_read <= 0)
        
            if (chrs_read == SOCKET_ERROR)
                MessageBox("Unable to read message");
            break;
        
        sBuffer.append(buf, chrs_read);
    
    while (true);

    free(buf);
    buf = NULL;

请注意,此逻辑一直在读取,直到套接字断开连接或在读取时遇到错误。如果每个套接字连接只有 1 条消息,那没关系。但是,如果您打算在单个连接上发送多个 XML 消息,那将不再适用。您需要在消息之间放置一个分隔符,以便您知道一条消息在哪里结束,而下一条消息在哪里开始。这可以是指定消息长度的前导标头,也可以是消息末尾的唯一终止符序列。

【讨论】:

感谢您的帮助,但还有一个问题:我保持与服务器通信的开放通道(您看到的代码),并且每 500 毫秒发送一个 XML。发生的情况是我的 sochet 只收到了几个 XML,因为发送速度很快。所以,简而言之 - 我返回 -1chrs_read 比我应该拥有的少,并且文件正在连接。 @cna 正如我在回答的最后一段中告诉您的那样,您需要在 XML 消息之间使用分隔符。 TCP 没有消息边界的概念,您需要在应用层处理它。循环调用recv(),直到到达消息的适当结尾,然后根据需要处理消息,然后循环调用recv(),直到到达下一条消息的适当结尾,依此类推。 recv() 本身不能为你做所有事情,你需要自己的逻辑。【参考方案2】:

您可以将缓冲区设置为系统允许的大小,但您无法立即从 TCP 接收所有数据。您必须循环,直到收到您期望的所有数据。

您的问题基于错误的假设。

【讨论】:

以上是关于功能 setsockopt 似乎不起作用的主要内容,如果未能解决你的问题,请参考以下文章

MDCardSwipe 滑动功能不起作用

Microsoft TTS SaveToWaveFileAsync 功能不起作用

在调用我的函数时,它不起作用?

Pig UDF 似乎总是在单个减速器中运行 - PARALLEL 不起作用

VueJS计算过滤器功能不起作用

知道为啥这个 Jquery 自动完成功能不起作用吗?