以前只是知道3次握手和4次挥手,但是对于其在连接和断开时的各个状态却不是很懂,今天就来看一下握手和挥手时的状态转换图:

1.三次握手和四次挥手时的状态转换图:

实线表示应用程序:

应用层首先发SYN的请求信号,应用层处于SYN_SENT的状态,当服务器端发送ACK应答,并且服务器端发送给它SYN请求时,处于数据传输状态(ESTABLISHED).当客户端关闭的时候(发送FIN),客户端变成FIN_WAIT_1的状态,当收到ACK时,变成FIN_WAIT_2的状态,当接收到服务器端的FIN时,变成TIME_WAIT的状态(2MSL后转化为closeing状态)。

TIME_WAIT

每个包过一个路由器,TTL减1,由于网络故障,可能有延迟,这时候如果直接进入到closing状态,就不能接受这个包。在TIME_WAIT状态内,这个端口不能被其他的进程所使用。

虚线表示服务器

服务器接收到客户端的SYN的时候,并且返回ACK,SYN的时候,会进入到一个SYN_RCVD的状态。客户端回复ACK的时候,就进入数据传输状态。当服务器端接受到FIN的时候,服务器进入CLOSE_WAIT状态,当服务器端给客户端发送FIN的时候,服务器进入LAST_ACK状态,当接收到ACK的时候,服务器关闭。

**CLOSED: **这个没什么好说的了,表示初始状态。

**LISTEN: **这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。

SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次

握手会话过程中的一个中间状态,很短暂,基本 上用netstat你是很难看到这种状态的,除非你特意写了一个客户

端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态 时,当收到客户端的ACK报文

后,它会进入到ESTABLISHED状态。

SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即

它会进入到了SYN_SENT状 态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN

报文。

ESTABLISHED:这个容易理解了,表示连接已经建立了。

FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报

文。而这两种状态的区别 是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向

对方发送了FIN报文,此时该SOCKET即 进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状

态,当然在实际的正常情况下,无论对方何种情况下,都应该马 上回应ACK报文,所以FIN_WAIT_1状态一般是比较

难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求

close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果

FIN_WAIT_1状态下,收到了对方同时带 FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过

FIN_WAIT_2状态。

CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送

FIN报文后,按理来说是应该先收到(或同时收到)对方的 ACK报文,再收到对方的FIN报文。但是CLOSING状态表

示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什 么情况下会出现此种情况

呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时

发送FIN报 文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。

CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自

己,你系统毫无疑问地会回应一个ACK报文给对 方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要

考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以 close这个SOCKET,发送FIN报文

给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。

**LAST_ACK: **这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收

到ACK报文后,也即可以进入到CLOSED可用状态了。

1.1. 设置TIME_WAIT状态的原因

2MSL TIME_WAIT状态存在的理由:

TIME_WAIT状态的存在有两个理由:(1)让4次握手关闭流程更加可靠;4次握手的最后一个ACK是是由主动关闭方发送出去的,若这个ACK丢失,被动关闭方会再次发一个FIN过来。若主动关闭方能够保持一个2MSL的TIME_WAIT状态,则有更大的机会让丢失的ACK被再次发送出去。(2)防止lost duplicate对后续新建正常链接的传输造成破坏。lost duplicate在实际的网络中非常常见,经常是由于路由器产生故障,路径无法收敛,导致一个packet在路由器A,B,C之间做类似死循环的跳转。IP头部有个TTL,限制了一个包在网络中的最大跳数,因此这个包有两种命运,要么最后TTL变为0,在网络中消失;要么TTL在变为0之前路由器路径收敛,它凭借剩余的TTL跳数终于到达目的地。但非常可惜的是TCP通过超时重传机制在早些时候发送了一个跟它一模一样的包,并先于它达到了目的地,因

此它的命运也就注定被TCP协议栈抛弃。另外一个概念叫做incarnation connection,指跟上次的socket pair一摸一样的新连接,叫做incarnation of previous connection。lost duplicate加上incarnation connection,则会对我们的传输造成致命的错误。大家都知道TCP是流式的,所有包到达的顺序是不一致的,依靠序列号由TCP协议栈做顺序的拼接;假设一个incarnation connection这时收到的seq=1000, 来了一个lost duplicate为seq=1000,len=1000, 则tcp认为这个lost duplicate合法,并存放入了receive buffer,导致传输出现错误。通过一个2MSL TIME_WAIT状态,确保所有的lost duplicate都会消失掉,避免对新连接造成错误。

该状态为什么设计在主动关闭这一方:

  • 发最后ack的是主动关闭一方
  • 只要有一方保持TIME_WAIT状态,就能起到避免incarnation connection在2MSL内的重新建立,不需要两方都有.

1.2.如何正确的对待2MSL TIME_WAIT?

RFC要求socket pair在处于TIME_WAIT时,不能再起一个incarnation connection。但绝大部分TCP实现,强加了

