免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
1234下一页
最近访问板块 发新帖
查看: 9208 | 回复: 32
打印 上一主题 下一主题

[Linux] socket中close和shutdown的区别 [复制链接]

论坛徽章:
1
双子座
日期:2013-11-06 17:18:01
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-04-01 20:47 |只看该作者 |倒序浏览
本帖最后由 seufy88 于 2014-04-01 21:01 编辑

这两个的区别在网上都有好多文章.
只是我还是有不明点.所以想请教一下大家.

这里不考虑多进程共享socket id这种情况.
单纯的讨论.

close (socket id): 马上发送FIN信号,所有的未完成发送或者接受的数据都将被丢失
close成功后,对这个socket id进行read/write都将失败.
但是从TCP层面将,close只是能控制本方发送FIN到对方,并不能控制对方何时发送FIN过来.
请问在这种情况下,如果对方还发data过来,本方的TCP层还会发送ack of data给对方吗?

      本方                  对方
       |                        |
close|-------FIN----->|
       |<--- ack of FIN -|
       |                        |
       |<----data ------ |
       |---ack of data -->|?????

在这种情况下,如果对方还发送data(而不是FIN)过来,本方是否直接返回RST?

shutdown (socket id):如果关闭写,如果输出缓冲区内有数据,则所有的数据将发送出去后将发送一个FIN信号
如果关闭写,则对这个socket的后续write会失败,但是允许后续对这个socket进行read.

本方                  对方
|                        |
|-------FIN----->|
|<--- ack of FIN -|
|                        |
|<----data ------ |
|---ack of data -->|


========
close,shutdown都会在TCP层面主动发送FIN
但是这些都只是local侧的,并不能掌控对方侧的行为(是继续发data还是发送FIN)
在这种情况下,我们local侧分别在使用close,shutdown后,如果对方侧仍有data发送过来,是否会有不同的操作?
已知的是shutdown之后,如果对方侧仍有data到达,则local侧可以发送回ack of data
但是close的情况又是怎么样的呢?

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
2 [报告]
发表于 2014-04-01 21:41 |只看该作者
一言难尽,也不用网上怎么搜,找到UNPv1(UNIX网络编程第一卷),专门有讲这个问题的章节,看完就没疑问了。

论坛徽章:
1
双子座
日期:2013-11-06 17:18:01
3 [报告]
发表于 2014-04-01 21:46 |只看该作者
回复 2# timespace
小弟有空会去看.
本人并不是究结于close ,shutdown这样的API
而是对下面的TCP实现有点疑问.


   

论坛徽章:
3
射手座
日期:2014-08-18 12:15:53戌狗
日期:2014-08-22 09:53:36寅虎
日期:2014-08-22 14:15:29
4 [报告]
发表于 2014-04-02 10:12 |只看该作者
close和shutdown的行为取决于内核的实现。
1. close调用之后如果在local TCP buffer内如果还有数据没有读取会给对方直接回RST, 否则发送FIN,这取决于调用close时刻local TCP buffer的状态, 跟对端是不是继续发送数据无关。
2. shutdown关闭读不会给对方发FIN, 只有关闭写才会发FIN, 而且跟local TCP buffer状态没关系,只发送FIN包,从不发送RST

论坛徽章:
1
双子座
日期:2013-11-06 17:18:01
5 [报告]
发表于 2014-04-02 11:16 |只看该作者
本帖最后由 seufy88 于 2014-04-02 11:26 编辑
gaojl0728 发表于 2014-04-02 10:12
close和shutdown的行为取决于内核的实现。
1. close调用之后如果在local TCP buffer内如果还有数据没有读取 ...


恩,我之前没说明白,这里的shutdown特指W,会发送FIN。这个本人能够理解。

1. close调用之后如果在local TCP buffer内如果还有数据没有读取会给对方直接回RST, 否则发送FIN,这取决于调用close时刻local TCP buffer的状态, 跟对端是不是继续发送数据无关
=》如果close造成的是发送FIN,在这之后,对方继续发送data 过来的话呢?
按照底层TCP协议,local侧是否继续发送ack of data给对方,直到local侧buffer满了
因为TCP两方的通信通道,local侧永远只能掌控local侧出去的方向,不能控制对方侧继续发送数据。即在FIN_WAIT2状态时,local侧依然可以接收对方的data

调用close,shutdown,内核侧做了不同的guard工作:
对socket继续read的话,
close之后不允许读取对方来的数据,shutdown之后还是允许继续读取。

之前提到调用clsoe会关闭bidirection,这里所谓的关闭bidirection其实是否是内核自己提供的guard动作,和TCP协议本身“不一致”
因为从TCP层面看,一方发送FIN到对方后,并不能真正关闭bidirection,需要对方也发送FIN之后,TCP connection才算真正关闭bidirection

不知道我的想法是否有误?

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
6 [报告]
发表于 2014-04-02 13:06 |只看该作者
回复 3# seufy88
如果是看API,我肯定推荐man shutdown或man close,既然是看书,那就不全是API的事情,网络API才有几个,值得UNPv1用近千页的篇幅来论述?UNPv1讲解API的同时,会有大量TCP/IP方面的解释,以助于理解本质问题,作者写TCP/IP详解三卷的功力也体现在UNPv1中。
你的问题不外乎就是shutdown/close/SO_LINGER三者的功能与区别,看完SO_LINGER就解释你剩下的疑问了。



   

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
7 [报告]
发表于 2014-04-02 13:47 |只看该作者
不过多数来论坛提问的人是没耐心看书的,简单说下我的理解:

首先,close对TCP协议栈的影响可以由setsockopt(SOL_SOCKET, SO_LINGER, struct linger *optval; socklen_t optlen)控制。
struct linger {
    int onoff; /* 0=off, nonzero=on */
    int l_linger; /* linger time, POSIX specifies units as seconds */
};

但是从TCP层面将,close只是能控制本方发送FIN到对方,并不能控制对方何时发送FIN过来.
请问在这种情况下,如果对方还发data过来,本方的TCP层还会发送ack of data给对方吗?

1. close后,默认情况与使用l_onoff=0效果一致,本方继续接收数据并ACK,但会丢弃这些数据,直到对方FIN过来,完成连接终止过程;
2. close后,l_onoff非0且l_linger=0,立即丢弃发送缓存区数据并给对方发送一个RST,也就是不会有正常的四分组终止过程,也没有TIME_WAIT,对方收到RST后,肯定不能继续发数据了;
3. close后,l_onoff非0且l_linger>0,close会等待l_linger(阻塞情况下),如果数据发完,执行#1,否则执行#2;
灵活运用上面三种方式,本地socket足以应付不知进退的对方socket,排除共享fd和关闭fd问题外,close相当于shutdown(fd, SHUT_RDWR)。

论坛徽章:
3
射手座
日期:2014-08-18 12:15:53戌狗
日期:2014-08-22 09:53:36寅虎
日期:2014-08-22 14:15:29
8 [报告]
发表于 2014-04-02 13:54 |只看该作者
回复 5# seufy88


看了下linux-3.12.6的实现,内核的实现还是有点小复杂的。

1. close调用因为会关闭文件句柄, 因此close之后既不能读也不能继续写,shutdown_write之后句柄还在,还能继续读
2. 调用close和shutdown_write效果是一样的,都会给对方发送FIN, local端进入FIN_WAIT1, 之后能和等待对端回复ACK,
    (1)如果此后收到的是不带ACK纯数据包, 内核会暴力回复RST, 所以一种可能的情况就是虽然我调的是shutdown_write, 但最终双向都关闭了,因为网络中的包很难说什么时候到达。
    (2)如果此后收到的是带ACK的纯数据包,会有所不同,
        a. 如果前面是close调用, local端进入FIN_WAIT2, 同时启动time wait定期器,最终会关闭local端的TCP链接.
        b. 如果前面是shutdown_write, 数据会进入local TCP buffer, 上层应用可以继续收数据。
3. close对于local端来说是bidirection, 但对于对端来说是unidirection的, 对端根本就不知道你本端是close还是shutdown_write, 对端可以选择不close socket, 继续发送数据,这就会触发上面的第2条的逻辑,对端会收到RST, 对端的内核会关闭TCP链接。
4. 以上讨论忽略了linger选项,如果加上他就更复杂了。

论坛徽章:
3
射手座
日期:2014-08-18 12:15:53戌狗
日期:2014-08-22 09:53:36寅虎
日期:2014-08-22 14:15:29
9 [报告]
发表于 2014-04-02 14:08 |只看该作者
回复 7# timespace


    我给你纠正一下, 第2条,
close后,l_onoff非0且l_linger=0, 只有在此时TCP的读队列没有数据的情况下, 才会暴力发送RST, 否则内核还是会四次握手的。

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
10 [报告]
发表于 2014-04-02 16:00 |只看该作者
回复 9# gaojl0728
多谢纠正。没研究过内核源码,不过有个疑问,close后,l_onoff非0且l_linger=0,如果本地TCP读取队列有数据且内核不RST,内核会逐步丢掉这些数据,一旦对方不发FIN,怎么终止连接?FIN_WAIT_2有超时?


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP