今天我们来看CSP202104C.DHCP服务器这道题目





题目分析
本题要求实现一个简化的DHCP服务器,需要处理四种IP地址状态(未分配、待分配、占用、过期)和两种报文类型(Discover、Request),并根据不同的情况做出相应的处理和回复。
解题思路
1. 核心数据结构
使用结构体数组来存储地址池信息:
cpp
struct Address {
int state; // 0:未分配, 1:待分配, 2:占用, 3:过期
string owner; // 占用者主机名
long long expire; // 过期时刻
};
2. 关键处理流程
2.1 过期地址处理
在每个报文到达时,先检查所有地址是否过期:
cpp
void update_expired(long long t) {
for (int i = 1; i <= N; ++i) {
if (pool[i].expire > 0 && pool[i].expire <= t) {
if (pool[i].state == 1) { // 待分配状态过期
pool[i].state = 0; // 变为未分配
pool[i].owner = "";
pool[i].expire = 0;
} else if (pool[i].state == 2) { // 占用状态过期
pool[i].state = 3; // 变为过期状态
pool[i].expire = 0;
}
}
}
}
2.2 Discover报文处理
按照优先级选择IP地址:
- 优先选择该客户端之前使用过的IP
- 选择最小的未分配IP
- 选择最小的过期IP
cpp
if (type == "DIS") {
int addr = find_addr_by_owner(sender); // 1. 查找之前分配的
if (addr == 0) addr = find_min_unassigned(); // 2. 最小的未分配
if (addr == 0) addr = find_min_expired(); // 3. 最小的过期
if (addr == 0) continue; // 没有可用地址,不处理
pool[addr].state = 1; // 设为待分配
pool[addr].owner = sender;
pool[addr].expire = calc_expire(t, expire);
cout << H << " " << sender << " OFR " << addr << " " << pool[addr].expire << endl;
}
2.3 Request报文处理
需要区分两种情况:
- 发给非本机的Request:释放该客户端的待分配地址
- 发给本机的Request:确认分配或拒绝
cpp
if (type == "REQ") {
if (receiver != H) {
// 发给其他服务器,释放待分配地址
for (int j = 1; j <= N; ++j) {
if (pool[j].owner == sender && pool[j].state == 1) {
pool[j].state = 0;
pool[j].owner = "";
pool[j].expire = 0;
}
}
continue;
}
// 发给本服务器
if (ip < 1 || ip > N || pool[ip].owner != sender) {
// 地址无效或不属于该客户端
cout << H << " " << sender << " NAK " << ip << " 0" << endl;
continue;
}
// 确认分配
pool[ip].state = 2;
pool[ip].expire = calc_expire(t, expire);
cout << H << " " << sender << " ACK " << ip << " " << pool[ip].expire << endl;
}
2.4 过期时间计算
cpp
long long calc_expire(long long t, long long req_exp) {
if (req_exp == 0) return t + Tdef; // 使用默认过期时间
long long delta = req_exp - t; // 请求的过期时长
if (delta < Tmin) return t + Tmin; // 小于最短时间
if (delta > Tmax) return t + Tmax; // 大于最长时间
return req_exp; // 在范围内
}
完整代码
cpp
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
struct Address {
int state; // 0:未分配, 1:待分配, 2:占用, 3:过期
string owner;
long long expire;
};
int N;
long long Tdef, Tmax, Tmin;
string H;
vector<Address> pool;
// 更新过期地址状态
void update_expired(long long t) {
for (int i = 1; i <= N; ++i) {
if (pool[i].expire > 0 && pool[i].expire <= t) {
if (pool[i].state == 1) {
pool[i].state = 0;
pool[i].owner = "";
pool[i].expire = 0;
} else if (pool[i].state == 2) {
pool[i].state = 3;
pool[i].expire = 0;
}
}
}
}
// 查找指定客户端的IP地址
int find_addr_by_owner(const string& owner) {
for (int i = 1; i <= N; ++i) {
if (pool[i].owner == owner) return i;
}
return 0;
}
// 查找最小的未分配IP
int find_min_unassigned() {
for (int i = 1; i <= N; ++i) {
if (pool[i].state == 0) return i;
}
return 0;
}
// 查找最小的过期IP
int find_min_expired() {
for (int i = 1; i <= N; ++i) {
if (pool[i].state == 3) return i;
}
return 0;
}
// 计算过期时刻
long long calc_expire(long long t, long long req_exp) {
if (req_exp == 0) return t + Tdef;
long long delta = req_exp - t;
if (delta < Tmin) return t + Tmin;
if (delta > Tmax) return t + Tmax;
return req_exp;
}
int main() {
// 输入配置
cin >> N >> Tdef >> Tmax >> Tmin >> H;
pool.resize(N + 1);
for (int i = 1; i <= N; ++i) {
pool[i].state = 0;
pool[i].owner = "";
pool[i].expire = 0;
}
int n;
cin >> n;
for (int i = 0; i < n; ++i) {
long long t;
string sender, receiver, type;
long long ip, expire;
cin >> t >> sender >> receiver >> type >> ip >> expire;
// 先更新过期地址
update_expired(t);
// 过滤不需要处理的报文
if (receiver != H && receiver != "*") {
if (type != "REQ") continue;
// 处理发给其他服务器的REQ
for (int j = 1; j <= N; ++j) {
if (pool[j].owner == sender && pool[j].state == 1) {
pool[j].state = 0;
pool[j].owner = "";
pool[j].expire = 0;
}
}
continue;
}
if (type != "DIS" && type != "REQ") continue;
if (receiver == "*" && type != "DIS") continue;
if (receiver == H && type == "DIS") continue;
// 处理Discover报文
if (type == "DIS") {
int addr = find_addr_by_owner(sender);
if (addr == 0) addr = find_min_unassigned();
if (addr == 0) addr = find_min_expired();
if (addr == 0) continue; // 没有可用地址
pool[addr].state = 1;
pool[addr].owner = sender;
pool[addr].expire = calc_expire(t, expire);
cout << H << " " << sender << " OFR " << addr << " " << pool[addr].expire << endl;
}
// 处理Request报文
else if (type == "REQ") {
// 检查IP地址是否有效
if (ip < 1 || ip > N || pool[ip].owner != sender) {
cout << H << " " << sender << " NAK " << ip << " 0" << endl;
continue;
}
pool[ip].state = 2;
pool[ip].expire = calc_expire(t, expire);
cout << H << " " << sender << " ACK " << ip << " " << pool[ip].expire << endl;
}
}
return 0;
}
复杂度分析
- 时间复杂度:O(n × N),其中n是报文数量,N是地址池大小。每个报文处理时最多遍历一次整个地址池。
- 空间复杂度:O(N),用于存储地址池信息。
注意事项
- 处理每个报文前都要先更新过期地址状态
- 注意区分发给本机和发给其他服务器的Request报文
- Discover报文中IP地址字段在接收时忽略
- 注意过期时间的上下限处理
- 地址池中可能有多个地址属于同一个客户端