本篇是Java工程师面试方面的一些计算机网络知识的整理,当然肯定不能囊括全部知识点,但是博主会根据自身面试经验以及工作经验来总结最常用、最经典的知识点,来方便大家进行系统化的学习,博主会持续更新更多文章,觉得不错的可以点个关注
- TCP是面向连接的,UDP是无连接</font>的。
- 每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一);而UDP 支持一对一、一对多、多对一和多对多的交互通信。
- TCP 提供可靠交付的服务,保证数据不丢失、按序到达;而UDP则是尽最大努力交付,不保证可靠交付,因此主机不需要维持复杂的链接状态。
- TCP面向字节流,UDP是面向报文的。
- TCP有拥塞控制,UDP无拥塞控制,网络出现阻塞不会使源主机发送频率降低(对实时应用直播、视频会议更合适)。
- UDP首部开销小,只有8个字节,比TCP的20个字节的首部要短。
TCP 提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP传输是可靠的,可靠主要体现在传递数据前需要三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。
UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如:QQ 语音、 QQ 视频 、直播等等。
TCP对应协议:
- FTP:定义了文件传输协议,使用 21 端口。
- Telnet:一种用于远程登陆的端口,使用23端口,可以进行远程连接。
- HTTP:Web服务器和浏览器之间的超文本传输协议。
- SMTP:邮箱有关协议,用于发送邮件,服务器开放的是 25 号端口。
- POP3:邮箱有关协议,用于接收邮件。
UDP对应协议:
- DNS:域名解析服务,用于将域名解析成IP,默认53端口。(比如将www.baidu.com解析成百度服务器主机的IP地址来访问)
- SNMP:网络管理协议,管理网络设备,使用161号端口。
- TFTP:简单文件传输协议,使用69端口和UDP交互。
TCP是点对点的,我们上面提到过,建立连接的三次握手和断开连接的四次挥手可以说是相当重要,既能帮助我们理解TCP交互的过程,又是面试的宠儿,帮拿offer的利器。
先介绍几个概念:
- seq:序号,占4个字节,范围[0,4284967296],由于TCP是面向字节流的,在一个1个TCP连接中传送字节流中的每一个字节都按照顺序编号,此外序号是循环使用的
- ACK: 仅当ACK=1时确认字段才有效,当ACK=0时确认字段无效,并且TCP规定,在连接建立后所有的传送报文段都必须要把ACK置为1
- SYN:同步序列号,用来发起一个连接。当SYN=1而ACK=0时表明这是一个请求报文段;若对方同意连接,则响应报文中SYN=1,ACK=1
- FIN :用来释放一个连接,当FIN=1表示此报文段的发送方已经发送完毕。并要求释放链接
TCP三次握手:TCP建立连接的过程叫做三次握手
- 第一次握手:客户端向服务端发送连接请求,首部中的同步位 SYN=1,同时选择一个初始序号 seq = x。TCP 规定,SYN 报文段不能携带数据,但要消耗掉一个序号。这时,TCP 客户进程进入 SYN-SENT(同步已发送)状态。(A给B打电话,A询问B是否听得到)
- 第二次握手:B 收到连接请求报文后,如果同意建立连接,则向 A 发送确认。在确认报文段中应把 SYN 位和 ACK 位都置 1,确认号是 ack = x + 1,同时也为自己选择一个初始序号 seq = y。(B回复听得到,同时需要让A知道自己听得到(ack参数),然后询问A是否听的到自己的回复(seq参数))
- 第三次握手:TCP 客户进程收到 B 的确认后,还要向 B 给出确认。确认报文段的 ACK 置 1,确认号 ack = y + 1,而自己的序号 seq = x + 1。这时 ACK 报文段可以携带数据。但如果不携带数据则不消耗序号,这种情况下,下一个数据报文段的序号仍是 seq = x + 1。此时TCP建立好连接。
问题:为什么不能是两次握手?
为了防止已经失效的连接请求报文突然又传送到了服务端B,试想上面的三次握手,如果A发出的第一个连接请求报文段并没有丢失,而是因为网络原因长时间滞留了,以至于延误到连接释放以后的某个时间段才达到B。本来这是一个已经失效的请求连接,但是服务端B收到之后以为是A发出的一个新的连接请求,于是B向A发出确认报文段,同意请求。此时建立连接,如果不进行第三次握手,B发出同意之后便认为连接已经建立,并一直等待A发来的数据,B的很多资源便会白白浪费。
问题:为什么不需要四次握手?
我们可能会疑问:A发出的第三次握手的信息如果没有发送到B就已经建立了连接,如果这个包丢失或者网络滞留了怎么办?
我们需要明白一点,完全可靠的通信协议是不存在的 ,在经过三次握手之后,客户端和服务端都已经确定两边之前的通信状况了,都收到了确认消息,是保证双方通信的最少次数。即使增加再多的我手次数也不能保证通信完全可靠。(试想,第四次握手服务端B确认了,那如何再确定客户端收到服务端B的确认消息?是不是感觉没完了?)
问题:Server 端收到 Client 端的 SYN 后,为什么还要传回 SYN?
接收端传回SYN的目的是为了告诉客户端:我确实收到的是你发来的请求信息,保证双方确认。
问题:传了 SYN,为什么还要传 ACK?
双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。
- 第一次挥手: A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。A 把连接释放报文段首部的终止控制位 FIN 置 1,其序号 seq = u(等于前面已传送过的数据的最后一个字节的序号加 1),这时 A 进入 FIN-WAIT-1(终止等待1)状态,等待 B 的确认。请注意:TCP 规定,FIN 报文段即使不携带数据,也将消耗掉一个序号。
- 第二次挥手:B 收到连接释放报文段后立即发出确认,确认号是 ack = u + 1,而这个报文段自己的序号是 k(等于 B 前面已经传送过的数据的最后一个字节的序号加1),然后 B 就进入 CLOSE-WAIT(关闭等待)状态。TCP 服务端进程这时应通知高层应用进程,因而从 A 到 B 这个方向的连接就释放了,这时的 TCP 连接处于半关闭(half-close)状态,即 A 已经没有数据要发送了,但 B 若发送数据,A 仍要接收。也就是说,从 B 到 A 这个方向的连接并未关闭,这个状态可能会持续一段时间。A 收到来自 B 的确认后,就进入 FIN-WAIT-2(终止等待2)状态,等待 B 发出的连接释放报文段。
- 第三次挥手:若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。这时 B 发出的连接释放报文段必须使 FIN = 1。假定 B 的序号为 y(在半关闭状态,B 可能又发送了一些数据)。B 还必须重复上次已发送过的确认号 ack = u + 1。这时 B 就进入 LAST-ACK(最后确认)状态,等待 A 的确认。
- 第四次挥手:A 在收到 B 的连接释放报文后,必须对此发出确认。在确认报文段中把 ACK 置 1,确认号 ack = v + 1,而自己的序号 seq = u + 1(前面发送的 FIN 报文段要消耗一个序号)。然后进入 TIME-WAIT(时间等待) 状态。请注意,现在 TCP 连接还没有释放掉。必须经过时间等待计时器设置的时间 2MSL(MSL:最长报文段寿命)后,A 才能进入到 CLOSED 状态,然后撤销传输控制块,结束这次 TCP 连接。当然如果 B 一收到 A 的确认就进入 CLOSED 状态,然后撤销传输控制块。所以在释放连接时,B 结束 TCP 连接的时间要早于 A。
为什么第二次跟第三次不能合并, 第二次和第三次之间的等待是什么?
很容易理解,你想啊,第一次客户端挥手证明客户端要发送的消息已经发送完了,第二次是服务端响应客户端的这个挥手。而此时服务端要发送给客户端的消息可能还没发送完,所以此时客户端会把之前未传输完的数据传输完毕之后再发送关闭请求。
为什么 TIME-WAIT 状态必须等待 2MSL 的时间呢?
这个是为了保证发送端的A的最后一个报文段ACK能够到达服务端B,这个报文段有可能丢失,使得处在 LAST-ACK 状态的 B 收不到对已发送的 FIN + ACK 报文段的确认。如果B收不到A发送的报文段确认消息,则会超时重传(第三步),则A可以在2MSL 时间内收到B的超时重传消息,接着A进行一次重新确认(第4步),重新启动2MSL 计时器。最后,A 和 B 都正常进入到 CLOSED 状态。
试想,如果A在第4步的TIME-WAIT状态不等待一段时间,而是在发送完确认报文之后立即释放链接,那么B有可能无法收到报文段,超时重传时也没有响应,B则无法进入正常的关闭状态。
TCP每次建立连接都要经过三次握手很麻烦,能不能优化呢?可以啊。今天来说说这个优化后的 TCP 握手流程,也就是 TCP 快速打开(TCP Fast Open, 即TFO)的原理。
TCP的快速打开TFO,就是在TCP的三次握手之间传输有用的数据。它通过第一次握手时的SYN数据包中的TFO cookie来验证一个之前连接过的客户端,如果验证成功,则可以在三次握手完成之前便可以进行数据传输,更在传输开始时就降低了延迟,这个加密的Cookie被存储在客户端,在一开始的连接时被设定好。然后每当客户端连接时,这个Cookie被重复返回。
增加Fast Open之后的请求过程:
- 客户端发送包含Fast Open选项的数据包,且该选项的Cookie,表示客户端请求Fast Open Cookie;
- 支持TCP快速打开的服务器便会生成Cookie,写到SYN-ACK数据包中的Fast Open选项返回给客户端;
- 客户端收到该Cookie之后缓存下来。
完成上述的过程并且存储着有效的Fast Open Cookie:
- 客户端发送SYN数据包,该数据包含数据(对比普通TCP三次握手不含数据),以及此前缓存的Cookie;
- 支持Fast Open 的服务端会收到Cookie进行校验,如果Cookie有效,服务端将数据包中的SYN和数据进行确认,然后将数据送到相应程序;否则服务器丢掉SYN中包含数据,且其随后发出的SYN-ACK数据包将仅确认(Acknowledgement)SYN的对应序列号;
- 若服务端接受了SYN数据包中的数据,服务端可在握手完成之前便可以发送数据;
- 客户端将发送ACK确认服务器返回的SYN,如果初始的时候的数据未被服务器接受,则客户端重新发送该数据;
- 此时TCP连接和正常一样的步骤。
客户端在请求并存储了Fast Open Cookie之后,可以不断重复TCP Fast Open直至服务器认为Cookie无效(通常为过期)。
- 长连接:一个TCP连接有连续发送多个数据包,在TCP连接保持期间,如果没有数据发送,需要双方发送检测包以维持此连接,这就要求长连接在没有数据通信时,定时发送数据包(心跳),以维持连接状态;长连接多用于操作频繁(读写),点对点的通讯,而且连接数不能太多情况,比如数据库的连接。
- 短连接: 短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接。WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话耗费资源太严重。
TCP中还有一个保活计时器,服务端每收到一次客户端的数据,就重新设置一下保活器,时间通常设置两小时。若两小时未收到客户端的数据,则发送一个探测报文段,以后则每隔75秒发送一个,若连续发送10个报文段仍然无客户端回应,则认为客户端已经断开,接着关闭连接。
- 应答机制:TCP收到发自TCP另一端的数据,它将发送一个确认,一般是延迟不到一秒再发。
- 超时重发:TCP发出一个段之后,启动一个定时器,等待目的端确认收到这个报文段,如果不能及时收到这个确认,将重发这个报文段。
- 数据包校验:目的是检测数据在传输过程中的任何变化,若校验出包有错,则丢弃报文段并且不给出响应,这时 TCP 发送数据端超时后会重发数据。
- 对失序数据重排序,重复数据去重。
- 流量控制:TCP 连接的每一方都有固定大小的缓冲空间。TCP 的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这可以防止较快主机致使较慢主机的缓冲区溢出,这就是流量控制。TCP 使用的流量控制协议是可变大小的滑动窗口协议。(下面介绍)
我们由上面最后一条知道了,TCP通过利用滑动窗口来实现流量控制,流量控制是为了控制发送方的发送数据速率,保证接收方来得及接收。接收方发送的确认报文中的窗口字段可以用来设置,然后控制发送方发送数据的大小,当设置为0时,发送方将不能发送数据。
接下来一起看TCP的滑动窗口协议:
滑动窗口是一种流量控制协议,TCP 中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。当滑动窗口为 0 时,发送方一般不能再发送数据报,但有两种情况除外,一种情况是可以发送紧急数据,例如,允许用户终止在远端机上的运行进程。另一种情况是发送方可以发送一个 1 字节的数据报来通知接收方重新声明它希望接收的下一字节及发送方的滑动窗口大小。
拥塞控制和流量控制是不同的,流量控制可以理解为点对点的通信量的控制,而拥塞控制则是一个全局性过程。在某段时间内,若对网络中某一资源的需求超过了这个资源所能提供的可用部分,网络的性能会变坏,这种情况即为拥塞。
流量控制是点对点的问题,为了抑制发送端数据,以保证接收方来得及接受数据。而拥塞控制则是为了防止过多数据注入网络,使网络中的路由器或者链路不至于过载。拥塞控制有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器等有关因素。
为了进行拥塞控制,TCP 发送方要维持一个拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。TCP 的拥塞控制采用了四种算法,即:慢开始、拥塞避免、快重传和快恢复。
- 慢开始:防止网络开始立即注入大量数据,先探测一下,由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd 初始值为 1,每经过一个传播轮次,cwnd 加倍。
- 拥塞避免:让拥塞窗口 cwnd 缓慢增大,即每经过一个往返时间 RTT 就把发送方的 cwnd 加 1。(更慢的慢开始)
- 快重传和快恢复:在 TCP/IP 中,快速重传和快恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。
TCP是基于字节流的,TCP的首部没有表示数据长度的字段,并且TCP把一个个数据块看成一连串无结构的字节流,没有边界。基于这些,在使用 TCP 传输数据时,才有粘包或者拆包现象发生的可能。一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。
接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。拆包和粘包的问题导致接收端在处理的时候会非常困难,因为无法区分一个完整的数据包。
怎么解决拆包和粘包?
一般有两个通用的方法:加特殊字符控制和在包的首部添加数据包的长度。如果使用 netty 的话,就有专门的编码器和解码器解决拆包和粘包问题了。
UDP 没有粘包问题,但是有丢包和乱序。不完整的包是不会有的,收到的都是完全正确的包。传送的数据单位协议是 UDP 报文或用户数据报,发送的时候既不合并,也不拆分。
3. 建立TCP连接后,浏览器向服务器发送http请求;
4. 服务器响应http请求,将资源返回给浏览器;
5. TCP的四次挥手断开连接;
6. 浏览器获得资源,渲染页面。
本文介绍了网络的一些面试经历,欢迎大家阅读,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,感激不尽。
你知道的越多,你不知道的也越多。keep hungry keep foolish!