今天来聊聊面试频率特别高的一个题目:TCP 协议中的三次握手与四次挥手。涉及到的知识点有:

1、TCP、UDP 协议的区别
2、TCP 头部结构
3、三次握手与四次挥手过程详解
4、什么是 TIME_WATI 状态

本文略长,需要有点耐心

一、TCP、UDP 协议的区别

在介绍这两者的区别之前,我们要需要了解一个概念:TCP/IP 协议族。定义如下:

目前 Internet(因特网)使用的主流协议族是 TCP/IP 协议族,它是一个分层、多协议的通信体系 《Linux高性能编程》

提取关键词:分层、多协议和通信。也就是说,它有多个层次,每个层次有不同的协议,这些层次之间通过协议相互协作,最终达到网络通信的目的。

说到分层,应该不会很陌生,TCP/IP 协议族是一个四层协议系统,自底而上分别是:数据链接层、网络层、传输层、应用层。我们这里要说到的 TCP 和 UDP 协议属于传输层。(各层的作用及相关协议这里暂时先不做介绍)

下面我们回到标题:TCP、UDP 协议的区别,总结起来这个问题的答案要点如下:

1、首页它们俩都是传输层的协议,而所谓“传输层”,它是为两台主机提供端到端的通信,即从 A <-> B 。

2、TCP 协议可靠,UDP 协议不可靠。可靠即指数据由 A 发送到 B,是否能确保数据真的有送达到 B。TCP 协议使用 超时重传、数据确认等方式来确保数据包被正确地发送至目的端,而 UDP 协议无法保证数据从发送端正确传送到目的端,如果数据在传输过程中丢失、或者目的端通过数据检验发现数据错误,则 UDP 协议只是简单地通知应用程序发送失败,对于 TCP 协议拥有的超时重传、数据确认等需要应用程序自己来处理这些逻辑。

3、TCP 是面向连接的,UDP 是无连接的。这也比较好理解,因为 TCP 连接才需要“三次握手,四次挥手”。

4、TCP 服务是基于流的,而 UDP 是基于数据报的,基于流的数据没有边界(长度)限制,而基于数据报的服务,每个 UDP 数据报都有一个长度,接收端必须以该长度为最小单位将其所有内容一次性读出。

5、当发送方多次执行写操作时,TCP 模块会先将这些数据放入 TCP 发送缓冲区中,当 TCP 模块真正开始发送数据时,发送缓冲区中这些等待发送的数据可能被封装成一个或多个 TCP 报文段发出,因此,TCP 模块发出的 TCP 报文段的个数与应用程序执行的写操作次数是没有固定数量关系的。同样,当接收端收到一个或多个 TCP 报文段后,TCP 模块将这些数据按照序号(序号说明见下面 的 TCP 头部结构)依次放入 TCP 接收缓冲区中,并通知应用程序读取数据。接收端可选择一次或者分多次将数据从缓冲区中读出(这取决于用户指定的应用程序读缓冲区的大小)。因此,接收端读取数据的次数与发送端发出多少个报文段个数也没有固定的数量关系。总结来说,即对于 TCP 连接,发送端执行的写操作次数与接收端执行的读操作次数之间没有任何数据关系,这也是基于流服务的特点。而对于 UDP 服务,发送端每执行一次写操作,就会将其封装成一个 UDP 数据报并发送之,同时接收端必须根据发送的进行读,否则就会丢包。因此,对于 UDP 连接,发送端写的次数据与读的次数是一致的,这也是基于数据报的服务的特点

6、TCP 的连接是一对一的,所以如果是基于广播或者多播的的应用程序不能使用 TCP,而 UDP 则非常适合广播和多播。

总结一句定义:

TCP 协议(Transmission Control Protocal,传输控制协议)为应用层提供可靠的、面向连接的、基于流的服务。而 UDP 协议(User Datagram Protocal,用户数据报协议)则与 TCP 协议完全相反,它为应用层提供不可靠、无连接和基于数据报的服务

二、TCP 头部结构

TCP 报文结构分为头部部分和数据部分,为什么需要了解 TCP 头部结构,因为在后面“三次握手与四次挥手”里会用到头部结构里的标志位。简单了解下对理解后面的过程有一定的好处。

下面一一说明每个的作用:
16位源端口号与目的端口号,这个比较好理解,就不过多解释。

32位序号:该序号为生成的随机值 ISN 加上该段报文段所携带的数据的第一个字节在整个字节流中的偏移量。比如,某个 TCP 报文段发送的数据是字节流中的第 100 ~ 200 字节,那该序号为 ISN + 100

4位头部长度:标识 TCP 头部有多少个 32 bit 字,因为是 4位,即 TCP 头部最大能表示 15,即最长是 60 字节。即它是用来记录头部的最大长度。

6位标志位,包括:
URG 标志:表示紧急指针是否有效。ACK 标志:确认标志。通常称携带 ACK 标志的 TCP 报文段为确认报文段。
PSH 标志:提示接收端应该程序应该立即从 TCP 接收缓冲区中读走数据,为接收后续数据腾出空间(如果不读走,数据就会一直在缓冲区内)。
RST 标志:表示要求对方重新建立连接。通常称携带 RST 标志的 TCP 报文段为复位报文段SYN 标志:表示请求建立一个连接。通常称携带 SYN 标志的 TCP 报文段称为同步报文段FIN 标志:关闭标志,通常称携带 FIN 标志的 TCP 报文段为结束报文段这些标志位说明了当前请求的目的,即要干什么。

16 位窗口大小:表示当前 TCP 接收缓冲区还能容纳多少字节的数据,这样发送方就可以控制发送数据的速度,它是 TCP 流量控制的一个手段。

16 位校验和:验证数据是否损坏,通过 CRC 算法检验。这个校验不仅包括 TCP 头部,也包括数据部分。

16 位紧急指针:正的偏移量,它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。TCP 的紧急指针是发送端向接收端发送紧急数据的方法。

TCP 头部选项:可变长的可选信息,这部分最多包含 40 字节,因为 TCP 头部最长是 60 字节,所以固定部分占 20 字节。这里不做详细介绍,可以参考《Linux高性能编程》3.2.2

三、三次握手与四次挥手过程详解

先来解释三次握手过程:

1、发送端发送连接请求,6位标志为 SYN,同时带上自己的序号(此时由于不传输数据,所以不表示字节的偏移量),比如是 223。

2、接收端接到请求,表示同意连接,发送同意响应,带上 SYN + ACK 标志位,同时将确认序号为 224(发送端序号加1),并带上自己的序号(此时同样由于不传输数据,所以不表示字节的偏移量),比如是 521。

3、发送端接收到确认信息,再发回给接收端,表示我已接受到你的确认信息,此时标志仍为 ACK,确认序号为 522。

涉及到的问题:为什么是三次握手,而不是四次或者两次?

首先解释为什么不是四次。四次的过程是这样的:

发送方:我要连你了。
接收方:好的。
接收方:我准备好了,你连吧。
发送方:好的。

显然接收方准备好连接并同意连接是可以合并的,这样可以提高连接的效率。

再来,我们解释为什么不是两次。其实也比较好理解,我们知道 TCP 是全双工通信的,同时也是可靠的,连接和关闭都是两边都要执行才算真正的完成,同时还需要确保两端都已经执行了连接或者关闭。如果只有两次,过程是这样的:

发送方:我要连你了。
接收方:好的。

很明显,接收方并不知道也不能保证发送方一定接收到 “好的” 这条信息,一旦接收方真的没有收到这条信息,就会出现接收收“单方面连接”的情况,这个时候发送方就会一直重试发送连接请求,直到真正收到 “好的” 这条信息之后才算连接完成。而对于三次,如果发送方没有等待到你回复确认,它是不会真正处于连接状态的,它会重试确认请求。

接着我们来看看四次挥手过程:

1、发送方发送关闭请求,标志位为:FIN,同时也会带上自己的序号(此时同样由于不传输数据,所以不表示字节的偏移量)。

2、接收方接到请求后,回复确认:ACK,同时确认序号为请求序号加1。

3、接收方也决定关闭连接,发送关闭通知,标志位为 FIN,同时还会带上第2步中的确认信息,即 ACK,以及确认序号和自己的序号。

4、发送方回复确认信息:ACK,接收方序号加1。

涉及到的问题:为什么需要四次握手,不是三次?

三次的过程是这样的:

发送方:我不再给你发送数据了。
接收方:好的,我也不给你发了。
发送方:好的,拜拜。