更为严格的限制。在2MSL等待期间,socket中使用的本地端口在默认情况下不能再被使用。若A 10.234.5.5:1234

和B 10.55.55.60:6666建立了连接,A主动关闭,那么在A端只要port为1234,无论对方的port和ip是什么,都不

允许再起服务。显而易见这是比RFC更为严格的限制,RFC仅仅是要求socket pair不一致,而实现当中只要这个

port处于TIME_WAIT,就不允许起连接。这个限制对主动打开方来说是无所谓的,因为一般用的是临时端口;但对

于被动打开方,一般是server,就悲剧了,因为server一般是熟知端口。比如http,一般端口是80,不可能允许

这个服务在2MSL内不能起来。解决方案是给服务器的socket设置SO_REUSEADDR选项,这样的话就算熟知端口处于

TIME_WAIT状态,在这个端口上依旧可以将服务启动。当然,虽然有了SO_REUSEADDR选项,但sockt pair这个限制

依旧存在。比如上面的例子,A通过SO_REUSEADDR选项依旧在1234端口上起了监听,但这时我们若是从B通过6666端

口去连它,TCP协议会告诉我们连接失败,原因为Address already in use.

2.TCP的流量控制(滑动窗口)

如果发送端发送的速度较快,接收端接收到数据后处理的速度较慢,而接收缓冲区的大小是固定的,就会丢失数据。TCP协议通过’滑动窗口(Sliding Window)’机制解决这一问题。看下图的通讯过程。

说明:

1.发送端发起连接,声明最大段尺寸是1460(单个包的最大长度),初始序号是0,窗口大小是4K,表示“我的接收缓冲区还有4K字节空闲,你发的数据不要超过4K”。接收端应答连接请求,声明最大段尺寸是1024,初始序号是8000,窗口大小是6K。发送端应答,三方握手结束。

2.发送端发出段4-9,每个段带1K的数据,发送端根据窗口大小知道接收端的缓冲区满了,因此停止发送数据。

3.接收端的应用程序提走2K数据,接收缓冲区又有了2K空闲,接收端发出段10,在应答已收到6K数据的同时声明窗口大小为2K。

4.接收端的应用程序又提走2K数据,接收缓冲区有4K空闲,接收端发出段11,重新声明窗口大小为4K。

5.发送端发出段12-13,每个段带2K数据,段13同时还包含FIN位。

6.接收端应答接收到的2K数据(6145-8192),再加上FIN位占一个序号8193,因此应答序号是8194,连接处于半关闭状态,接收端同时声明窗口大小为2K。

7.接收端的应用程序提走2K数据,接收端重新声明窗口大小为4K。

8.接收端的应用程序提走剩下的2K数据,接收缓冲区全空,接收端重新声明窗口大小为6K。

9.接收端的应用程序在提走全部数据后,决定关闭连接,发出段17包含FIN位,发送端应答,连接完全关闭。

从这个例子还可以看出,发送端是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),在底层通讯中这些数据可能被拆成很多数据包来发送,但是一个数据包有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。

3.TCP半链接状态

当TCP链接中A发送FIN请求关闭,另一段B回应ACK后,B没有立即发送FIN给A时,A方处在半链接状态,此时A可以接收B发送的数据,但是A已不能再向B发送数据。

#include <sys/socket.h>
int shutdown(int sockfd, int how);
sockfd: 需要关闭的socket的描述符
how:允许为shutdown操作选择以下几种方式:
SHUT_RD:关闭连接的读端。也就是该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被丢弃。
进程将不能对该套接字发出任何读操作。对 TCP套接字该调用之后接受到的任何数据将被确认然后无声的
丢弃掉。
SHUT_WR:关闭连接的写端,进程不能在对此套接字发出写操作
SHUT_RDWR:相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR

使用close中止一 个连接,但它只是减少描述符的参考数,并不直接关闭连接,只有当描述符的参考数为0时才关闭连接。 shutdown可直接关闭描述符,不考虑描述 符的参考数,可选择中止一个方向的连接。

注意:

1.如果有多个进程共享一个套接字,close每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close,套接字将被释放。

2.在多进程中如果一个进程中shutdown(sfd, SHUT_RDWR)后其它的进程将无法进行通信. 如果一个进程close(sfd)将不会影响到其它进程.

