TCP 的四次挥手,连接关闭的2种方式(close和shutdown)

一个 TCP 连接需要经过三次握手进入数据传输阶段,最后来到连接关闭阶段(“半连接”状态)
客户端主动发起连接的中断,关闭到服务器端的数据流方向,此时,客户端不再往服务器端写入数据,服务器端读完客户端数据后就不会再有新的报文到达。TCP 连接不是已经完全关闭,服务器端可能正在对客户端的最后报文进行处理,比如去访问数据库,存入一些数据;或者是计算出某个客户端需要的值,当完成这些操作之后,服务器端把结果通过套接字写给客户端,我们说这个套接字的状态此时是“半关闭”的。最后,服务器端才有条不紊地关闭剩下的半个连接,结束这一段 TCP 连接的使命。

close函数
int close(int sockfd)
  • 对已连接的套接字执行 close 操作,若成功则为 0,若出错则为 -1
  • 这个函数会对套接字引用计数减一,一旦发现套接字引用计数到 0,就会对套接字进行彻底释放,并且会关闭 TCP 两个方向的数据流。
    • 套接字引用计数的含义:因为套接字可以被多个进程共享,可以理解给每个套接字都设置了一个积分,如果通过 fork 的方式产生子进程,套接字就会积分 +1, 如果调用一次 close 函数,套接字积分就会 -1。

close 函数具体是如何关闭两个方向的数据流呢?

  1. 输入方向,系统内核会将该套接字设置为不可读,任何读操作都会返回异常。
  2. 输出方向,系统内核尝试将发送缓冲区的数据发送给对端,并最后向对端发送一个 FIN 报文,接下来如果再对该套接字进行写操作会返回异常
  3. 如果对端没有检测到套接字已关闭,还继续发送报文,就会收到一个 RST 报文,告诉对端:“Hi, 我已经关闭了,别再给我发数据了。”
  4. 我们会发现,close 函数并不能帮助我们关闭连接的一个方向, shutdown 函数才可以。
shutdown函数
int shutdown(int sockfd, int howto)
  • 若成功则为 0,若出错则为 -1。

howto设置三个选项:

  • SHUT_RD(0):关闭连接的“读”这个方向,对该套接字进行读操作直接返回 EOF。从数据角度来看,套接字上接收缓冲区已有的数据将被丢弃,如果再有新的数据流到达,会对数据进行 ACK,然后悄悄地丢弃。也就是说,对端还是会接收到 ACK,在这种情况下根本不知道数据已经被丢弃了。
  • SHUT_WR(1):关闭连接的“写”这个方向,这就是常被称为”半关闭“的连接。此时,不管套接字引用计数的值是多少,都会直接关闭连接的写方向。套接字上发送缓冲区已有的数据将被立即发送出去,并发送一个 FIN 报文给对端。应用程序如果对该套接字进行写操作会报错。
  • SHUT_RDWR(2):相当于 SHUT_RD 和 SHUT_WR 操作各一次,关闭套接字的读和写两个方向。

使用 SHUT_RDWR 来调用 shutdown 不是和 close差别:

  1. close 会关闭连接,并释放所有连接对应的资源,而 shutdown 并不会释放掉套接字和所有的资源。
  2. close 存在引用计数的概念,并不一定导致该套接字不可用;shutdown 则不管引用计数,直接使得该套接字不可用,如果有别的进程企图使用该套接字,将会受到影响。
  3. close 的引用计数导致不一定会发出 FIN 结束报文,而 shutdown 则总是会发出 FIN 结束报文,这在我们打算关闭连接通知对端的时候,是非常重要的。

close 和 shutdown 的差别
在这里插入图片描述

  • close 函数只是把套接字引用计数减 1,未必会立即关闭连接;
  • close 函数如果在套接字引用计数达到 0 时,立即终止读和写两个方向的数据传送。
  • 基于这两个确定,在期望关闭连接其中一个方向时,应该使用 shutdown 函数。
问题1:直接调用exit(0)完成了 FIN 报文的发送,这是为什么呢?为什么不调用 close 函数或 shutdown 函数呢?

