TCP三次握手四次挥手都做了什么?

三次握手做了什么

三次握手和四次挥手都位于传输层

1 建立连接

  1. 调用socket,创建fd

  2. 调用connect,向服务器发起连接请求

  3. connect会发出SYN段,并阻塞等待服务器应答(第一次)

  4. 服务器收到客户端的SYN,会应答一个确认收到即SYN+ACK段来表示 “同意建立连接”(第二次)

  5. 客户端收到服务器的SYN+ACK后,会从connect()返回,同时向服务器应答一个ACK,确认收到(第三次)

TCP客户端和服务器建立连接的过程,称为三次握手

2 数据传输过程

1.,TCP协议提供全双工的通信服务;全双工(同一时刻,同一连接中通信双方可同时写数据)

  1. 建立连接后,服务器从accept()返回返回后立刻调用read(),读socket就像管道一样,如果数据未到达就阻塞等待到达就从read()返回

    扫描二维码关注公众号,回复: 10013807 查看本文章
  2. 服务器阻塞等待时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理。服务器在处理客户端请求期间,客户端也调用read阻塞等待服务器的应答.

服务器调用write()将处理的结果发送给客户端后,继续调用read()阻塞式等待客户端的下一条请求。

  1. 客户端从read()返回,继续发送下一条数据,这样循环下去,否则准备断开连接(四次挥手)。

四次挥手又做了什么

3 断开连接

  1. 如果客户端没有更多的请求,就调用close()关闭连接,此时客户端会向服务器发送FIN(第一次)

  2. 此是服务器收到FIN后,会回应一个ACK,同时read()返回0 (第二次)

  3. read()返回之后,服务器就知道客户端关闭了连接,也调用close()关闭连接,这个时候服务器会向客户端发送一个FIN(第三次)

  4. 客户端收到FIN,最后再返回一个ACK给服务器(第四次)

断开连接的过程称为四次挥手

要清楚:read()是读请求的,write()是用来读响应的。

connect函数和TCP交互是通过发出SYN段
read()返回,说明收到了FIN 段

connect成功之后,
服务器
accept返回,并分配新的文件描述符和客户端通信。
read(fd_,buf,size)阻塞等待客户端数据请求
write(fd,buf,size)发送数据应答。

应用层

应用层的作用: 据说程序员写的网络程序基本都在应用层来进行编写,可见应用层重要性

再谈协议: 协议是一种约定,通信双方事先制定好的

HTTP协议

虽然我们说应用层的协议都是我们程序员自己定制的,实际上,大佬已经给我制定好了,我们拿来直接用就好了。
http协议-------超文本传输协议 。就是其中之一。

认识URL
平时我们所说的网址其实就是URL;
https://editor.csdn.net/md?articleId=104169840

协议名:https
articleId : 查询字符串
104169840:片段标识符
csdn.net:服务器地址
/md?:带层次的文件路径

在这里插入图片描述

’ + ‘ 被转译成了%2B

urldecode : 反转译
urlencode : 转译

来看一个fiddler抓包情况
在这里插入图片描述

看右侧的Raw

** 1 http请求(上面的):**

在这里插入图片描述

  1. 首行:方法 + url + 版本

  2. Header: 请求的属性,冒号分隔的键值对;每组属性之间使用\n来分隔 ,即换行分割
    遇到空行说明Header部分结束

3.Body空行后面的内容都是Body,Body允许为空字符串,如果Body存在,则在Header中会有一个Content-length属性来标识Body的长度;

2 http 响应(下边的)
在这里插入图片描述

  1. 首行:版本号 + 状态码 + 状态解释

  2. Header:请求的属性,冒号分隔的键值对,每组属性之间\n 分隔,行分割,遇到空行表示Header结束

  3. Body :空行后边的全是Body,Body存在,则在Header后有一个Content-Length属性标识Body的长度,如果服务器返回一个html页面,那么页面内容就在Body中;

HTTP的方法

