有没有办法阻塞套接字send(),直到我们得到该数据包的确认?

时间:2009-08-14 17:46:43

标签: c++ linux network-programming sockets

或者我是否必须在应用程序级别实现它?

7 个答案:

答案 0 :(得分:6)

TCP通常要求您在应用程序级别同步接收方和发送方。单独SO_SNDBUF调整或TCP_NODELAY的组合不可能完全解决问题。这是因为在send()之前可以“在飞行中”的数据量将会阻塞或多或少等于:

  1. 发送方发送缓冲区中的数据,包括被Nagle's algorithm延迟的小数据片段,
  2. 未确认的正在进行的数据包中携带的数据量,该数据量随congestion windowCWIN)和接收窗口(RWIN)大小而变化。当TCP在慢启动,拥塞避免,快速恢复和快速重传模式之间转换时,TCP发送器不断地将拥塞窗口大小调整到网络条件。和,
  3. 接收方的接收缓冲区中的数据,接收方的TCP堆栈已经发送了ACK,但应用程序尚未看到。
  4. 换句话说,在接收器停止从套接字读取数据后,send()将仅在以下情况下阻止:

    1. 接收方的TCP接收缓冲区填充和TCP停止ACK
    2. 发件人将未ACK个数据传输至拥塞或接收窗口限制,
    3. 发件人的TCP发送缓冲区填充或发件人应用程序请求发送缓冲区刷新。
    4. TCP中使用的算法的目标是创建流动的字节流而不是数据包序列的效果。一般来说,它试图尽可能地隐藏传输量化为数据包的事实,而大多数套接字API反映了这一点。其中一个原因是套接字可能根本不在顶级TCP(甚至IP)上实现:考虑使用相同API的Unix域套接字。

      通常不建议尝试依赖TCP的底层实现细节来应用程序行为。坚持在应用层同步。

      如果在您进行同步的情况下延迟是一个问题,您可能还想阅读在某些情况下会引入不必要延迟的interactions between Nagle's algorithm and delayed ACK

答案 1 :(得分:5)

几周前我在实施VoIP服务器时也遇到了同样的问题。花了好几天后,我可以想出一个解决方案。正如许多其他人所提到的那样,没有任何直接的系统调用来完成这项工作。相反,

  1. 您可以在发送带有ACK选项的数据包后检查我们是否收到了TCP_INFO
  2. 如果我们没有收到ACK,请等待几毫秒再检查一下。
  3. 这可能会持续到超时为止。您必须将其实现为send()调用的包装函数。 您需要tcp_info的{​​{1}}结构。它是用于保存有关 tcp连接的信息的数据结构。

    这是伪代码

    <netinet/tcp.h>

    此处int blockingSend(const char *msg, int msglen, int timeout) { std::lock_guard<std::mutex> lock(write_mutx); int sent = send(sock_fd, msg, msglen, 0); tcp_info info; auto expireAt = chrono::system_clock::now() + chrono::milliseconds(timeout); do { this_thread::sleep_for(milliseconds(50)); getsockopt(sock_fd,SOL_TCP, TCP_INFO, (void *) &info, sizeof(info)); //wait till all packets acknowledged or time expires } while (info.tcpi_unacked > 0 && expireAt > system_clock::now()); if(info.tcpi_unacked>0) { cerr << "no of unacked packets :" << info.tcpi_unacked << endl; return -1; } return sent; } 成员包含您的连接的未确认数据包的数量。如果您在send()调用后很快读取它,它将包含 unacked 数据包的数量,这等于发送的数据包数量。随着时间的推移, unacked 数据包的数量将减少到零。因此,您需要定期检查tcpi_unacked的值,直到达到零。如果连接打开了一半,则在导致无限循环时永远不会收到tcpi_unacked。对于此类情况,您可能需要添加上面实现的超时机制。

    尽管很久以前就提出过这个问题,但这个答案可能会帮助那些遇到同样问题的人。我必须提一下,除了这个解决方案,这个问题可能有更准确的解决方案。因为我是系统编程和C / C ++的新手,所以我可以想出来。

答案 2 :(得分:3)

如果您正在谈论TCP,那么没有 - 我见过的套接字API不允许您这样做。

如果您需要确保另一端收到(并可能已处理)您的数据,您需要在应用程序协议中实现确认。

答案 3 :(得分:1)

数据包的确认位于传输层(远低于应用层)。您甚至不能保证您的整个缓冲区都属于网络上自己的数据包。你想做什么?

答案 4 :(得分:0)

为什么不使用阻塞套接字?

这可能有点过时,但这里有关于阻塞/非阻塞和重叠IO的一些解释。

http://support.microsoft.com/kb/181611

如果我们知道您使用哪种语言和操作系统BTW,以便更好地显示代码段,将会有所帮助。

答案 5 :(得分:0)

如果使用setsockopt()SO_SNDBUF降低到仅足以发送一个数据包的值,则该套接字上的下一个send()应该阻塞,直到确认上一个数据包为止。但是,根据tcp(7),套接字缓冲区大小必须在listen() / connect()之前设置。

答案 6 :(得分:0)

使用TCP的全部意义是隐藏来自应用程序的单个ACK。如果需要检测每个ACK,请使用UDP或IP实现自己的协议。 TCP可能是一种矫枉过正。或者你可以上堆栈并使用HTTP之类的协议作为传输。