网络基础-再议TCP的更多相关文章

  1. 异常处理与网络基础中的tcp,udp协议

    # 异常处理: # 什么是异常?异常和错误的区别 # Error 语法错误 比较明显的错误 在编译代码阶段就能检测出来 # Iteration 异常 在执行代码的过程中引发的异常 # 异常发生之后的效 ...

  2. 网络基础之2——TCP/IP参考模型

    本内容主要来源于<看透Spring MVC源码分析与实践——韩路彪>一书 BS结构网络传输的分解方式有两种: 1.OSI参考模型. 2.TCP/IP参考模型. OSI和TCP/IP分层模型 ...

  3. 网络基础篇(一)--TCP/IP协议族

    TCP/IP协议族是一个分层,多协议通信体系. 1 TCP/IP协议族体系结构 TCP/IP协议族自底而上分为四层: 数据链路层, 网络层, 传输层和应用层. 1.1 数据链路层 实现网卡接口的网络驱 ...

  4. 【网络基础】【TCP/IP】私有IP地址段

    私有IP地址段 Class A:10.0.0.0    - 10.255.255.255 Class B:172.16.0.0  - 172.31.255.255 Class C:192.168.0. ...

  5. 【网络基础】【TCP/IP】IP的分级

    节选自 <鸟哥的linux私房菜>  http://cn.linux.vbird.org/linux_server/0110network_basic_3.php#ps12 InterNI ...

  6. 网络基础 二 (TCP协议代码,UDP协议代码)

    TCP  三次握手,四次断开 三次握手(必须先由客户端发起) 客户端:发送请求帧给服务器. 服务器:收到客户端的请求,并回复可以建立连接 客户端:与服务器建立连接 四次断开 (谁先发起都行,以客户端为 ...

  7. 网络编程—网络基础概览、socket,TCP/UDP协议

    网络基础概览 socket概览 socket模块—TCP/UDP的实现 TCP/UDP总结 网络基础概览 osi七层协议各层主要的协议 # 物理层传输电信号1010101010 # 数据链路层,以太网 ...

  8. 【图解】给面试官解释TCP的三次握手与四次挥手-Web运用原理及网络基础

    作者 | Jeskson 来源 | 达达前端小酒馆 轻松了解HTTP协议 为什么要学习网络协议呢?为什么要学习计算机完了呢?显然这很重要,至少能够帮助你找到工作的原因之一,学习网络知识点太多太多,没有 ...

  9. 【Linux网络基础】网络拓扑、OSI层次模型、TCP/IP协议簇

    一.前言 一个运维有时也要和网络打交道,所以具备最基本的网络知识,对一个运维人员来说是必要的.但,对于我们的工作来说这些并不是重点,因此,我不可能从最基础的网络知识开始讲起.本节内容更多是从一个梳理和 ...

随机推荐

  1. VMware安装时Error 1324. The path My Documents contains a invalid character的原因和解决方法

    终于找到了自己想要的答案,顶顶,吼吼~ 我今天安装VMware Workstation时,总是提示我Error 1324. The path My Documents contains a inval ...

  2. django搭建web (三) admin.py -- 待续

    demo 关于模型myQuestion,myAnswer将在后述博客提及 # -*- coding: utf-8 -*- from __future__ import unicode_literals ...

  3. iOS开发之Objective-C与JavaScript的交互

    UIWebView是iOS最常用的SDK之一,它有一个stringByEvaluatingJavaScriptFromString方法可以将javascript嵌入页面中,通过这个方法我们可以在iOS ...

  4. 关于tomcat部署应用的三种方式

    关于tomcat部署应用虽然不是一个经常的操作,因为一旦选择了一种部署方式,我们其他的应用就会不经大脑的使用这种既定模式, 如果不使用这种部署方式,但是对于其他的部署方式不是很清楚的话,很容易抓瞎,所 ...

  5. div+css命名规则

    作为一个前端菜鸟,进公司的第一个项目就是中途从外包公司接过来的公司网站,在别人写过了的基础上接着写,命名什么的,简直不要太痛苦. 目前,这个网站已经完成,但是被后台人员指出命名不规范.有心想解释一两句 ...

  6. Python扩展模块——自动化(testlinkAPI的使用)

    使用TESTLINKAPI首先要安装TestLink_API_Python_client-0.6.4(当前最新版本) 目前只使用到了通过api获取testlink中的自定义字段and值 url = ' ...

  7. api-gateway实践(16)【租户模块:修改api定义】通过mq通知【开发者模块:更新开发者集市】

    一.订阅关系 二.接收消息 dev模块接收更新本地集市

  8. BlueMix - IBM的Paas云计算平台

    Bluemix,2015年年中,IBM推出了名为Bluemix的云计算平台.这一"平台即服务"的PaaS云将帮助开发者更快的进行应用开发和部署.   Bluemix正是IBM回应这 ...

  9. SqlServer优化:当数据量查询不是特别多,但数据库服务器的CPU资源一直100%时,如何优化?

    最近和同事处理一个小程序,数据量不是特别大,某表的的数据记录:7000W条记录左右,但是从改别执行一次查询时,却发现查询速度也不快,而且最明显的问题就是CPU100%. sql语句: select g ...

  10. #定义一个方法get_num(num),num参数是列表类型,判断列表里面的元素为数字类型。其他类型则报错,并且返回一个偶数列表:(注:列表里面的元素为偶数)。

    #定义一个方法get_num(num),num参数是列表类型,判断列表里面的元素为数字类型.其他类型则报错,并且返回一个偶数列表:(注:列表里面的元素为偶数). def get_num(num): i ...