网络协议栈简单设计(tcp)

news2025/2/25 5:51:58

网络协议栈简单设计(tcp)

接着这篇文章写的

TCP相对于Udp,分为两个部分:连接(三次握手、四次挥手)、交互(数据传输)

三次握手

tcp包结构体定义

依照tcp包头字段定义就行:

20230204154214

注意,tcp协议头不像udp有包长字段,因此TCP在建立连接时,客户端和服务端会协商设置每个报文的最大长度mss,比如send(buff)中buff的数据长度为2k,mss设置为0.5k,那么这个数据将会被切割成4个包进行传输

mtu和mss的区别:mtu处于数据链路层,最小传输单元,通过设置为1500,而mss处于传输层

// TCP协议头
struct tcphdr {
	unsigned short sport;  // 源目端口
	unsigned short dport;
    // 初始值:随机值,最大值4G,越界了可从1开始。
    // 序列号是字节的数量,不是包的数量,比如客户端发送的第一个tcp包是512个字节,第一个包的序列号是0,发送的第二个包的序列号就是512(或者服务器对这个包的确认好ack就是512),然后是1024... 
    // 这也是TCP基于流传输的重要体现
	unsigned int seqnum;   
	unsigned int acknum;

	unsigned char hdrlen_resv; 
    // 标志位,通过位操作实现包类别定义,包含FIN、SYN、RST等bit位标志,也可以像udp那样一个个定义
	unsigned char flag;    
	unsigned short window; // 滑动窗口大小,发送端和接收端都有

	unsigned short checksum;   // 校验和
	unsigned short urgent_pointer;
	unsigned int options[0];
};

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

#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

tcb

服务端收到第一次握手后,需要初始化tcb,将连接加入半连接队列,其结构体定义如下,

struct ntcb {
	unsigned int sip;
	unsigned int dip;
	unsigned short sport;
	unsigned short dport;

    // arp table mac地址可以从arp表读取
	unsigned char smac[ETH_ADDR_LENGTH];
	unsigned char dmac[ETH_ADDR_LENGTH];

	unsigned char status;
};

然后,进行三次握手,主要涉及连接状态的改变,以及每个状态下所作的事情

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,
};

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 *udp = (struct udppkt *)stream;
				if (udp->ip.type == PROTO_UDP) { 
					int udplength = ntohs(udp->udp.length);
					udp->data[udplength-8] = '\0';
					printf("udp --> %s\n", udp->data);
				} else if (udp->ip.type == PROTO_ICMP) { 

				} else if (udp->ip.type == PROTO_TCP) {    // 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) {
							tcb->status = TCP_STATUS_SYN_REVD;  // 转状态
							// send syn, ack pkt   // 发送ack
							// seqnum, ack 
						} 						
					} else if (tcb->status == TCP_STATUS_SYN_REVD) {
						if (tcp->tcp.flag & TCP_ACK_FLAG) {
							tcb->status = TCP_STATUS_ESTABLISHED;
						}						
					}
				}		
			} else if (ntohs(eh->h_proto) ==  PROTO_ARP) {

			}
		}
	}
}

处理完tcb状态机,就说明三次握手完成了,接下来进入数据传输阶段

tcp数据传输

滑动窗口:

20230204164652
  • 发送端:

慢启动:采用发一确认一的模式,数据发送太慢了,所以tcp允许多个包同时发送,tcp发送端的滑动窗口按照1mss、2mss、4*mssd的指数级增长发送数据,直到到达一直阈值

拥塞避免:达到阈值后,按照线性增长发送数据,如果出现网络抖动了,就会将接收端滑动窗口大小减半,然后继续线程增长
- 网络抖动:一个包出发出去到接收响应包的间隔RTT,如果小于0.1新包的rtt+0.9旧包的rtt,就说明发生了网络抖动,也就是数据接收时延太大

快重传:不等重传定时器过期,只要连续收到三个相同的ack,就立即重传

  • 接收端:

