计算机网络-传输层
文章目录运输层主机间进程通信运输层协议TCPARQ-自动重传请求滑动窗口报文格式拥塞控制TCP建立连接过程三次握手为什么A最后要确认?四次挥手为什么A要等待2MSL时间?运输层主机间进程通信网络层虽然能把数据送到目的主机上,但是具体通信的是主机上的具体进程。如何将数据送到目的主机正确的进程上?操作系统层面会给一个进程标识符,pid-xxxx。但是每个操作系统的进程标识符都不能保证相同。使用端口-协
文章目录
传输层
主机间进程通信
网络层虽然能把数据送到目的主机上,但是具体通信的是主机上的具体进程。如何将数据送到目的主机正确的进程上?
操作系统层面会给一个进程标识符,pid-xxxx。但是每个操作系统的进程标识符都不能保证相同。使用端口-协议端口号-protocol port number .通过使用端口,来找到目的主机上使用该端口的进程。
复用
分用
传输层协议
UDP-User Datagram Protocol-用户数据报协议
TCP-Transmission Control Protocol-传输控制协议,是可靠的。因下面的网络层是不保证可靠传输
TCP
TCP是可靠连接,如何做到可靠?
TCP是面向字节流的
应用层的应用程序要发送的数据,最终都会变为字节流,向运输层传递。运输层将接收到的字节流,加头部封装成TCP报文段,继续向下网络层传递,到了网络层,网络层将TCP报文段,加上IP头部,组成IP数据报,继续向数据链路层传递,如果IP数据报长度超过了数据链路层的MTU-最大传输单元,则这个IP数据报要进行分片传递。在数据链路层加上帧头、帧尾,由网卡组成成Mac帧。在物理层一个一个比特发送出去。