方法 说明 支持的HTTP协议版本
GET 获取资源 1.0、1.1
POST 传输实体主体 1.0、1.1
PUT 传输文件 1.0、1.1
HEAD 获得报文首部 1.0、1.1
DELETE 删除文件 1.0、1.1
OPTIONS 询问支持的方法 1.1
TRACE 追踪路径 1.1
CONNECT 要求用隧道协议连接代理 1.1
LINK 建立和资源之间的联系 1.0
UNLINK 断开连接关系 1.0

其中最常见的就是 GET 和POST

HTTP状态码

状态码 类别 原因
1XX 信息性 请求正在处理
2XX 成功 正常处理完毕
3XX 重定向 需进行附加操作来完成
4XX 客户端错误码 服务器无法处理
5XX 服务器错误码 服务器处理请求失败

最常见的状态码:
200(OK)
404(Not Found)客户端请求有问题
403(Forbidden) 禁止访问
302 (Redirect)重定向
504 (Bad GateWay) 网关超时

HTTP最常见的 Header

Content-Type 数据类型(text/html等)

Content-Length Body长度

Host:客户端高速服务器,请求的资源在那个端口上;

User-Agent:声明用户的操作系统和浏览器的版本信息

Referer:声明当前页面从哪个页面过来的

location:搭配3XX状态码使用,告诉客户端接下来去哪里访问

Cookie:用于在客户端存储少量的信息,通常用于实现会话功能。它保存在客户端浏览器,是一个字符串,字符串协议程序员可以自己约定

IP协议

1 地址管理
ipv4 32位
地址不够怎么办;  
		1  动态分配            主机联网才分配,断网就回收
		 2 :NAT 机制		  局域网内共用一个IP
		 3	ipv6
	
2 路由选择

  IP地址为 点分十进制 192.169.1.106
  1 同一个网段内的主机网络好一定相同,主机号一定不同。(出现特例一定不能正常上网)
  2 相邻网段(连接在一个路由器上的)的主机,网络号一定不相同。
主机地址全0  就成为 了 网络号,代表此局域网
主机地址全1  就成了广播地址。(UDP能广播,TCP不能广播)

私网IP 和 贡丸IP
私网IP 局域网内部使用
1: 10.*前8位是网络号	
2:172.16.* --- 172.31.*  前12位是网络号
3:192.168.* 
其余 均为公网ip


路由选择(类似于导航)
路由选择的过程就是进行路由间相互问路的过程。

为了防止一台路由器挂掉导致整个体系瘫痪,我们常做多份备份路由,叫做冗余。	




route指令查看路由表

数据包 来到路由器后 ,跟子网掩码Genmask 相与,相与结果再在Destination中找目的IP,找到了一样的就发送,没有就找下一跳(default) ,

数据链路层:

以太网,  以太网 不是一种具体的网络,而是一种技术标准。称为协议


以太网为什么存在MTU?
  因为硬件的缘故,必须要求数据帧在1500字节范围内	

MTU在数据链路层中 ,最大的数据帧为1500字节,不同协议中,MTU最大值也不同	



加入要发送的数据帧大于MTU,就会将数据帧分解,前几部分都是1500字节,后边余多少就是多少了。


**

DNS域名解析协议

**

**DNS是一套从域名映射到IP的系统**

IP地址 + 端口号 来进行唯一确定一台主机上的一个进程
因为IP地址不好记,人们就发明了主机名(string),
并用hosts文件来管理维护主机名和IP的地址关系。

假如DNS服务器挂掉了,全世界的人民都上不了网了吗?
   大佬都想好方案:采用分布式的DNS服务器,在全球建立多个DNS系统(类似于路由冗余)	

DNS 地址;
8.8.8.8   谷歌维护的全球的根域名解析服务器  ---
114.114.114.114


在局域网中 ,路由器自己也维护了一个映射关系
内网IP和端口映射一个路由器IP和端口号   所以外网感知不到局域网内网(内部主机)
因为端口号的数量是有限的,所以  NAT只能维护端口号范围内的主机数量的网络


