在C语言中,关闭一个socket意味着该socket会被销毁并可以在之后重新使用。
那么shutdown呢?描述中说它关闭了与该socket的双工连接的一半。但是该socket是否会像close系统调用一样被销毁?
在C语言中,关闭一个socket意味着该socket会被销毁并可以在之后重新使用。
那么shutdown呢?描述中说它关闭了与该socket的双工连接的一半。但是该socket是否会像close系统调用一样被销毁?
这在 Beej's 网络编程指南中有 解释。 shutdown
是一种灵活的方式,可以阻止单向或双向的通信。 当第二个参数是 SHUT_RDWR
时,它将阻止发送和接收(就像 close
一样)。 不过,close
才是实际上销毁套接字的方法。
使用 shutdown
,您仍然能够接收对等方已经发送但未处理的数据(感谢 Joey Adams 指出这一点)。
fdopen
将socket转换成了FILE
引用,那么你可能会希望在两个方向上使用shutdown
而不是close
,因为如果你关闭了socket,可能会有一个新打开的文件使用相同的文件描述符(fd),这样后续使用FILE
读写操作就会造成错误,后果可能非常严重。如果你只是使用shutdown
,那么后续对于FILE
的使用只会产生错误,直到调用fclose
函数。 - R.. GitHub STOP HELPING ICE现有的回答都没有说明如何在TCP协议级别上工作的shutdown
和close
,因此值得添加这个内容。
标准的TCP连接通过4次挥手来终止:
但是,还存在另一种“紧急”关闭TCP连接的方式:
在我的Wireshark测试中,使用默认套接字选项,shutdown
向另一端发送FIN数据包,但仅此而已。在另一方发送FIN数据包之前,您仍然可以接收数据。一旦发生这种情况,您的Receive
将得到0大小的结果。因此,如果你是第一个关闭"send"的人,你应该在接收到数据后立即关闭套接字。
另一方面,在连接仍处于活动状态(另一方仍处于活动状态且您的系统缓冲区中可能有未发送的数据)时,如果调用close
,将向另一端发送RST数据包。这对于错误处理很有用。例如,如果你认为另一方提供了错误的数据或拒绝提供数据(DOS攻击?),你可以立即关闭套接字。
我的规则意见是:
shutdown
而不是close
SHUT_RD和SHUT_WR的理想实现
以下实现尚未测试,请自行信任。但是,我相信这是一种合理和实用的处理方式。
如果TCP堆栈仅接收到带有SHUT_RD的关机请求,它将标记该连接为不再期待数据。无论哪个线程还在进行中,所有挂起的和随后的read
请求都将返回零大小的结果。但是,连接仍然处于活动状态且可用 - 您仍然可以接收OOB数据,例如。此外,操作系统将删除接收到的任何此连接的数据。但是这就是全部,不会向另一侧发送任何数据包。
如果TCP堆栈仅接收到带有SHUT_WR的关机请求,则应将此连接标记为无法发送更多数据。所有挂起的写入请求将完成,但随后的写入请求将失败。此外,将向另一侧发送FIN包以通知其我们没有更多数据可发送。
shutdown()
关闭连接,这样它就不再活动了。你仍然拥有文件描述符,可以从接收缓冲区中继续 recv()
。但你仍需要调用 close()
来释放文件描述符。 - Pavel Šimerda如果使用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
:禁止进一步的发送和接收操作
这可能与平台有关,但我有些怀疑。无论如何,我看到的最好的解释在 这个 msdn 页面 上,他们解释了关于关闭、逗留选项、套接字关闭和一般连接终止序列。
总之,在 TCP 级别使用 shutdown 发送关闭序列,并使用 close 释放您的进程中套接字数据结构使用的资源。如果在调用关闭之前您还没有发出显式的关闭序列,则会为您启动一个序列。
我也在Linux下使用shutdown()
成功地实现了一个线程强制中止另一个线程,该线程当前正被阻塞在connect()
上。
在其他操作系统(至少是OSX)中,我发现调用close()
就足以使connect()
失败。
关闭
当你使用完一个套接字后,可以使用close函数来关闭其文件描述符;如果仍有数据等待传输到连接上,通常close会尝试完成这个传输。你可以使用SO_LINGER套接字选项来控制这种行为,指定一个超时时间;请参见Socket Options。
关闭连接
你也可以通过调用shutdown函数来仅关闭连接的接收或传输。
shutdown函数关闭套接字的连接。它的参数how指定要执行的操作: 0 停止接收此套接字的数据。如果有更多的数据到达,拒绝它。 1 停止尝试从此套接字传输数据。丢弃任何等待发送的数据。停止寻找已发送数据的确认;如果数据丢失,则不重新传输。 2 停止接收和传输。
成功返回0,失败返回-1。
在我的测试中。
close
会发送fin包并在套接字未与其他进程共享时立即销毁fd。
shutdown
SHUT_RD,进程仍然可以从套接字中接收数据,但是如果TCP缓冲区为空,则recv
会返回0。在对等方发送更多数据后,recv
将再次返回数据。
shutdown
SHUT_WR将发送fin包以指示进一步发送被禁止。对等方可以接收数据,但如果其TCP缓冲区为空,则它将接收到0。
shutdown
SHUT_RDWR(等同于同时使用SHUT_RD和SHUT_WR)将在对等方发送更多数据时发送rst包。
close()
在 Linux 上发送的是 RST 而不是 FIN。 - Pavel Šimerdarecv()
不会再次返回数据" 是不正确的。 SHUT_RD
之后对等方发送了更多数据,则其行为取决于平台。Linux: 调用shutdown()会导致监听线程的select()被唤醒并产生错误。调用shutdown(); close();将导致无限等待。
Winsock: 相反,shutdown()没有效果,而close()可以成功捕获。