【问题标题】:receive from unix local socket and buffer size从 Unix 本地套接字和缓冲区大小接收
【发布时间】:2012-04-23 20:58:28
【问题描述】:

我遇到了 unix 本地套接字的问题。在读取超过我的临时缓冲区大小的消息时,请求花费的时间太长(可能无限期)。

经过一些测试后添加: ::recv 的冻结仍然存在问题。当我向 UNIX 套接字发送 (1023*8) 字节或更少字节时 - 一切正常,但是当发送超过 (1023*9) 时 - 我在 recv 命令上冻结。 可能是它的 FreeBSD 默认 UNIX 套接字限制或 C++ 默认套接字设置?谁知道?


我做了一些额外的测试,我 100% 确定它在执行 ::recv 命令时的最后 9 次迭代中“冻结”,当尝试读取 >= (1023*9) 字节长的消息时。 (第 8 次迭代进展顺利。)

我在做什么: 这个想法是从带有

的套接字中读取 do/while 循环
::recv (current_socket, buf, 1024, 0);

并检查buf 是否有特殊符号。如果没有找到:

  1. 合并缓冲区内容到stringxxx += buf;
  2. bzero temp buf
  3. 继续 ::recv 循环

如何解决请求在 while 循环中耗时过长的问题?

有没有更好的方法来清除缓冲区?目前是:

 char buf [1025];
 bzero(buf, 1025);

但我知道 bzero 在新的 c++ 标准中已被弃用。

编辑: *"为什么需要清理缓冲区*

我在 cmets 看到有这个问题的问题。如果在下一次(最后一次)读取缓冲区时不清理缓冲区,它将包含消息第一部分的“尾部”。

例子:

 // message at the socket is "AAAAAACDE"
 char buf [6];
 ::recv (current_socket, buf, 6, 0); // read 6 symbols, buf = "AAAAAA"
 // no cleanup, read the last part of the message with recv
 ::recv (current_socket, buf, 6, 0); 
 // read 6 symbols, but buffer contain only 3 not readed before symbols, therefore
 // buf now contain "CDEAAA" (not correct, we waiting for CDE only)

【问题讨论】:

  • 为什么一定要清空缓冲区?
  • 你会从recv()的返回值中知道复制了多少字节。我不知道为什么你需要一个特殊字符来知道你已经超过了缓冲区长度。
  • 套接字是否以非阻塞模式打开?请参阅fcntl() 的手册页。
  • Martin,回答第一条消息的“编辑”部分。 .
  • chrisaycock,因为缓冲区大小可以小于消息的大小。 (在我的示例中,它不再读取缓冲区大小,受 ::recv 的第 3 个参数限制)

标签: c++ sockets unix


【解决方案1】:

当您的recv() 进入无限循环时,这可能意味着它在迭代中没有取得任何进展(即,您总是立即获得零大小的短读,因此您的循环永远不会退出,因为您'没有得到任何数据)。对于流套接字,大小为零的recv() 意味着远程端已断开连接(类似于read()ing 当输入位于 EOF 时从文件中获取零字节),或者至少它已关闭关闭发送通道(专门针对 TCP)。

检查您的 PHP 脚本是否实际发送了您声称发送的数据量。

添加一个小的(无意义的)示例以在循环中正确使用 recv():

char buf[1024];
std::string data;
while( data.size() < 10000 ) { // what you wish to receive
    ::ssize_t rcvd = ::recv(fd, buf, sizeof(buf), 0);
    if( rcvd < 0 ) {
        std::cout << "Failed to receive\n";  // Receive failed - something broke, see errno.
        std::abort();
    } else if( !rcvd ) {
        break; // No data to receive, remote end closed connection, so quit.
    } else {
        data.append(buf, rcvd); // Received into buffer, attach to data buffer.
    }
}

if( data.size() < 10000 ) {
    std::cout << "Short receive, sender broken\n";
    std::abort();
}

// Do something with the buffer data.

【讨论】:

  • php 脚本完全可以完成他的工作。对于您描述的情况,循环中有“if keeper”,“if (status_or_nbytes_from_recv
  • 我做了一些额外的测试,我 100% 确定它在 ::recv 命令循环的最后 9 圈“冻结”。
  • SO_SNDBUF 和 SO_RCVBUF nothing 与此有关。如果您的 C++ 进程进入一个尝试接收数据的无限循环,这仅仅意味着发送方没有发送您期望接收的数据量并关闭连接,或者您的 recv() 中缺少中断条件-循环。
  • 你确定发件人实际发送了9*1024字节吗?根据我的收集,您最初也没有检查 recv() 的返回值,因此 PHP 脚本中的 send() 可能仅将您尝试一次性发送的缓冲区的一部分排队(这将使 unix-domain-socket 的 SO_SNDBUF 为 8192 字节),并且您还必须在该端实现一个循环以发送所有数据。
  • 再一次,你确定 PHP 端确实发出了 8*1024+x 字节吗? ::send() 可能在通过套接字发送所有数据之前返回;就像你实现一个循环来连续接收数据一样,你(可能)必须实现一个循环来发送另一端的缓冲区。
【解决方案2】:

不用bzero,你可以直接使用

memset(buf, 0, 1025);

【讨论】:

  • 我不知道,但他肯定会问如何在没有被弃用的bzero()的情况下做到这一点
  • Martin,有关此问题的答案,请参阅第一个问题的编辑说明。
【解决方案3】:

这是两个不同的问题。由于代码中的错误,长时间可能是无限循环,与清除缓冲区的方式无关。事实上,您不需要清除缓冲区; receive 返回读取的字节数,因此您可以扫描缓冲区中的 SPECIAL_SYMBOL 直到该点。

如果您粘贴代码,也许我可以提供帮助。更多。

【讨论】:

  • 也许它是更好的解决方案。谢谢 1)在读取小于 1024 字节(缓冲区大小)的迭代后检查,如果是这样,检查 SPECIAL_SYMBOL 直到 ::recv 返回的位置,但它不会解决循环问题。似乎它不是无限循环,因为消息接近 2 kb 一切正常,但 50 kb 消息和连接到 c++ 服务器的 php 部分超时。
  • 我按照你的建议重写了我的“接收”功能。非常感谢,现在它可以在每次迭代时不清理缓冲区的情况下工作,但无限 loo[.当我向套接字发送 (1023*8) 或更少字节时 - 一切正常,但是当发送 (1023*9) 或更多字节时 - 我在使用此套接字的线程上得到无限循环。可能是它的 FreeBSD 默认 UNIX 套接字限制或 C++ 默认套接字设置?
【解决方案4】:

澄清一下:bzero 在 C++ 11 中并未被弃用。相反,它从未成为任何 C 或 C++ 标准的一部分。 C 从 20 多年前的 memset 开始。对于 C++,您可以考虑改用std::fill_n(或者只使用std::vector,它可以自动填零)。再说一次,我不确定在这种情况下是否有充分的理由对缓冲区进行零填充。

【讨论】:

  • 谢谢,std::fill_n 是我正在为我的 c++ 程序寻找的东西。关于为什么我每次迭代 ::recv 都要清理缓冲区,请在第一条消息中检查“编辑”注释。
猜你喜欢
  • 1970-01-01
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-13
  • 1970-01-01
  • 2018-11-08
  • 1970-01-01
相关资源
最近更新 更多