CSP202104-3 DHCP服务器

试题背景
动态主机配置协议(Dynamic Host Configuration Protocol, DHCP)是一种自动为网络客户端分配 IP 地址的网络协议。当支持该协议的计算机刚刚接入网络时,它可以启动一个 DHCP 客户端程序。后者可以通过一定的网络报文交互,从 DHCP 服务器上获得 IP 地址等网络配置参数,从而能够在用户不干预的情况下,自动完成对计算机的网络设置,方便用户连接网络。DHCP 协议的工作过程如下:

  1. 当 DHCP 协议启动的时候,DHCP 客户端向网络中广播发送 Discover 报文,请求 IP 地址配置;
  2. 当 DHCP 服务器收到 Discover 报文时,DHCP 服务器根据报文中的参数选择一个尚未分配的 IP 地址,分配给该客户端。DHCP 服务器用 Offer 报文将这个信息传达给客户端;
  3. 客户端收集收到的 Offer 报文。由于网络中可能存在多于一个 DHCP 服务器,因此客户端可能收集到多个 Offer 报文。客户端从这些报文中选择一个,并向网络中广播 Request 报文,表示选择这个 DHCP 服务器发送的配置;
  4. DHCP 服务器收到 Request 报文后,首先判断该客户端是否选择本服务器分配的地址:如果不是,则在本服务器上解除对那个 IP 地址的占用;否则则再次确认分配的地址有效,并向客户端发送 Ack 报文,表示确认配置有效,Ack 报文中包括配置的有效时间。如果 DHCP 发现分配的地址无效,则返回 Nak 报文;
  5. 客户端收到 Ack 报文后,确认服务器分配的地址有效,即确认服务器分配的地址未被其它客户端占用,则完成网络配置,同时记录配置的有效时间,出于简化的目的,我们不考虑被占用的情况。若客户端收到 Nak 报文,则从步骤 1 重新开始;
  6. 客户端在到达配置的有效时间前,再次向 DHCP 服务器发送 Request 报文,表示希望延长 IP 地址的有效期。DHCP 服务器按照步骤 4 确定是否延长,客户端按照步骤 5 处理后续的配置;
    在本题目中,你需要理解 DHCP 协议的工作过程,并按照题目的要求实现一个简单的 DHCP 服务器。

详细题目见链接:csp在线模拟网站
一些思路已经写在代码的注释里了:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define NOT_ASSIGN 1
#define WAIT_ASSIGN 2
#define OCCUPIED 3
#define IP_TIME_OUT 4

int N, Tdef, Tmax, Tmin, n;
char H[20];

struct Node_ip {
	//地址的状态有未分配、待分配、占用、过期四种
	int id;  //1-4,分别对应上面四种情况
	char host[20];  //处于未分配状态的 IP 地址没有占用者,而其余三种状态的 IP 地址均有一名占用者。
	int timeout; //处于待分配和占用状态的 IP 地址拥有一个大于零的过期时刻
	//在到达该过期时刻时,若该地址的状态是待分配,则该地址的状态会自动变为未分配,且占用者清空,过期时刻清零;否则该地址的状态会由占用自动变为过期,且过期时刻清零。
	//处于未分配和过期状态的 IP 地址过期时刻为零,即没有过期时刻。
}DH_IP[10005]; //dh_ip[i]表示第i个ip的情况


void initial_dhcp(int N) //在 DHCP 启动时,首先初始化 IP 地址池,将所有地址设置状态为未分配,占用者为空,并清零过期时刻
{
	for (int i = 1; i <= N; i++)  //共N个ip地址
	{
		DH_IP[i].id = NOT_ASSIGN;
		strcpy(DH_IP[i].host, "");
		DH_IP[i].timeout = 0;
	}
}

void Update_ip(int time)
{
	//每次处理前要更新ip地址池状态,此时时间为time
	//处于待分配和占用状态的 IP 地址拥有一个大于零的过期时刻。
	//在到达该过期时刻时,若该地址的状态是待分配,则该地址的状态会自动变为未分配,且占用者清空,过期时刻清零;
	//否则该地址的状态会由占用自动变为过期,且过期时刻清零。
	//处于未分配和过期状态的 IP 地址过期时刻为零,即没有过期时刻。

	for (int i = 1; i <= N; i++)  //共N个ip地址
	{
		if (DH_IP[i].id == WAIT_ASSIGN)
		{
			if (time >= DH_IP[i].timeout)
			{
				DH_IP[i].id = NOT_ASSIGN;
				strcpy(DH_IP[i].host, "");
				DH_IP[i].timeout = 0;
			}
		}
		else if (DH_IP[i].id == OCCUPIED)
		{
			if (time >= DH_IP[i].timeout)
			{
				DH_IP[i].id = IP_TIME_OUT;
				DH_IP[i].timeout = 0;
			}
		}
	}
}