服务器从buff中读取数据,但是当buff剩余空间不足时,比如滑动窗口(可看作buff上的两个指针,滑动窗口前的是可读的,滑动窗口内部的是正在接收组织的,比如丢包超时重传,按照序列号将包进行排列)大小为0,将会通知客户端暂时无法接收数据。对于什么时候恢复数据发送,一般有两种做法:

  • 服务器检测到buff可写时,发送主推消息给客户端;这种方法实时性较高,但是有如下缺点:
    • 主推包丢失 =》 设置定时器发送多次主推 =》 客户端可能关机 =》 服务端资源浪费
    • 不符合TCP协议的潜规则,不利于编程实现,主动的一般是客户端
  • 客户端轮询服务器:TCP就采用这种发送,定时发送探测包

心跳包:TCP内部实现了keeplive机制,但TCP超时了会直接回收tcb,很不灵活。所以需要在用户态设计keeplive策略,比如第一次超时后,将超时间隔设置为当前的2倍

四次挥手:略

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/197933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jenkins配置

ssh配置 选择Manager Jenkins 选择Configure System&#xff0c;进入页面一直拉到底 Remote Directory&#xff1a;是你需要从jenkins服务拷贝到远程应用服务指定的目录 高级&#xff1a;可以配置ssh密码 配置maven&#xff0c;git&#xff0c;jdk 选择 Global Tool Configur…

dpdk无锁队列rte_ring实现分析

1. 概述 rte_ring(以下简称ring)是一个高效率的无锁环形队列&#xff0c;它具有以下特点&#xff1a; FIFO队列长度是固定的&#xff0c;所有指针存放在数组中无锁实现(lockless)多消费者或单消费者出队多生产者或单消费者入队批量(bulk)出队 - 出队N个对象&#xff0c;否则失…

05.抽象工厂模式

05.抽象工厂模式 概念 抽象工厂模式&#xff1a;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无须指定他们具体的类。抽象工厂又称为Kit模式&#xff0c;属于对象创建型模式。 抽象工厂可以将统一产品族的单独工厂封装起来&#xff0c;在正常使用中&#xff0…

08.适配器模式

08.适配器模式 概念&#xff1a; 将一个类的接口转化成用户需要的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 适配器模式可以分为类适配器和对象适配器两种&#xff0c;区别在于适配器角色对应被适配角色的适配是通过继承还是组合…

单月涨粉30w+,他们掌握引流法宝,小红书1月创作趋势是什么?

新年来到&#xff0c;在刚过去的1月&#xff0c;小红书平台涌现出哪些黑马博主&#xff1f;品牌在投放种草方面有何亮眼表现&#xff1f;为洞察小红书平台的内容创作趋势及品牌营销策略&#xff0c;新红推出1月月度榜单&#xff0c;从创作者及品牌两方面入手&#xff0c;解析月…

计算机网络-I/O多路复用机制

I/O多路复用机制 I/O多路复用&#xff08;multiplexing&#xff09;的本质是通过一种机制&#xff08;系统内核缓冲I/O数据&#xff09;&#xff0c;让单个进程可以监视多个文件描述符&#xff08;File descriptor是计算机科学中的一个术语&#xff0c;是一个用于表述指向文件的…

2.11 PID控制算法(四)补充

文章目录 1、理论1、代码3、分析3.1 比例项:3.2 比例+积分3.3 比例+积分+微分1、理论 1、代码 typedef struct {s32 Uplimit; //输出限幅s32 Downlimit; //输出限幅s32 target; // 目标输出量s3

leveldb源码解析二——SSTable

本章解析leveldb的基本组件——SSTable&#xff0c;SSTable一旦形成&#xff0c;就不会被改变&#xff0c;SSTable的操作有以下2种&#xff1a; 1、构建SSTable&#xff0c;在minor compaction和major compaction时&#xff0c;会构建SSTable&#xff0c;其中minor compaction是…

Opencv DNN C++ CPU 平台编译配置过程

Opencv DNN C CPU 平台编译配置过程 以下内容基于 windows 平台&#xff0c;实际上不同平台在基础工具齐全的情况下&#xff0c;编译过程差异并不大。 opencv 随着版本的更新&#xff0c;对于不同算子的支持也会逐步完善&#xff0c;所以尽量使用新的版本。 同时也可以把对应…

TCP滑动窗口协议与流量控制

谈到TCP的滑动窗口协议与流量控制&#xff0c;便会想起2006年去华为-3COM&#xff08;现H3C公司时&#xff09;面试时的场景。 当年毕业后&#xff0c;刚刚学了一点TCP的皮毛&#xff0c;仅仅是知道了TCP是面向连接的协议&#xff0c;以对每个报文都进行确认超时重传的机制来保…

摸鱼时间,画个吃豆人玩一下

Ⅰ . 吃豆人小游戏 Canvas API&#xff08;画布&#xff09;是在 HTML5 中新增的标签用于在网页实时生成图像&#xff1b;是一个非常适合&#xff0c;做一些有趣的小游戏 和 动画&#xff1b;下面我们来简单的写一下 这个小例子 &#x1f447; 文章目录Ⅰ . 吃豆人小游戏Ⅱ. 实…

学习嵌入式必读十本书,从C语言到ARM

学习嵌入式必读的十本书籍&#xff0c;按照C语言、数据结构、Linux、C、QT、单片机、ARM的顺序给大家推荐。 01 C语言 凡是计算机、电子、通信、自动化、机械专业的同学&#xff0c;大一的时候必学C语言&#xff0c;而且大部分高校选择的教材都是谭浩强。这本书在网上的评价褒…

【计算机程序设计思想与方法】2 什么是计算思维?

1.2 什么是计算思维? 如《【计算机程序设计思想与方法】1 什么是计算?》中所述,计算是利用计算机一步一步地执行指令来解决问题的过程,计算机科学是关于计算的科学。 正如数学家在证明数学定理时,有独特的数学思维。工程师在设计制造产品时,有独特的工程思维。艺术家在…

【验证码逆向专栏】某验“初代”滑块验证码逆向分析

声明 本文章中所有内容仅供学习交流&#xff0c;抓包内容、敏感网址、数据接口均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 本文章未经许可禁止转载…

【算法】递归

目录1.递归概述2.何时使用递归2.1.定义是递归的2.2.数据结构是递归的2.3.问题的求解方法是递归的3.递归模型4.应用本文参考&#xff1a; 《数据结构教程》第 5 版 李春葆 主编 1.递归概述 &#xff08;1&#xff09;在定义一个过程或函数时&#xff0c;出现直接或者间接调用自…

【微服务】Elasticsearch文档索引库操作(二)

&#x1f697;Es学习第二站~ &#x1f6a9;Es学习起始站&#xff1a;【微服务】Elasticsearch概述&环境搭建(一) &#x1f6a9;本文已收录至专栏&#xff1a;微服务探索之旅 &#x1f44d;希望您能有所收获 一.索引库操作 索引库就类似数据库表&#xff0c;mapping映射就类…

DGIOT低代码场景部门的搭建过程

[小 迪 导读] : 通过低代码页面与konva 大屏的页面设计,围绕部门&#xff0c;实现应用场景快速搭建1.部门创建以及权限分配1.1 打开部门管理页面1.2新增部门1.3 权限分配&#xff0c;点击刚创建的部门&#xff0c;在菜单分配中选择总控台和设备管理(低代码平台会过滤掉非低代码…

Wandb:make visualization better than Tensorboard

Wandb&#xff1a;make visualization better than Tensorboard wandb :一个在线的可多人协作的多功能可视化工具包 我最开始使用的tensorboard&#xff0c;还写了一些相关tensorboard的脚本用于实验。tensorboard这里就不详细介绍了&#xff0c;相信大家都比较了解。直到尝试了…

【MySQL数据库入门】:表的约束

表的约束 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务 逻辑角度保证数据的正确性。比如有一个字段是email&#xff0c;要求是唯一的。 表的约束很多&#xff0c;这里主…

版本管理之Git

一.版本控制器的方式1.1集中式版本控制工具集中式版本控制工具&#xff0c;版本库是集中存放在中央服务器的&#xff0c;team里每个人work时从中央服务器下载代 码&#xff0c;是必须联网才能工作&#xff0c;局域网或互联网。个人修改后然后提交到中央版本库。 举例&#xff1…