在浏览器中输入一个url都发生了什么?我们可以从以下几个角度进行分析:

1  从操作系统管理硬盘设备角度:

2  从网络通信的角度看: 
	1  进行DNS域名解析
	2  HTTP角度
	3  从自定制协议的角度(URL中关键字的query string怎么设计,body都包含了什么内容等等,cookie)

3 从传输层的角度
  	1  TCP连接建立的角度
	2  长短连接的角度(一个TCP连接涵盖好几个HTTP的交互过程  称为长连接)  好处:开销小效率高

4 从网络层和数据链路层的角度
	1 从IP地址的相关规则
	2 路由选择的角度
	3 数据链路层的相关规则

5 通信原理的角度(谨慎哦,不清楚别提)  

	浏览器 (外部也可连接一个CDN )   
    	搜索入口服务器
		(1 分词服务器 
		 2 检索服务器 
		 3 物料服务器(查询最终数据)  
		 4 用户服务器(用户的一些信息) 
		 5 广告服务器(类似于搜索入口服务器))	
	
		分布式服务器使用大量的反向代理服务器 :就像各种运营商提供的服务器一样

		负载均衡:

		1  提高效率
		2 提高可靠性
		3 可伸缩性(通过增加或减少服务器的数量来让负载均衡)

在TCP/IP中 ,用源IP,源端口号 ,目的IP,目的端口号,协议号这个五元组,来表示一个通道,可以通过 netstat - n命令查看;

端口号 分为知名端口号操作系统动态分配的端口号

知名端口号 0 - 1023
操作系统动态分配端口号 ;1024 - 65535

ssh服务器: 22端口
http服务器: 80端口
ftp服务器: 21端口
https服务器: 443端口
telnet服务器: 23端口

cat /etc/services 命令查看所有知名端口号

UDP协议

UDP协议端格式

|-16位源端口号 -|16位目的端口号 |
|-16位UDP长度-|-16位UDP检验和-|
| ——————数据(可有可无)-|

16位 UDP长度代表数据部分+UDP首部,表示整个数据报的最大长度
它与16位检验和若不相等,则丢弃。

UDP的特点:

  • 无连接
    知道对方的IP和 port就直接发送,不用建立连接,代码中不会connect。

  • 不可靠
    1没有确认机制,
    2没有重传机制。
    如发生网络故障,UDP协议层不会给应用层返回故障信息。

  • 面向数据报
    不能灵活的控制读取数据的次数和数量(长度)。
    面向数据报也意味着应用层发送给UDP多长的报文,UDP原样发送,不会拆分,也不会合并。
    eg:如果发送端一次sendto()100字节数据,那么接受端也只能一次recvfrom()100字节,而不能分多次接收。

  1. UDP不存在真正意义的发送缓冲区 ,发送数据时,调用sendto, 将要发送的数据交给内核,由内核将数据传给网络层协议,并进行后续操作。
  2. UDP具有接收缓冲区,,但这个接收缓冲区不能保证收到的数据顺序和发送时的顺序一致,而且若缓冲区满了,后续数据会被丢弃。

另外,UDP的socket 既能读,也能写,即全双工

因为 UDP协议首部中说 UDP16位长,那么最大数据长度+首部的长度为2^16 ,即 64K,
那么如果我们要发送的数据大于 64K ,我们就得在应用层进行手动分包,多次发送,同样还得在接收端进行手动拼装。

TCP协议

TCP全称传输控制协议。

TCP6个标志位

  1. URG:紧急指针是否有效
  2. ACK:确认好是否有效
  3. PSH:提示接收端应用程序立刻将缓冲区数据读走。
  4. SYN:请求建立连接。我们把携带SYN标识的称为同步报文段
  5. RST:对方要求重新建立连接。复位报文段
  6. FIN:请求断开连接。结束报文段。。

16位校验和:由发送端填充,在接受端进行CRC(循环冗余检验),检验不通过,则认为接收数据有问题。

