繁体   English   中英

有没有办法刷新 POSIX 套接字?

[英]Is there a way to flush a POSIX socket?

是否有标准调用将 POSIX 套接字的传输端一直刷新到远程端,或者这是否需要作为用户级协议的一部分来实现? 我环顾了通常的标题,但找不到任何东西。

设置 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() ,但我认为您可能指的是网络套接字。 真的没有冲洗这些的概念。 最接近的事情是:

  1. 在会话结束时,调用shutdown(sock, SHUT_WR)关闭套接字上的写入。

  2. 在 TCP 套接字上,使用 sockopt TCP_NODELAY禁用 Nagle 算法,这通常是一个糟糕的想法,它不会可靠地执行您想要的操作,即使它在初步调查中似乎已经解决了它。

在用户协议级别处理任何需要“刷新”的问题很可能是正确的。

在 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 套接字接口中可以“一直到远程端”刷新数据并确保它实际上已被确认。

一般来说,如果您的协议需要“实时”传输数据,通常最好的做法是设置TCP_NODELAYsetsockopt() 这会禁用协议栈中的 Nagle 算法,并且套接字上的 write() 或 send() 更直接地映射到发送到网络上......而不是实现发送延迟等待更多字节可用并使用所有TCP 级定时器决定何时发送。 注意:关闭 Nagle 不会禁用 TCP 滑动窗口或任何东西,因此这样做总是安全的....但是如果您不需要“实时”属性,数据包开销可能会增加很多。

除此之外,如果正常的 TCP 套接字机制不适合您的应用程序,那么通常您需要回退到使用 UDP 并在 UDP 的基本发送/接收属性上构建您自己的协议功能。 当您的协议有特殊需求时,这是很常见的,但不要低估做好这件事的复杂性,并在除了相对简单的应用程序之外的所有应用程序中都保持稳定和功能正确。 作为起点,对 TCP 设计特性的彻底研究将阐明许多需要考虑的问题。

我认为如果不是不可能正确实施,那将是极其困难的。 在这种情况下,“flush”是什么意思? 字节传输到网络? 接收方的 TCP 堆栈确认的字节数? 字节传递给接收者的用户模式应用程序? 字节完全由用户模式应用程序处理?

看起来您需要在应用程序级别执行此操作...

有一种特定linux 的方法可以在 TCP 套接字上执行此操作:

您不会强制 tcp 堆栈“立即发送数据”,但请记住,您和内核正在尽可能快地发送数据。 您可以使用TCP_NODELAY稍微减少send和实际交付之间的延迟。

但基本上,通过send你已经完成了你的部分,你只需要等待 tcp 完成它的部分。

您可以使用ioctl在套接字上使用SIOCOUTQTIOCOUTQ来查询交付状态并等待它完成。 这应该足够了。 特别是对于发送应用程序级数据包片段等用例。

您可以设置 tcp 选项 SO_LINGER 以设置某个超时,然后关闭套接字以确保在关闭连接时已发送所有数据(或检测到发送失败)。 除此之外,TCP 是一种“尽力而为”的协议,它不提供任何真正的保证数据将真正到达目的地(与某些人似乎相信的相反),它只是尽力将其交付以正确的顺序并尽快。

TCP 只提供尽力而为的传送,因此让所有字节离开机器 A 的行为与机器 B 接收到的所有字节是异步的。 TCP/IP 协议栈当然知道,但我不知道任何询问 TCP 堆栈以确定发送的所有内容是否已被确认的方法。

到目前为止,处理这个问题的最简单方法是在应用程序级别 打开第二个 TCP 套接字以充当反向通道,并让远程伙伴向您发送确认消息,表明它已收到您想要的信息。 它的成本会翻倍,但完全便携,并且可以为您节省数小时的编程时间。

使用 fsync():

sock_fd 是来自 socket(..., ...) 调用的整数文件描述符

fsync(sock_fd);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM