【发布时间】:2011-05-08 19:16:18
【问题描述】:
在C语言中,我理解如果我们关闭一个套接字,就意味着该套接字将被销毁,以后可以重新使用。
关机怎么样?描述说它关闭了与该套接字的一半双工连接。但是那个套接字会像close 系统调用一样被销毁吗?
【问题讨论】:
-
以后不能重复使用。它关门了。完成的。完成。
标签: c sockets networking
在C语言中,我理解如果我们关闭一个套接字,就意味着该套接字将被销毁,以后可以重新使用。
关机怎么样?描述说它关闭了与该套接字的一半双工连接。但是那个套接字会像close 系统调用一样被销毁吗?
【问题讨论】:
标签: c sockets networking
linux: shutdown() 导致监听线程 select() 唤醒并产生错误。关掉();关闭();会导致无休止的等待。
winsock:反之亦然 - shutdown() 无效,而 close() 成功捕获。
【讨论】:
这是 Beej 网络指南中的 explained。 shutdown 是一种灵活的方式来阻止一个或两个方向的通信。当第二个参数为SHUT_RDWR 时,会同时阻塞发送和接收(如close)。但是,close 是实际销毁套接字的方式。
使用shutdown,您仍然可以接收对等方已发送的待处理数据(感谢 Joey Adams 注意到这一点)。
【讨论】:
shutdown 而不是close 的一个原因是,如果您使用fdopen 对套接字进行了FILE 引用。如果你 close 套接字,一个新打开的文件可能会被分配相同的 fd,随后使用 FILE 将读/写错误的地方,这可能是非常糟糕的。如果您只是 shutdown,则后续使用 FILE 只会出错,直到调用 fclose。
shutdown:向对等方发出 EOF 信号,并且仍然能够接收对等方发送的待处理数据。
现有的答案都没有告诉人们shutdown 和close 在 TCP 协议级别是如何工作的,因此值得添加。
一个标准的 TCP 连接被 4-way finalization 终止:
但是,还有另一种“紧急”方式来关闭 TCP 连接:
在我对 Wireshark 的测试中,使用默认套接字选项,shutdown 向另一端发送一个 FIN 数据包,但这就是它所做的一切。直到对方向你发送 FIN 数据包,你仍然能够接收数据。一旦发生这种情况,您的 Receive 将获得 0 大小的结果。因此,如果您是第一个关闭“发送”的人,则应在完成接收数据后关闭套接字。
另一方面,如果您在连接仍处于活动状态时调用close(另一端仍处于活动状态,并且您可能在系统缓冲区中也有未发送的数据),则会向另一端发送一个 RST 数据包边。这对错误有好处。例如,如果您认为对方提供了错误的数据或拒绝提供数据(DOS 攻击?),您可以立即关闭套接字。
我对规则的看法是:
close 之前考虑 shutdown
SHUT_RD 和 SHUT_WR 的理想实现
以下内容尚未经过测试,请自行承担风险。但是,我相信这是一种合理且实用的做事方式。
如果 TCP 堆栈仅接收到带有 SHUT_RD 的关闭,则应将此连接标记为不再需要数据。任何未决和后续的read 请求(无论它们在哪个线程中)都将返回大小为零的结果。但是,连接仍然处于活动状态且可用——例如,您仍然可以接收 OOB 数据。此外,操作系统将丢弃它为此连接接收到的任何数据。但仅此而已,不会有任何包裹寄到对方。
如果 TCP 堆栈仅接收到 SHUT_WR 关闭,它将标记此连接,因为无法发送更多数据。所有挂起的写请求都将完成,但后续的写请求将失败。此外,将向另一端发送一个 FIN 数据包,通知他们我们没有更多数据要发送。
【讨论】:
shutdown() 连接,然后它不再存在。您仍然拥有文件描述符。您仍然可以从接收缓冲区中recv()。而且你仍然需要调用close() 来处理文件描述符。
关闭
当你使用完一个套接字后,你可以简单地用 close 关闭它的文件描述符;如果仍有数据等待通过连接传输,则正常关闭尝试完成此传输。您可以使用 SO_LINGER 套接字选项来控制此行为以指定超时时间;请参阅套接字选项。
关机
您也可以通过调用shutdown来仅关闭连接上的接收或传输。
shutdown函数关闭socket的连接。它的参数 how 指定要执行的操作: 0 停止接收此套接字的数据。如果有更多数据到达,则拒绝它。 1 停止尝试从此套接字传输数据。丢弃任何等待发送的数据。停止寻找已发送数据的确认;如果丢失,请勿重新传输。 2 停止接收和发送。
成功时返回值为0,失败时返回-1。
【讨论】:
在我的测试中。
close在socket不与其他进程共享时会立即发送fin包并销毁fd
shutdown SHUT_RD,进程仍然可以从套接字接收数据,但是如果 TCP 缓冲区为空,recv 将返回 0。当对端发送更多数据后,recv 将再次返回数据.
shutdown SHUT_WR 将发送 fin 数据包以指示不允许进一步发送。对等方可以接收数据,但如果其 TCP 缓冲区为空,它将接收 0
shutdown SHUT_RDWR(等于同时使用 SHUT_RD 和 SHUT_WR)如果对等方发送更多数据,将发送第一个数据包。
【讨论】:
close() 在 Linux 上发送了 RST 而不是 FIN。
recv() 将再次返回数据”不正确。 2、在SHUT_RD之后peer发送更多数据的行为是平台相关的。
“shutdown() 实际上并没有关闭文件描述符——它只是改变了它的可用性。要释放一个套接字描述符,你需要使用 close()。”1
【讨论】:
close() 有一些限制,如果使用shutdown() 代替,可以避免这些限制。
close() 将终止 TCP 连接上的两个方向。有时您想告诉其他端点您已完成发送数据,但仍想接收数据。
close() 减少描述符引用计数(保存在文件表条目中并计算当前打开的引用文件/套接字的描述符数)并且如果描述符不为 0,则不会关闭套接字/文件。这意味着如果您正在分叉,则只有在引用计数降至 0 后才会进行清理。使用shutdown() 可以启动正常的 TCP 关闭序列,而忽略引用计数。
参数如下:
int shutdown(int s, int how); // s is socket descriptor
int how 可以是:
SHUT_RD 或 0
不允许进一步接收
SHUT_WR 或 1
不允许进一步发送
SHUT_RDWR 或 2
不允许进一步发送和接收
【讨论】:
这可能是特定于平台的,我对此表示怀疑,但无论如何,我见过的最好的解释是 here on this msdn page,他们解释了关闭、逗留选项、套接字关闭和一般连接终止序列。
总之,使用shutdown 在TCP 级别发送一个关闭序列,并使用close 来释放进程中套接字数据结构使用的资源。如果您在调用 close 时尚未发出明确的关闭序列,则会为您启动一个。
【讨论】:
我在 linux 下也取得了成功,使用来自一个 pthread 的 shutdown() 强制当前在 connect() 中阻塞的另一个 pthread 提前中止。
在其他操作系统(至少是 OSX)下,我发现调用 close() 足以让 connect() 失败。
【讨论】: