http://stackoverflow.com/questions/14740852/linux-socket-close-vs-shutdown

  1. shutdown(sd, SHUT_WR)  发送FIN给对端,对端会响应一个ACK(协议层-内核所做). 未来任何尝试写这个socket会发生错误。但是对端仍然可以可以读,因为本端关闭写时,可能由于协议层的策略(滑动窗口、拥塞窗口、angle算法等)导致发送延迟,如果有待发送的消息,那么要尽力保证这些消息都发出去的。所以,会在最后一个报文中加入FIN标志,同时,关闭用于减少网络中小报文的angle算法,向连接对端发送消息。如果没有待发送的消息,则构造一个报文,仅含有FIN标志位,发送出去关闭连接;对端可以继续写数据给本端(主动端),因为对端收到FIN(收到FIN之前其实还有主动端缓冲区待发送消息)并响应ACK了(在响应ACK之前可能还有数据发送给本端),响应ACK后就进入CLOSE_WAIT状态,这个状态一般比较短暂,如果存在大量的CLOSE_WAIT,说明程序未关闭socket或者并发量太大来不及,或者你用的网络库有bug

  2. shutdown(sd, SHUT_RD) 不发送任何网络消息:他只是限制本地API对于随后的socket的读取直接返回EOS. 在已经在读操作被shutdown的socket上接收数据的行为(内核的行为, 表明对端要发送数据过来的行为)是依赖于系统的: Unix会确认它并且丢弃它; Linux会确认它并且缓冲它,最终会使得sender停止运行(发送rst给sender); Windows会发出一个RST, 此时sender继续写会报错“Connection reset by peer”

  3. 注:本端(主动端)发出fin进入FIN_WAIT1,收到对端ACK(对端发送ACK之前可能还有数据要发送给本端)进入FIN_WAIT2状态,收到对端FIN,发送ACK则进入TIME_WAIT,在2MSL内可以重发最后的ACK防止最后的ACK丢包;对端收到主动端的fin,如果有数据要发送给主动端,就发送数据,然后发送ACK,如果没有就直接发送ACK给主动端,然后对端进入CLOSE_WAIT状态,此时程序要保证及时关闭socket,不然会close_wait泄漏造成服务挂掉,当关闭socket时发送FIN给主动端,对端进入LASK_ACK状态,主动端收到FIN并ACK给对端,主动端进入TIME_WAIT,对端关闭(TIMEWAIT存在的原因就是防止最后一个ack丢包,在2MSL内可重传ACK)。

close函数:

  参数sockfd为套接字描述符,成功返回0,失败返回-1;该函数的功能是关闭套接字描述符引用计数,当计数大于0时什么都不干,当计数等于0时触发TCP/IP的四次挥手,即主动关闭方发送FIN。

  如果在调用close以前,TCP协议栈的发送队列中有已排队等候发送的数据,则协议栈尝试将数据发送出去,发送完毕后根据套接字描述引用计数来决定是否关闭连接。
      如果在调用close以后,且套接字描述符引用计数为0,则在其上调用write或者read函数则会产生错误码为9即EBADF(bad file descriptor 和Broken Pipe不一样,Broken Pipe表示文件描述符还合法,但是连接状态非法)的错误。

  调用close函数时,TCP协议栈对发送队列中已排队等候发送数据的处理流程受SO_LINGER选项的影响,该选项关联的数据结构如下:
          struct linger{
              int l_onoff;    //0=off,nonzero=on
              int l_linger;    //linger time
          };
      l_onoff是选项开关,如果是0则关闭选项并且忽略参数l_linger,如果是非零则打开选项,默认是0;l_linger是延迟关闭连接时间,只有l_onoff打开时即为非零时,该参数的值才有效。这几个参数与close时TCP协议栈对待发数据的处理流程如下表:

  表1 待发数据处理流程

l_onoff

l_linger

close行为

发送队列

TCP协议栈

忽略

立即返回

保持直至发送完成

接管套接字并保证将数据发送至对端

非零

立即返回

立即放弃

直接发送RST,自身立即复位,不用经过2MSL状态,对端收到复位错误码

非零

非零

阻塞直到l_linger时间超时或数据发送完成(套接字必须设置为阻塞)

在超时时间段内保持尝试发送,若超时则立即放弃

超时则同第二种情况,若发送完成则皆大欢喜

  当应用程序在调用close()函数关闭TCP连接时,Linux内核的默认行为是将套接口发送队列里的原有数据(比如之前残留的数据)以及新加入 的数据(比如函数close()产生的FIN标记,如果发送队列没有残留之前的数据,那么这个FIN标记将单独产生一个新数据包)发送出去并且销毁套接口 (并非把相关资源全部释放,比如只是把内核对象sock标记为dead状态等,这样当函数close()返回后,TCP发送队列的数据包仍然可以继续由内 核协议栈发送,但是一些相关操作就会受到影响和限制,比如对数据包发送失败后的重传次数)后立即返回。这需要知道两点:

  第一,当应用程序获得 close()函数的返回值时,待发送的数据可能还处在Linux内核的TCP发送队列里,因为当我们调用write()函数成功写出数据时,仅表示这些 数据被Linux内核接收放入到发送队列,如果此时立即调用close()函数返回后,那么刚才write()的数据限于TCP本身的拥塞控制机制(比如 发送窗口、接收窗口等等),完全有可能还呆在TCP发送队列里而未被发送出去;当然也有可能发送出去一些,毕竟在调用函数close()时,进入到 Linux内核后有一次数据包的主动发送机会,即:
