引言
还记得那个经典的图吗?
今天我们要说的就是其中著名的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之间的连接就算成功的断开了。