16位紧急指针:标识哪部分数据是紧急数据。

确认应答机制ACK

eg:
客户端发送数据1-1000
服务器确认应答下一个1001,

TCP 将每个字节的数据都进行的编号,即序列号

每一个ACK都带有对应的确认序列号,意思是告诉发送者,我们已经收到了哪些数据,下一次你从哪里开始发。

超时从传机制

主机A发送数据给主机B ,但可能因为网络问题等,主机B迟迟未收到该数据,
此时,主机A在特定的时间段内,未收到主机B的确认应答,就会进行重发。。

但!! 主机A未收到主机B的确认应答,也可能是主机B的确认应答ACK丢失了。

这时,主机B就会收到重复的数据,,所以TCP协议需要能够识别哪些是重复的数据,需要将重复的数据丢弃,这时,我们就可以利用序列号了,利用序列号可以很容易的达到去重的目的。

那么,超时重传的世间如何设定?

	不同的网络环境,超时重传的时间会存在差异
	**TCP**为了保证在任何网络环境下都能最大效率的
	保证重传,**会动态计算**该网络情况下的**超时重传时间**。

Linux下以500ms作为超时重传的单位。,
重发,仍得不到应答,时间将指数形式增加,(每次✖️2)

当累积重传到一定次数时,,TCP会认为主机连接存在问题(网络,或对端主机连接问题),并强制关闭。

连接管理机制

在正常情况下,TCP要进行三次握手建立连接,四次挥手断开连接。
在这里插入图片描述

注意:并不存在所谓的closed状态

服务器的状态变化:

closed–listen:服务器调用listen后进入listen状态,等待客户端连接

listen—SYN_RCVD:一旦监听到连接请求SYN,就将该连接放入内核等待队列中,并向客户端发送SYN+ACK确认报文段

SYN_RCVD–ESTABLISHED :服务器一旦收到客户端的确认报文,就进入该状态,连接成功就可以读写数据了。

ESTABLISHED–CLOSE_WAIT:客户端想向务器发送close请求。服务器收到FIN结束报文段,返回ACK确认报文段并进入CLOSE_WAIT;

CLOSE_WAIT—LAST_ACK: 进入CLOSED_WAIT后,服务器(处理完当前数据后)准备关闭连接, ,向客户端发送FIN,此时等待最后一个ACK到来

LAST_ACK— CLOSED:最后一个ACK到达,彻底关闭连接。

客户端状态变化:

CLOSED–SYN_SENT:客户端调connect,发送同步报文;

SYN_SENT–ESTABLiSHED: 服务器返回FIN+ACK;connect 成功。可以读写数据了

ESTABLISHED—FIN_WAIT1: 客户端主动调用close函数,向服务器发送FIN;

FIN_WAIT1 —FIN_WAIT2:客户端收到服务器发送回来的ACK,确认报文段。

FIN_WAIT2—TIME_WAIT:客户端收到服务器发送的FIN结束报文段,收到,回复LAST_ACK 。即最后一次ACK

TIME_WAIT–CLOSED:客户端发送完LAST_ACK后等待2MSL;(防止最后一个ACK未到达,超时重传)

为什么时间规定为2MSL?

MSL是TCP报文的最大生存时间,2MS了能够保证两个传输方向上尚未被接收/迟到的报文段都已经消失,(防止服务器立即重启,收到了上一个进程的的数据)。
同时2MSL也可以保证最后一个ACK可以到达,假如最后一个AC看丢失,因为这是tcp连接还没断开,服务器会再发送一个FIN,这时客户端进程虽然不在了,但tcp连接还在,仍然可以重发LAST——ACK ;

使用setsockopt() 函数可以设置socket,SO_REUSEADDR 为1 ,表示可以穿件端口号相同,但IP地址不同的socket

int opt =1 ;
setsockopt(listenfd,SOL_SOCKET,SO_)

