DHCP服务器
第一次做这样的模拟题,看了大佬的视频后以为自己又行了,敲完代码一提交,过了20%测试数据。。。。。。
感觉这就是个翻译题,下面的代码中我把实现细节的每一部分作为注释贴在了对应的代码段上面,希望可以帮助大家理解。
满分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 10010; // 可能的ip地址数目
int n, m, tDef, tMax, tMin; // ip地址数目,请求个数,默认过期时间,最大最小过期时间
string h; // 主机名称
struct IP {
/*
在 DHCP 启动时,首先初始化 IP 地址池,将所有地址设置状态为未分配,
占用者为空,并清零过期时刻。
其中地址的状态有未分配、待分配、占用、过期四种。
*/
int t; // 过期时刻
int state; // 0:未分配,1:待分配,2:占用,3:过期
string owner; // 占有者
}ip[N];
// 更新当前时间各个ip地址的状态
void updateIpState(int ct) {
/*
处于待分配和占用状态的 IP 地址拥有一个大于零的过期时刻。
在到达该过期时刻时,若该地址的状态是待分配,则该地址的状态会自动变为未分配,
且占用者清空,过期时刻清零;否则该地址的状态会由占用自动变为过期,
且过期时刻清零。处于未分配和过期状态的 IP 地址过期时刻为零,即没有过期时刻。
*/
for (int i = 1; i <= n; ++i) {
if (ip[i].t > 0 && ip[i].t <= ct) { // 如果存在过期时刻并且已经过期
if (ip[i].state == 1) {
ip[i].state = 0;
ip[i].owner = "";
ip[i].t = 0;
}
else if (ip[i].state == 2) {
ip[i].state = 3;
ip[i].t = 0;
}
}
}
}
// 检查是否有占用者为发送主机的 IP 地址
int getIpByOwner(string client) {
for (int i = 1; i <= n; ++i) {
if (ip[i].owner == client) {
return i;
}
}
return 0;
}
// 通过状态查找ip
int getIpByState(int state) {
for (int i = 1; i <= n; ++i) {
if (ip[i].state == state) {
return i;
}
}
return 0;
}
int main() {
cin>> n >> tDef >> tMax >> tMin >> h;
cin>>m;
for (int i = 0; i < m; ++i) {
int ct; // 接受报文的时间
string client, server, type; // 发送主机,接收主机,报文类型
int k, te; // IP地址,过期时间
cin>> ct >> client >> server >> type >> k >> te;
// 对于收到的报文:
//判断接收主机是否为本机,或者为 *,若不是,则判断类型是否为 Request,若不是,则不处理;
if (server != h && server != "*") {
if (type != "REQ") continue;
}
//若类型不是 Discover、Request 之一,则不处理;
if (type != "DIS" && type != "REQ") continue;
//若接收主机为 *,但类型不是 Discover,或接收主机是本机,但类型是 Discover,则不处理。
if ((server == "*" && type != "DIS") || (server == h && type == "DIS")) {
continue;
}
// 更新ip地址的状态
updateIpState(ct);
// 对于Discover报文:
if (type == "DIS") {
/*
检查是否有占用者为发送主机的 IP 地址:
若有,则选取该 IP 地址;
若没有,则选取最小的状态为未分配的 IP 地址;
若没有,则选取最小的状态为过期的 IP 地址;
若没有,则不处理该报文,处理结束;
*/
int p = getIpByOwner(client);
if (!p) {// 选取未分配的
p = getIpByState(0);
if (!p) {// 选取过期的
p = getIpByState(3);
if (!p) {
continue;
}
}
}
//将该 IP 地址状态设置为待分配,占用者设置为发送主机;
ip[p].state = 1;
ip[p].owner = client;
/*
若报文中过期时刻为 0 ,则设置过期时刻为 ct + tDef;
否则根据报文中的过期时刻和收到报文的时刻计算过期时间,
判断是否超过上下限:若没有超过,则设置过期时刻为报文中的过期时刻;
否则则根据超限情况设置为允许的最早或最晚的过期时刻;
*/
if (!te) {// 过期时刻为0
ip[p].t = ct + tDef;
}
else {
int t = te - ct; // 想要使用时间长度 = 期待时刻 - 当前时刻
ip[p].t = ct + min(max(tMin, t), tMax);// 期待过期时间不能超过最大和最小的过期时间
}
// 向发送主机发送 Offer 报文,
// 其中,IP 地址为选定的 IP 地址,过期时刻为所设定的过期时刻。
cout<< h << " " << client << " " << "OFR" << " " << p << " " << ip[p].t <<endl;
}
else if (type == "REQ"){ // 处理Request报文
/*
检查接收主机是否为本机:
若不是,则找到占用者为发送主机的所有 IP 地址,
对于其中状态为待分配的,将其状态设置为未分配,
并清空其占用者,清零其过期时刻,处理结束;
*/
if (server != h) {
for (int i = 1; i <= n; ++i) {
if (ip[i].owner == client) {
if (ip[i].state == 1) {
ip[i].state = 0;
ip[i].owner = "";
ip[i].t = 0;
}
}
}
continue;
}
/*
检查报文中的 IP 地址是否在地址池内,且其占用者为发送主机,
若不是,则向发送主机发送 Nak 报文,处理结束;
*/
if (!(k >= 1 && k <= n && ip[k].owner == client)) {
cout<< h << " " << client << " " << "NAK" << " " << k << " " << 0 <<endl;
continue;
}
// 无论该 IP 地址的状态为何,将该 IP 地址的状态设置为占用;
ip[k].state = 2;
// 与 Discover 报文相同的方法,设置 IP 地址的过期时刻;
if (!te) {// 过期时刻为0
ip[k].t = ct + tDef;
}
else {
int t = te - ct; // 想要使用时间长度 = 期待时刻 - 当前时刻
ip[k].t = ct + min(max(tMin, t), tMax); // 过期时间不能超过最大和最小的过期时间
}
// 向发送主机发送 Ack 报文
cout<< h << " " << client << " " << "ACK" << " " << k << " " << ip[k].t <<endl;
}
}
return 0;
}