从1写TCPIP协议栈7:TCPIP协议!!!!!

发布时间 2023-04-05 22:58:51作者: 张一默

前言

  本次课程练习的重点来啦!!TCP(Transmission Control Protrol)协议,存在于传输层,面向字节流、带有确认与累计的滑动窗口协议。TCP可以是说IP子协议中最基础的、最完备的前辈协议,它首次实现了差错修正,弥补了IP协议与UDP协议的缺点。、

TCP协议简介(初步)

滑动窗口

 

  1、我们假设一条链路中有且仅有一个分组/一个数据包进行传输,那如何确定这个分组有没有到达呢?我们需要添加ACK进行接收成功的确认即可。

  2、那如果我们要保证两个分组之间有序传输呢?我们需要给ACK添加一个累计的属性(即ACK为确认成功的包序列号+1 = 下一次想要接收的包序列号)。

  3、在1、2的基础上我们已经可以实现一个有序的、可靠的数据包传输了。可是这样效率太低了,那我们如果一次性发很多包呢?这样就会产生很多问题?这些包的有序性在发送时如何保证?这些包发送的速率比接收的速率快怎莫办?其实这些我们都可以总结为一个问题点:“我们到底要在多长时间内注入多少包?”

  4、由此诞生了滑动窗口协议,即事先布置好发送的数个包装在一个窗口里上,每次发车都会把窗口上所有的数据包全部发送完然后慢慢等待每个包的ACK确认,如果某个包没有收到确认,发送方将进行重传。如果最先发送的包收到了ACK,这个窗口便有空间继续装下一个包,此为滑动。

  总之,分组滑动窗口解决了:

  • 数据不可靠的问题:通过维护发送方和接收方缓冲区,解决网络之间数据不可靠的问题,例如丢包、重复包、出错、乱序等。

  • 次序问题:通过滑动窗口协议,发送方和接收方可以按次序传输数据,从而保证数据次序正确。

  • 吞吐量问题:通过滑动窗口协议可以提高吞吐量。在传统的停止等待协议中,每发送一个包都需要等待对方的确认包,这样就会造成很大的延迟和低吞吐量。而在滑动窗口协议中,发送方可以连续发送多个数据包,并且不需要等待对方的确认,从而提高了数据传输的效率。

拥塞控制

  拥塞控制一般有速率的流量控制,比如根据环境的上线计算一个发送速率的上限值。另一种是基于窗口大小的流量控制,比如通过限定窗口的大小来限定一次性发送包的数量,窗口大则发的多,窗口小则发得少。会过来继续上面的内容,基本的滑动窗口协议已经帮助我们解决了数据的有序可靠传输,一定程度上也提高了传输效率。但是还有一个亟待解决?滑动协议的窗口具体应该多大?大了的话会不会导致延时?小的话比如1会不会导致失去作用?既然我都要,那就设计为动态的吧。具体动态如何变化我们后文继续说。

TCP头部

TCP头部仍然是跟在IP头部之后,TCP头部和IP头部一样一般都是20字节:

 

  • 源端口、目标端口:与IP协议中的IP源/目标IP地址进行绑定以对应唯一确定的应用进程,这一绑定关系称之为Sokcet。
  • 序列号:用来保证包次序的正确性,起始值0-2^32中间的随机值。
  • 确认号:期望接收的下一个序列号,只有在ACK位生效的前提下才有用。
  • 头部长度:TCP头部长度,以dword为单位。
  • CWR:拥塞窗口减少。
  • ECE:拥塞通知,发送方接受了一个拥塞通知。
  • URG:表示紧急指针字段有效,很少使用。
  • ACK:确认。
  • PSH:推送标志,接收方应该尽快给应用程序传输这个数据。
  • RST:重置链接,通常是发生错误的错误时候置位。
  • SYN:同步位,握手的期间置位。
  • FIN:结束数据传输标志,在分手的期间置位。
  • 窗口大小:控制滑动协议窗口的大小以实现拥塞控制。
  • 校验和:同UDP一样是伪intel校验和,因此每跳依次需要重新计算依次校验和。

TCP协议状态机

 

  图中的红色路径标注的是客户端的状态转换路径,也就是主动发起方。在完整的TCP协议开发时,我们需要给每一个线程(Socket)都要准备两种状态转换机制,也就是要求我们开发的协议栈既能作为服务器,也能作为客户端。因为是整车中ECU往往请求其他ECU的同时而被另外的ECU请求者,既是服务器又是客户端。而我们本次开发的工程代替的是服务器,因此实际上只需要考虑四个状态“Established”、“Closewait”、“Last ACK”、“Closed”之间的状态即可。这里需要强调一个参数就是2MSL(maximum segment lifetime),设置这个参数的主要原因是为了保证FIN重传,说大话就是要保证TCP链接的可靠性和完整性,还有另外一种作用就是为了确保此Sokcet不可被重复使用,否则将产生资源耗尽的情况。若是上层应用做了相关校验,也会抛出对应异常。

三次握手

四次分手

TCP接口定义

TCP_initial

用于初始化TCP协议栈参数。

TCP_input

用于处理TCP的输入包。

TCP_reset

用于重置TCP的链接。

TCP_open

随机挑选一个未使用的sokcet进行链接建立。

TCP_bind

用于Socket/IP-Port的绑定

TCP_listen

用于TCP链接请求的监听及回复。

TCP_close

用于关闭TCP链接。

TCP_find

用于查询TCP socket。

TCP_read_mss

读取选项字段的MSS,用于限制握手链接中的数据包长度。