**在四次挥手中怎么理解客户端收到服务器的FIN时的

TIME——WAIT状态**

做测试时,我们同时运行服务器和客户端,关闭服务器,然后又迅速打开,就可以发现
bind error;

因为,虽然server程序终止了,但是底层TCP协议层的连接还未彻底断开,因此端口号,IP地址此时还在占用,所以不能再次监听同样的server端口。

我们可以用netstat -apn命令查看一下当前端口号进程信息

TCP协议规定:主动断开连接的一方要处于TIME_WAIT状态,等待2*MSL时间后回到CLOSED状态

MSL在RFC1122中规定为2minutes,,各个操作系统实现不同,centos7默认配置60秒

可以通过
cat /proc/sys/net/ipv4/tcp_fin_timeout 查看
在这里插入图片描述
使用 setsockopt()函数可以设置产生port相同,但IP不同的 端口号

选项SO_REUSEADDR 为1,表示允许port相同,但IP不同的socket;

eg:
int opt =1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

3:滑动窗口

实质 :一大段要发送的数据,收到ACK,滑动窗口就向后移动。

内核同时开辟了一段发送缓冲区来维护滑动窗 口,发送缓冲区记录未应答的数据,若数据被应答,就从缓冲区删掉。

作用 :提高吞吐量,提高传输效率
管理 :由内核维护

    1 批量发送数据
		
		2 **批量发送的数据量称为窗口大小 ,窗口越大,吞吐率越高,但一定要保证可靠性哦,所以不能无限大**。
		
		3 内核开辟一个发送缓冲区,保证没有收到ACK的数据别被删掉,随时可以进行超时重传,
		
		4 后边的ACK能够保证前边的数据已经正确到达,所以前边的ACK丢了有时也没关系,这也意味着,好多条数据才可能收到一条ACK ,所以这样只会发保留发送失败数据的ACK,所以这称为**快速重传**。

正确数据放在内核的接收缓冲区中,传送失败的数据就会不断收到相应字节序的ACK,这种机制被称为高速重发机制-----或者 快重传机制。

	最后,接收端怎么将滑动窗口大小告知发送端呢?

	在TCP报文首部,有一个16位的窗口字段,存放了窗口大小的信息。那么问题又来了,16位表示最大数据2^16次方,那么滑动窗口最大只有64K吗?
	
	并不是,TCP首部40字节选项中还包括一个窗口扩大因子M,实际窗口的大小为窗口字段左移M位;

5:流量控制机制:

		设置原因:
		接收端处理数据的能力是有限的,假如发送端发送数据太快,导致接收端不能及时处理,就会使接收端缓冲区逐渐变满,再传输数据就会发生丢包。
		
		是什么:
		因此TCP协议支持根据接收端处理数据的能力,决定发送端的发送速度,这个机制称为流量控制机制

		如果接收缓冲区满了,就会使滑动窗口大小为0,这是发送方就不会发送数据了,然后发送端定期发送一个窗口探测数据端,获取接收端大的窗口大小

	接收缓冲区的空余空间大小用来衡量接收端的处理性能。(细想,空间越大,不就是接收端处理的越快吗?)
	TCP协议中有一个窗口大小字段,通过ACK端通知发送端

6:拥塞控制:

引入原因

	虽然TCP有滑动窗口这个宝贝,能高效提高传输效率,但是,网络上有很多计算机,当前的网络状态可能已经很糟糕了,这时若还是一下发送大量数据,对本就糟糕的网络来说无意雪上加霜了。

因此,TCP引入慢启动机制,先发一丢丢数据探探路,摸摸当前的网络状态,再决定按多大速率发送数据。

在这里插入图片描述

像不像恋爱中小情侣的感觉呢?
热恋,吵架,和好,热恋……

1 怎么来判断是否拥塞呢?

     根据丢包的量来判定
     **少量丢包认为超时重传,大量丢包认为网络拥塞**

