DHCP
本篇文章我们将详细讲解DHCP协议的基本信息、优势特点、工作原理、应用场景,并通过实战例程, 为大家讲解如何使用W5500动态获取IP信息,帮助读者更好地了解并运用这一协议。
有关初始化过程,请参考Network install章节,这里将不再详述。
DHCP协议简介
DHCP(Dynamic Host Configuration Protocol)即动态主机配置协议,是一个应用层协议。它主要 用于在 IP 网络中为客户端自动分配 IP 地址及其他相关网络配置参数,如子网掩码、默认网关、DNS 服务器地址等。这种动态分配的方式大大简化了网络管理员的工作,并且能够更有效地利用有限的 IP 地址资源。
DHCP协议特点
- 便捷配置与管控:DHCP可自动分配IP地址、子网掩码、网关、DNS等网络参数,设备入网即 自动获取,用户无需手动操作。管理员能通过服务器集中管理IP分配,网络架构调整时,修改 服务器设置,客户端自动适配;静态IP则要逐台手动输入、调整,流程繁琐还易出错。
- 灵活资源利用:DHCP动态分配IP,设备离线后地址回池再利用,契合公共场所临时大量接入 需求,提升地址利用率;还能按需灵活调配,为关键设备保留静态IP,其余动态分配。静态IP 固定占用,闲置浪费资源,灵活性差。
- 高效维护与排障:DHCP自动分配,规避手动配置错误与IP地址冲突,服务器详细记录分配情 况。网络故障时,管理员依服务器日志锁定故障设备排查;静态IP手动配置易冲突,故障排查缺少有效记录,难度大、耗时久。
- 适配移动与拓展:移动设备横跨网络时,DHCP让其自动获取IP配置,无需手动切换;网络规 模扩大、新增设备时,DHCP自动分配地址,助力快速扩容。静态IP需提前规划,易现地址不足、分配不合理问题,还增加设备移动操作难。
DHCP工作原理
DHCP工作原理如图所示:

从图示中我们可以直观明了地看出DHCP地工作原理,一般为四个阶段:
- 发现阶段:客户端接入网络时以广播形式发送DHCP Discover报文 (目的IP是255.255.255.255,源IP是0.0.0.0)寻找DHCP服务器,报文中含客户端MAC地址。若服务器和客户端不在同一子网,会通过中继代理(如路由器)转发。
- 提供阶段:DHCP服务器收到Discover报文后, 从IP地址池选一个未分配的IP地址,将其和子网掩码、默认网关、DNS服务器地址等信息封装进DHCP Offer报文,以广播或单播方式发给客户端,可能会有多个Offer报文。
- 请求阶段:客户端收到多个Offer后选择一个,以广播形式发送DHCP Request报文请求分配该IP地址等配置信息,且发送ARP请求检查IP地址唯一性。
- 确认阶段:服务器收到Request报文后,检查IP地址是否可用。若可用, 将以广播或单播的形式发送DHCP Ack报文,客户端收到后完成网络配置正常上网。若不可用,发送DHCP Nak报文,客户端收到后重新发起Discover流程。
DHCP协议报文
DHCP的报文格式如下:

DHCP 报文各字段的说明如下表所示:

Discover 报文实例:客户端通过 UDP 广播的方式发送 DHCP 发现报文,报文中包含了客户端 MAC 地址、主机名和请求的 IP 地址等信息
|报文解析|
Message type: Boot Request (1) (op code为1,客户端请求报文)
Hardware type: Ethernet (0x01)
Hardware address length: 6
Hops: 0
Transaction ID: 0xbf600589
Seconds elapsed: 0
Bootp flags: 0x0000 (Unicast)
Client IP address: 0.0.0.0
Your (client) IP address: 0.0.0.0
Next server IP address: 0.0.0.0
Relay agent IP address: 0.0.0.0
Client MAC address: HP_b1:37:11 (64:4e:d7:b1:37:11)
Client hardware address padding: 00000000000000000000
Server host name not given
Boot file name not given
Magic cookie: DHCP
Option: (53) DHCP Message Type (Discover)
Option: (61) Client identifier
Option: (50) Requested IP Address (192.168.1.115)
Option: (12) Host Name
Option: (60) Vendor class identifier
Option: (55) Parameter Request List
Option: (255) End
Padding: 0000000000000000
|报文原文|
01 01 06 00 bf 60 05 89 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 4e
d7 b1 37 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63 82 53 63
35 01 01 3d 07 01 64 4e d7 b1 37 11 32 04 c0 a8 01 73 0c 05 46 41 45 5f 33 3c 08 4d 53 46
54 20 35 2e 30 37 0e 01 03 06 0f 1f 21 2b 2c 2e 2f 77 79 f9 fc ff 00 00 00 00 00 00 00 00
Offer报文实例:DHCP服务器收到Discover报文后,从IP地址池选一个未分配的IP地址,将其和子网掩码、默认网关等信息封装进Offer报文以广播或单播(这里为广播的方式)方式发给客户端
|报文解析|
Message type: Boot Reply (2) (op code为2,服务器响应报文)
Hardware type: Ethernet (0x01)
Hardware address length: 6
Hops: 0
Transaction ID: 0xbf600589
Seconds elapsed: 0
Bootp flags: 0x0000 (Unicast)
Client IP address: 0.0.0.0
Your (client) IP address: 192.168.1.115
Next server IP address: 0.0.0.0
Relay agent IP address: 0.0.0.0
Client MAC address: HP_b1:37:11 (64:4e:d7:b1:37:11)
Client hardware address padding: 00000000000000000000
Server host name not given
Boot file name not given
Magic cookie: DHCP
Option: (53) DHCP Message Type (Offer)
Option: (54) DHCP Server Identifier (192.168.1.1)
Option: (51) IP Address Lease Time
Option: (6) Domain Name Server
Option: (1) Subnet Mask (255.255.255.0)
Option: (3) Router
Option: (255) End
|报文原文|
02 01 06 00 bf 60 05 89 00 00 00 00 00 00 00 00 c0 a8 01 73 00 00 00 00 00 00 00 00 64 4e
d7 b1 37 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63 82 53 63
35 01 02 36 04 c0 a8 01 01 33 04 00 00 1c 20 06 08 ca 60 86 21 ca 60 80 56 01 04 ff ff ff
00 03 04 c0 a8 01 01 ff
其他报文信息这里就不一一展示了,感兴趣的朋友可以自行抓取查看。
DHCP 应用场景
DHCP 的应用场景通常集中在需要动态分配 IP 地址的局域网环境中。例如,在大型的办公环境或者 学校中,由于有大量的网络设备需要连接到网络,手动为每个设备分配和管理 IP 地址会非常繁琐且 容易出错。使用 DHCP 可以集中管理 IP 地址的分配,提高网络管理员的工作效率,减少错误的发生, 且可以适应网络变化。
实现过程
接下来,我们一起来看看如何在 W5500 上实现 DHCP 动态获取网络地址信息。
步骤1:注册 DHCP 定时器中断到 1s 定时器中:
/**
* @brief Hardware Platform Timer Interrupt Callback Function
*/
void TIM2_IRQHandler(void)
{
static uint32_t wiz_timer_1ms_count = 0;
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
wiz_timer_1ms_count++;
if (wiz_timer_1ms_count >= 1000)
{
DHCP_time_handler();
wiz_timer_1ms_count = 0;
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
注册 DHCP 定时器中断主要为了 DHCP 超时处理。 在 dhcp.h 文件中,定义了 DHCP 超时时间、重试次数、端口号和主机名等内容:
/* Retry to processing DHCP */
#define MAX_DHCP_RETRY 2 ///< Maximum retry count
#define DHCP_WAIT_TIME 10 ///< Wait Time 10s
/* UDP port numbers for DHCP */
#define DHCP_SERVER_PORT 67 ///< DHCP server port number
#define DHCP_CLIENT_PORT 68 ///< DHCP client port number
#define MAGIC_COOKIE 0x63825363 ///< You should not modify it number.
#define DCHP_HOST_NAME "WIZnet\0"
步骤2:启用DHCP动态获取:
首先需要将默认网络地址信息结构体中的模式改为DHCP模式
/* network information */
wiz_NetInfo default_net_info = {
.mac = {0x00, 0x08, 0xdc, 0x12, 0x22, 0x12},
.ip = {192, 168, 1, 30},
.gw = {192, 168, 1, 1},
.sn = {255, 255, 255, 0},
.dns = {8, 8, 8, 8},
.dhcp = NETINFO_DHCP
};
请注意:
使用 DHCP 动态获取 IP 时,必需将网络结构体配置中 dhcp 的值改为 NETINFO_DHCP,这样 才能运行 DHCP 模式。
步骤3:DHCP获取网络地址信息
首先是在初始化完硬件和W5500之后调用network_init进行网络地址信息配置
network_init(ethernet_buf, &default_net_info);
这个函数需要将DHCP处理用到的缓存数组以及默认网络地址信息传入,函数具体内容如下:
/**
* @brief set network information
*
* First determine whether to use DHCP. If DHCP is used, first obtain the Internet Protocol Address through DHCP.
* When DHCP fails, use static IP to configure network information. If static IP is used, configure network information directly
*
* @param sn: socketid
* @param ethernet_buff:
* @param net_info: network information struct
* @return none
*/
void network_init(uint8_t *ethernet_buff, wiz_NetInfo *conf_info)
{
int ret;
wizchip_setnetinfo(conf_info); // Configuring Network Information
if (conf_info->dhcp == NETINFO_DHCP)
{
ret = wiz_dhcp_process(0, ethernet_buff);
if (ret == 0)
{
conf_info->dhcp = NETINFO_STATIC;
wizchip_setnetinfo(conf_info);
}
}
print_network_information();
}
在这个函数中,会先设置一遍网络地址到W5500中,然后判断模式是否为DHCP模式,如果为DHCP模式,则使用wiz_dhcp_process函数来执行DHCP进程, 在通过DHCP方式成功获取到网络地址后更新到W5500中,最后将网络地址信息打印出来。wiz_dhcp_process函数内容如下:
/**
* @brief DHCP process
* @param sn :socket number
* @param buffer :socket buffer
*/
static uint8_t wiz_dhcp_process(uint8_t sn, uint8_t *buffer)
{
wiz_NetInfo conf_info;
uint8_t dhcp_run_flag = 1;
uint8_t dhcp_ok_flag = 0;
/* Registration DHCP_time_handler to 1 second timer */
DHCP_init(sn, buffer);
printf("DHCP running\r\n");
while (1)
{
switch (DHCP_run()) // Do the DHCP client
{
case DHCP_IP_LEASED: // DHCP Acquiring network information successfully
if (dhcp_ok_flag == 0)
{
dhcp_ok_flag = 1;
dhcp_run_flag = 0;
}
break;
case DHCP_FAILED:
dhcp_run_flag = 0;
break;
}
if (dhcp_run_flag == 0)
{
printf("DHCP %s!\r\n", dhcp_ok_flag ? "success" : "fail");
DHCP_stop();
if (dhcp_ok_flag)
{
getIPfromDHCP(conf_info.ip);
getGWfromDHCP(conf_info.gw);
getSNfromDHCP(conf_info.sn);
getDNSfromDHCP(conf_info.dns);
conf_info.dhcp = NETINFO_DHCP;
getSHAR(conf_info.mac);
wizchip_setnetinfo(&conf_info); // Update network information to network information obtained by DHCP
return 1;
}
return 0;
}
}
}
在该函数体中,首先会调用DHCP_init 函数进行初始化DHCP配置:
void DHCP_init(uint8_t s, uint8_t * buf)
{
uint8_t zeroip[4] = {0,0,0,0};
getSHAR(DHCP_CHADDR);
if((DHCP_CHADDR[0] | DHCP_CHADDR[1] | DHCP_CHADDR[2] | DHCP_CHADDR[3] | DHCP_CHADDR[4] | DHCP_CHADDR[5]) == 0x00)
{
// assigning temporary mac address, you should be set SHAR before call this function.
DHCP_CHADDR[0] = 0x00;
DHCP_CHADDR[1] = 0x08;
DHCP_CHADDR[2] = 0xdc;
DHCP_CHADDR[3] = 0x00;
DHCP_CHADDR[4] = 0x00;
DHCP_CHADDR[5] = 0x00;
setSHAR(DHCP_CHADDR);
}
DHCP_SOCKET = s; // SOCK_DHCP
pDHCPMSG = (RIP_MSG*)buf;
DHCP_XID = 0x12345678;
{
DHCP_XID += DHCP_CHADDR[3];
DHCP_XID += DHCP_CHADDR[4];
DHCP_XID += DHCP_CHADDR[5];
DHCP_XID += (DHCP_CHADDR[3] ^ DHCP_CHADDR[4] ^ DHCP_CHADDR[5]);
}
// WIZchip Netinfo Clear
setSIPR(zeroip);
setGAR(zeroip);
reset_DHCP_timeout();
dhcp_state = STATE_DHCP_INIT;
}
然后是在DHCP主循环中运行DHCP_run函数,它的主要作用是进行DHCP组包,发送发现、请求等报文,对服务器的提供、 响应等内容进行解析以及超时处理,这里只需要根据DHCP_run函数的返回值进行相应处理即可。DHCP_run函数内容如下:
uint8_t DHCP_run(void)
{
uint8_t type;
uint8_t ret;
if(dhcp_state == STATE_DHCP_STOP) return DHCP_STOPPED;
if(getSn_SR(DHCP_SOCKET) != SOCK_UDP)
socket(DHCP_SOCKET, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00);
ret = DHCP_RUNNING;
type = parseDHCPMSG();
switch ( dhcp_state ) {
case STATE_DHCP_INIT :
DHCP_allocated_ip[0] = 0;
DHCP_allocated_ip[1] = 0;
DHCP_allocated_ip[2] = 0;
DHCP_allocated_ip[3] = 0;
send_DHCP_DISCOVER();
dhcp_state = STATE_DHCP_DISCOVER;
break;
case STATE_DHCP_DISCOVER :
if (type == DHCP_OFFER){
#ifdef _DHCP_DEBUG_
printf("> Receive DHCP_OFFER\r\n");
#endif
DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0];
DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1];
DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2];
DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3];
send_DHCP_REQUEST();
dhcp_state = STATE_DHCP_REQUEST;
} else ret = check_DHCP_timeout();
break;
case STATE_DHCP_REQUEST :
if (type == DHCP_ACK) {
#ifdef _DHCP_DEBUG_
printf("> Receive DHCP_ACK\r\n");
#endif
if (check_DHCP_leasedIP()) {
// Network info assignment from DHCP
dhcp_ip_assign();
reset_DHCP_timeout();
dhcp_state = STATE_DHCP_LEASED;
} else {
// IP address conflict occurred
reset_DHCP_timeout();
dhcp_ip_conflict();
dhcp_state = STATE_DHCP_INIT;
}
} else if (type == DHCP_NAK) {
#ifdef _DHCP_DEBUG_
printf("> Receive DHCP_NACK\r\n");
#endif
reset_DHCP_timeout();
dhcp_state = STATE_DHCP_DISCOVER;
} else ret = check_DHCP_timeout();
break;
case STATE_DHCP_LEASED :
ret = DHCP_IP_LEASED;
if ((dhcp_lease_time != INFINITE_LEASETIME) && ((dhcp_lease_time/2) < dhcp_tick_1s)) {
#ifdef _DHCP_DEBUG_
printf("> Maintains the IP address \r\n");
#endif
type = 0;
OLD_allocated_ip[0] = DHCP_allocated_ip[0];
OLD_allocated_ip[1] = DHCP_allocated_ip[1];
OLD_allocated_ip[2] = DHCP_allocated_ip[2];
OLD_allocated_ip[3] = DHCP_allocated_ip[3];
DHCP_XID++;
send_DHCP_REQUEST();
reset_DHCP_timeout();
dhcp_state = STATE_DHCP_REREQUEST;
}
break;
case STATE_DHCP_REREQUEST :
ret = DHCP_IP_LEASED;
if (type == DHCP_ACK) {
dhcp_retry_count = 0;
if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] ||
OLD_allocated_ip[1] != DHCP_allocated_ip[1] ||
OLD_allocated_ip[2] != DHCP_allocated_ip[2] ||
OLD_allocated_ip[3] != DHCP_allocated_ip[3])
{
ret = DHCP_IP_CHANGED;
dhcp_ip_update();
#ifdef _DHCP_DEBUG_
printf(">IP changed.\r\n");
#endif
}
#ifdef _DHCP_DEBUG_
else printf(">IP is continued.\r\n");
#endif
reset_DHCP_timeout();
dhcp_state = STATE_DHCP_LEASED;
} else if (type == DHCP_NAK) {
#ifdef _DHCP_DEBUG_
printf("> Receive DHCP_NACK, Failed to maintain ip\r\n");
#endif
reset_DHCP_timeout();
dhcp_state = STATE_DHCP_DISCOVER;
} else ret = check_DHCP_timeout();
break;
default :
break;
}
return ret;
}
运行结果
请注意:
测试实例需要PC端和W5500处于同一网段。
烧录例程运行后,首先打印了PHY链路检测的结果以及DHCP日志信息, 然后打印了网络地址信息,这里可以看到配置方式为DHCP,IP地址为192.168.1.137,最后是PING提示消息。 接着在PC端打开CMD,PING W5500的IP地址,可以正常PING通。

总结
本文介绍 DHCP 协议,包括其在 IP 网络自动分配参数的功能、便捷配置等特点、工作原理、报文格式和应用场景。通过 W5500 实战例程展示动态获取网络地址信息过程,含注册定时器中断、启用模式和获取信息等步骤,烧录后可完成检测与信息打印,PC 端能 PING 通设备。 下一篇文章将讲解如何在 W5500 芯片上实现 TCP 客户端模式,解析 TCP 客户端连接服务器进行回环测试的核心原理及应用,同时通过实战例程讲解具体实现步骤与要点,敬请期待!