tcp_close() -> tcp_send_fin() -> __tcp_push_pending_frames() -> tcp_write_xmit()

  第二,所有这些数据的发送,最终却并不一定能全部被对端确认(即数据包到了对端TCP协议栈的接收队列),只能做到TCP协议本身提供的一定程度的 保证,比如TCP协议的重传机制(并且受close()函数影响,重传机制弱化,也就是如果出现类似系统资源不足这样的问题,调用过close()函数进 行关闭的套接口所对应的这些数据会优先丢弃)等,因为如果网络不好可能导致TCP协议放弃继续重传或在意外收到对端发送过来的数据时连接被重置导致未成功 发送的数据全部丢失(后面会看到这种情况)。

【整理】close 和 shutdown 的原理的更多相关文章

  1. java 面试题整理(不定期更新)

    一.Java基础 1.Java面向对象的三个特征与含义 三大特征是:封装.继承和多态. 封装是指将某事物的属性和行为包装到对象中,这个对象只对外公布需要公开的属性和行为,而这个公布也是可以有选择性的公 ...

  2. [Java面经]干货整理, Java面试题(覆盖Java基础,Java高级,JavaEE,数据库,设计模式等)

    如若转载请注明出处: http://www.cnblogs.com/wang-meng/p/5898837.html   谢谢.上一篇发了一个找工作的面经, 找工作不宜, 希望这一篇的内容能够帮助到大 ...

  3. webdriver的工作原理

    selenium1的原理就是使用js来驱动浏览器,因为现在基本不用,所以不做过多讨论,下面是我整理的webdriver的工作原理,大致就是通过命令请求webdriver,然后webdriver通过浏览 ...

  4. 转载:[Java面经]干货整理, Java面试题(覆盖Java基础,Java高级,JavaEE,数据库,设计模式等)

    原文:http://www.cnblogs.com/wang-meng/p/5898837.html 一:继承.抽象类与接口区别.访问控制(private, public, protected,默认) ...

  5. Web程序员应该知道的Javascript prototype原理

    有同事问了我几个和Javascript的类继承的小问题,我在也不太理解的情况下,胡诌了一通. 回来以后有些内疚, 反省一下, 整理整理Javascript的prototype的原理, 自己清楚点, 也 ...

  6. 针对《面试心得与总结—BAT、网易、蘑菇街》一文中出现的技术问题的收集与整理

    最近,我在ImportNew网站上,看到了这篇文章,觉得总结的非常好,就默默的收藏起来了,觉得日后一定要好好整理学习一下,昨天突然发现在脉脉的行业头条中,居然也推送了这篇文章,更加坚定了我整理的信心. ...

  7. 磁盘阵列 RAID 技术原理详解

    RAID一页通整理所有RAID技术.原理并配合相应RAID图解,给所有存储新人提供一个迅速学习.理解RAID技术的网上资源库,本文将持续更新,欢迎大家补充及投稿.中国存储网一如既往为广大存储界朋友提供 ...

  8. 一文读懂 Kubernetes APIServer 原理

    前言 整个Kubernetes技术体系由声明式API以及Controller构成,而kube-apiserver是Kubernetes的声明式api server,并为其它组件交互提供了桥梁.因此加深 ...

  9. 重新整理 .net core 实践篇————配置应用[一]

    前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...

随机推荐

  1. visual studio 不能进入调试状态

    解决Windows操作系统在处理回环地址 1. 第一种解决方案是禁用环回检查. 步骤如下 a) 依次单击“开始”和“运行”,键入 regedit,然后单击“确定” b) 在注册表编辑器中,找到并单击下 ...

  2. Angular建立待办事项应用

    建立路由 接前一小节,在src/app/app.component.html把login组件去掉 第一步: 在src/index.html指定基准路径 浏览器会根据这个路径下载css,图像,js文件, ...

  3. bonecp回缩功能实现

    起因 bonecp不具备回缩功能,即连接池持有连接之后,不会主动去释放这些连接(即使这些连接始终处于空闲状态),因此在使用一段时间之后,连接池会达到配置的最大值. 这种方式一定程度上造成了资源的浪费. ...

  4. Spring界的HelloWorld

    1.Spring依赖的jar包下载网址: https://repo.spring.io/webapp/#/artifacts/browse/tree/General/libs-release-loca ...

  5. Android - 单例模式线程安全

    https://blog.csdn.net/Mars_idea/article/details/80724404 https://blog.csdn.net/cselmu9/article/detai ...

  6. 视区相关单位vw, vh..简介以及可实际应用场景——张鑫旭

    一.N多的唠哩唠叨 CSS3中一些新的单位早在去年春暖花开的时候就介绍了,参见:CSS长度值及时间.频率.角度单位.显然,其中就提到了本文要感叹的单位vw, vh,见下图: 不过“我看见你”和“我触碰 ...

  7. Android 对话框 (AlertDialog)

    Android 提供了 AlertDialog 类可通过其内部类 Builder 轻松创建对话框窗口,但是没法对这个对话框窗口进行定制,为了修改 AlertDialog 窗口显示的外观,解决的办法就是 ...

  8. DOMINO的JDBC和ODBC连接方法

     利用ODBC实现Domino和关系数据库的互操作 Lotus Domino是当今办公自动化系统的主流开发平台之一,Domino自带一个非关系型数据库–文档型数据库,而目前大部分企业的信息都储存在 ...

  9. redis 数据淘汰策略与配置

    redis 数据淘汰策略 volatile-lru:从已设置过期的数据集中挑选最近最少使用的淘汰volatile-ttr:从已设置过期的数据集中挑选将要过期的数据淘汰volatile-random:从 ...

  10. 9.Java注解(Annotation)

    一.系统内置标准注解 1.@Override 是一个标记注解类型,它被用作标注方法. 它说明了被标注的方法重载了父类的方法,起到了断言的作用.如果我们使用了这种Annotation在一个没有覆盖父类方 ...