在TCP两端,都会有一个缓存,当应用层进程将数据准备好,就会把数据放到发送缓存,发送方会根据情况自动将数据方出去。同样,接收方,接收到数据之后,会把数据放到接收缓存内,应用层的进程会在合适的时候读取缓存内的数据
应用层和运输层之间通过API接口传递数据
-
连接可靠,即主机间进程通信开始必须是双方都确认的。两边的端点就叫做套接字-socket也就是IP:Port,构成了套接字。
Socket = IP:Port
例如:46.243.35.6:8999 代表一个套接字
而一条TCP连接是由两个套接字组成的
TCP连接 = {socket1, socket2} = {(IP1:Port1), (IP2:Port2)}
-
数据可靠,接收方对到达的数据进行检测,确保数据时正确的,无差错的
ARQ-自动重传请求
Automatic Repeat reQuest
发送端点socketA,向端点socketB发送数据
- socketA发送完毕一个数据之后,会把当前发送的数据暂时保留,并且会启动一个定时器。
- socketB接收到数据,回复socketA,确认数据已经收到
- 如果socketA在定时器结束之后,还未收到socketB的确认,则会重新发送刚刚保留的那个数据,也就是重传。
- socketB刚才已经发送了响应给socketA,可能因为网络原因,或者数据在网络中丢失,没能在socketA的超时时间发送过去,这时A又重传,导致B收到了和刚才一样的数据。则B丢弃这个重复的数据,同时向A发送确认。告诉A刚才已经收到了编号为1的数据
- socketA收到了B的确认消息,得知编号为1的数据已经收到了,则按照编号继续发送剩下的数据
- 如果第4步是因为网络原因导致B的确认回复延迟到达了,此时A已经重传。A直接丢弃。等待B的重传确认分组。
滑动窗口
将字节按照一组一组的方式发送,发送方根据接收方返回的确认序号+窗口值,就可以构造出滑动窗口。
确认序号是21,滑动窗口是50,代表,前20个自己已经收到了,则可以发送字节序号在21-70之间的字节。滑动窗口的大小是动态变化的
报文格式
TCP报文格式 = 头部 + TCP报文段
TCP头部(20字节) = 源端口(2字节) + 目的端口(2字节) + 序号(4字节) + 确认号(4字节) + 确认ACK(1字节) + 推送PSH(1字节) + 复位 RST(1字节) + 同步SYN(1字节) + 终止FIN(1字节) + 窗口(2字节)
还有几个未列出
-
ACK-ACKnowledgement
只有ACK = 1时,才认为这是一个确认,TCP两端在传送数据时,每次接受都必须把ACK设置为1
-
PSH-push
当发送方进程希望立刻得到接收方进程响应时,应用层的数据进入缓存之后,会立即发送,不会等待缓存满了才发,发送方同时将PSH置为1,接收方接到数据之后,发现PSH是1,立刻交给应用进程。而不是等缓存满了,才交付。
-
RST-ReSet
当RST=1时,表明TCP连接出现问题,需要释放连接,重新建立连接。或者受到了一个非法报文,也会置为1
-
SYN-SYNChronization
置为1代表请求建立连接的标志。最开始建立连接时,发送方ACK =0,SYN = 1。接收方接收到之后,同意建立连接,则恢复 SYN =1,ACK=1
-
FIN-FINis
置为1代表请求释放连接的标志
-
序号-Sequence number
TCP对每个字节按顺序编号。序号范围[0, 2的32次方-1],如果达到了最大,则下一个序号,从0继续开始.例如:seq = 12
-
确认号-Acknowledgement number
接收端期望发送到下一个报文段的第一个数据字节的序号。如B接受到A的一段报文,序号为100,数据长度为200,则B会回复给A的确认号为301。即ack = 301
确认号计算公式为如果发送方序号为N,那么正常接收方返回的确认号应该为 N+1
或者
如果接收方序号为M, 那么代表到M-1为止的所有数据都已经正确收到
-
窗口
和确认号ack一起返回,都属于接收端返回。窗口的值代表,接收端还可以接收多少数据。如确认号ack-301,窗口值-400,表示字节从序号301开始,我的缓存空间还可以接受400字节的数据。你慢点发,或者等一会再发
-
数据偏移-首部长度
表示报文段的首部有多长,固定长度为20个字节,如果选项中还有其他值,则数据偏移值是要大于20的。 数据偏移单位是4字节字长,因此,数据偏移的最大值为
1111 转为10进制 2 3 + 2 2 + 2 + 1 = 15 2^3 + 2^2 + 2 +1 = 15 23+22+2+1=15, 再乘以4 ,60字节
减去固定首部长度20,那么就表明TCP报文段的选项长度不能超过40
拥塞控制
了解即可
TCP建立连接过程
建立连接、数据传输、释放连接
三次握手
A发送SYN请求,请求建立连接,SYN =1,初始序号seq = x ,B同意接受连接,回复A,SYN =1 ACK =1(代表接收到了),ack = x+1,确认序号;seq = y.A接收到B的响应之后,回复B,ACK =1,seq = x +1 ,ack = y+1.此时连接建立完毕

