【计算机网络】面试官眼中的 TCP

通过从多方面详细讲解TCP和UDP的区别这篇博客,大家都知道,我们使用的TCP是面向连接的可靠性传输,所以TCP协议的通讯过程必然要涉及建立连接和断开连接的过程,即常听到的面试官口中:三次握手和四次挥手过程
当我们和面试官 吧啦吧啦~讲完过程以后,立马有一个新问题:TCP是如何保证可靠性的,又是如何提高性能的
我们再次认真的讲完以后,还会有TCP出现的粘包问题该怎样解决?其他问题?

所以,我接下来的任务就是一一攻破它们!!

1. TCP 协议通讯过程

先看下面这张图,并尽量记住它,因为很重要!!
在这里插入图片描述
这里我结合上图详细讲解这个过程:
1. 服务器初始化:
调用socket, 创建文件描述符;
调用bind, 将当前的文件描述符和ip/port绑定在一起; 如果这个端口已经被其他进程占用了, 就会bind失败;
调用listen, 声明当前这个文件描述符作为一个服务器的文件描述符, 为后面的accept做好准备;
调用accecpt, 并阻塞, 等待客户端连接过来;
2. 建立连接的过程:即 三次握手
调用socket, 创建文件描述符;
调用connect, 向服务器发起连接请求;
connect会发出SYN段并阻塞等待服务器应答; (第一次)
服务器收到客户端的SYN, 会应答一个SYN-ACK段表示"同意建立连接"; (第二次)
客户端收到SYN-ACK后会从connect()返回, 同时应答一个ACK段; (第三次)
3. 数据传输的过程:
建立连接后,TCP协议提供全双工的通信服务; 所谓全双工的意思是, 在同一条连接中, 同一时刻, 通信双方可以同时写数据; 相对的概念叫做半双工, 同一条连接在同一时刻, 只能由一方来写数据;
服务器从accept()返回后立刻调 用read(), 读socket就像读管道一样, 如果没有数据到达就阻塞等待;
这时客户端调用write()发送请求给服务器, 服务器收到后从read()返回,对客户端的请求进行处理, 在此期间客户端调用read()阻塞等待服务器的应答;
服务器调用write()将处理结果发回给客户端, 再次调用read()阻塞等待下一条请求;
客户端收到后从read()返回, 发送下一条请求,如此循环下去;
4. 断开连接的过程:即四次挥手
如果客户端没有更多的请求了, 就调用close()关闭连接, 客户端会向服务器发送FIN段,服务器进入CLOSE_WAIT状态 (第一次);
此时服务器收到FIN后, 会回应一个ACK, 同时read会返回0 (第二次);
read返回之后, 服务器就知道客户端关闭了连接, 也调用close关闭连接, 这个时候服务器会向客户端发送一个FIN;,客户端进入TIME_WAIT状态 (第三次)
客户端收到FIN, 再返回一个ACK给服务器; (第四次)