这是因为当接收方收到关闭请求后,它能立马响应的就是确认关闭,它这里确认的是接收方的关闭,即发送方不再发数据给接收方了,但他还是可以接收接收方发给他的数据。而接收方是否需要关闭“发送数据给发送方”这条通道,取决于操作系统。操作系统也有可能 sleep 个几秒再关闭,如果合并成三次,就可能造成接收方不能及时收到确认请求,可能造成超时重试等情况。因此需要四次。

四、什么是 TIME_WAIT 状态

首先,我们来看一段代码(说了这么多理论,终于要看点代码了)。这里举一个 python 简单的使用 socket 进行 tcp 通信的示例:

服务端:

socket_server_test.py# -*- coding: utf-8 -*-
"""
@Time    : 2019/6/26 下午4:58
@Author  : Demon
@File    : socket_server_test.py
@Desc    :
"""import socketHOST = '127.0.0.1'  # 标准的回环地址 (localhost)
PORT = 9999        # 监听的端口 (非系统级的端口: 大于 1023)s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 第三个参数,如果为0,也不可复用
# 第三个如果为1,可以复用
# s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:print('Connected by', addr)while True:data = conn.recv(1024)print("data:", data)if data:print("close")s.close()breakconn.sendall(data)

客户端:

# -*- coding: utf-8 -*-
"""
@Time    : 2019/6/26 下午4:55
@Author  : yrr
@File    : socket_client_test.py
@Desc    : 测试 self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
"""import socketHOST = '127.0.0.1'  # 服务器的主机名或者 IP 地址
PORT = 9999        # 服务器使用的端口s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)print('Received', repr(data))

执行效果:

我们会发现,当我们服务端主动关闭时,如果我们再次运行这个程序,会报错误说端口仍然被占用。这就很奇怪了,明明已经关闭了连接,为什么还会占用着端口呢?我们使用 netstat -an|grep 9999 命令查看,发现当前这个连接处于 TIME_WAIT 状态。

我们来说下 TIME_WAIT 状态。即当一方断开连接后,它并没有直接进入 CLOSED 状态,而是转移到 TIME_WAIT 状态,在这个状态,需要等待 2MSL(Maximum Segment Life,报文段最大生存时间)的时间,才能完全关闭。

涉及问题:
1、为什么需要有 TIME_WAIT 状态存在?

简单来说有两点原因如下:

a. 当最后发送方发出确认信息后,仍然不能保证接收方能收到信息,万一没收到,那接收方就会重试,而此时发送方已经真正关闭了,就接受不到请求了。

b. 如果发送方在发出确认信息后就关闭了,在接收方接到确认信息的过程中,发送方是有可能再次发出连接请求的,那这个时候就乱套了。刚连接完,又收到确认关闭的信息。

2、为什么时长是 2MSL 呢?
这个其实也比较好理解,所以我发送确认信息,到达最长时间是 MSL,而你如果没接受到,再重试,时间最长也是 MSL,那我等 2MSL,如果还没收到请求,证明你真的已经正常收到了。

正因为我们有这个 TIME_WAIT 状态,所以通常我们说是客户端先关闭,一般不会让服务器端先关闭。那如何避免出现关闭后端口被占用的情况(即上面代码示例问题怎么解决)呢?很简单,加一行代码即可实现:

# socket.SO_REUSEADDR 表示 close 后端口可复用
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

