CSP202104C.DHCP服务器

今天我们来看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地址:

  1. 优先选择该客户端之前使用过的IP
  2. 选择最小的未分配IP
  3. 选择最小的过期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),用于存储地址池信息。

注意事项

  1. 处理每个报文前都要先更新过期地址状态
  2. 注意区分发给本机和发给其他服务器的Request报文
  3. Discover报文中IP地址字段在接收时忽略
  4. 注意过期时间的上下限处理
  5. 地址池中可能有多个地址属于同一个客户端
相关推荐
跨境技工小黎1 小时前
LLM蜜罐系统是什么?AI爬虫如何精准绕过?
开发语言·人工智能·python
-今昭-1 小时前
OpenStack Dashboard 部署与图形化创建云主机实战指南
开发语言·php
lswzw2 小时前
K8s容器网络排查实战:从宿主机穿透命名空间定位端口监听问题
开发语言·docker·容器
zh73143 小时前
typephp的编译核心phpx原理解析
android·java·开发语言
warpdrivelabs5 小时前
Codex 开源 harness 全面了解
开发语言·人工智能
丰锋ff9 小时前
基于 Qt 的智能门禁系统(二)
开发语言·qt
fpcc9 小时前
跟我学C++中级篇——编译期的条件选择
开发语言·c++
SomeB1oody10 小时前
【RustyML入门】6.3. 并行归约
开发语言·后端·机器学习·rust·教程