CSP CCF: 202104-3 DHCP服务器 (C++)

题目来源

链接: DHCP服务器.

解题过程

暴力解法 100分

  1. 通读了一遍题目后,可以看出来,我们主要是实现 对收到的报文进行格式检查返回正确格式下的DIS、REQ报文内容对应的报文。格式检查可融入到对DIS、REQ报文的回复中。
  2. 对 DIS 报文
if (messType == "DIS" && recvHost == "*") {
  // 找合适的IP地址
   long long int ip = choseIP(sendHost);
   if (ip == 0) {
       continue;
   }

   // 修改 IP 地址状态
   ips[ip].state = 1;  // 将该 IP 地址状态设置为待分配,
   ips[ip].host = sendHost;  // 占用者设置为发送主机;

   // 设置过期时刻
   long long int eT = setExpireTime(ti, expiTime);
   ips[ip].expiTime = eT;

   // 向发送主机发送 Offer 报文,其中,IP 地址为选定的 IP 地址,过期时刻为所设定的过期时刻
   cout<<H<<" "<<sendHost<<" OFR "<<ip<<" "<<eT<<endl;

}
  1. 对 REQ 报文
else if (messType == "REQ" && recvHost != "*"){
	// 接收主机是本服务器
    if (recvHost == H) {
        // 检查报文中的 IP 地址是否在地址池内,且其占用者为发送主机,若不是,则向发送主机发送 Nak 报文,处理结束;
        bool flag = false;

        //if ( (ips[ipAddr].state == 1 || ips[ipAddr].state == 2 || ips[ipAddr].state == 3) && ips[ipAddr].host == sendHost) {
        if (ipAddr > 0 && ipAddr <= n && ips[ipAddr].state != 0 && ips[ipAddr].host == sendHost) {
            flag = true;
        }
        if ( !flag) {
            cout<<H<<" "<<sendHost<<" NAK "<<ipAddr<<" "<<0<<endl;
            continue;
        }

        // 无论该 IP 地址的状态为何,将该 IP 地址的状态设置为占用;
        ips[ipAddr].state = 2;

        // 设置过期时刻
        long long int eT = setExpireTime(ti, expiTime);
        ips[ipAddr].expiTime = eT;

        // 向发送主机发送 Ack 报文
        cout<<H<<" "<<sendHost<<" ACK "<<ipAddr<<" "<<eT<<endl;
    }

    // 接收主机不是本服务器的
    else {
        cleanUseless(sendHost);
    }
}
  1. 完整代码
/* 100 */
#include <iostream>
#include <fstream>
#include <string>

using namespace std;
// 定义 IP 结构体
struct IP {
    string host;  // 分配给的主机的名称
    int state;  // 0: 未分配; 1: 待分配; 2:: 占用中; 3:过期了;
    long long int expiTime;  // 过期时刻
};

const long long int ipNum = 10001;  // ip池大小
IP ips[ipNum];

long long int N, Tdef, Tmax, Tmin;
string H;
long long int n;

// 初始化 IP池
void initIPstate() {
    for (long long int i = 1; i <= N; ++i) {
        ips[i].state = 0;
        ips[i].host = "";
        ips[i].expiTime = 0;
    }
}


// 选择 合适的 IP
long long int choseIP(string &sendHost) {
    long long int ip = 0;

    // 是否有占有者为发送主机的IP地址
    for (long long int i = 1; i <= N; ++i) {
        if ((ips[i].state == 1 || ips[i].state == 2 || ips[i].state == 3) && ips[i].host == sendHost) {
            ip = i;
            break;
        }
    }
    if (ip != 0) return ip;
    // 没有,则选取最小的状态为未分配的 IP 地址
    for (long long int i = 1; i <= N; ++i) {
        if (ips[i].state == 0) {
            ip = i;
            break;
        }
    }
    if (ip != 0) return ip;
    // 若没有,则选取最小的状态为过期的 IP 地址;
    for (long long int i = 1; i <= N; ++i) {
        if (ips[i].state == 3) {
            ip = i;
            break;
        }
    }
    if (ip != 0) return ip;
    // 若没有,则不处理该报文,处理结束;
    return ip;
}


// 设置 过期时间
long long int setExpireTime(long long int &t, long long int &expiTime) {
    long long int eT;
    // 若报文中过期时刻为 0 ,则设置过期时刻为 t + Tdef
    if (expiTime == 0) {
        eT = t + Tdef;
    }
    // 否则根据报文中的过期时刻和收到报文的时刻计算过期时间,判断是否超过上下限
    // 若没有超过,则设置过期时刻为报文中的过期时刻
    else if ((expiTime - t) >= Tmin && (expiTime - t) <= Tmax) {
        eT = expiTime;
    }
    // 否则则根据超限情况设置为允许的最早或最晚的过期时刻
    else if ((expiTime - t) < Tmin) {
        eT = t + Tmin;
    }
    else if ((expiTime - t) > Tmax) {
        eT = t + Tmax;
    }

    return eT;
}