void deal_discover(int time, char send[], char recv[], int ip, int time_out)
{
	Update_ip(time);
	int ip_select;
	//1.检查是否有占用者为发送主机的 IP 地址
	bool hint = false;
	for (int i = 1; i <= N; i++)
	{
		if (strcmp(DH_IP[i].host, send) == 0)
		{
			ip_select = i;
			hint = true;
			break;
		}
	}

	if(!hint)
	{
		//分配给它最小的尚未占用过的那个 IP 地址
		bool find = false;
		for (int i = 1; i <= N; i++)
		{
			if (DH_IP[i].id == NOT_ASSIGN)
			{
				ip_select = i;
				find = true;
				break;
			}
		}

		if (!find)  //如果未分配的ip不存在,则分配给它最小的此时未被占用的那个 IP 地址
		{
			for (int i = 1; i <= N; i++)
			{
				if (DH_IP[i].id == IP_TIME_OUT)
				{
					ip_select = i;
					find = true;
					break;
				}
			}

			if (!find) //若没有,说明地址池已经分配完毕则不处理该报文,处理结束;
			{
				return;
			}
		}
	}

	//2.将该 IP 地址状态设置为待分配,占用者设置为发送主机;
	DH_IP[ip_select].id = WAIT_ASSIGN; 
	strcpy(DH_IP[ip_select].host, send);
	//host_to_ip[send] = ip_select;


	//printf("-------------send = %s ip_select=%d\n", send, ip_select);
	//3.若报文中过期时刻为 0 ,则设置过期时刻为 t+Tdef;
	//否则根据报文中的过期时刻和收到报文的时刻计算过期时间,判断是否超过上下限:
	//若没有超过,则设置过期时刻为报文中的过期时刻;
	//否则则根据超限情况设置为允许的最早或最晚的过期时刻;
	if (time_out == 0)
		DH_IP[ip_select].timeout = time + Tdef;
	else
	{
		int time_len = time_out - time;
		if (time_len < Tmin)
			DH_IP[ip_select].timeout = time + Tmin;
		else if (time_len > Tmax)
			DH_IP[ip_select].timeout = time + Tmax;
		else
			DH_IP[ip_select].timeout = time_out;
	}
	
	//4.向发送主机发送 Offer 报文,其中,IP 地址为选定的 IP 地址,过期时刻为所设定的过期时刻
	printf("%s %s OFR %d %d\n", H, send, ip_select, DH_IP[ip_select].timeout);
	return;
}

void deal_req(int time, char send[], char recv[], int ip, int time_out)
{
	Update_ip(time);
	//1.检查接收主机是否为本机:
	//若不是,则找到占用者为发送主机的所有 IP 地址,对于其中状态为待分配的,将其状态设置为未分配,并清空其占用者,清零其过期时刻,处理结束;
	if (strcmp(recv, H) != 0)  //接受主机不是本机
	{
		for (int i = 1; i <= N; i++)
		{
			if (strcmp(DH_IP[i].host, send) == 0)
			{
				if (DH_IP[i].id == WAIT_ASSIGN)
				{
					DH_IP[i].id = NOT_ASSIGN;
					strcpy(DH_IP[i].host, "");
					DH_IP[i].timeout = 0;
				}
			}
		}
		return;
	}

	//2.检查报文中的 IP 地址是否在地址池内,且其占用者为发送主机,
    //若不是,则向发送主机发送 Nak 报文,处理结束;
	if (ip > N || ip < 1 || strcmp(DH_IP[ip].host, send) != 0)
	{
		printf("%s %s NAK %d %d\n", H, send, ip, 0);
		return;
	}

	//3.无论该 IP 地址的状态为何,将该 IP 地址的状态设置为占用;
	DH_IP[ip].id = OCCUPIED;

	//4.与 Discover 报文相同的方法,设置 IP 地址的过期时刻;
	if (time_out == 0)
		DH_IP[ip].timeout = time + Tdef;
	else
	{
		int time_len = time_out - time;
		if (time_len < Tmin)
			DH_IP[ip].timeout = time + Tmin;
		else if (time_len > Tmax)
			DH_IP[ip].timeout = time + Tmax;
		else
			DH_IP[ip].timeout = time_out;
	}
	//5.向发送主机发送 Ack 报文

	printf("%s %s ACK %d %d\n", H, send, ip, DH_IP[ip].timeout);
	return;
}

int main(void)
{
	scanf("%d%d%d%d%s%d", &N, &Tdef, &Tmax, &Tmin, H, &n);
	initial_dhcp(N);

	char send[20], recv[20], type[10];
	int ip, time_out, time;
	for (int i = 0; i < n; i++)
	{
		scanf("%d %s %s %s %d %d", &time, send, recv, type, &ip, &time_out);
		

		//若类型不是 Discover、Request 之一,则不处理
		if (strcmp(type, "DIS") != 0 && strcmp(type, "REQ") != 0)
			continue;

		//判断接收主机是否为本机,或者为* ,若不是,则判断类型是否为 Request,若不是,则不处理;
		if (strcmp(recv, H) != 0 && strcmp(recv, "*") != 0)
		{
			if (strcmp(type, "REQ") != 0)
				continue;
		}

		//或接收主机是本机,但类型是 Discover,则不处理。
		if (strcmp(recv, H) == 0 && strcmp(type, "DIS") == 0)
			continue;

		//若接收主机为* ,但类型不是 Discover,则不处理。
		if (strcmp(recv, "*") == 0 && strcmp(type, "DIS") != 0)
			continue;

	    //正常处理的情况
		if (strcmp(type, "DIS") == 0)
			deal_discover(time, send, recv, ip, time_out);
		else
			deal_req(time, send, recv, ip, time_out);
	}
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值