【计算机网络】深入解析TCP协议

🔥 博客主页: 我要成为C++领域大神
🎥系列专栏【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于分享知识,欢迎大家共同学习和交流。

TCP协议头

源端口、目的端口

分别占16位,存放发送端的端口号和接收端的端口号。

序号

简称Seq,占4个字节32位,记录数据发送顺序。因为数据在网络中并不是先发先到达的,所以需要有序号来确保数据能够按照发出的的顺序进行排列。与确认号一起使用。数据是按照字节排列的,序号是这个数据包起始的字节号位置。

确认号

简称ack,占4个字节32位,与seq一起使用来保证发出去的数据有序可靠

首部长度

占4位,用来告知我们数据包的首部长度是多少。

保留

占6位,暂不使用。

标志位

6个标志位,分别代表了TCP包是什么样的包。
URG:Urgent,紧急标志位,若为1,代表紧急指针生效,说明此包中有紧急需要看的数据,紧急指针指向数据地址。

ACK:确认序号标志位,若为1,就代表当前的包是确认包,确认序号生效。

PSH:push标志位,若为1,就代表当前包中的数据需要应用层尽快处理,不需要等待接收缓冲区填满,尽快将数据传递给应用层。

RST:reset标志位,重置连接,若连接的两端出错导致连接断开,需要RST标志位为1来重新连接。

SYN:Synchronize Sequence Numbers,握手信号,建立连接。

FIN:finish,断开连接。

窗口

占16位,滑动窗口。表示接收方可以接收的数据量,用于流量控制。

校验和

占16位,检验TCP头里的数据是否有问题。

紧急指针

占16位,与紧急位配合使用,用来识别紧急数据的。

可变部分选项

TVL类型的,Type:设置什么类型的属性;Value:设置属性的值;Length:长度是可变的。

ACK机制

ACK的全称为Acknowledge character,即确认字符,表示接收到的字符无错误。 

服务端对所收到的报文进行检查,若未发现错误,便向客户端发出确认回答ACK,表明信息已被正确接收,并准备好接收下一份报文。

其格式取决于采取的网络协议。当发送方接收到ACK信号时,就可以发送下一个数据。

data(seq=1...100),代表数据序列号是1~100字节的部分;
ack=101,说明字节号101之前的数据收到了,确认应答;ACK,将ACK标志位置1。
data(seq=101...200),从应答的ack开始,代表的数据序列号是101~200字节的部分;
ack=201,说明字节号201之前的数据收到了,确认应答;ACK,将ACK标志位置1。

服务端发送数据,对端响应。ack和数据可以在同一个包里,需要对端也回复一个确认包。

超时重传

超时重传用来防止数据丢包。有两种情况:发送的数据包丢失了和服务端回复的确认包丢失了。

数据包丢失:客户端会开启一个定时器,当发送数据以后开始计时,若超过了定时器设定的时间客户端仍未收到数据包,就会重新发送一遍刚才的数据。

确认包丢失:客户端正常收到了服务端发送的数据包,但是客户端发送的确认包超过了定时器设定的时间,那么就需要将确认包丢失不处理,发送相同的确认包给服务端。

TCP三次握手

三次握手

TCP传输控制协议是面向连接的,可靠的、基于字节流的传输协议。数据传输是在连接的基础上进行的。三次握手指的是需要发送三次包才能建立连接。
握手信号(SYN)一般是客户端作为主动方的,服务端作为被动方。
0)首先,客户端和服务端都处于CLOSE状态。先是服务端主动监听某个端口,处于LISTEN状态。
1)客户端主动发起连接SYN(即发送握手信号,要将SYN标志位置为1),发送一个数据包包含序列号和SYN,之后客户端处于SYN-SEND状态。
2)服务端收到发起的连接,返回SYN,并且确认客户端的SYN,之后处于SYN-RCVD状态。
3)客户端收到服务端发送的SYN和ACK之后,发送ACK的ACK,之后处于ESTABLISHED状态,因为它一发一收就成功了。
4)服务端收到ACK的ACK之后,处于ESTABLISHED状态,因为它也一发一收了。

三次握手中可以携带有效数据吗?

可以。需要从第三次握手才可以携带有效数据,因为此时已经建立了连接。

为什么是三次握手?不是两次或四次

为什么不是四次:若是服务端发送给客户端的数据包中,SYN和ACK分开发送则需要四次。但是TCP协议格式中的ACK和SYN标志位可以同时为1,互不影响,所以可以同时发送,没有必要进行四次握手。

为什么不是两次:假设客户端在发送完SYN后立刻关闭连接,若是两次握手,服务端会认为已经建立与客户端了连接,而客户端并没有建立连接。会出现状态的错误,后续无法进行数据的发送。

四次挥手

四次挥手分为主动端和被动端。
我们来看一下四次挥手的过程:
1)主动方打算关闭连接,此时会发送一个TCP首部FIN标志位被置为1的报文,也即报文,之后主动方进入FIN_WAIT_1状态。

2)被动方收到该报文后,就向主动方发送ACK应答报文,接着被动方进入CLOSED_WAIT状态。

3)主动方收到被动方的ACK应答报文后,之后进入FIN_WAIT_2状态。

4)等待被动方处理完数据后,也向主动方发送FIN报文,之后被动方进入LAST_ACK状态。

5)主动方收到被动方的FIN的报文后,回一个ACK应答报文,之后进入TIME_WAIT状态。

6)被动方收到了ACK应答报文后,就进入了CLOSED状态,至此被动方已经完成了连接的关闭。

7)主动方在经过2MSL一段时间后,自动进入CLOSED状态,至此主动方也完成连接的关闭。

为什么TIME_WAIT状态要等待2MSL

MSL是报文段最大存活时间,超过这个时间将触发超时重传。2MSL的时间可以保证数据包的一去一回,防止四次挥手的第四次数据包丢包后,无法重传数据包,导致无法关闭连接。假如第三个包丢了,那么1个MSL时间以后,服务器会给客户端重传一个FIN的包,2MSL的时间正好够收到服务器重传FIN包的时间。所以是2MSL。

为什么ACK和FIN要分两次发送(为什么是四次挥手)

因为在主动方发送FIN时,可能数据包里还带有有效数据没有处理完,为了确保所有数据都能到达,并且被动方能够处理完所有数据,需要处理一段时间。所以被动方的FIN断开连接请求不能和ACK数据报一起发送,要等待一段时间以后单独发送一个断开连接的数据报。因此需要四次挥手。

RTT(Round-Trip Time)

RTT全称Round-Trip Time 往返时延。是计算机网络中的重要性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。

RTT由三个部分决定: 即链路的传播时间、末端系统的处理时间以及路由器的缓存中的排队和处理时间。其中,前面两个部分的值作为一个TCP连接相对固定,路由器的缓存中的排队和处理时间会随着整个网络拥塞程度的变化而变化。所以RTT的变化在一定程度上反映了网络拥塞程度的变化。

RTO

RTO全程Retransmission Timeout 超时重传时间

TCP每发送一个报文段,就对此报文段设置一个超时重传计时器。此计时器设置的超时重传时间RTO应当略大于TCP报文段的平均往返时延RTT,一般可取RTO = 2RTT。但是,也可以根据具体情况人为调整RTO的值,例如可以设置此超时重传时间RTO = 90秒。当超过了规定的超时重传时间还未收到对此TCP报文段的预期确认信息,则必须重新传输此TCP报文段

累计应答

在收到多个包后,统一进行回复,而不是一收一发。

滑动窗口

窗口概念:TCP 是每发送一个数据,都要进行一次确认应答。当上一个数据包收到了应答了,再发送下一个。这种方式的缺点是效率比较低的。

有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。

窗口大小由哪一方决定?

窗口的大小是在不断变化的。三次握手的时候,会交换窗口大小。通常窗口的大小是由接收方的窗口大小来决定的。发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。

发送方滑动窗口:

接收方滑动窗口:

流量控制

所谓流量控制,主要是接收方传递信息给发送方,使其不要发送数据太快,是一种端到端的控制。主要的方式就是返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送。

流量控制会影响滑动窗口的大小。如果接收端没有及时处理完数据,会告知发送端减少数据的发送。等处理完以后,再正常发送数据。

粘包问题

TCP是基于字节流传输的,没有边界、可以合并的传输数据方式。
合并就要能拆开、拆不开就是粘包

解决方法:
1、设置标志位(起始/结束标记)


缺点:标记是用特殊符号来表示的,所以可能会出现应用程序发送的数据与标志位重复

