目录
引言
TCP协议
🍑TCP 与 UDP 的 区别
🍑TCP客户端和服务器建立连接的三次握手
🍑TCP客户端和服务器断开连接的四次挥手
🍑滑动窗口
🍑流量控制
🍑拥塞控制
引言
还记得那个经典的图吗?
今天我们要说的就是其中著名的TCP/IP协议。
TCP协议
🍑TCP 与 UDP 的 区别
有连接与无连接
有链接:像打电话
需要双方建立连接后才能进行通话
比如说:现在我们要打电话给某个朋友。
输入号码,按下手机拨号键。
手机开始发出 嘟嘟嘟 声音,开始等待对方接听,
而且,我们拨号之后,并不是马上就能接通的!
必须要等待 对方接听之后,我们才能与其交流。
之所以说:有连接 就像 打电话一样,是因为 打电话,必须要接通了之后,才能交流;没有接通,双方就无法交流。
有连接的意思:就是在两者确认建立联系后,就可以开始交互了。
无连接:发微信
不需要接通,直接就能发数据。
发微信,我们都知道:发送信息的时候,是不需要对方在线或者回复,按下回车,立马就能加个信息发送出去,不过 对方 看没看见这条消息,我们是不确定的 。
这种情况,就叫做无连接。TCP,就是要求双发先建立连接,连接好了,才能进行传数据。
而 UDP,直接传输数据,不需要双方建立连接。
可靠传输与不可靠传输
可靠传输:发送方 知道 接收方 有没有接收到数据
🔔注意!不要理解错了。
可靠传输,不是说数据发送之后,对方100% 就能收到。而是说我数据发送之后,发送方知道对方收没收到我发的消息
比如钉钉在你向别人发送完数据后,如果对方收到了——就会显示已读,如果没收到——就还是未读状态。
🔔这里还有个坑,这里可靠和安全可没有半毛钱关系。
安全,指的是 数据在传输过程,不容易被黑客窃取,不容易被篡改。
可靠,指的是 数据发给对方,发送方能知道接收方有没有收到数据。
不可靠传输:发送方 不知道 接收方有没有接收到数据
面向字节流和面向数据报
面向字节流:数据是以字节为单位,进行传输的。
这个就非常类似于 文件操作中的文件内容相关的操作中的字节流。
网络传输也是一样!
假设,现有100个字节的数据。
我们可以一直发完。
也可以 一次发 10个字节,发送十次。
也可以 一次发 2 个字节,发送50次。
…面向数据报:以数据报为单位,进行传输。
一个数据报都会明确大小。
一次 发送/接收 必须是 一个 完整的数据报。
不能是半个,也不能是一个半,必须是整数个。在代码中,这两者的区别是非常明显的!
🍑TCP客户端和服务器建立连接的三次握手
概述:
首先客户端主动向服务器发送建立连接的请求——一个SYN同步报文段,服务器收到后就对客户端的请求做出回应,发送ACK确认报文段(表示我收到你的请求了)。同时在服务器发送ACK时候,服务器也会向客户端发送建立连接的请求——SYN(双向奔赴),最后客户端再对我们刚刚服务器发送的建立连接的请求做出回应ACK。
至此,客户端与服务器就成功建立了连接,接下来就可以进行通信了。
那么三次握手具体的意义何在呢?
1、三次握手首先要保证的是当前我们直接的连接是可靠的,接下来我们双方是可以进行相关的通信的 。
就跟我们打电话似的,我们双方在进行通信前,首先要确保我们双方之间的网络是没有问题的——是能够互相听到对方的声音的。
经过这三次握手,我们互相都知道对方的设备是没有问题的,网络也是没有问题的,我们之间是可以进行正常通信的
2、通过三次握手可以让双方协商一些必要的信息。
三次握手,就让客户端和服务器之间建立好了连接。
其实建立好连接之后,操作系统内核中,就需要使用一定的数据结构来保存连接相关的信息。
🍑TCP客户端和服务器断开连接的四次挥手
那么如果我们想断开连接,就要经历四次挥手的过程。
与三次握手不同,四次挥手中,既可以是客户端主动断开连接,也可以是服务器主动断开连接。
看到这里,你可能会觉得这个和上面的三次握手的过程好像呀!那么中间那两个的ACK响应报文段和FIN结束报文端能不能一起发送呢?就像三次握手那样
答案是:中间那两次挥手不一定能够合并!
为啥呢,如下图所示:
再回头看看三次握手的过程
总结:
与TCP的连接不同 ,对应TCP连接的断开来说。服务器和客户端任意一方都可以主动断开连接。
比如上面的A想要主动断开连接——A执行了socket.close方法,向B发送了一个FIN(结束报文端),B在收到后操作系统内核马上进行反馈,向A发送了一个ACK(确认报文段)。那么在向A发送ACK的同时,能不能B也给A发送一个FIN(结束报文段)呢?
这样多省事呀!抱歉,还真不一定。我们要明白我们的ACK和之前的SYN都是由操作系统内核进行处理的,所以在之前的三次握手中,内核可以安排ACK和SYN合并一下,一起发送。
但这里呢,我们的FIN(结束报文段)是在用户代码执行了socket.close方法后才会触发的,也就是说FIN什么时候触发是由用户的代码逻辑来决定的——你这里虽然收到了A传来的FIN,但是我们这里的工作还没做完(B这里还没执行到close方法)。那有什么办法,只能等呀!
那么此时B向A发送的ACK确认报文段和FIN结束报文段之间的时间间隔就可能很大——自然就没办法合并到同一个报文段里一起发送。
终于当B中的代码执行到了socket.close方法,向A发送了他的FIN终止报文段。A接收到后,操作系统内核马上做出反馈——对B刚刚发送的报文段进行确认(ACK确认报文段)
至此,A、B之间的连接就算成功的断开了。
🍑滑动窗口
滑动窗口存在的意义就是在保证可靠性的前提下,尽量提高传输效率!!!
先来看如果不使用滑动窗口,传输是怎样进行的
如上图所示,我们这样发送一次,收到确认后再发送一次,这样重复的操作是很费时间的。我们把大量的时间都花费在了等待ACK响应上。
于是就有了滑动窗口这样一个机制——一次发送一波数据,然后等待接收方的响应ACK(表示当前接收方已经收到了第xxx个字节的数据,你接着发送就好,窗口就进行了滑动)
上面这段话,你可能不同理解,没关系,接着往下看就好——让我们看看什么是窗口?什么是滑动?
看到这里,想必你已经对滑动窗口这个概念有了一个初步的认识,但是这里还是有些小问题。滑动窗口是提示了发送效率。但是如果出现了丢包该怎么办?
丢包分为两种情况
第一种:ACK丢了
第二种:数据包丢了
总结:
滑动窗口提高发送效率(但可能会影响数据的可靠性)
于是就有了流量控制、拥塞控制来对滑动窗口的发送速率做一个限制(让滑动窗口的大小在一个合理的范围,让他别一次发那么多数据,接收方可以来不及接收,容易造成丢包)
那么具体是怎样来限制呢?
🍑流量控制
对于流量控制——就是根据接收方的处理能力(接收缓冲区剩余空间大小)通过ACK确认报文段来告知发送方当前接收缓冲区中的剩余空间大小是多少,然后发送方就根据这个数据来不断的改变当前滑动窗口的大小。
那么问题来了,这个所谓的接收缓冲区大小是个什么东东?
那么如何让我们的发送方知道当前接收方的接收缓冲区的大小呢?
理论说完了,我我们来看一个例子
如上图所示:第一次收到接收方的ACK时候,此时的接收缓冲区的大小还是3000字节,然后随着发送方数据的不断发送,这个值不断的减小。终于当接收发收到了发送方的第4000个字节(下一个要接收的字节是4001的时候,此时接收缓冲区满了,发送方就会暂停发送数据.....就这样通过接收缓冲区来动态的调整发送方滑动窗口的大小。
🍑拥塞控制
流量控制是站在接收方的角度来控制发送速率的(滑动窗口大小)的, 但是对于整体的网络传输而言,不光有发送方和接收方,还有中间各自转发设备。
既然知道了拥塞控制—— 就是从中间这一系列的转发设备来考虑,进而来限制发送方的发送速率(滑动窗口大小)的,那么他处理的具体流程是什么呢?
中间可是有很多设备的,又多又杂,不好处理呀!
总结: