网卡 (八) 前几篇中介绍的协议存在的问题以及解决方案


如果tcp ip 协议 只包括 udp ip arp 看来也能通信
但是为什么 tcp ip 协议 搞这么复杂呢? 

我们先看一下 前几篇中介绍的协议存在的问题


1. 目标主机网络是否可用
2. ip层及udp层默默丢包,但发数据一方想要知道ip丢了包,以及为什么丢包
3. 包的顺序
4. 怎么确认信息已达
这些问题在原来那些协议中都没有解决,那么为了解决这个问题,要怎么办?

一般在计算机中要解决这种问题,一般会增加一层或者兼容一层.

或者在tcpip解决这种问题
	引入 icmp ,解决 1 2  问题,icmp 活跃在 ip层和 传输层
	引入 tcp ,解决 3 4 问题, tcp 和 udp 并存在 传输层
或者在应用层解决这种问题
	什么应用层协议基于udp且解决了可靠性问题



前几篇中 用到的结构体


struct netif // 这个结构体是用户建立的,ip层和 udp和arp 层都用到了
	ip udp arp 层 用到的结构体
		用来存储 ip 子网掩码 网关地址 网卡初始化函数 网卡接收函数(从底层网卡驱动接收数据,被网卡驱动调用)
		// ip 层用来校验ip,收的时候匹配netif,匹配到的netif 是 目的网卡
		// arp 层用来是否用ip,用netif->linkoutput来发包 , ethernet_input 中的 netif 是 收数据网卡的netif
		// udp 层用ip netmask ,发的时候调用ip层函数ip_route 来获取netif
		// 使用ip 的时候会判断是否广播,是否多播,是否null,两个ip是否在同一子网

struct etharp_entry
	arp层 用到的结构体 // 只有 arp 维护且用到了
		从来存储 ip mac 对

struct udp_pcb // 只有udp维护了,且用到了

	udp 层 用到的结构体
	用来存储 己方 ip 和 port 以及 对端 ip 和 port (目前看起来 存储的对端ip 和port 没大用) 

udp 及ip 及arp 封包过程

用户层初始化
用户层开始发包
sprintf((char*)lwip_udp_send_buf,"udp_send send %d\r\n",t_send_cnt); // 填充用户数据
ubuf_client = pbuf_alloc(PBUF_TRANSPORT, strlen((char *)lwip_udp_send_buf), PBUF_RAM);
ubuf_client->payload = lwip_udp_send_buf; // lwip_udp_send_buf 已经有数据了
udp_send(udp_client_pcb,ubuf_client); // 发包
udp层
udp_send
	调用 ip_route 获取 netif // 涉及到 ip 层.
	udp_sendto_if
		组包
			1. 将用户层数据拷贝到udp包里面
			2. 填充包头
				源端口号从 udp_pcb 中拿
				dest port 从 参数中拿
				length 和 checksum 自己算
		ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif);
		// q->payload 是 封装了udp 的 buf
		// src_ip 是 netif->ip_addr 或者 pcb->local_ip
		// ttl 为 udp_new的时候的 pcb->ttl = UDP_TTL; 传给 ip 层中,ip包头部要设置这个参数
		// pcb->tos 给 ip 用的 
		// IP_PROTO_UDP 代表协议是 udp,用来填充ip包头.
		// netif ,给 ip 层  用的


ip 层
ip_output_if
	1. 将udp层数据拷贝到ip包里面
	2. 填充包头
		IPH_TTL_SET(iphdr, ttl);
		IPH_PROTO_SET(iphdr, proto);
		ip_addr_copy(iphdr->dest, *dest);
		IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
		IPH_TOS_SET(iphdr, tos);
		IPH_LEN_SET(iphdr, htons(p->tot_len));
		IPH_OFFSET_SET(iphdr, 0);
		IPH_ID_SET(iphdr, htons(ip_id));
		ip_addr_copy(iphdr->src, netif->ip_addr);
		iphdr->_chksum = chk_sum;
		发包
			netif_loop_output(netif, p, dest);
			ip_frag(p, netif, dest); // 拆分包
			netif->output(netif, p, dest); // etharp_output
arp 层
etharp_output
	1.将ip层数据拷贝到arp包里面
	找到dest // 这个过程即使 遍历 struct etharp_entry 的过程
	etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
		2.填写帧头
			  ETHADDR32_COPY(&ethhdr->dest, dst);
			  ETHADDR16_COPY(&ethhdr->src, src);
			  ethhdr->type = PP_HTONS(ETHTYPE_IP);
		3. 发包
			netif->linkoutput(netif, p); //low_level_output
				// 调用网卡驱动发包 ENC28J60_Packet_Send(send_len,lwip_buf);
				

ip 及 udp 拆包过程

  • 用户层做初始化
// 其实数据时从网卡层来的,但是用户层肯定要先做初始化,绑定端口,ip 之类的.
udp_server_pcb = udp_new(); // 新建控制块
udp_bind(udp_server_pcb,IP_ADDR_ANY,UDP_RECV_PORT); // 绑定端口及ip
udp_recv(udp_server_pcb,Udp_Server_Recv,NULL); // 注册udp数据接收回调函数
  • 网卡层

ethernetif_input(&enc28j60_netif); 
	p = low_level_input(netif);
		ENC28J60_Packet_Receive(MAX_FRAMELEN,lwip_buf); // 网卡驱动
	ethhdr = p->payload;
	switch (htons(ethhdr->type)){ // 网卡层只是对以太网帧的头部中的 type 做了过滤,其他没过滤 // 目前以太网帧只支持 ARP 和 IP ,除了 PPOE
	  case ETHTYPE_IP:
	  case ETHTYPE_ARP:
	  	netif->input(p, netif) // ethernet_input // 虽然过滤了,还是将以太网帧全部交付到了 arp 层
	  	break;
	  default:
	  	;
	}	
  • arp 层
1. 解析 dest , 如果是多播或者广播,置位 p->flags |= PBUF_FLAG_LLMCAST; p->flags |= PBUF_FLAG_LLBCAST;
2. 解析 type
	如果是 IP 
		拆包 pbuf_header(p, -ip_hdr_offset)
		ip_input(p, netif); // 发包给ip层
	如果是 arp // payload 就是 arp 请求或者 arp 应答
		etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
			ethhdr = (structeth_hdr *)p->payload;
			hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
			对hdr->hwtype hdr->hwlen hdr->protolen hdr->proto 解析
			拿出 ip mac ,更新 ip mac 对
			switch (hdr->opcode) {
				ARP_REQUEST: 
					// 组reply包
					netif->linkoutput(netif, p); //low_level_output
					// 调用网卡驱动发包 ENC28J60_Packet_Send(send_len,lwip_buf);
				ARP_REPLY:
					更新 ip mac 对 ,但是刚才已经更新过,所以不更新了.
			}
  • ip层
ip_input(p, netif);
	1.拆包 iphdr = (struct ip_hdr *)p->payload;
	2. 对长度和校验和进行判断
	3. 根据 dest(目标ip) 来匹配 netif
	4. 校验 src(源ip),不能是多播及广播地址
	5. 如果netif 为NULL,表示不是给本机的,调用 ip_forward 转发出去
	6. 如果为分组包,需要拼接.
	7. 如果netif 不为空,判断ip头中的协议类型字段
		switch (IPH_PROTO(iphdr)) {
			UDP : udp_input(p, inp);// 这个p 是 ip 的包,并不是udp 的包头
			TCP : tcp_input(p, inp); 
			icmp: icmp_input(p, inp);
		}
udp
udp_input(p, inp);
	1. 拆ip包 iphdr = (struct ip_hdr *)p->payload;
	2. 对 ip 包的 帧头进行判断
	3. 拆出udp包 udphdr = (struct udp_hdr *)p->payload;
	4. 根据ip查找控制块
	5. 查看 控制块的信息(port 和 ip) 是否和 当前包匹配
	6. pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); // 调用用户注册的回调 Udp_Server_Recv
用户层
Udp_Server_Recv
	memcpy(lwip_udp_recv_buf,p->payload,200); // 拷贝
	LCD_ShowString(18,156,240,320,(u8*)lwip_udp_recv_buf,LCD_BLACK); //显示用户数据 

LWIP 混乱的地方

以太网和arp混乱
	封以太网包的时候用的是arp 的函数
arp 和 IP 混乱
	解析以太网包的时候对于type:IP没有直接送给ip包,而是给了arp处理函数.然后IP包由arp处理函数传递给IP模块
IP 和UDP 混乱
	IP模块传递给UDP模块的包是IP包,UDP对IP包进行解析处理了.


其他

一层 主要处理硬件连接
二层交换机 处理 mac 与 网口 的关系
三层交换机 尽量不用路由功能(但是刚上电的时候硬件转发表示空的,需要路由),用硬件转发表// 包括目的IP地址,目的IP地址(下一跳)对应的mac地址,mac地址对应的vlan,以及对应的端口
三层典型设备为路由器,主要做路由控制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值