stm32f107 lwip dhcp
第一篇 DHCP自动获取功能的实现
概要
DHCP在电脑中经常用到,因为我们并不关心我们电脑的IP是多少 只要能连上网络就行了。在嵌入式的产品设备中,DHCP用到的比较少,因为很多的操作是通过ip来进行的。除非那种有服务器的系统设备会用到。这里我们只能简单说一下流程,设计的代码太多了,具体的需要学习者自己去慢慢研究。
dhcp模块:
dhcp模块用于获取设备ip地址的相关信息。其处理入口主要有这么几个dpch的启动、dpch的接收报文处理以及定时器模块的处理。
主要的接口原型如下:
err_t dhcp_start(struct netif *netif)
该接口用于设备启动dhcp模块,主要是客户端的功能。该模块实现设备dhcp描述结构生成,并将dhcp的端口绑定到udp协议中,以及将本dhcp模块跟远端服务器端口进行绑定。最后启动dhcp申请。
static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
该接口为一个注册接口,用于dhcp报文接收。在start dhcp时,该接口通过dhcp的udp pcb注册到udp协议层。Udp进行报文处理后,根据端口调用该注册接口。该接口中,实现dhcp报文的协议处理。
Void dhcp_fine_tmr()
Void dhcp_coarse_tmr()
这两个函数接口实现了dhcp的相关超时处理监控。上面一个用于请求应答超时处理。下面一个用于地址租用情况的到期处理。
从源码分析看,上述的接口在应用lwip的协议栈时,需要重点关注。对于小内存应用的场合,该协议栈的内存管理以及pbuf应用部分需要自行改写。
打开工程《科星F107开发板网络应用篇之DHCP自动获取功能》
进入主函数
int main(void)
{
System_Setup();
LwIP_Init();
while (1)
{
/* Periodic tasks */
System_Periodic_Handle();
}
}
主要看一下 DHCP和静态的时候配置有哪些不同
进入函数LwIP_Init();
void LwIP_Init(void)
{
struct ip_addr ipaddr;
struct ip_addr netmask;
struct ip_addr gw;
uint8_t macaddress[6]={0,0,0,0,0,1};
mem_init();
memp_init();
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
IP 网关 子网掩码 我们设置为0,就是没有值。需要我们自动获取到。
Set_MAC_Address(macaddress);设置MAC地址,这个不管任何时候都要设置。
//创建一个网络接口
netif_add(&netif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
netif_set_default(&netif);
dhcp_start(&netif);主要是这句,这一句就开始了自动获取的进程,以2,4,8,16秒为间隔,加上1-1000毫秒之间随机长度的时间
netif_set_up(&netif);
}
DHCP获取IP的简单过程
当DHCP客户机第一次登录网络的时候(也就是客户机上没有任何IP地址数据时),它会通过UDP 67端口向网络上发出一个DHCPDISCOVER数据包(包中包含客户机的MAC地址和计算机名等信息)。因为客户机还不知道自己属于哪一个网络,所以封包的源地址为0.0.0.0,目标地址为255.255.255.255,然后再附上DHCP discover的信息,向网络进行广播。
DHCP discover的等待时间预设为1秒,也就是当客户机将第一个DHCP discover封包送出去之后,在1秒之内没有得到回应的话,就会进行第二次DHCP discover广播。若一直没有得到回应,客户机会将这一广播包重新发送四次(以2,4,8,16秒为间隔,加上1-1000毫秒之间随机长度的时间)。
执行完dhcp_start(&netif);这个函数,开发板就开始和所接的路由器获取IP的进程。
那么我们程序怎么知道是否获取到了没有呢 接着看函数
System_Periodic_Handle();
进入
void System_Periodic_Handle(void)
{
GET_DHCP_IP(LocalTime);
LwIP_Periodic_Handle(LocalTime);
}
进入函数GET_DHCP_IP(LocalTime);
void GET_DHCP_IP(__IO uint32_t localtime)
{
struct ip_addr ipaddr;
struct ip_addr netmask;
struct ip_addr gw;
/* 250 ms */
if (localtime - DisplayTimer >= GET_DHCP_MSECS) 每250ms检测一次是否得到IP
{
DisplayTimer = localtime;
/* We have got a new IP address so update the display */
if (IPaddress != netif.ip_addr.addr)
{
__IO uint8_t iptab[4];
/* Read the new IP address */
IPaddress = netif.ip_addr.addr;
iptab[0] = (uint8_t)(IPaddress >> 24);
iptab[1] = (uint8_t)(IPaddress >> 16);
iptab[2] = (uint8_t)(IPaddress >> 8);
iptab[3] = (uint8_t)(IPaddress);
if (netif.flags & NETIF_FLAG_DHCP)如果得到IP的话 建立端口连接
{
iptab[0] = (uint8_t)(IPaddress >> 24);
iptab[1] = (uint8_t)(IPaddress >> 16);
iptab[2] = (uint8_t)(IPaddress >> 8);
iptab[3] = (uint8_t)(IPaddress);
server_init();初始化UDP端口,其实到这里DHCP已经成功了 这里初始化这个端口 是为了增加UDP扫描,以便我们能知道得到的ip是什么。这个函数 还有扫描的功能就不在讲了 前面刚刚讲过。可以自己点进去看一下程序。
}
}
else if (IPaddress == 0)
{
if (netif.dhcp->tries > MAX_DHCP_TRIES) 如果是超时进入超时处理
{
struct ip_addr ipaddr;
struct ip_addr netmask;
struct ip_addr gw
dhcp_stop(&netif);
IP4_ADDR(&ipaddr, 192, 168, 1, 8);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 1, 1);
netif_set_addr(&netif, &ipaddr , &netmask, &gw);
}
}
}
上面的函数主要是检测是不是获取到IP
再看另一个主函数LwIP_Periodic_Handle(LocalTime);
进入 主要看和DHCP有关的两个函数
void LwIP_Periodic_Handle(__IO uint32_t localtime)
{
/* TCP periodic process every 250 ms */
if (localtime - TCPTimer >= TCP_TMR_INTERVAL)
{
TCPTimer = localtime;
tcp_tmr();
}
/* ARP periodic process every 5s */
if (localtime - ARPTimer >= ARP_TMR_INTERVAL)
{
ARPTimer = localtime;
etharp_tmr();
}
#if LWIP_DHCP
/* Fine DHCP periodic process every 500ms */
if (localtime - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS)
{
DHCPfineTimer = localtime;
dhcp_fine_tmr();请求应答超时处理
}。
/* DHCP Coarse periodic process every 60s */
if (localtime - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS)
{
DHCPcoarseTimer = localtime;
dhcp_coarse_tmr();地址租用情况的到期处理
}
#endif
}