还是我必须在应用程序级别实现它?
TCP通常会要求您在应用程序级别同步接收者和发送者。 SO_SNDBUF
调整或TCP_NODELAY
单独组合不可能完全解决问题。 这是因为在send()
之前可以“正在运行”的数据量将会被阻塞,或多或少等于以下总和:
CWIN
)和接收窗口( RWIN
)大小而变化。 随着TCP在慢启动,拥塞避免,快速恢复和快速重传模式之间转换,TCP发送方不断调整拥塞窗口大小以适应网络条件。 和, ACK
,但是应用程序还没有看到。 换句话说,在接收器停止从套接字读取数据之后, send()
将仅在以下情况下阻塞:
ACK
, 在TCP中使用的算法的目标是创建流动的字节流的效果,而不是一系列的数据包。 一般来说,它试图尽可能隐藏传输被量化成数据包的事实,而大多数套接字API反映了这一点。 其中一个原因是套接字可能无法在顶级TCP(甚至是IP)上实现:考虑一个使用相同API的Unix域套接字。
尝试依靠TCP的应用程序行为的底层实现细节通常是不可取的。 坚持在应用层进行同步。
如果在执行同步的情况下延迟是一个问题,那么您可能还需要了解Nagle的算法和延迟的ACK
之间的交互,这可能会在某些情况下引入不必要的延迟。
在几个星期前,我在实施VoIP服务器时也面临同样的问题。 花了几天后,我可以想出一个解决方案。 和其他人一样,没有直接的系统调用来完成这项工作。 代替,
TCP_INFO
选项的数据包之后检查是否收到了ACK
。 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这样的协议作为传输。