完整分析可以看下面的利用Wireshark分析TCP三次握手
为什么A最后要确认?
如果A一开始发送了一个请求,因为网络原因,长时间没有达到B。A又发起了一次建立连接请求,和B建立正常连接,通信完毕之后。此时一开始的请求到达B,如果没有最后一个握手,则B确认,连接直接建立。B会一直在等待A发数据,可是这时A已经结束了通信,B端依旧认为,此连接有效,一直等待A发来数据
如果有了最后一个握手,B会发送确认给A,等待A回复。这时A是不会回复的。由于收不到最后一次的确认,则认为未成功建立连接
理解防火墙inbound和outbound规则与TCP握手的关系
现在有一个要求那就是要让主机A能够访问主机B,主机B不能访问主机A,那么防火墙能够做到么?答案是可以的,当主机A的防火墙入站规则限制了主机B,那么主机B想访问主机A的时候,发出的网络包确实会被A的防火墙拦截,那么网络包无法到达A,自然B无法访问A。但是在A访问B的时候,有不少同学有这样的疑惑:此时防火墙不会拦截A发送B网络包,但是在B收到A发来的包以后,向A发送针对之前的网络包的回复包,那么在到达A的时候,岂不是也被主机A的防火墙拦截了?那么主机A迟迟收不到主机B的网络包,岂不是就连接失败,无法访问B了?
事实上,这里面的原理需要解释一下:tcp连接有三次握手,请求连接,同意连接,建立连接。那么入站出站规则则分别均对应请求连接。假设有A和B均开启了80端口,B想访问A的80端口,那么假设A防火墙上面设置了入站规则是禁止B访问,B对A发出的请求连接的网络包确实会被A的防火墙拦截,那么网络包无法到达A,自然B无法访问A,但是在A想访问B的时候,A对B的请求连接的网络包不会被A的防火墙拦截,B收到请求连接的网络包之后对A发出的回复同意连接的网络包实际上是不会被A上的防火墙拦截的,因为A上防火墙拦截的只是请求连接的数据包,不会拦截之后的回复包。同理假如设置了出站规则,那么防火墙拦截的也是请求连接的数据包,假如此时在A的入站规则里面删去B,在出站连接里面添加B,那么就会导致主机B能够访问主机A,主机A不能访问主机B的情况,因为A向B建立连接的请求包被拦截了
上述答案来自于知乎
四次挥手
客户端A发送FIN请求释放连接,FYN =1 ,seq = u.服务端B收到A的请求,回复确认。ACK =1,ack = u+1,seq = v。此时数据从B到A可能还未发完,继续发送,最后发送完毕,B也请求释放连接,FIN = 1, seq = 2, ack = u+1,A收到最后的释放连接,回复确认ACK =1,seq = u+1,ack = w+1.B收到后就进入关闭状态。此时连接还未释放,等到等待计时器时间(2MSL,一般为2分钟,2x2=4分钟)过了之后A进入关闭状态。

这里要注意:
- CLOSE-WAIT是被动接受关闭连接的那一方
- 如果被动接受关闭连接的那一方,上层应用代码一直没有调用close方法,那么就会导致TCP连接处于半关闭状态。反应在系统或异常层次为,CLOSE-WAIT很多,此时应该排查代码,是不是哪里忘记关闭连接了
完整分析见下方四次挥手分析
为什么A要等待2MSL时间?
MSL -Maximum Segment Lifetime-最长报文寿命。防止A的最后回复报文B收不到,此时B会进行重传,这是A就会在这个时间段内收到。这是重新启动计时器(4分钟)。如果B重传的时候A为收到进行回复,提前关闭了,B就会关闭不了。
服务器出现大量TIME-WAIT是什么原因?
从4次挥手过程来看,当服务器是主动关闭连接的一方时,当最后1个ACK包发出去后,服务器会进入TIME-WAIT状态。这是一个必经过程
如果出现大量TIME-WAIT,说明服务器端有大量即将关闭的连接,可能是短时间内出现了大量的客户端发起了短连接随即断开
服务器出现大量CLOSE-WAIT是什么原因?
从4次挥手过程来看,当服务器是被动关闭连接的一方时,当收到客户端的FIN包时,回复ACK后,则进入CLOSE-WAIT状态。只有上层应用关闭连接后,底层OS才能真正释放资源,进入到第3步。
如果出现大量CLOSE-WAIT,说明服务器端迟迟没有关闭连接,要么是数据太多;要么是代码存在bug,没有及时关闭连接
使用Wireshark抓取网络包,分析TCP
下载、安装
安装之后,切换语言为中文
设置捕获过滤器
菜单栏---->捕获---->找到自己的网卡---->设置捕获过滤器---->开始捕获
开始捕获
捕获之前,建议把浏览器的缓存清空,这样可以看到完整的请求过程
在浏览器输入www.baidu.com,会自动打开百度首页,这时候,查看wireshark捕获到的信息
可以看到,在真正地发起HTTP请求之前,有三次TCP连接。可以推测,这就是真正传输数据之前的TCP三次握手。接下来验证
分析三次握手
-
先看整个报文段的结构
-
Source Port:56448 - 源端口
-
Destination Port:80 -目标端口
-
Sequence number:0 -序号
-
Acknowledgment number:0 - 确认号
-
Head Length: 1011- TCP报文首部长度(数据偏移) 转为10进制是11,11 x 4 = 44字节
-
剩下的标记位 各占一位, 此时Syn被置为1
注意Head Length和其他标记位一共占用2个字节 完整的格式为1011 0000 0000 0010 ,可以从上面截图中看出来
-
Window size value:65535-窗口
-
Checksum:检验和
-
Urgent pointer:紧急指针
-
Options:24字节 -选项 44 - 20 = 24,还可以看到MSS最大长度为1460字节
-
Timestamps: 时间戳
可以看到,这和上面的TCP报文段结构一致
如何看到,每一个字段究竟是不是占了上面说的字节数?

