要实现用户态协议栈,必须要搞懂TCP,TCP 11个状态、滑动窗口、拥塞控制、定时器等等。

要使用用户态协议栈,内核提供的epoll就不起作用了,我们需要自己实现用户态的epoll。epoll内部涉及到一个回调的时机,回调的作用是将红黑树中的节点添加进就绪队列,具体在epoll原理里面会具体讲解。搞清楚TCP的11个状态,我们就明白应该在什么时机进行回调了。

TCP状态转换图

在前面的[posix与网络协议栈](Build software better, together api和网络协议栈.md)中,已经介绍了tcp的状态转换。可以结合tcp状态转换图一起看。

TCP状态保存在哪里?保存在TCB中,即TCP PCB,协议控制块。里面包含了socket信息,以及sendbuffer,recvbuffer。TCB保存了从listen到time_wait的所有状态。

用户态TCP协议栈实现

前面实现了UDP协议栈,TCP协议栈实现也是类似的,但是比UDP要复杂很多。

TCP头定义

​seq num初始值是多少,到达最大值(2^32 - 1)后怎么样, 会越界吗?

seq num初始值是一个随机值,之后累加。到达最大值后又从0开始计算,不会越界。

seq num指的是包的数量,还是字节数量?

计算的时候,使用的都是字节数。

TCP的包是什么意思?TCP头为什么没有包长?

TCP前后两个包都有序号,就可以计算出包的长度。

ack num = seq num + 包长。

header length是4bit,最大值是15,单位是4个字节,所以TCP头最大时15*4 = 60字节。没有option的话,TCP头是20字节,header length值就是5。

window size,能够接收数据的最大容量。

urgent pointer, 如果URG位置1,就是告诉对端从这个位置开始的数据,要马上处理。

struct tcphdr {unsigned short sport;unsigned short dport;unsigned int seqnum;unsigned int acknum;unsigned char hdrlen_resv;unsigned char flag; unsigned short window;unsigned short checksum;unsigned short urgent_pointer;unsigned int options[0];};

定义TCP flag

#define TCP_CWR_FLAG     0x80
#define TCP_ECE_FLAG        0x40
#define TCP_URG_FLAG        0x20
#define TCP_ACK_FLAG        0x10
#define TCP_PSH_FLAG        0x08
#define TCP_RST_FLAG        0x04
#define TCP_SYN_FLAG        0x02
#define TCP_FIN_FLAG        0x01

后面5个flag比较重要

ACK 是用来确认的

PSH 告诉对端赶紧通知应用程序把数据包处理了,在数据传输过程都可以设置成PSH。

RST,收到的ack num,或者seq num、widow size非法,或者数据不对了,就给对端回一个RST。三次握手发送第一次后,超时没收到对端的第二次握手,也会发送一个RST。

SYN只是在连接开始的时候,用于告诉对端seq num,也就是发送的第一个包的序号。

FIN,终止。

C++后台开发系统学习地址:C/C++Linux服务器开发高级架构师/C++后台开发架构师​

以下学习资料,C++后台开发面试题,教学视频,C++后台开发学习路线图,免费分享有需要的可以自行添加:学习资料群720209036 自取

定义TCP包

struct tcppkt {struct ethhdr eh; // 14struct iphdr ip;  // 20 struct tcphdr tcp; // 8unsigned char data[0];};

定义TCP状态

typedef enum _tcp_status {TCP_STATUS_CLOSED,TCP_STATUS_LISTEN,TCP_STATUS_SYN_REVD,TCP_STATUS_SYN_SENT,TCP_STATUS_ESTABLISHED,TCP_STATUS_FIN_WAIT_1,TCP_STATUS_FIN_WAIT_2,TCP_STATUS_CLOSING,TCP_STATUS_TIME_WAIT,TCP_STATUS_CLOSE_WAIT,TCP_STATUS_LAST_ACK,};

定义TCB

struct ntcb {unsigned int sip;unsigned int dip;unsigned short sport;unsigned short dport;unsigned char smac[ETH_ADDR_LENGTH];unsigned char dmac[ETH_ADDR_LENGTH];unsigned char status;};

实现TCP三次握手

服务端处理好三次握手的状态转换,客户端就能与服务器建立连接。

int main() {struct nm_pkthdr h;struct nm_desc *nmr = nm_open("netmap:eth0", NULL, 0, NULL);if (nmr == NULL) return -1;struct pollfd pfd = {0};pfd.fd = nmr->fd;pfd.events = POLLIN;struct ntcb tcb;while (1) {int ret = poll(&pfd, 1, -1);if (ret < 0) continue;if (pfd.revents & POLLIN) {unsigned char *stream = nm_nextpkt(nmr, &h);struct ethhdr *eh = (struct ethhdr *)stream;if (ntohs(eh->h_proto) ==  PROTO_IP) {struct udppkt *tcp = (struct udppkt *)stream;if (tcp->ip.type == PROTO_TCP) {struct tcppkt *tcp = (struct tcppkt *)stream;unsigned int sip = tcp->ip.sip;unsigned int dip = tcp->ip.dip;unsigned short sport = tcp->tcp.sport;unsigned short dport = tcp->tcp.dport;tcb = search_tcb();if (tcb->status == TCP_STATUS_LISTEN) { //if (tcp->tcp.flag & TCP_SYN_FLAG) {client_tcb = create_tcb();client_tcb->status = TCP_STATUS_SYN_REVD;// 将sip,sport,smac与dip,dport,dmac互换// send syn, ack pkt// seqnum, ack } } else if (tcb->status == TCP_STATUS_SYN_REVD) {if (tcp->tcp.flag & TCP_ACK_FLAG) {client_tcb->status = TCP_STATUS_ESTABLISHED;}}}}}}}

数据发送过程

MSS(Maximum Segment Size,最大报文长度),是TCP协议定义的一个选项,MSS选项用于在TCP连接建立时,收发双方协商通信时每一个报文段所能承载的最大数据长度。

MTU,是对数据链路层的限制。

客户端到服务器

  1. 发送1M的文件

  2. sendbuffer = 2k

  3. mss = 512

  4. mtu = 1500

while (1) {poll(fd)send(fd, buffer, 1k, 0);}

如果客户端sendbuff = 2k, mss = 512。要分4个包发送

客户端能否发出去这4个包呢?

不一定,取决于服务器的接收窗口window size大小。如果window size是1024。客户端如果发送两个包,每个包大小512,则如果服务器的应用程序没有取,那么回的ack包里面的window size就会是0,那么客户端的sendbuffer里面就会剩下1k数据发送不了。就会等服务器数据处理完了再发送。

如果每发送一个包,就等待ack,这种效率太慢。我们需要能够同时发送多个包,就是慢启动的过程。

慢启动的过程

第一次发送 1 * mss

第二次发送 2 * mss

第三次发送 4 * mss

​慢启动的过程,发送1mss,2mss,4*mss,…

如何判断数据包超出网络负载?

通过判断超时。超时时间又怎么算呢?

拥塞避免,从客户端往服务器发送数据包,网络上的数据越来越多,造成网络拥塞,致使服务器没有办法正确接收数据。

如何判断数据包超出网络负载?

rtt, round trip time, 数据包往返一次的时间。

进入电梯这种弱网的环境下,rtt突然变大,叫做抖动。

当前rtt计算方法

rtt = 0.1 * rtt(new) + 0.9 * rtt(old), 是一个消抖的过程。

用于判断当前这一次有没有超时,一旦出现超时,判断在发送包的数量上是否需要减一减,超出网络负载。

如果服务器的window size是0,没有接收的空间了,客户端就不能再发送了。如果服务器处理完数据,有空间了,客户端怎么能知道服务器有空间了呢?

服务器端window是0,等到服务端将数据处理完,window不为0的时候,客户端怎么能知道服务器已经有接收空间了呢?

  1. 服务器主动告诉客户端。-- 不好的地方,如果通知包在网络中丢失了怎么办?

  2. 客户端定时查询 – TCP是这种做法,当收到对端window为0,定时发送探测包, 就是探测定时器。

客户端定时查询更好。

滑动窗口

滑动窗口也是以mss作为单位的。

滑动窗口。

在接收的过程中间,准备好指针。一根指针对应已经发送确认的,另一根指针对应允许接收的最大位置。两根指针之间的长度表示window size。

回ack的表示前面的数据都已经收到,都可以调用recv进行处理;未发送ack先不用管,表示数据还没有组织好,还不能调用recv处理。

​window大小和recvbuff的关系?

window size和recvbuff是有关系的,但是是两个概念。

看起来window size = recvbuff / 2, 这个没有找到具体的说明。

定时器

重传定时器、探测定时器(坚持定时器)、keepalive、TIME_WAIT定时器、延迟ack定时器

重传定时器,发送端发送一个包后,启动重传定时器,RTT超时重传,如果在规定时间内收到ack包,则撤销定时器;

探测定时器,如果对端window size 是0,则启动探测定时器;

TCP已经有keepalive,应用层为什么还要提供心跳包?

TCP keepalive也是心跳包,超时主动回收TCB,应用层感知不到。应用层心跳包可控制性更强。

TIME_WAIT定时器,time_wait时间是2msl,防止4次挥手的最后一次ack丢失。

延迟ack定时器,接收端收到TCP包,启动200ms定时器,后面再次收到数据,则重置定时器,超时后发送ack。

参考资料

推荐一个零声教育C/C++后台开发的免费公开课程,个人觉得老师讲得不错,分享给大家:C/C++后台开发高级架构师,内容包括Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

tcp协议栈实现,tcp定时器与滑动窗口实现相关推荐

  1. 后端开发【一大波有用知识】tcp/ip定时器与滑动窗口详解

    为什么udp有包长,而tcp没有包长. 首先,send()发送一次发送1k,发送一次缓冲区满了就会返回-1.2k发送出去后缓冲区被清空,send()才会被再次调用.最大传输片会打印四个包发送.而最大传 ...

  2. 网络基础2-3(TCP协议,三次握手,四次挥手,TIME_WAIT状态的作用,TCP如何保证可靠传输,TCP连接中状态转化,滑动窗口,流量控制,快速重传,拥塞窗口,延迟应答,捎带应答,粘包问题)

    TCP协议 TCP协议概念 TCP全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传输进行一个详细的控制 TCP协议 ...

  3. TCP/IP学习笔记(三)TCP流量控制以及滑动窗口

    众所周知,TCP是有缓冲区的,比如接收缓冲区用于存放已经到达但是还没有被应用程序及时处理的数据.但是任何缓冲区都是有一定大小的,如果发送方发送数据过快,而接收方处理数据过慢,就会导致接收方的接收缓冲区 ...

  4. tcp协议头窗口,滑动窗口,流控制,拥塞控制关系

    tcp协议头窗口,滑动窗口,流控制,拥塞控制关系 参考文章 TCP 的那些事儿(下) http://coolshell.cn/articles/11609.html tcp/ip详解--拥塞控制 &a ...

  5. TCP滑动窗口(发送窗口和接受窗口)

    TCP窗口机制 TCP header中有一个Window Size字段,它其实是指接收端的窗口,即接收窗口.用来告知发送端自己所能接收的数据量,从而达到一部分流控的目的. 其实TCP在整个发送过程中, ...

  6. tcp 协议中发送窗口的大小应该是_面试必备--TCP协议中的窗口机制滑动窗口详解...

    窗口机制分类 在TCP协议当中窗口机制分为两种: 1.固定的窗口大小 2.滑动窗口 固定窗口存在的问题 我们假设这个固定窗口的大小为1,也就是每次只能发送一个数据,只有接收方对这个数据进行了确认后才能 ...

  7. TCP/IP(十一)TCP滑动窗口和拥塞控制

    目前建立在TCP协议上的网络协议特别多,有telnet,ssh,有ftp,有http等等.这些协议又可以根据数据吞吐量来大致分成两大类:(1)交互数据类型,例如telnet,ssh,这种类型的协议在大 ...

  8. tcp欢动窗口机制_TCP 滑动窗口(发送窗口和接收窗口)

    TCP 滑动窗口(发送窗口和接收窗口) TCP的滑动窗口主要有两个作用,一是提供TCP的可靠性,二是提供TCP的流控特性.同时滑动窗口机制还体现了TCP面向字节流的设计思路. TCP的Window是一 ...

  9. TCP 滑动窗口的简介

    TCP的滑动窗口主要有两个作用,一是提供TCP的可靠性,二是提供TCP的流控特性.同时滑动窗口机制还体现了TCP面向字节流的设计思路.TCP 段中窗口的相关字段. TCP的Window是一个16bit ...

最新文章

  1. 在日志文件中输出当前时间
  2. C#--动态操作DataTable
  3. 360视域分析 cesium_Cesium-空间分析之通视分析(附源码下载)
  4. [剑指offer]面试题第[43]题[Leetcode][第233题][JAVA][1~n整数中1出现的次数][找规律][递归]
  5. ssh框架常见错误与解决方法
  6. 全球数字经济白皮书——疫情冲击下的复苏新曙光
  7. Embedding 技术在推荐系统中的应用实践
  8. Python脚本控制的WebDriver 常用操作 十八 获取测试对象的css属性
  9. C++自己实现一个String类
  10. [算法]px4位置估计-inav (2017/10/26更新)
  11. Android App 架构设计
  12. Origin抗锯齿和出现大C
  13. tibco rv java实例_Tibco介绍及应用.pptx
  14. MapReduce作业提交流程
  15. 源码:Mybatis的LogFactory生成逻辑
  16. python第三方库 invalid requirement_Python - 生成 requirement.txt 文件
  17. 代码审计--17--修复方案汇总(上)
  18. 腾讯大佬的 Python 编码规范
  19. Removal【套路DP】
  20. Vi,Java,Ant,Junit的自学报告

热门文章

  1. 服务器安全狗 Linux 安装教程
  2. IDEA如何配置Maven及版本统一管理
  3. 计算机网络华为路由器配置实验,计算机网络 路由器基本命令操作实验报告格式 华为.doc...
  4. 图片上传到阿里云服务器_07-文件上传到阿里云OSS实战(二)
  5. 益和VA走出国门,中新企业交易会硕果累出
  6. 无人机机臂上可以绑上杆子,防撞防侧摔
  7. 汉能面试-互联网老辛根据学员面试录音整理
  8. 25个HTML5和JavaScript游戏引擎库(转)
  9. 1/t的傅里叶变换证明
  10. 【测开基础之计算机网络】一: 计算机网络概述