前言
DHCP(Dynamic Host Configuration Protocol),动态主机配置协议,是一个应用层协议。当我们将客户主机IP地址设置为动态获取方式时,DHCP服务器就会根据DHCP协议给客户端分配IP,使得客户端机器能够利用这个IP上网。我们的电脑和手机使用WIFI的时候,都是使用DHCP协议来获取IP的。
ipconfig /all
windows系统下,我们可以使用上面的命令查看IP的获取方式和DHCP服务器地址。
分配原理
- DHCP客户端以广播的方式发出Discover报文。
- 所有的DHCP服务器都能够接收到此报文,都会给出响应,向客户端发送一个Offer报文。该报文中包含提供给客户端使用的IP地址,也包含服务器自己的IP地址,以便客户端区分不同的服务器。服务器在发出此报文后会保存一个已分配IP地址的纪录。
- 客户端只能处理其中的一个Offer报文,一般的原则是处理最先收到的报文。客户端再次以广播的方式发出Request报文,会包含选中的服务器的IP地址和需要的IP地址。
- 服务器收到Request报文后,判断其中服务器的IP地址是否与自己的地址相同。如果不相同,不做任何处理只清除相应IP地址分配记录,如果相同,就会向客户端响应一个ACK报文,其中会包含IP地址的使用租期信息(可以参考上面ipconfig命令的结果)。
- 客户端接收到ACK报文后,检查服务器分配的IP地址是否能够使用。如果可以使用,则客户端成功获得IP地址并根据IP地址使用租期自动启动续延过程,如果发现分配的IP地址已经被使用,则向服务器发出Decline报文,通知服务器禁用这个IP地址,然后客户端开始新的IP申请过程。
- 客户端在成功获取IP地址后,随时可以通过发送Release报文释放自己的IP地址,服务器收到Release报文后,会回收相应的IP地址并重新分配。
更多工作原理相关,查看DHCP百度百科。
代码实现
maven依赖
<dependency>
<groupId>com.helger</groupId>
<artifactId>dhcp4java</artifactId>
<version>1.1.0</version>
</dependency>
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import org.dhcp4java.DHCPConstants;
import org.dhcp4java.DHCPPacket;
import org.dhcp4java.HardwareAddress;
public class DHCPTest {
public static void main(String[] args) throws IOException {
//当前电脑的物理地址
byte[] macAddress = HardwareAddress.getHardwareAddressByString("5C:80:B6:FD:91:A7")
.getHardwareAddress();
//封装DHCP请求包
DHCPPacket discover = new DHCPPacket();
//发送DISCOVER报文
discover.setOp(DHCPConstants.DHCPDISCOVER);
//硬件类别为以太网
discover.setHtype(DHCPConstants.HTYPE_ETHER);
//硬件地址长度 以太网为6
discover.setHlen((byte) 6);
//局域网为0
discover.setHops((byte) 0);
//请求ID
discover.setXid(123);
//客户端启动时间(秒)
discover.setSecs((short) 10000);
discover.setFlags((short) 0);
//客户端的硬件地址
discover.setChaddr(macAddress);
//消息类型
discover.setDHCPMessageType(DHCPConstants.DHCPDISCOVER);
//客户端请求服务器的68端口,服务器请求客户端的67端口
DatagramSocket socket = new DatagramSocket(DHCPConstants.BOOTP_REPLY_PORT);
byte[] discoverBytes = discover.serialize();
DatagramPacket sendPacket = new DatagramPacket(discoverBytes,
discoverBytes.length,
InetAddress.getByName("255.255.255.255"), DHCPConstants.BOOTP_REQUEST_PORT);
//发送请求报文
socket.send(sendPacket);
DatagramPacket receivePacket = new DatagramPacket(new byte[1500], 1500);
//接收服务器的响应报文
socket.receive(receivePacket);
DHCPPacket resultDhcpPacket = DHCPPacket.getPacket(receivePacket);
//返回报文包含macAddress
if (bytesToHexString(resultDhcpPacket.getChaddr()).contains(
bytesToHexString(macAddress))) {
//获取的IP地址
System.out.println(resultDhcpPacket.getYiaddr());//192.168.0.142
}
socket.close();
}
private static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
for (byte b : src) {
int v = b & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
}
查看当前电脑的物理地址,5C:80:B6:FD:91:A7
参考
DHCP百度百科
java实现DHCP协议获取ip地址
java 探测网络中是否有dhcp环境
Wireshark分析DHCP