停在每个字段处,会自动显示,占的字节数
-
-
第一个报文段中可以看到 序号是0,确认号是0 ,标记位中的Syn是1,这和上面说的三次握手的第一步一致,即客户端发送TCP报文段,将Syn置为1,代表请求和服务端建立TCP连接
-
按照上面所述,那么第二个TCP报文段,则应该是由服务端响应给客户端的,那么服务端的标记位会将Ack置为1,代表服务端收到了刚才的报文;Syn置为1,代表同意建立连接;并且服务端的序号为0,确认号为1。为什么确认号为1呢?上面的TCP报文格式那里说过了,1 -1 = 0,代表,到0号为止,所有数据都已经收到了。查看第二个TCP报文段,

源端口和目标端口已经更换了为止,确实是服务器的80端口返回的,标记位也是正确的,确认序号也是正确的。
-
那么最后一次握手。则发送的信息应该是,序号为1,确认号为1,标记位ACK为1

到此为止,TCP三次握手建立完毕。接下来,才会真正发起HTTP请求
-
客户端发起HTTP请求,序号为1,确认号为1,ACK为1

可以看到,这次发送的数据字节长度为403。如果服务端接收正常的话,那么服务端的确认序号应该为404,ACK为1,序号为1.

-
也可以在任意一个HTTP请求中看到,我们说的5层结构

从下到上分别为,HTTP - TCP - IP - Ethernet - Frame
分析四次挥手
随便找一条TCP连接,这里这端口为56450的TCP连接
一开始同百度服务器的443端口建立TCP连接。可以看到,建立连接之后,使用TLS-Transport Layer Security-传输层安全 协议,对TCP传输信息进行加密
找到释放连接的位置
可以看到,
- 先是由443端口主动断开连接,443最后发送的序号是593946,那么,此时,要决定断开连接,向56450发送TCP数据报,告知,我这边准备断开连接了,这一条TCP数据报的序号为最后发送信息序号+1,即593947,标志位FIN为1。
- 此时本机46450端口收到,确认收到信息。回复给443端口,序号为4625,ACK 是1,确认号为593948,即到593727为止的编号字节,全部收到。
- 收到ACK信息之后,此时443端口处于终止等待状态
- 过了一段时间,注意看,是过了一小段时间,本机向443端口发送释放TCP连接请求,FIN 是1,ACK是1,确认号依旧为593948,序号为4625
- 443收到释放请求之后,进行回复。ACK 为1,序号依旧为593948,确认序号为4626
至此四次挥手完毕,56450端口收到443的ACK确认之后,即关闭。等待2MSL时间后,443进入关闭状态。
56450必须收到最后ACK的确认才关闭。所以,443要等待一段时间,一旦在收到FIN的TCP报文之后,发送完ACK,就关闭。那么网络一旦出现问题,56450客户端,则收不到ACK确认,那么56450则会进行超时重传,而此时的443端口的TCP连接已经关闭,无法对56450进行回复,那么客户端就无法关闭
如何快速找到握手开始的地方和挥手开始的地方?

根据上述博文,直接看TCP头部的控制位即可
- 发起连接的第1个包是SYN标记
- 关闭连接的第1个包是FIN标记,来自主动关闭连接的一方;第3个包也是FIN,来自于被动关闭连接的一方
更多推荐
所有评论(0)