或者我是否必须在应用程序级别实现它?
答案 0 :(得分:6)
TCP通常要求您在应用程序级别同步接收方和发送方。单独SO_SNDBUF
调整或TCP_NODELAY
的组合不可能完全解决问题。这是因为在send()
之前可以“在飞行中”的数据量将会阻塞或多或少等于:
CWIN
)和接收窗口(RWIN
)大小而变化。当TCP在慢启动,拥塞避免,快速恢复和快速重传模式之间转换时,TCP发送器不断地将拥塞窗口大小调整到网络条件。和,ACK
,但应用程序尚未看到。换句话说,在接收器停止从套接字读取数据后,send()
将仅在以下情况下阻止:
ACK
,ACK
个数据传输至拥塞或接收窗口限制,TCP中使用的算法的目标是创建流动的字节流而不是数据包序列的效果。一般来说,它试图尽可能地隐藏传输量化为数据包的事实,而大多数套接字API反映了这一点。其中一个原因是套接字可能根本不在顶级TCP(甚至IP)上实现:考虑使用相同API的Unix域套接字。
通常不建议尝试依赖TCP的底层实现细节来应用程序行为。坚持在应用层同步。
如果在您进行同步的情况下延迟是一个问题,您可能还想阅读在某些情况下会引入不必要延迟的interactions between Nagle's algorithm and delayed ACK
。
答案 1 :(得分:5)
几周前我在实施VoIP服务器时也遇到了同样的问题。花了好几天后,我可以想出一个解决方案。正如许多其他人所提到的那样,没有任何直接的系统调用来完成这项工作。相反,
ACK
选项的数据包后检查我们是否收到了TCP_INFO
。ACK
,请等待几毫秒再检查一下。 这可能会持续到超时为止。您必须将其实现为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之类的协议作为传输。