2  慢开始:

	1 开始时设置一个比较小的窗口来发送数据。
	2 若没发生丢包,窗口大小指数增长,
	3 当到达一个指定的阈值时,变为线性增长
	4 这样到达一定大小时,就会发生丢包,这时立马将窗口大小设为一个特别小的值(1),阈值乘法减小(*0.5)
	  然后再重新慢开始循环

	
			

那么问题来了?窗口大小怎么控制呢?

答:拥塞控制 + 流量控制 
TCP首部的 16位窗口大小 说明滑动窗口的基础大小是65535 同时 ,首部还包含一个M因子,他决定滑动窗口最大值可以 向右移动几位,2进制哦。

拥塞控制归根到底还是 TCP协议为了尽可能快的把数据传输过去,又要尽可能保证网络压力不要太大的折中方案

当TCP通信开始时,吞吐量会逐渐上升,一旦网络发生拥塞,吞吐量 立刻急剧下降。

7:延时应答

	为什么要延时应答?
	答:因为处理器处理数据的能力可能很大,若不延时应答,一次接收的数据可能还不够服务器处理时开胃菜,所以延时应答
	
	延时应答控制方法:	
		1:数量条件:每收几个包就应答一次
		2:时间条件,每过多久应答一次

	具体的数量和超时时间,根据操作系统而定,一般数量取2,超时时间为200ms。

8:捎带应答

eg:
How are you
i’m fine thanks and you

捎带应答:可能将四次挥手变为三次挥手。

	在延迟应答的基础上,服务器有可能将在收到FIN断开连接时,将  ACK 和 FIN一起发送过去,这时四次挥手变成三次挥手

	TCP协议是操作系统内核实现的,不同的系统实现细节可能存在细微差异,
	这时fiddler抓包就不靠谱了,fiddler只能抓HTTP协议    
	wireshark可以	抓TCP协议的数据包

9:面向字节流

TCP传输时可以将一个完整的数据包分多次发送或接收。

10:粘包问题

   由面向字节流传输方式导致,但应用程序需要一次取出一个完整的数据包,怎么来解决这个问题?
   
	       我们从应用层的角度来解决这个问题,只需要能够确定数据包的边界就可以了。
	       
		确定包的边界的方案: 1	分隔符    2 指定长度

	UDP会发生粘包问题吗?
	因为UDP是一个数据包一个数据包的提交数据,因此不可能发生提交半了包的情况,也就不可能出现粘包问题

连接异常:

	1 进程终止(正常----先关闭客户端)
	2 重启(相当于开始菜单的重启:即既定流程重启)
	3 机器断电/网线断掉:接收端认为连接还存在,很久了还么收到数据,就会超时重传,  重传次数过多服务器复位RST (reset)

 4内核自己也内置了一个保活定时器。保活定时器会定期发送一个询问信号,询问对方(客户端)是否存在。不存在就释放连接

TCP 和 UDP 的对比

	1TCP使用在可靠传输的场合
	
	2UDP传输速度高,也可以使用在传输环境安全性本身就高的环境,达到可靠传输的目的。
	
	3数据包大的话只能使用TCP, 因为 UDP最大包只用64K即16位UDP最大长度2^16
	
	4**如果要广播的话,只能用UDP**,不能用TCP。只有UDP可以将物理IP最后的几位全设置为1,代表广播地址。

	5TCP 面向字节流有粘包问题 UDP面向数据报,不存在粘包问题

6  UDP不存在真实的的发送缓冲区,只有内核创建接收缓冲区,而TCP都有。

Listen的第二个参数理解

我们可以去掉服务器程序里面的accept。同时打开多个客户端连接服务器,再查看状态netstat,就会发现一些现象。

Linux内核协议栈为tcp连接管理使用了两个队列:
半连接队列:保存处于半连接状态SYN-SENT和SYN-RCVD状态的请求

全连接对立:连接成功的处于established的请求,即accept队列,这个队列受accept影响。全连接对=队列的长度为 listen第二个参数的+1

发布了90 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44030580/article/details/104169840