tcp 协议中发送窗口的大小应该是_TCP 协议中的三次握手与四次挥手相关推荐

  1. 网络基础2-3(TCP协议,三次握手,四次挥手,TIME_WAIT状态的作用,TCP如何保证可靠传输,TCP连接中状态转化,滑动窗口,流量控制,快速重传,拥塞窗口,延迟应答,捎带应答,粘包问题)

    TCP协议 TCP协议概念 TCP全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传输进行一个详细的控制 TCP协议 ...

  2. Python常见面试题:TCP 协议中的三次握手与四次挥手相关概念详解

    今天来聊聊Python常见面试题中面试频率特别高的一个题目:TCP 协议中的三次握手与四次挥手. 涉及到的知识点有: 1.TCP.UDP 协议的区别 2.TCP 头部结构 3.三次握手与四次挥手过程详 ...

  3. tcp协议报文和三次握手与四次挥手

    tcp协议: tcp是面向连接.可靠的进程到进程之间的协议.tcp提供全双工服务:即:数据可在同一时间双向传输. tcp报文段首部格式: 各字段含义: 源端口号:16位字段,为发送端进程对应的端口号 ...

  4. 划重点 传输层协议 tcp三次握手和四次挥手

    文章目录 传输层的协议 1.TCP/IP协议组的传输层协议 2. TCP报文段 3.TCP建立连接的过程 3.2 TCP常用端口号及其功能 4.UDP协议 4.1 UDP报文的首部格式 4.2 UDP ...

  5. TCP三次握手和四次挥手?TCP如何保证可靠性?什么是TCP滑动窗口?

    TCP三次握手和四次挥手? 三次握手 tcp3handshake.gif tcp3handshake2.gif tcp3handshake3.gif tcp3handshake4.gif 四次挥手 t ...

  6. 网络协议:TCP三次握手与四次挥手

    本篇内容包括:TCP/IP 传输协议(TCP/IP 传输协议简介,IP 协议,UDP 协议,TCP 协议介绍),TCP 的三次握手.TCP 的四次挥手 以及 TCP 协议是怎么保证有效传输等内容. 一 ...

  7. TCP协议---三次握手和四次挥手详解 (不看后悔系列)

    目录 TCP协议简介 TCP报头 TCP工作原理 科来解码详解 wireshark解码详解 三次握手和四次挥手 数据包的大致结构 你不知道的三次握手 为什么需要有三次握手? 为啥只有三次握手才能确认双 ...

  8. 【转载】万字详文彻底弄懂TCP协议:从三次握手和四次挥手说起

    今日头条 腾讯技术工程 作者:morganhuang,腾讯 IEG 后台开发工程师 说到 TCP 协议,相信大家都比较熟悉了,对于 TCP 协议总能说个一二三来,但是 TCP 协议又是一个非常复杂的协 ...

  9. 常见TCP/IP、HTTP协议以及三次握手和四次挥手

    面试常见TCP/IP.HTTP协议以及三次握手和四次挥手 IP(Internet Protocol):网际协议 IP协议是用于将多个包交换网络连接起来的,它在源地址和目的地址之间传送一种称之为数据包的 ...

最新文章

  1. MYSQL密码解密函数的使用
  2. c# 泛型有什么作用?
  3. Jenkins全新的UI体验-Blue Ocean
  4. 这几天微软发布的一些好玩的东西(顺祝女性程序员朋友们节日快乐!)
  5. linux内存映射函数mmap
  6. manjaro 搜狗输入法_Manjaro日常使用 之一:日常办公
  7. 用sklearn mysql_sklearn 生成随机数据
  8. 《RHEL6.3 FTP服务器虚拟用户的配置(含图)》——如此简单
  9. HDU1869 六度分离【Dijkstra算法】
  10. overflow与BFC解说
  11. 点云应用于电力行业助力输配电安全距离检测分析以及精细化巡检
  12. 树莓派GPIO引脚详解
  13. 淘宝上传图片到淘宝 API 返回值说明(upload_img-上传图片到淘宝)
  14. 4行代码 超级简单 html/css 实现平移动画
  15. 消费新品周报 | 歌帝梵携手大白兔推出全冰品系列;MK推出新一代智能触屏腕表...
  16. android iphone 记事本,苹果手机上有简单实用的便签记事本app吗?
  17. 戴尔科技 赢在“边缘”
  18. 爬虫进阶-如何进行app爬取
  19. Fzu 2206 函数求解【规律】
  20. 深度学习基础知识(人工智能)

热门文章

  1. Java多线程优化方法及使用方式
  2. 在 Pycharm 中安装 wxPython
  3. python 入门拾遗
  4. fir.im Weekly - 2016 开年技术干货分享
  5. MegaWizard Plug-in Manager产生的目录结构及关键文件
  6. Google 加入反 IE6 联盟:IE6 真的能被消灭吗?
  7. 湖北警方打掉一制贩假酒团伙 涉案金额1.6亿元
  8. IDEA取消默认打开的项目工程
  9. 使用Doxygen + graphviz生成Unity 3d的UGUI类图
  10. 青云的机房组网方案(简单+普通+困难)(虚树+树形DP+容斥)