是否有标准调用将 POSIX 套接字的传输端一直刷新到远程端,或者这是否需要作为用户级协议的一部分来实现?我环顾了通常的标题,但找不到任何东西。
9 回答
设置 TCP_NODELAY 而不是重新设置它呢?可能它可以在发送重要数据之前完成,或者在我们完成发送消息时完成。
send(sock, "notimportant", ...);
send(sock, "notimportant", ...);
send(sock, "notimportant", ...);
int flag = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
send(sock, "important data or end of the current message", ...);
flag = 0;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
正如linux手册页所说
TCP_NODELAY ...设置此选项会强制显式刷新挂起的输出...
所以可能最好在消息之后设置它,但不确定它如何在其他系统上工作
对于 Unix 域套接字,您可以使用fflush()
,但我认为您可能是指网络套接字。真的没有冲洗那些的概念。最接近的事情是:
在会话结束时,调用
shutdown(sock, SHUT_WR)
关闭套接字上的写入。在 TCP 套接字上,使用 sockopt 禁用 Nagle 算法
TCP_NODELAY
,这通常是一个糟糕的想法,即使在最初的调查中它似乎会处理它,它也不能可靠地完成你想要的事情。
在用户协议级别处理任何需要“刷新”的问题很可能是正确的。
在 RFC 1122 中,您正在寻找的东西的名称是“PUSH”。但是,似乎没有实现“PUSH”的相关 TCP API 实现。唉,没有运气。
一些答案和评论涉及 Nagle 算法。他们中的大多数似乎都假设 Nagle 算法会延迟每次发送。这个假设是不正确的。仅当至少一个先前的数据包尚未确认时, Nagle 才会延迟发送( http://www.unixguide.net/network/socketfaq/2.11.shtml )。
换句话说:TCP 将立即发送(一排数据包中的)第一个数据包。只有当连接速度很慢并且您的计算机没有得到及时的确认时,Nagle 才会延迟发送后续数据,直到其中任何一个(以先发生者为准)
- 达到超时或
- 最后一个未确认的数据包被确认或
- 您的发送缓冲区已满或
- 您禁用 Nagle 或
- 你关闭了连接的发送方向
一个好的缓解措施是尽可能避免后续数据的业务。这意味着:如果您的应用程序调用send()
多次以传输单个复合请求,请尝试重写您的应用程序。在用户空间组装复合请求,然后调用send()
. 一次。这也节省了上下文切换(比大多数用户空间操作更昂贵)。
此外,当发送缓冲区包含足够的数据以填充网络数据包的最大大小时,Nagle 也不会延迟。这意味着:如果您发送的最后一个数据包大到足以填满您的发送缓冲区,那么无论如何,TCP 都会尽快发送您的数据。
总结一下:Nagle 并不是一些人认为的减少数据包碎片的蛮力方法。相反:对我来说,它似乎是一种有用的、动态的和有效的方法,可以同时保持良好的响应时间和用户数据与标头数据之间的良好比例。话虽如此,您应该知道如何有效地处理它。
我不知道在标准 TCP/IP 套接字接口中无法“一直到远程端”刷新数据并确保它实际上已被确认。
一般来说,如果您的协议需要“实时”传输数据,通常最好的做法是设置setsockopt()
. TCP_NODELAY
这将禁用协议栈中的 Nagle 算法,并且套接字上的 write() 或 send() 更直接地映射到发送到网络上......而不是实现发送延迟等待更多字节变得可用并使用所有TCP 级别的计时器来决定何时发送。注意:关闭 Nagle 不会禁用 TCP 滑动窗口或任何东西,所以这样做总是安全的....但是如果您不需要“实时”属性,数据包开销可能会增加很多。
除此之外,如果正常的 TCP 套接字机制不适合您的应用程序,那么通常您需要回退到使用 UDP 并在 UDP 的基本发送/接收属性上构建自己的协议功能。当您的协议有特殊需求时,这很常见,但不要低估做好这件事并使其在所有相对简单的应用程序中保持稳定和功能正确的复杂性。作为起点,对 TCP 设计特性的深入研究将阐明许多需要考虑的问题。
我认为这将是非常困难的,如果不是不可能正确实施的话。在这种情况下,“冲洗”是什么意思?传输到网络的字节数?接收方的 TCP 堆栈确认的字节数?传递给接收者用户模式应用程序的字节数?用户模式应用程序完全处理的字节?
看起来您需要在应用程序级别执行此操作...
TCP 只提供尽力而为的传输,因此让所有字节离开机器 A 的行为与机器 B 接收到的所有字节是异步的。当然,TCP/IP 协议栈知道,但我不知道任何询问 TCP 堆栈以查明所有发送的内容是否已被确认的方法。
到目前为止,处理该问题的最简单方法是在应用程序级别。打开第二个 TCP 套接字以充当反向通道,并让远程伙伴向您发送确认它已收到您想要的信息。它的成本将翻倍,但完全可移植,并为您节省数小时的编程时间。
您可以设置 tcp 选项 SO_LINGER 以设置某个超时,然后关闭套接字,以确保在关闭连接时所有数据都已发送(或检测到发送失败)。除此之外,TCP 是一个“尽力而为”的协议,它并没有提供任何真正的保证数据将真正到达目的地(与某些人似乎相信的相反),它只是尽最大努力让它传递以正确的顺序并尽快。
有一种特定于 linux 的方法可以在 TCP 套接字上执行此操作:
您不会强制 tcp 堆栈“立即发送数据”,但请记住,您和内核正在尽可能快地发送数据。您可以稍微减少send
与实际交付之间的延迟TCP_NODELAY
。
但基本上,通过做send
你已经完成了你的部分,你只需要等待 tcp 完成它的部分。
您可以ioctl
在套接字上使用SIOCOUTQ
或TIOCOUTQ
查询交付状态,然后等待它完成。这应该足够了。特别是对于发送应用程序级数据包片段等用例。
使用 fsync():
sock_fd 是来自 socket(..., ...) 调用的整数文件描述符
fsync(sock_fd);