❣博主主页: 33的博客❣
▶️文章专栏分类:JavaEE◀️
🚚我的代码仓库: 33的代码仓库🚚
🫵🫵🫵关注我带你了解更多网络知识
目录
- 1.前言
- 2.应用层
- 2.1xml
- 2.2json
- 3.传输层
- 3.1UDP协议
- 3.2TCP协议
- 3.2.1确认响应
- 3.2.2超时重传
- 3.2.3连接管理(3次握手,4次挥手)
- 3.2.4滑动窗口
- 3.2.5流量控制
- 3.2.6拥塞控制
- 3.2.7延时应答
- 3.2.8捎带应答
- 3.2.9面向字节流
- 3.2.10异常情况处理
- 3.3TCP和UDP适用场景
- 4.总结
1.前言
在前面的文章中,我们已经知道协议就是一种约定,在TCP的五层网络模型中也每一层都有很多协议对应的协议,例如在网络编程中我们已经用到了传输TCP和UDP协,这篇文章我们就进一步学习。
2.应用层
应用层是的程序员接触最精密的,设想我们要通过美团获取附件的美食。那么在客户端请求的时候:我们就需要包含当前用户的id,以及位置信息,这个时候代码中就构造出一个程序包含(ID,经纬度)构造一个字符串写入UDP Socket或者TCP Socket中,那么服务端响应的时候就会做出响应如(商家名,距离)。
在上诉这个过程就是自定义协议这里没有固定的格式的要求,用啥格式都可以,只要客户端服务器能够对应上。但还是有一些常见的数据格式,例:
2.1xml
通过标签来组织数据
<request>
<userID>10</userID>
<position>100,30</position>
</request>
优点:可读性提高
缺点:标签写起来非常繁琐,传输的时候消耗额外网络带宽
2.2json
通过键值对来组织数据
{
userID:"10",
position:"100,30"
}
优点:可读性提高,比xml简洁
缺点:传输的时候消耗额外网络带宽
(关于应用层的http协议在下篇文章中细讲)
3.传输层
传输层包含两个重要的协议UDP和TCP。
UDP:无连接,不可靠,面向数据报,全双工
TCP:有连接,可靠,面向字节流,全双工
端口号:占2个字节写一个服务器的时候必须手动指定一个端口号,用来区分不同程序。但写客户端的时候不用手动指定,系统会自动分配。
1-1023称为知名端口号,给一些比较知名的服务器使用例如:22为ssh服务器,80为http协议,443为https服务器。1024-65535为普通端口号。
3.1UDP协议
我们学习一个协议时,最主要的就是要学习它的报文格式:
UDP报头:一共占8个字节,每一个部分都各占两个字节。
UDP报文长度:占2个字节,最长65535即64kb,这个长度是定长的,不能最初修改。
校验和:发送方把要发送的数据data1通过一定的算法,算出一个校验和sum1,并存入UDP报头,并通过网络一起把data和sum1一起发送出去,此时接受放收到数据data2,通过算法计算出校验和sum2,如果sum1不等于sum2那么data2数据与data1一定不同,如果相同,那么data1与data2大概率也相同。通过此方法就能法相在传输过程中数据是否传输错误。那么校验和具体怎么算,可以用CRC(循环冗余算法),也可以使用md5,通过md5生成的校验和都是定长的,分散的,不可逆的。
3.2TCP协议
注意
4位首部长度:TCP报头长度最短是20字节,最长是60字节,4 bit=15,此处是以4字节为单位(选项都是以4字节为单位)。
校验和:同UDP
(其他部分在下面一 一介绍)
3.2.1确认响应
TCP的一个重要特性就是可靠传输,那么具体什么是可靠传输呢?可靠传输就是当发送方把信息发出去,能够知道接收方是否接受了数据,一旦发现对方没有接受数据,那么发送方就会通多一定的手段来进行补救。
当发送方把信息发出去,接收方如收到信息,那么就会返回一个应答报文给发送方,如果发送方收到了这个报文,那么就证明信息发送成功了。但是在这个过程中可能会出现一些问题:例如我连续发送了两次请求,但是我后发送的消息却先响应了。
例如:你给你朋友发了一条消息想约她今天出来玩,后来又想明天也约她就又立马给她发了一条消息,B这个时候也做出了响应今天可以,明天不行,但两条数据在网络传送过程中的路径可能不相同,就导致,后发的先到达了A,这时候你就认为今天不想出来,明天可以。这时候收到的数据就出现了偏差。
为了避免出现这样的情况,TCP就需要确保应答报文和发出去的数据能够相互对应,即使出现后发先至的现象时,仍然能够按照正常的顺序来理解数据:我们发数据的时候就给数据编一个号,同时接收端做出响应也编一个号,这个时候就需要用到报头中的32位序号和32位确认序号。
假设在一个TCP报文中有1000个字节的载荷数据,那么第一个字节的序号就为1,就在TCP序号中协议,由于一共1000个字节,那么此时最后一个字节的序列号就为1000,这时候就响应返回确认序列为1001。
那么如何区分一个数据是普通数据还是ack数据呢?我们只需要把ACK设置为1.
确认应答是TCP核心机制,保证了TCP的可靠传输。
3.2.2超时重传
确认应答描述的是一个比较理想的情况,但是如果数据在网络传输过程中出现了丢包现象该如何处理呢?
有的同学可能会好好奇为什么会出现丢包的情况,其实,把网络想象成一条高速公路,路由器或者交换机想象为收费站,把一个一个数据包可以想象成一辆一辆的车,在普通情况下,这些车辆都可以快速通过收费站,但在特殊情况下例如节假日,这时车流量就非常的大,就会出现堵车的情况,但是当路由器针对堵车的处理是非常粗暴的,并不会把这些挤压得数据保存好,而是直接丢弃!这样数据就在网络中消失了。
由于丢包是一个随机的事件,因此在tcp传输过程中,丢包会出现两种情况:
1.传输的数据丢了
2.返回的ack丢了
主机A并不知道是出现了上诉哪种情况,这时发送方A,都会进行重新传输。当发送方发送数据之后,等待一段时间,没有收到ack那么就会进行重传那么一般是等多久呢?
- 1.这个等待时间是可以配置的,不同系统上可能不一样,也可以通过修改一些内核参数来引起这里的时间变化。
- 2.等待的时间是动态变化的每多经历一次超时重传,等待的时间都会变长,当如,这个时间也不是无限拉长的,当达到一定的程度,这个时候就会触发tcp的重置连接操作了。
那么还有一个问题,如果返回的ack数据包丢了,实际上数据是已经发送到接收方B,那么重传以后,此时B就收到了两条一样的数据,这个时候会对程序带来bug吗?其实 TCP已经帮我们处理好了这样的情况,当我们再发送一个数据时,接收方如果发现接受缓冲区中存在该数据,那么就会直接丢弃掉确保read的时候只读到一条数据。接收缓冲区不仅仅能去重,还能进行重排序,确保发送顺序和接收顺序一致。
3.2.3连接管理(3次握手,4次挥手)
握手这个操作就是给对方传输一个简短的,没有业务逻辑的数据包,通过这个数据包来唤起接收方的注意,从而触发后续操作。
三次握手:
当A想要和B建立连接时候,会先发送一个不带任何‘业务数据’的包,这个包就称为同步“同步报文段”SYN.当B收到这个包时会给A反馈一个ACK应答报文,同时也会给A发送一个SYN,当A收到此包时,会返回一个ACK此时建立连接就完成了。
那么3次握手变成两次握手可以吗?变成4次握手可以吗?
4次握手是可以的,但是没有必要。
那么可以2次握手吗?当然是不行的。举个例子,当我和朋友打电话时,为了确认我们双方的通信都是良好的,我和朋友就约定了当听到我的声音时,要回复,例如:
三次握手的作用:
1.确认当前网络是否通畅
2.让发送方和接收方都能确认自己的发送能力和接受能力正常
3.让通信双方在握手的过程中,针对一些重要的参数进行协商比如tcp的序号从几开始,每次建立的时候,都会协商出一个比较大的,和上次这个设定可以避免“前朝的剑,斩本朝的官”,比如当网络不好时,客户端与服务器会尝试重连,如果这个时候旧连接的数据姗姗来迟那么这种迟到的数据就应该丢掉,不应该影响现在的业务逻辑,那么如何区分这个数据是来自前朝还是当朝呢?就可以通过tcp的序号来判断,当接收方发现接收到的数据和序号和当前数据的序号差别非常大,就可以认为这个数据是前朝的就可以直接丢弃掉。
在3次握手中有几个比较重要的状态:
Listen:服务器的状态,当服务器已经把socket创建成功并且已经把端口号绑定好,那么此时就会进入listen状态,就允许客户端随时来与服务器建立连接了。
estabushed:客户端与服务器都会有的状态,表示连接建立完成可以进行正常通信了。
4次挥手:
可能有的同学会有疑问,B的ACK和FIN可不可以也合并,这是不一定的,因为ACK应带报文是直接由内核响应的,当B收到FIN就会立即返回ACK而FIN是由程序的代码触发的,需要调用close()方法才会触发,当服务器返回ACK到代码执行到close发起FIN要经历多少时间是不确定,要看代码写了多少,但是TCP收中还有一个延时响应机制,能够拖住ACK的返回时间那么就有可能和FIN是同一时刻,那么此时就可以合并。
在4次握手中的重要状态
哪一方主动断开连接,哪一方就会就会进入TIME_WAIT状态,TIME_WAIT状态是为了z防止ACK丢失,站在B的角度,如果ACK包丢掉了,B就会重传FIN,如果此时已经没有TIME_WAIT状态了,就直接断了了连接,那么B就永远都不会再搜到ACK了,A使用TIME_WAIT状态进行等待,等待的这个书简就是为了处理B的重传FIN,此时由=有重传的FIN来了,就可以继续返回ACK了,B这边重传才有意义。那么TIME_WAIT状态等待多久呢?假设网络上两个节点通信消耗的最大时间为MSL,此时TIME_WAIT的时间就是2MSL。
3.2.4滑动窗口
TCP的可靠传输会影响传输的效率,滑动窗口可以缩短确认应答的等待时间,就可提高传输效率。
原本是每收到一个报文才发送一个数据:
但引入滑动窗口只是,就不等ACK回来就直接发送下一个数据,,但批量传输也是有上限的,达到上限之后,再等待ack,批量传输的数据多少就是滑动窗口的大小。
当A给B批量发送了4份数据,此时A已经达到滑动窗口的大小,就不能再发数据了,当收到一一条ACK之后才能再发数据,窗口越大,等待的ack就越多,此时传输速率就越高。但如果在活动窗口丢包了该如何处理呢?
1.ack丢了:没有任何影响,如果是1001这个ack丢了,但是2001这个ack到了就表示2001之前的数据都传输成功了。
2.数据包丢了:返回的ack就一直是相同的,这时主机A连续收到相同的ACK就会重传
滑动窗口确实会提高传输效率,但窗口设置得越大传输效率就越大吗?如果传输速度太快,那么接收方的数据就可能处理不过来了,此时接收方就会出现丢包的情况。
3.2.5流量控制
站在接收方的角度,发送方的发送速率不应该超过接收方的处理能力
当数据到达B中的内核时,此时接收方有一个接收缓冲区,A给B发的数据会先到达接收缓冲区,在通过read读到应用程序中,但数据被read了就可以从缓冲区内删除。接收方每次接收数据时,都会把缓冲区的剩余大小返回给发送方,发送方就会按照这个数值来调整下一轮的速度。
假设在这个过程中,接收方的应用程序还没来得级处理任何数据,每收到一个数据,缓冲区剩余空间就减少1。最开始B返回剩余空间为3000,那么A收到这个消息就确定了这一轮的窗口大小,当反馈为0就表示B已经满了,暂停发数据,那么要暂停多久呢?那么超过超时重传的时间,A就会发出一个窗口探测的包,这个包不携带任何业务数据,只是为了触发ack查询当前接收方的剩余缓冲区空间。
3.2.6拥塞控制
流量控制是考虑对方的接收能力,拥塞控制就是在通信过程中的节点情况,由于中间某一个节点已经达到了处理能力的上限,那么对传输速率也是会有影响的。
我们可以让A先以一个较低的速度发送,如果这个过程非常顺利,并且没有包丢失的情况,那么尝试增大速率,随着速率增大即窗口大小不停增大,达到一定程度过后,中间节点就可能出现问题,出现数据包丢失的情况,这个时候,就把窗口的大小调整小,如果还是丢包就调整更小,发现不丢包以后就尝试逐渐变大。
3.2.7延时应答
正常情况下,当A把数据传送给A,此时会立即返回ACK,但是有的时候A传输给B之后,此时B会等一会再返回ACK,本质上是提升传输效率,延时返回ACK就是给接收方更多的时间来读取缓冲区的数据,此时返回缓存区的剩余空间就更大。
3.2.8捎带应答
捎带应是再演示应答的基础上实现的,假设主机A给主机B发送了一个请求,正常情况下会立即返回一个ACK,但由于tcp引入了延时应答的机制就没有立即返回,由于response是的应答时间要根据实际代码情况来定,如果此时response正好要返回,与此同时顺便就把刚要返回的ACK也一起捎带上了。
3.2.9面向字节流
在这里有一个粘包的问题,当多个应用从数据从A传输给B时,此时,会先进入B的接收缓冲区,在从接收缓冲区读取数据,但TCP是面向字节流的,每次从缓冲区读取多少数据都是随机的,可以读取一个字节也可以读十个字节,但最终的目的都是为了得到一个完整的应用层数据包,B程序就不知道从哪儿到哪儿才是一个完整的数据包。
那么如何解决这样的粘包问起呢?
- 1.引入分隔符:在网络编程的时候一个设计到用\n作为分隔符,那么此时传输数据时候,每传输完一个数据,就在其后加入\n,在读取数据的时候,读到\n时就代表这段数据读完了。
- 2.引入长度:在传输数据的时候标明一共传输的长度,读取数据的时候就按照这个长度来读取。
3.2.10异常情况处理
- 1)进程崩溃
当进程挂了,那么文件描述符表也就没有了,就相当于调用了colse()方法,此时会触发FIN对方收到以后,会返回ACK与FIN,再进行ACK,实际上就是TCP的4次挥手的正常流程。 - 2)主机关机
在关机的时候,会强制触发终止进程的操作,相当于1。关机不仅仅是进程消失了,系统也会关闭,如果在系统关闭之前对端返回了ACK与FIN那么此时系统还能返回ACK,如果在系统关闭之后对端才返回了ACK与FIN那么此时对端迟迟没有收到ACK响应就会认为是自己的包FIN包丢了,就会重传FIN,但也不会一直重传,当重传了几次FIN都没有响应以后,对端就会放弃连接。 - 3)主机掉电
此时是一瞬间的事情,根本来不及杀死进程触发FIN,对端不知道啥情况。
如果对端在发送数据的时候,接收方掉电,那么此时对端迟迟没有收到ACK就会触发超时重传,如果发了几次依然没有ACK那么就会触发TCP连接重置功能,发起复位报文段(RST),如果复位报文段发送以后依然没有任何响应,就直接释放连接了。
如果对端在接收数据的时候,发送方掉电,那么此时对方一直在等待数据的到达,等了很久都没任何动静,这时对端就不知道是发送方没有发消息还是什么情况,这时TCP就贴心的提供了心跳包机制,即接收方会周期性的给对方发起一个特殊的,不携带任何业务的数据包,并期待对方返回一个应答,如果重复了很多次,对方都没有应答,那么久视为对方挂了,就单方面释放连接。 - 4)网线断开
如果此时A正在给B发数据,此时网线突然断了
A就会触发超时重传,连接重置,单方面释放连接
B就会触发心跳机制,没响应,,单方面释放连接
3.3TCP和UDP适用场景
TCP的优势在于可靠传输,TCP适用于绝大多数场景。
UDP优势在于更高效,UDP适用于“可靠性不敏感,性能敏感”的场景如局域网内部主机之间通信。
如果要传输比较大的数据包,TCP优先,UDP限制大小为64KB
如果要进行广播传输,UDP天然支持广播,TCP不支持
4.总结
在这篇文章中主要介绍了TCP和UDP的报文格式和TCP的几大重要机制,例如,确认响应,超时重传,连接管理,滑动窗口,流量控制,拥塞控制等等…