因为在调用exit之后进程会退出,而进程相关的所有的资源,文件,内存,信号等内核分配的资源都会被释放,在linux中,一切皆文件,本身socket就是一种文件类型,内核会为每一个打开的文件创建file结构并维护指向改结构的引用计数,每一个进程结构中都会维护本进程打开的文件数组,数组下标就是fd,内容就指向上面的file结构,close本身就可以用来操作所有的文件,做的事就是,删除本进程打开的文件数组中指定的fd项,并把指向的file结构中的引用计数减一,等引用计数为0的时候,就会调用内部包含的文件操作close,针对于socket,它内部的实现应该就是调用shutdown,只是参数是关闭读写端,从而比较粗暴的关闭连接。

问题2:
  1. 代码运行先显示hi data1,之后才接收到标准输入的close,为什么时序图中化的是先close才接收到hi data1?

    close表示的是客户端发起的close调用

  2. 当一方主动 close 之后,另一方发送数据的时候收到 RST。主动方缓冲区会把这个数据丢弃吗?这样的话,应用层应该读不到了吧?

    不会收到,即使收到RST回执,应用层也读不到的。

  3. 代码中 SIGPIPE 的作用不是忽略吗?为什么服务器端会退出?

默认的 SIGPIPE 忽略行为就是退出程序,什么也不做,当然,实际程序还是要做一些清理工作的。

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 在Socket编程中,shutdown()和close()都是用来关闭一个套接字(Socket)的方法,但二者有不同的作用。 shutdown()方法是用来关闭一个已连接的套接字,它可以在客户端和服务器端分别调用。shutdown()的调用会使得套接字不能再进行数据的发送和接收,但它可以继续进行一些管理操作,例如可以发送一些控制信息给对方,告知对方套接字已关闭。 而close()方法是用来关闭一个套接字的连接,它可以在客户端和服务器端分别调用。调用close()方法后,套接字不再使用,也不会再接收或发送数据。当客户端调用close()时,它会向服务器发送一个关闭连接的请求,服务器收到请求后也会调用close()方法关闭套接字。 ### 回答2: 在Socket编程中,shutdownclose都是用于关闭连接的方法,但它们有一些区别。 首先,shutdown是用于半关闭连接的操作,它可以关闭Socket的输入和输出流的其中一方。shutdown方法可以接受一个参数,该参数可以是常量Socket.SHUT_RD,用于关闭输入流;也可以是常量Socket.SHUT_WR,用于关闭输出流;还可以是常量Socket.SHUT_RDWR,用于关闭输入和输出流。通过这方式,可以控制关闭连接的一方。 相比之下,close是用于完全关闭连接的操作,它会关闭Socket的输入和输出流,并释放相关的资源。调用close方法后,再次尝试使用该Socket将会抛出异常。 其次,shutdown可以在一个Socket处于连接状态时多次调用,并且可以对输入和输出流进行独立的关闭,而close方法只能在一个Socket连接被完全关闭后调用一次。 最后,通过调用shutdown方法关闭连接,可以保留Socket的状态信息和相关资源,并继续使用该Socket进行数据传输。而调用close方法关闭连接则会释放Socket相关的资源,并且无法再次使用该Socket。 综上所述,shutdownclose方法在Socket编程中都可以用于关闭连接,但shutdown方法可以控制关闭连接的一方,可以多次调用并保留Socket状态,而close方法只能完全关闭连接并释放相关资源。 ### 回答3: 在Socket编程中,shutdown()和close()是两个不同的方法,用于关闭连接和释放Socket资源。 shutdown()方法用于关闭连接的Socket连接,它需要传入一个参数,来指定关闭方式。参数可以是SHUT_RD、SHUT_WR或SHUT_RDWR。SHUT_RD表示禁止读取数据,SHUT_WR表示禁止写入数据,SHUT_RDWR表示同时禁止读写数据。通过调用shutdown()方法,我们可以选择关闭连接的读端、写端或者同时关闭两者,这样就可以保持双方的连接而不再传输数据。 相比之下,close()方法是用于完全关闭Socket连接,它不需要传入任何参数。调用close()方法会立即关闭Socket连接,并释放所有与该Socket相关的资源。关闭连接后,将无法再进行读取或写入操作,也无法再建立连接。 总结来说,shutdown()方法是暂时关闭Socket连接的一手段,可以选择性地关闭读端、写端或者两者。而close()方法是永久关闭Socket连接,并释放相关资源的一方式关闭连接后,将无法再进行数据传输或者建立新的连接

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值