// 将待分配、占用ip中过期的给处理掉
void cleanIpAddress(long long int &ti, string &sendHost) {
    for (long long int i = 1; i <= N; ++i) {
        // 删掉 if, 从 90-100分。 
        // 主要原因是:当host == sendHost && expiTime == ti && messType == "REQ" && ipAddr != i 时 ip[i] 的状态需要改变, 而我之前的做法没有考虑到这一点。
       // 但我不懂, 如果还是上述的条件, 但ipAddr == i && ips[i].state == 1, 那转换了状态不就错了嘛?———————— 再看了一下题目,"在到达该过期时刻时,若该地址的状态是待分配,则该地址的状态会自动变为未分配,且占用者清空,过期时刻清零;"。所以 此时它已经过期了,不能再作为待分配状态了。
        /*if (ips[i].host == sendHost && ips[i].expiTime == ti) {
            continue;
        }*/
		 // 待分配的ip到期便转换成未分配状态;
        if (ips[i].state == 1 && ips[i].expiTime <= ti) {
            ips[i].state = 0;
            ips[i].host = "";
            ips[i].expiTime = 0;
        }
        // 占用中的ip到期便转换为过期状态;
        else if (ips[i].state == 2 && ips[i].expiTime <= ti) {
            ips[i].state = 3;
            ips[i].expiTime = 0;
        }
    }
}


// 当主机不是本机且报文类型为“REQ”时, 处理发送主机在本机的ip的状态
void cleanUseless(string &sendhost) {
    // 找到占用者为发送主机的所有 IP 地址,对于其中状态为待分配的,将其状态设置为未分配,并清空其占用者,清零其过期时刻,处理结束;
    for (long long int i = 1; i <= n; ++i) {
        if (ips[i].host == sendhost && ips[i].state == 1) {  // 不要忘记 ips[i].state == 1 !!! 没有的话只有 60 分, 有的话有90分。
            ips[i].state = 0;
            ips[i].expiTime = 0;
            ips[i].host = "";
        }
    }
}


int main() {
    //ifstream cin("in.txt");

    cin>>N>>Tdef>>Tmax>>Tmin>>H;
    cin>>n;

    initIPstate();

    for (long long int i = 0; i < n; ++i) {
        long long int ti, ipAddr, expiTime;
        string sendHost, recvHost, messType;
        cin>>ti>>sendHost>>recvHost>>messType>>ipAddr>>expiTime;

        cleanIpAddress(ti, sendHost);
        // DIS
        if (messType == "DIS" && recvHost == "*") {
            // 找合适的IP地址
            long long int ip = choseIP(sendHost);
            if (ip == 0) {
                continue;
            }

            // 修改 IP 地址状态
            ips[ip].state = 1;  // 将该 IP 地址状态设置为待分配,
            ips[ip].host = sendHost;  // 占用者设置为发送主机;

            // 设置过期时刻
            long long int eT = setExpireTime(ti, expiTime);
            ips[ip].expiTime = eT;

            // 向发送主机发送 Offer 报文,其中,IP 地址为选定的 IP 地址,过期时刻为所设定的过期时刻
            cout<<H<<" "<<sendHost<<" OFR "<<ip<<" "<<eT<<endl;
        }

        // REQ
        else if (messType == "REQ" && recvHost != "*"){
            if (recvHost == H) {
                // 检查报文中的 IP 地址是否在地址池内,且其占用者为发送主机,若不是,则向发送主机发送 Nak 报文,处理结束;
                bool flag = false;

                if (ipAddr > 0 && ipAddr <= n && ips[ipAddr].state != 0 && ips[ipAddr].host == sendHost) {
                    flag = true;
                }
                if ( !flag) {
                    cout<<H<<" "<<sendHost<<" NAK "<<ipAddr<<" "<<0<<endl;

                    continue;
                }

                // 无论该 IP 地址的状态为何,将该 IP 地址的状态设置为占用;
                ips[ipAddr].state = 2;

                // 设置过期时刻
                long long int eT = setExpireTime(ti, expiTime);
                ips[ipAddr].expiTime = eT;

                // 向发送主机发送 Ack 报文
                cout<<H<<" "<<sendHost<<" ACK "<<ipAddr<<" "<<eT<<endl;
            }

            // 接收主机不是本服务器的
            else {
                cleanUseless(sendHost);
            }
        }
        //  不用管
    }

    return 0;
}

非暴力解法 (只有想法)

  1. 关键思路: 将发送者的名称 (sendHost) 与 ip(ips[i]) 连接 。 通过一个 map<string, int> name2ip ({sendHost, i})实现。
  2. 代码 (略)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值