UIP源码之ARP过程分析
之前我们使用UIP实现了tcp和udp通讯今天来说说UIP的实现流程,当然,这篇文章里面只会涉及tcp和udp,暂时还没办法说DHCP,因为UIP的DHCP实现使用了协程的概念,下一章将协程之后再说DHCP.
void uip_init(void)
{
for(c = ; c < UIP_LISTENPORTS; ++c) {
uip_listenports[c] = ; //首先,清零监听的全部tcp端口
}
for(c = ; c < UIP_CONNS; ++c) {
uip_conns[c].tcpstateflags = UIP_CLOSED;//将全部的tcp连接设置为关闭
}
#if UIP_ACTIVE_OPEN
lastport = ;//设置用于tcp链接的端口(tcp客户端连接服务器的时候,客户端这边应该有一个端口,这个端口不需要用户手动指定)
#endif /* UIP_ACTIVE_OPEN */ #if UIP_UDP
for(c = ; c < UIP_UDP_CONNS; ++c) {
uip_udp_conns[c].lport = ;//如果使能了UDP,将UDP监听端口清零
}
#endif /* UIP_UDP */ /* IPv4 initialization. */
#if UIP_FIXEDADDR == 0
/* uip_hostaddr[0] = uip_hostaddr[1] = 0;*/
#endif /* UIP_FIXEDADDR */
}
系统启动之后就可以进入循环过程中,循环过程就是uip_polling,我们的接下来的处理应该按照网络的分层结构来,首先肯定是要实现arp的请求,包括四个主要函数
uip_arp_ipin(); //这个函数是一个空宏,不重要哈
uip_arp_arpin();//处理arp输入
uip_arp_out();//加以太网头结构,在主动连接时可能要构造ARP请求
uip_arp_timer();//定时更新arp缓存
首先是uip_arp_out,当我们的设备接入互联网的时候,本地机器会构造ip包发出去,在构造ip包之前要先构造arp包,说明如下
首先,uip启动的时候先要进行初始化,初始化代码如下
void uip_arp_out(void)
{
struct arp_entry *tabptr; // 在ARP表中找到目的IP地址,构成以太网头.如果目的IP地址不在局域网中,则使用默认路由的IP.
// 如果ARP表中找不到,则将原来的IP包替换成一个ARP请求.
// 首先检查目标是不是广播
if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr)) {
memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, );
} else {
//检查发送的目标地址是否在局域网内
if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask)) {
//如果不在局域网内,那么使用默认的路由器IP地址取代目标地址
uip_ipaddr_copy(ipaddr, uip_draddr);
} else {
//在局域网内,使用目标的ip地址
uip_ipaddr_copy(ipaddr, IPBUF->destipaddr);
} //此时,遍历ARP缓存表,比较目标IP和ARP缓存表中的IP
for(i = ; i < UIP_ARPTAB_SIZE; ++i) {
tabptr = &arp_table[i];
if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr)) {
break;
}
} //如果遍历到表头也没有找到目标ip在arp缓存表中的缓存
if(i == UIP_ARPTAB_SIZE) {
//将原IP请求替换成ARP请求并返回 memset(BUF->ethhdr.dest.addr, 0xff, );//以太网目的地址
memset(BUF->dhwaddr.addr, 0x00, );//目的以太网地址
memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, );
memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, );//源以太网地址 uip_ipaddr_copy(BUF->dipaddr, ipaddr);//目的IP地址
uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr);//源IP地址
BUF->opcode = HTONS(ARP_REQUEST); //在这里将请求切换为ARP请求
BUF->hwtype = HTONS(ARP_HWTYPE_ETH);//硬件类型ARP_ETH
BUF->protocol = HTONS(UIP_ETHTYPE_IP);//0x0800表示IP类型,这个字段标识协议类型
BUF->hwlen = ;
BUF->protolen = ;
BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN]; uip_len = sizeof(struct arp_hdr);
return;//可以看到,在此处返回了
} //建立最终的以太网头
memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, );
}
//如果在局域网中并且arp缓存表中有,那么直接构造以太网投
memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, ); IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP); uip_len += sizeof(struct uip_eth_hdr);
}
这里我们可以看到,uip里面自建了一个arp的缓存表,定义如下
struct arp_entry {
u16_t ipaddr[];//IP地址
struct uip_eth_addr ethaddr;//网卡信息
u8_t time;
};
struct uip_eth_addr {
u8_t addr[];//mac地址
};
那么这个缓存表在什么时候用,就要看arp的响应,也就是uip_arp_arpin这个函数了,
void uip_arp_arpin(void)
{
//检测ARP的头长度对不对
if(uip_len < sizeof(struct arp_hdr)) {
uip_len = ;
return;
}
uip_len = ;
//检测操作码
switch(BUF->opcode) {
case HTONS(ARP_REQUEST)://如果是一个arp请求,则发送应答
if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) {
//首先我们将发送ARP请求的主机更新到ARP缓存表中,因为接下来和可能有更多的交流
uip_arp_update(BUF->sipaddr, &BUF->shwaddr); //ARP请求回应的操作码为2
BUF->opcode = HTONS();
//将受到的ARP包的源以太网地址当成我们这次回应的目的以太网地址
memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, );
//加入自身的以太网地址做源以太网地址
memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, );
//对应以太网源地址
memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, );
//对应以太网目的地址
memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, ); BUF->dipaddr[] = BUF->sipaddr[];
BUF->dipaddr[] = BUF->sipaddr[];
BUF->sipaddr[] = uip_hostaddr[];
BUF->sipaddr[] = uip_hostaddr[];
//构造回应包之后等待下一次的发送
BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
uip_len = sizeof(struct arp_hdr);
}
break;
//如果现在我们接受到的是一个ARP回应,那么将这个回应的ip加入arp缓存中
case HTONS(ARP_REPLY):
if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) {
uip_arp_update(BUF->sipaddr, &BUF->shwaddr);
}
break;
} return;
}
这其中我们使用了uip_arp_update来更新缓存,基本原理就是先找找已经存在的arp缓存中有没有这个ip的主机,没有加进去,有不加进去,有一半更新缓存表
接下来是arp的周期性处理函数uip_arp_timer,因为arp协议规定每过多长时间更新一次arp表,所以有这个操作,代码如下
void uip_arp_timer(void)
{
struct arp_entry *tabptr; ++arptime;
//遍历arp表
for(i = ; i < UIP_ARPTAB_SIZE; ++i) {
tabptr = &arp_table[i];
//检测缓存时间有没有超时
if((tabptr->ipaddr[] | tabptr->ipaddr[]) != &&
arptime - tabptr->time >= UIP_ARP_MAXAGE) {
//超时清空这个arp条目
memset(tabptr->ipaddr, , );
}
}
}
此时ARP的过程基本上就OK了,
然后就是UIP的数据处理了,主要是一个函数
uip_process(),该函数很复杂,还没弄明白,但是不想去改源码的也不用看明白只需要知道,在uip_process中调用了叫做UIP_APPCALL();的宏,这是一个空宏,需要用户实现,当使用uip的时 候,实际上的协议uip_process实现,然后实际性的数据有uip_appcall宏实现,当使用udp的时候还有一个宏uip_udp_periodic中的UDP_APPCALL(),用户实现该宏(实质上是一个函数),进而参与到通信处理的过程中来
UIP源码之ARP过程分析的更多相关文章
- AppWidget源码分析---updateAppWidget过程分析
转[原文] 前面一篇文章,分析了AppWidgetProvider和RemoteView的源码,从中我们可以知道它们的实现原理,AppWidgetProvider是一个BroadcastReceive ...
- spark源码阅读--shuffle过程分析
ShuffleManager(一) 本篇,我们来看一下spark内核中另一个重要的模块,Shuffle管理器ShuffleManager.shuffle可以说是分布式计算中最重要的一个概念了,数据的j ...
- Tomcat源码解析-启动过程分析之主干流程
Tomcat启动入口就在脚本startup.sh中,具体脚本可以看tomcat的源码,这个启动脚本主要用来判断环境,找到catalina.sh脚本路径,将启动参数传递给catalina.sh执行.ca ...
- Spring之SpringMVC(源码)启动初始化过程分析
1.说明 SpringMVC作为Spring提供的MVC实现,可以实现与Spring的天然无缝联合,因为具有很广泛的用途.具体的关于SpringMVC的处理流程逻辑我在这里就不在赘述了.还是来通过源码 ...
- 【Android】Android 4.0 Launcher2源码分析——启动过程分析
Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:[html] <manifest xmlns:android="http://schemas. ...
- 【转】Android 4.0 Launcher2源码分析——启动过程分析
Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:[html] <manifest xmlns:android="http://schemas. ...
- Fresco源码解析 - 创建一个ImagePipeline(一)
在Fresco源码解析 - 初始化过程分析章节中, 我们分析了Fresco的初始化过程,两个initialize方法中都用到了 ImagePipelineFactory类. ImagePipeline ...
- Mac和Linux系统的:Arp欺骗源码
linux系统, 简化版的ARP欺骗工具 精简版, 没有很多代码, 只要把准备好的数据, 发送给到网卡接口, 利用这个工具, 可以让局域网内的一台计算机暂时掉线: #include <stdio ...
- 深入vue - 源码目录及构建过程分析
公众号原文链接:深入vue - 源码目录及构建过程分析 喜欢本文可以扫描下方二维码关注我的公众号 「前端小苑」 “ 本文主要梳理一下vue代码的目录,以及vue代码构建流程,旨在对vue源码整 ...
随机推荐
- C++调用C#之C++DLL调用C# COM控件
1. 新建项目 这里我们使用ATL,来接受C# COM控件向外发送的事件. 2. 初始化ATL #include "stdafx.h" CComModule _module; BO ...
- XMEAG-128A1
JTAGUSERID = 0x00 WDWP = 8CLK WDP = 8CLK DVSDON = [ ] BOOTRST = BOOTLDR BODPD = DISABLED RSTDISBL = ...
- JSON文件存入MySQL数据库
目标:将不同格式的JSON文件存入MySQL数据库 涉及的点有: 1. java处理JSON对象,直接见源码. 2. java.sql.SQLException: Incorrect string v ...
- 【转载】GDI 映像方式 之 SetViewportExtEx 与 SetWindowExtEx 解析
所谓视口代表设备,比如屏幕. 窗口代表我们的思维. 我们对windows说在(5,6)处画个点(调用GDI函数).windows认为是在我们的思维的(5,6)处画了个点.(也就是说5,6是逻辑坐标,G ...
- CodeForces--TechnoCup--2016.10.15--ProblemB--Bill Total Value(字符串处理)
Bill Total Value time limit per test 1 second memory limit per test 256 megabytes input standard inp ...
- Servlet简介及使用
javaweb学习总结(五)——Servlet开发(一) 一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口 ...
- UVALive 2035 The Monocycle(BFS状态处理+优先队列)
这道题目真是非常坎坷啊,WA了很多次,但所有的思路都是奔着广搜去想的,一开始出现了比答案大的数据,才想到了应该是优先队列,再说加上也肯定不会错.一开始我读错了题意,以为旋转并且前行需要的时间跟其他一样 ...
- 当使用System,out.println()打印一个对象是自动调用toString方法
在Java中,所有的对象都是继承自Object,自然继承了toString方法,在当使用System,out.println()里面为一个对象的引用时,自动调用toString方法讲对象打印出来.如果 ...
- php 连接 mssql sql2008
摘要 sql server 2008 1.下载微软提供的dll 下载地址:http://www.microsoft.com/en-us/download/details.aspx?id=20098 p ...
- Centos yum 安装mysql报错 No package mysql-server available.
这是因为大多数mysql-*的资源名称被mariadb-*重命名了 所以换成 yum install mariadb-server 就可以了 PS[摘自网络] MariaDB不仅仅是Mysql的一个替 ...