如果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 子网掩码 网关地址 网卡初始化函数 网卡接收函数(从底层网卡驱动接收数据,被网卡驱动调用)
struct etharp_entry
arp层 用到的结构体
从来存储 ip mac 对
struct udp_pcb
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;
udp_send(udp_client_pcb,ubuf_client);
udp层
udp_send
调用 ip_route 获取 netif
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);
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);
arp 层
etharp_output
1.将ip层数据拷贝到arp包里面
找到dest
etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
2.填写帧头
ETHADDR32_COPY(ðhdr->dest, dst);
ETHADDR16_COPY(ðhdr->src, src);
ethhdr->type = PP_HTONS(ETHTYPE_IP);
3. 发包
netif->linkoutput(netif, p);
ip 及 udp 拆包过程
udp_server_pcb = udp_new();
udp_bind(udp_server_pcb,IP_ADDR_ANY,UDP_RECV_PORT);
udp_recv(udp_server_pcb,Udp_Server_Recv,NULL);
ethernetif_input(&enc28j60_netif);
p = low_level_input(netif);
ENC28J60_Packet_Receive(MAX_FRAMELEN,lwip_buf);
ethhdr = p->payload;
switch (htons(ethhdr->type)){
case ETHTYPE_IP:
case ETHTYPE_ARP:
netif->input(p, netif)
break;
default:
;
}
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);
如果是 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:
netif->linkoutput(netif, p);
ARP_REPLY:
更新 ip mac 对 ,但是刚才已经更新过,所以不更新了.
}
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);
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
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 与 网口 的关系
三层交换机 尽量不用路由功能(但是刚上电的时候硬件转发表示空的,需要路由),用硬件转发表
三层典型设备为路由器,主要做路由控制