当前位置: 首页 > 后端技术 > Java

一天看懂TCP套路面试

时间:2023-04-02 09:12:13 Java

本文已收录在Github仓库,内容包括计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计patterns架构、校招代理招聘分享等核心知识点,欢迎star~Github地址:https://github.com/Tyson0314/Java-learning为什么需要TCP协议?IP层是“不可靠的”。它不保证网络数据包的传递、网络数据包的有序传递或网络数据包中数据的完整性。由于TCP是一种工作在传输层的可靠数据传输服务,它可以保证接收端接收到的网络数据包是无损的、无间隔的、无冗余的、顺序的。假设发送方是客户端,接收方是服务器,说一下TCP的三次握手。一开始,客户端和服务器的状态都是CLOSED。第一次握手:客户端向服务器发起连接建立请求,客户端会随机生成一个起始序号x,客户端发送给服务器的字段包含标志位SYN=1和序号seq=x.第一次握手前客户端状态为CLOSE,第一次握手后客户端状态为SYN-SENT。此时服务器的状态为LISTEN。第二次握手:服务器端收到客户端的报文后,会随机生成一个服务器的起始序号y,然后回复一个报文给客户端,包括标志位SYN=1,ACK=1。序号seq=y,确认号ack=x+1。第二次握手前服务器的状态为LISTEN,第二次握手后服务器的状态为SYN-RCVD,客户端的状态为SYN-SENT。(SYN=1表示与客户端建立连接,ACK=1表示确认序号有效)第三次握手:客户端收到服务器的报文后,会再次向服务器发送报文,其中包含标志位ACK=1,序号seq=x+1,确认号ack=y+1。在第三次握手之前,客户端的状态是SYN-SENT,在第三次握手之后,客户端和服务器的状态都是ESTABLISHED。至此连接建立。两次握手可以吗?之所以需要第三次握手,主要是为了防止无效的连接请求段突然传给服务器,造成问题。例如,当客户端A发送连接请求时,由于网络拥塞,A可能收不到确认消息,于是A再次重传连接请求。然后连接成功,等待数据传输完成后,释放连接。然后A发送的第一个连接请求直到连接被释放后的某个时间才到达服务器B。此时B误认为A发送了新的连接请求,于是发送确认报文段给A,如果不使用三次握手,只要B发送确认,就会建立新的连接.这时A不会回应B的确认,不会发送数据,那么B就会等待A发送数据,浪费资源。再来说说TCP的四次挥手。A的应用进程首先向自己的TCP发送一个连接释放报文段(FIN=1,seq=u),并停止发送数据,主动关闭TCP连接,进入FIN-WAIT-1(终止等待1)状态,等待B的确认。B收到连接释放报文段后,发送确认报文段(ACK=1,ack=u+1,seq=v),B进入CLOSE-WAIT(关闭等待)状态,TCP处于a此时处于半关闭状态,A到B的连接被释放。A收到B的确认后,进入FIN-WAIT-2(终止等待2)状态,等待B发送的连接释放报文段。B发送完数据后,会发送一个连接释放报文段(FIN=1,ACK=1,seq=w,ack=u+1),B进入LAST-ACK(最终确认)状态,等待A的确认。A收到B的连接释放报文段后,发送确认报文段(ACK=1,seq=u+1,ack=w+1),A进入TIME-WAIT(时间等待)状态。此时TCP没有被释放,只有经过时间等待定时器设置的时间2MSL(maximumsegmentlifetime)后A才进入CLOSED状态。B收到A的确认报文段后关闭连接,如果没有收到A的确认报文段,B将重传连接释放报文段。为什么要等待第四波的2MSL?保证A发送的最后一个ACK报文段能到达B,这个ACK报文段可能会丢失。如果B没有收到这个确认报文,会超时重传连接释放报文段,然后A在2MSL时间内可以收到重传的连接释放报文段,然后A重传一个确认,重启2MSL定时器,最后两者A和B进入CLOSED状态。如果A在TIME-WAIT状态下没有等待一段时间,而是在发送完ACK报文段后立即释放连接,则无法收到B重传的连接释放报文段,不会再发送确认报文段,B也不会被能够正常进入CLOSED状态。防止无效的连接请求段出现在这个连接中。A发送完最后一个ACK报文段后,再经过2MSL,这个连接产生的所有报文段都可以从网络中消失,这样在下一个新的连接部分就不会出现旧的连接请求报文了。为什么是四波?因为服务器端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。但是在关闭连接的时候,当服务端收到客户端的连接释放消息后,可能不会马上关闭SOCKET,所以服务端先回复一个ACK消息,告诉客户端我收到了你的连接释放消息。只有当服务器端的所有消息都发送完后,服务器端才能发送连接释放消息,然后双方才真正断开连接。所以需要四波。说说TCP报文的头部有哪些字段,它们的作用是什么?16位端口号:源端口号,主机的报文段来自哪里;目标端口号,传递给哪个上层协议或应用程序;字节流在一个传输方向上的每个字节数。32位确认号:用作对对方发送的tcp报文段的回应。其值为接收到的TCP报文段的序号值加1。4-bitheaderlength:表示tcpheader中有多少个32bit字(4字节)。因为4位最多可以识别15个,所以最长的TCP头是60字节。6位标志位:URG(紧急指针是否有效)、ACk(表示确认号是否有效)、PSH(缓冲区未满)、RST(表示要求对方重新建立连接)、SYN(连接建立消息标志)、FIN(表示对方即将关闭连接)16位窗口大小:是TCP流量控制的一种手段。这里所说的窗口是指接收通知窗口。它告诉对方本地TCP接收缓冲区可以容纳多少字节的数据,以便对方控制发送数据的速度。16位校验和:由发送方填写,接收方对TCP报文段进行CRC校验算法,检查TCP报文段在传输过程中是否损坏。请注意,此检查不仅包括TCP标头,还包括数据部分。这也是TCP可靠传输的重要保证。16位紧急指针:正偏移量。它被添加到序列号字段的值中,以指示最后一个紧急数据的下一个字节的序列号。因此,准确的说,该字段是紧急指针相对于当前序号的偏移量,可以称之为紧急偏移量。TCP的紧急指针是发送方向接收方发送紧急数据的一种方式。TCP有什么特点?TCP是面向连接的传输层协议。点对点,每个TCP连接只能有两个端点。TCP提供可靠的交付服务。TCP提供全双工通信。面向字节流。TCP和UDP的区别?TCP是面向连接的;UDP是无连接的,即发送数据前不需要建立连接。TCP提供可靠的服务;UDP不保证可靠传送。TCP是面向字节的,把数据看成是一系列非结构化的字节流;UDP是面向数据包的。TCP有拥塞控制;UDP没有拥塞控制,所以网络拥塞不会降低源主机的发送速率(对实时应用很有用,比如实时视频会议等)。每个TCP连接只能是点对点的;UDP支持一对一、一对多、多对一和多对多的通信方式。TCP头开销为20字节;UDP头开销很小,只有8个字节。TCP和UDP对应的常见应用层协议有哪些?基于TCP的应用层协议包括:HTTP、FTP、SMTP、TELNET、SSHHTTP:超文本传输??协议(HypertextTransferProtocol),默认端口80FTP:文件传输协议(FileTransferProtocol),默认端口(20用于数据传输,21用于传输控制信息)SMTP:简单邮件传输协议(SimpleMailTransferProtocol),默认端口25TELNET:TeletypeovertheNetwork(网络电传),默认端口23SSH:SecureShell(安全外壳协议),默认端口22基于应用层协议UDP:DNS、TFTP、SNMPDNS:域名服务(DomainNameService),默认端口53TFTP:简单文件传输协议(SimpleFileTransferProtocol),默认端口69SNMP:简单网络管理协议(SimpleNetworkManagementProtocol),接收通过UDP161端口,只有Trap信息使用UDP162端口。TCP的stickypacket和unpackTCP是面向流的,一串没有边界的数据。TCP底层不理解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况来划分数据包,所以在业务上,一个完整的数据包可能会被TCP拆分成多个数据包用于传输,也有可能将多个小数据包封装成一个大数据包发送。这就是所谓的TCP粘包和解包问题。为什么会出现粘包和脱包?待发送的数据小于TCP发送缓冲区的大小,TCP会将多次写入缓冲区的数据一次发送出去,会出现粘包;接收数据端的应用层没有及时读取接收缓冲区中的数据,会出现粘包;待发送数据大于TCP发送缓冲区剩余空间,会发生拆包;待发送数据大于MSS(最大消息长度),TCP将进行解包。即TCP包长度-TCP头长度>MSS。解决方案:发送方将每个数据包封装成固定长度,并在数据末尾添加特殊字符,将数据分为两部分,一个是包头,一个是内容体;header结构具有固定大小,并有一个字段声明内容主体的大小。说说TCP如何保证可靠性?首先,TCP连接是基于三次握手,而断开连接是基于四次握手。确保连接和断开的可靠性。其次,TCP的可靠性还体现在它的状态上;TCP会记录发送了哪些数据,接收了哪些数据,不接受了哪些数据,并确保数据包到达,以保证数据传输无差错。再次,TCP的可靠性还体现在它的可控性上。它具有数据包验证、ACK响应、超时重传(发送方)、乱序数据重传(接收方)、丢弃重复数据、流量控制(滑动窗口)和拥塞控制等机制。再说说TCP的滑动窗口机制TCP使用滑动窗口来实现流量控制。流量控制就是控制发送方的发送速率,保证接收方有时间接收。TCP会话的双方都维护一个发送窗口和一个接收窗口。接收窗口的大小取决于应用程序、系统和硬件的限制。发送窗口取决于对等点通告的接收窗口。接收方发送的确认消息中的窗口字段可以用来控制发送方的窗口大小,从而影响发送方的发送速率。如果接收方确认消息的窗口字段设置为0,则发送方不能发送数据。TCP头中包含一个窗口字段,16位,代表窗口的字节容量,最大为65535。这个字段是为了接收端告诉发送端它有多少缓冲区来接收数据。因此,发送端可以根据接收端的处理能力发送数据,而不会导致接收端无法处理。接收窗口的大小大约等于发送窗口的大小。能详细说说拥塞控制吗?防止过多的数据注入网络。几种拥塞控制方法:慢启动(slow-start)、拥塞避免(congestionavoidance)、快速重传(fastretransmit)和快速恢复(fastrecovery)。慢启动将拥塞窗口cwnd设置为等于最大段MSS的值。在收到对新消息段的确认后,拥塞窗口最多增加一个MSS值。在每一轮传输之后,拥塞窗口cwnd加倍。为了防止拥塞窗口cwnd增长过快导致网络拥塞,还需要设置一个慢启动阈值ssthresh状态变量。当cwndssthresh时,停止使用慢启动算法,改用拥塞避免算法。当cwnd=ssthresh时,可以使用慢启动算法或拥塞控制避免算法。拥塞避免允许拥塞窗口cwnd缓慢增加,每经过一个往返时间RTT,发送方的拥塞窗口cwnd就增加1,而不是加倍。这样,拥塞窗口cwnd按照线性规律缓慢增长。不管是在慢启动阶段还是拥塞避免阶段,只要发送方判断网络拥塞(依据是没有收到确认),慢启动阈值ssthresh必须设置为一半发生拥塞时的发送窗口值(但不小于2)。然后将拥塞窗口cwnd重置为1,执行慢启动算法。这样做的目的是为了快速减少主机向网络发送的数据包数量,使拥塞的路由器有足够的时间来处理队列中积压的数据包。在快速重传中,有时个别段可能会在网络中丢失,但实际上网络并不拥塞。如果发送方长时间没有收到确认,就会发生超时,就会被误认为是网络拥塞。这会导致发送方误启动慢启动,将拥塞窗口cwnd设置为1,从而降低传输效率。快速重传算法可以避免这个问题。快速重传算法首先要求接收方在收到乱序报文段后立即发送重复确认,以便发送方及早知道报文段没有到达对方。只要发送方连续收到三个重复的确认,就应该立即重传对方还没有收到的报文段,而不是等待重传定时器超时。由于发送方尽快重传未确认的报文段,采用快速重传后,整个网络的吞吐量可以提高20%左右。快速恢复当发送方连续收到3次重复确认后,会将慢启动阈值ssthresh减半,然后将cwnd值设置为慢启动阈值ssthresh减半后的值,然后开始执行拥塞避免算法,使拥塞窗口缓慢地线性增加。使用快恢复算法时,慢启动算法只有在TCP连接建立,网络超时时才使用。采用这样的拥塞控制方法,使得TCP的性能得到显着提高。什么是SYN攻击?我们都知道TCP连接的建立需要三次握手。假设攻击者在短时间内伪造了一条不同IP地址的SYN报文。服务器每收到一条SYN报文,就进入SYN_RCVD状态,但是服务器发出的ACK+SYN报文得不到未知IP主机的ACK响应,就会填满SYN接收队列(未连接队列)随着时间的推移,服务器无法为正常用户提供服务。如何唯一确定一个TCP连接?一个TCP四元组可以唯一确定一个连接,四元组包括:源地址源端口目的地址目的端口。源地址和目的地址字段(32位)在IP头中,作用是通过IP协议向对方主机发送报文。源端口和目的端口字段(16位)在TCP头中,它们的作用是告诉TCP协议该报文应该发送给哪个进程。最后给大家分享一个Github仓库,里面有大斌编译的300多本经典计算机书籍PDF,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构还有算法,机器学习,编程生活等等,可以star一下,下次找书的时候可以直接在上面搜索,仓库持续更新中~Github地址:https://github.com/Tyson0314/java-books