这里我再加一点介绍——基于状态机的转化,因为我之前面试有被问到哦
服务端状态转化:

  • [CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接;
  • [LISTEN -> SYN_RCVD] 一旦监听到连接请求(SYN - 同步报文段), 就将该连接放入内核的等待队列中, 并向客户端发送SYN确认报文。
  • [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态, 可以进行读写数据了。
  • [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到FIN - 结束报文段, 服务器返回确认报文段并进入CLOSE_WAIT。
  • [CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)。
  • [LAST_ACK -> CLOSED] 服务器收到了客户端确认的ACK, 彻底关闭连接。

客户端状态转化:

  • [CLOSED -> SYN_SENT] 客户端调用connect, 发送SYN - 同步报文段。
  • [SYN_SENT -> ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态, 开始读写数据。
  • [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时, 向服务器发送FIN - 结束报文段, 同时进入FIN_WAIT_1。’
  • [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的FIN - 结束报文段。
  • [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的FIN - 结束报文段, 进入TIME_WAIT, 并发出LAST_ACK。
  • [TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(MSL 报文最大生存时间)的时间, 才会
    进入CLOSED状态。
TCP保证可靠性,并提高了传输性能

1. 保证可靠性:

  • 校验和 - - 接收端对发送端的报头和数据进行校验
  • 序列号(去重并按序到达) - - 保证接收端收到的数据与发送端相同
  • 确认应答/ACK 机制- - 每个ACK带有对应的确认序列号,告诉对端下次发送哪个数据
  • 超时重传机制 - - A在特定时间间隔内未收到B的确认应答就会进行重发(时间设定!)
  • 连接管理机制 - - 三次握手与四次挥手,CLOSE_WAIT,TIME_WAIT=2*MSL
  • 流量控制机制 - - 根据探测到的接收端的窗口大小,来决定发送端的发送速度
  • 拥塞控制机制 - - 根据网络状况不断试探传输数据的最大值

做法:引入慢启动机制衡量网络状况,先按2^n指数规律增长拥塞窗口,当拥塞窗口等于慢启动阈值时只加法增大。探测到网络拥塞(大量丢包)时再次触发慢启动机制,利用拥塞避免算法:新阈值=上次拥塞窗口最大值/2 ,重新计算慢启动阈值

2. 提高传输性能

  • 滑动窗口 - - 一次发送多条数据,将多个段的等待时间重叠。

滑动窗口是OS开辟的发送缓冲区的一部分,它的大小决定无需应答可继续发送数据的最大值,它的大小可根据接收方信息的反馈进行调整。滑动窗口满后,只有收到前面的应答才能向后移动,并将应答过的数据从缓冲区中删除。窗口越大网络吞吐率越高。
若出现丢包:数据包递达,ACK丢失 - - 只要后续ACK有确认则认为前面全部收到;数据包丢失 - - 收到三个相同数据包请求则进行快速重发

  • 快速重传 - - 收到三个相同的数据包请求则立即进行重传

超时重传是重传的底线,快重传是重传的极限

  • 延迟应答

数量限制:每隔N个包就应答一次 - - 2
时间限制:超过最大时间就应答一次 - - 200ms

  • 捎带应答

服务器不单应答,而且还携带数据

3. 定时器设置

  • 超时重传定时器:500ms的整数倍,累积到一定重传次数就强制断开连接
  • 保活定时器:接收端定期询问对方是否还在,不在的话就主动释放连接
  • TIME_WAIT定时器:2*MSL是可接受等待的最大时间,超时就断开连接
    其中MSL 是指 最大传送时间
TCP的粘包问题

包:应用层的数据包
粘包:TCP协议中并没有像UDP中“报文长度”的字段,只有序号将数据包排列好放在缓冲区中。虽然在传输层的TCP看来数据包是一个一个过来的,但是对于上层应用只能看到一连串的字节数据。应用层便无法判断完整的数据包。
解决办法:明确两个数据包之间的边界

  • 对于定长的包,保证每次都按固定的大小读取 - sizeof(request)
  • 对于变长的包,可以在包头的位置约定设置包总长度字段,从而就知道包结束的位置
  • 对于变长的包,还可以在包与包之间使用明确的分隔符 - 比如:空行

思考:UDP是否存在粘包问题
UDP的报文中包含了UDP的总长度,并且它交付给上层的是一个一个数据包,直接有很明显的边界。当上层在使用数据时,要么收到完整数据包要么什么都没有。
所以UDP中不会出现粘包问题。

TCP 的其他异常
  • 进程终止/机器重启:进程终止会释放文件描述符,OS调用close关闭文件。而进程的生命周期随内核,所以仍然可以正常进行四次挥手。
  • 机器断电/网线断开:接收端认为链接还在,一旦进行写入操作发现错误,就会进行reset。即使没有写入操作,TCP也内置一个保活定时器,会定期询问对方是否存在,若不存在就主动释放连接。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值