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. js静态数据分页展示

    拿vue做示例 首先定义data data() { return { tableData: [], // 当前页的数据,用于给表格展示用的 tableDataAll: [], // 需要拿来分页的总数 ...

  2. ECMAScript 5和ECMAScript6的新特性以及浏览器支持情况

    ECMAScript简介: 它是一种由Ecma国际(前身为欧洲计算机制造商协会)制定和发布的脚本语言规范,javascript在它基础上经行了自己的封装.但通常来说,术语ECMAScript和java ...

  3. Oracle数据库函数总结

    1.ceil()向上取整,结果为124.select ceil(123.123) from dual;2.floor()向下取整,结果为123.select floor(123.123) from d ...

  4. iview select下拉bug

    1场景:弹框内有一个下拉组件(支持搜索),当选择完数据后弹框关闭,再次打开后,下拉框内的数据是刚才选中的数据.原因:分析后觉得是搜索内容没有清空,导致下拉的数据只有一个解决:调用下setQuery方法 ...

  5. Ubuntu下彻底卸载wine

    简介: wine是linux下模拟windows的一个东西,可以用来安装exe程序,但是对于wine的卸载确 实非常麻烦的,这里是彻底卸载wine的一个教程. ##首先卸载wine sudo apt- ...

  6. Global Average Pooling Layers for Object Localization

    For image classification tasks, a common choice for convolutional neural network (CNN) architecture ...

  7. MySQL语句应该注意的问题

    刚刚开始写mysql语句 比较繁琐,乱哄哄的,总结了几点应该注意的事项: 1 注意标点符号,在创建表格 create table的时候  括号里面包含的内容用“,”号分割开,最后一条语句不要加标点符号 ...

  8. Pwn With longjmp

    前言 这个是 seccon-ctf-quals-2016 的一个题,利用方式还是挺特殊的记录一下. 题目链接 http://t.cn/RnfeHLv 正文 首先看看程序的安全措施 haclh@ubun ...

  9. 2018-10-13 21:30:51 conversion of number systems

    2018-10-13 21:30:51  c language 二进制.八进制和十六进制: 1) 整数部分 十进制整数转换为 N 进制整数采用“除 N 取余,逆序排列”法. 十进制数字 36926 转 ...

  10. is_palindrome 回文递归

    # coding=utf-8def is_palindrome(n,start,end): if start>end: return 1 else: return is_palindrome(n ...