例如:???是我们标记符号,而应用程序发送的数据中也有???,则标记无法正常起作用

2、固定包大小
将每个发送的数据包都设置为相同大小

缺点:如果发送小数据,则会浪费空间;如果发送大数据,则发送不完。
适用场景:没有用户输入数据的时候

3、先发送数据长度,然后再发送数据包(通用)


缺点:多发了一个包
这种方法比较通用

实现原理:

struct Node n1;
int nSize=sizeof(n1);
//发送
send(sockClient,(char*)&nSize,sizeof(int),0); //先发送包的大小
send(sockClient,(char*)&nS1,sizeof(n1),0);  //发送数据包
//接收int nPackSize=0;
recv(sockClient,(char*)&nPackSize,sizeof(int),0); //先接收包大小
char* buf=new char[nPackSize];
recv(sockClient,buf,nPackSize,0); //再接收包
delete []buf;

4、短连接
在每次连接成功之后只发送一个包,就断开连接
缺点:效率低
适用场景:浏览网站

心跳机制

心跳机制是定时发送一个自定义结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制。

什么是心跳机制?

每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息。如果服务端几分钟内没有收到客户端信息,则视客户端断开

应用场景

在长连接下,有可能很长一段时间都没有数据往来。理论上来说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候就需要心跳包了,用于维持长连接,保活。

心跳包的发送技术

①应用层自己实现的心跳包(可以自己设置,灵活)
②使用SO_KEEPALIVE套接字选项(传输层)(不灵活)

Nagle算法

Nagle算法就是为了尽可能发送大数据块,避免网络中充斥着许多小数据块。
(数据块越多,传输速度越慢。我们可以将这种现象和“塞车”类比)

Nagle算法的规则

(1)如果包长度达到MSS(最大报文段长度),则允许发送;
(2)如果包中含有FIN,则允许发送;
(3)设置了TCP_NODELAY选项,则允许发送;(该选项为了关闭Nagle算法)
(4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
(5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

关闭Nagle算法

Nagle算法默认是打开的,如果对于一些需要小数据包交互的场景的程序,比如,telnet或ssh这样的交互性比较强的程序,则需要关闭Nagle算法。

int value=1;
setsockopt(sock_fd,IPPROTO_TCP,TCP_NODELAY,(char*)&value,sizeof(int));

拥塞控制

网络控制中的链路容量和交换结点中的缓存和处理机都有工作的极限,当网络的需求超过它们的工作极限时,就出现了拥塞。在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时TCP就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断的放大。

TCP的四种拥塞控制算法

1、慢开始
2、拥塞避免
3、快重传
4、快恢复

慢开始与拥塞避免

发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。

慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。

为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。
ssthresh的用法如下:
当cwnd<ssthresh时,使用慢开始算法
当cwnd>ssthresh时,改用拥塞避免算法。
当cwnd=ssthresh时,慢开始与拥塞避免算法任意

拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1而不是加倍。这样拥塞窗口按线性规律缓慢增长。

快重传

在TCP连接中,当发送方收到三个连续的重传请求时,会进入快重传状态,立即重传最后一次发送的数据包。在快重传状态下,发送方会停止发送新的数据包,等待接收方的确认,而不必继续等待设置的重传计时器时间到期。

快恢复

①当触发了快重传机制后,把ssthresh门限减半,但是之后不执行慢开始算法
②如果网络出现拥塞,就无法触发快重传机制收到重复确认,所以现在发送方认为网络可能没有出现拥塞。所以不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法
cwnd=ssthresh+3,是因为发送方连续收到三个重复确认消息。

TCP协议总结

面向连接的,可靠的传输,基于字节流的传输方式。
面向连接指发送数据之前必须在双端建立连接,建立连接使用“三次握手”。
可靠传输:seq和ack
基于字节流的传输:粘包问题
解决方案:1、先包大小,再数据 2、加结束标记 3、固定包大小 4、短连接
为什么TCP是可靠的
1、三次握手和四次挥手
2、重传和确认机制
3、合理的分段
4、检验重新排序
5、滑动窗口-流量控制
6、拥塞窗口-4种拥塞控制算法
TCP可以发广播吗?
TCP是一对一传输的,理论上是不能发广播的。

UDP和TCP对比

  • 60
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值