超详细TCP/UDP面试常考(八大特性)

TCP/IP

传输层
1.端口号(Port)标识了一个主机上进行通信的不同的应用程序;
找到一个应用程序:端口号
在TCP/IP协议中, 用 “源IP”, “源端口号”, “目的IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信。
0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的.
1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的
端口号的最大范围65535(不可改变,16个比特位组成)
在这里插入图片描述

面试问题:
1.两个程序可以使用一个端口号吗?不可以
2.一个端口号可以被两个程序使用吗?可以

UDP
在这里插入图片描述

16位UDP长度,因此一个UDP的最大数据包为64K((2^16-1)/1024)
如果数据包大于64K,有两种能够解决的方法:
1:从应用层来分离和组装数据(程序员)
2:以大包的方式去发,在数据链路层进行分包和组包。(交给协议自行处理)
实际工作中,会采用第一种方式,来实现UDP大数据的传递,因为如果采用第二种方式,那么任意一个包丢失之后,整个数据包就丢失了,风险很大。
16位校验和:校验数据完整性,校验和执行逻辑:架设校验和的算法是MD5,MD5(UDP头部信息+UDP数据)发送端会将UDP所有内容发送给接收端,此时接收端就有了UDP的校验和和所有的数据,接收端就可以根据MD5(UDP头部信息+UDP数据)得到一个正确的校验和,然后用这个正确的校验和和UDP头部信息中的校验和做对比,如果相等则表示数据正确,否那么表示数据错误,直接舍弃。
UDP缓存区:只有接收缓存区,可以提高UDP的工作效率,之所以不需要发送缓存区,是因为UDP是不需要连接的,也就是不需要等待对方先连接的,所以最快的工作方式就是拿到消息就发。
双全工:发送端和接收端即可以接收消息又可以发送消息。
TCP协议(传输控制协议)
在这里插入图片描述

报头长度最大:四位最大可以标识15,TCP头部则有4*15个字节
最小为:20字节
6位标志位:
URG: 紧急指针是否有效ACK: 确认号是否有效
PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
TCP八大特性
1.确认应答(ACK)机制
在这里插入图片描述

每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发.
异常情况:
1.未成功发送消息-》再次发送消息
2.ACK确认消息丢失-》重新发送ACK
缺点:发消息会占用大量带宽。
2.超时重传
目的:解决确认应答的异常情况,对于发送发重复的消息会进行去重效果,
TCP稳定核心:确认应答和超时重传
超时重传策略:
1:使用递增的发送时间进行重试
设计思路:如果第一次发送失败,那么大概率第二次发送也会失败,为了节省带宽和程序的开销,那么他会次用递增的方式发送。
2:最大尝试失败后就会“停止”发送
当经过一定次数的发送之后,还是没有结果,那么发送端就会认为接收端下线了,就会“停止”发送,即使确认对方已经下线,还是会以固定的频率发送给没有内容的检查包,来探测对方是否上线。
3:连接管理
连接:3次握手
在这里插入图片描述

[CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接;
[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中,
并向客户端发送SYN确认报文.
[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态,
可以进行读写数据了.
TCP确认有连接:
1:发送端有发送能力
2:发送端有接收能力
3:接收端有发送能力
4:接收端有接收能力
在第一次握手时不能确认发送端和接收端的能力,第二次握手时,可以确定发送端的发送能力和接收端的接受能力,第三次握手,可以确定发送端的接收能力,接收端的发送能力。故两次握手不可以确定连接成功,四次握手可以但是没有必要。
断开连接(4次挥手)

在这里插入图片描述

[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到结束报文
段, 服务器返回确认报文段并进入CLOSE_WAIT;
[CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前
的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK
状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
[LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK, 彻底关闭连接.
CLOSE_WAIT:对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题。
TIME_WAIT:最大生存时间为2MSL(确认ACK MSL+看是否还会发消息FIN MSL)。
3次挥手有可能可以,基于TCP的延时应答,捎带应答,成不成功去决议接收缓冲区的数据多少。
4:滑动窗口
目的是:为了保障传输的性能。
滑动窗口的大小是固定的,窗口大小指的是无需等待确认应答而可以继续发送数据的最大值,所以无需在信息传输中带入
发送前四个段的时候, 不需要等待任何ACK, 直接发送;
收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推;
操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉;窗口越大, 则网络的吞吐率就越高;
出现丢包,如何进行重传?
异常情况1:
在这里插入图片描述

返回了ACK=6001此时服务器已将接收到1—6001的数据,返回的是接收区的最大值。
异常情况2:数据包丢失。
在这里插入图片描述

5:流量控制
根据接收缓冲区的实际情况,控制发送速度
在这里插入图片描述
接收缓存区窗口大小
接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等一系列连锁反应. 因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control);
如果TCP协议头的16位窗口大小为0,表示接收缓冲区已经满了,不能再进行发送,此时发送端就不会进行消息的发送,发送端会定时发送一个探测包,用来检测接收缓冲区的大小,如果接收缓冲区有位置了,那么消息就可以继续进行发送。
6:拥塞控制
在这里插入图片描述

TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;
规则:发包从一开始,以默认值16为临界值,当小于此值时,以指数增加的方式发包,当等于这个值时,就开始了线性增长方式发包,一直到有大量丢包的请求时(发包已经到当前时间段的极值),这个时候就会将为1,然后将临界值设置为上次最大发包的一半,继续进行此操作。
7:延迟应答
延迟应答是在流量控制的基础上优化发送效率的。
策略1:固定一定的时间段,发送一个延迟应答包。
策略2:接受一定次数的包后,来一个延迟应答。
延迟应答时间不能超过MSL(最大生存时间),如果超过MSL,就会触发超时重传,它以为消息丢失。
窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽
量提高传输效率;那么所有的包都可以延迟应答么? 肯定也不是;
数量限制: 每隔N个包就应答一次;
时间限制: 超过最大延迟时间就应答一次;

8:捎带应答
提高消息传输的性能
是在延迟应答的基础上继续优化传输效率的。
在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应用层也是 “一发一收” 的. 意味着客户端给
服务器说了 “How are you”, 服务器也会给客户端回一个 “Fine, thank you”;
那么这个时候ACK就可以搭顺风车, 和服务器回应的 “Fine, thank you” 一起回给客户端

TCP的稳定性确认应答,超时重传,连接管理,滑动窗口,流量控制(如何保证UDP发送消息的稳定性)
保证提高性能:拥塞控制,延时应答,捎带应答

沾包问题
首先要明确, 粘包问题中的 “包” , 是指的应用层的数据包.
在TCP的协议头中, 没有如同UDP一样的 “报文长度” 这样的字段, 但是有一个序号这样的字段.
站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中. 站在应用层的角度, 看到的只是一串连续的字节数据. 那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包

那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界.
对于定长的包, 保证每次都按固定大小读取即可; 例如Request结构, 是固定大小的, 那么就从缓冲区从头开始按sizeof(Request)依次读取即可;对于变长的包, 可以在包头的位置, 约定一个包总长度的字段, 从而就知道了包的结束位置; 对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔符不和正文冲突即可“\n”);
bufferedWriter.write(sermsg + “\n”);
bufferedWriter.flush();//缓冲区刷新
思考: 对于UDP协议来说, 是否也存在 “粘包问题” 呢?
对于UDP, 如果还没有上层交付数据, UDP的报文长度仍然在. 同时, UDP是一个一个把数据交付给应用层. 就有很明确的数据边界. 站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收不会出现"半个"的情况
TCP异常情况
进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
机器重启: 和进程终止的情况相同.
机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在,也会把连接释放.
另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ断线之后, 也会定期尝试重新连接.

TCP VS UDP
1:UDP是无连接的,TCP是有连接的
2:UDP是不稳定的,TCP是稳定的
3:UDP是面向数据报的,TCP是面向数据流的
4:UDP没有发送缓冲区,TCP有发送缓冲区
5:TCP稳定,UDP高效

TCP 连接有多重状态,如何在系统中查看某个连接的状态 ?
(1) ping :连接是否正常,主要测试抖动,丢包率
(2) netstat :某个连接的状态
(3) ifconfig:显示或配置网络的设备
(4) traceroute:跟踪数据包到达主机所经过的路由工具

相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页