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

还是我必须在应用程序级别实现它?

Solutions Collecting From Web of "有没有办法阻止套接字发送(),直到我们得到该数据包的确认?"

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

  1. 发送端的发送缓冲区中的数据,包括由Nagle算法延迟的小数据片段,
  2. 未确认的空中包中携带的数据量随拥塞窗口 ( CWIN )和接收窗口( RWIN )大小而变化。 随着TCP在慢启动,拥塞避免,快速恢复和快速重传模式之间转换,TCP发送方不断调整拥塞窗口大小以适应网络条件。 和,
  3. 接收方的接收缓冲区中的数据,接收方的TCP堆栈已经发送了一个ACK ,但是应用程序还没有看到。

换句话说,在接收器停止从套接字读取数据之后, send()将仅在以下情况下阻塞:

  1. 接收方的TCP接收缓冲区填满,TCP停止ACK
  2. 发送方发送未确认的数据,直到拥塞或接收窗口限制
  3. 发送方的TCP发送缓冲区填充或发送方应用程序请求发送缓冲区刷新。

在TCP中使用的算法的目标是创建流动的字节流的效果,而不是一系列的数据包。 一般来说,它试图尽可能隐藏传输被量化成数据包的事实,而大多数套接字API反映了这一点。 其中一个原因是套接字可能无法在顶级TCP(甚至是IP)上实现:考虑一个使用相同API的Unix域套接字。

尝试依靠TCP的应用程序行为的底层实现细节通常是不可取的。 坚持在应用层进行同步。

如果在执行同步的情况下延迟是一个问题,那么您可能还需要了解Nagle的算法和延迟的ACK之间的交互,这可能会在某些情况下引入不必要的延迟。

在几个星期前,我在实施VoIP服务器时也面临同样的问题。 花了几天后,我可以想出一个解决方案。 和其他人一样,没有直接的系统调用来完成这项工作。 代替,

  1. 您可以在发送一个包含TCP_INFO选项的数据包之后检查是否收到了ACK
  2. 如果我们没有收到ACK ,等待几毫秒后再检查。

这可能会持续下去,直到超时。 你必须把它作为一个包装函数来实现send()调用。 你将需要从<netinet/tcp.h> tcp_info <netinet/tcp.h> tcp_info结构。 这是保存你的tcp连接信息的数据结构。

这是伪代码

 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; } 

这里tcpi_unacked成员持有的连接数据包未确认 。 如果在send()调用之后不久读取它,它将包含与发送的包数相等的包数的包数。 随着时间的推移, 包数据包的数量将减少到零。 因此,您需要定期检查tcpi_unacked的值,直到达到零。 如果连接打开一半,则永远不会收到ACK同时导致无限循环。 对于这样的场景,您可能需要添加上面实现的超时机制。

即使这个问题很久以前就被问到了,这个答案可能会帮助那些面临同样问题的人。 我必须提到,这个解决方案可能有更准确的解决方案。 由于我是系统编程和C / C ++的新手,这就是我所能想到的。

如果你正在谈论TCP,那么没有 – 我见过的任何套接字API都允许你这样做。

如果您需要确定另一端已经收到(并且可能处理了)您的数据,则需要在您的应用程序协议中实施ack。

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

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

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

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

如果我们知道您正在使用哪种语言和操作系统,BTW就能更好地显示代码片断。

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

使用TCP的重点在于隐藏应用程序中的单个ACK。 如果您需要检测每个ACK,则使用UDP或IP实现您自己的协议。 TCP可能是一个矫枉过正的问题。 或者你可以上去堆栈,并使用像HTTP这样的协议作为传输。