CCF 202104-3 DHCP服務器


------------恢復內容開始------------

報文格式

為了便於實現,我們簡化地規定 DHCP 數據報文的格式如下:

<發送主機> <接收主機> <報文類型> <IP 地址> <過期時刻>

DHCP 數據報文的各個部分由空格分隔,其各個部分的定義如下:

  • 發送主機:是發送報文的主機名,主機名是由小寫字母、數字組成的字符串,唯一地表示了一個主機;
  • 接收主機:當有特定的接收主機時,是接收報文的主機名;當沒有特定的接收主機時,為一個星號(*);
  • 報文類型:是三個大寫字母,取值如下:
    • DIS:表示 Discover 報文;
    • OFR:表示 Offer 報文;
    • REQ:表示 Request 報文;
    • ACK:表示 Ack 報文;
    • NAK:表示 Nak 報文;
  • IP 地址,是一個非負整數:
    • 對於 Discover 報文,該部分在發送的時候為 0,在接收的時候忽略;
    • 對於其它報文,為正整數,表示一個 IP 地址;
  • 過期時刻,是一個非負整數:
    • 對於 Offer、Ack 報文,是一個正整數,表示服務器授予客戶端的 IP 地址的過期時刻;
    • 對於 Discover、Request 報文,若為正整數,表示客戶端期望服務器授予的過期時刻;
    • 對於其它報文,該部分在發送的時候為 0,在接收的時候忽略。

服務器配置

為了 DHCP 服務器能夠正確分配 IP 地址,DHCP 需要接受如下配置:

  • 地址池大小

:表示能夠分配給客戶端的 IP 地址的數目,且能分配的 IP 地址是 、

  • :表示運行 DHCP 服務器的主機名。

分配策略

當客戶端請求 IP 地址時,首先檢查此前是否給該客戶端分配過 IP 地址,且該 IP 地址在此后沒有被分配給其它客戶端。如果是這樣的情況,則直接將 IP 地址分配給它,否則,
總是分配給它最小的尚未占用過的那個 IP 地址。如果這樣的地址不存在,則分配給它最小的此時未被占用的那個 IP 地址。如果這樣的地址也不存在,說明地址池已經分配完畢,因此拒絕分配地址。

實現細節

在 DHCP 啟動時,首先初始化 IP 地址池,將所有地址設置狀態為未分配,占用者為空,並清零過期時刻。
其中地址的狀態有未分配、待分配、占用、過期四種。
處於未分配狀態的 IP 地址沒有占用者,而其余三種狀態的 IP 地址均有一名占用者。
處於待分配和占用狀態的 IP 地址擁有一個大於零的過期時刻。在到達該過期時刻時,若該地址的狀態是待分配,則該地址的狀態會自動變為未分配,且占用者清空,過期時刻清零;否則該地址的狀態會由占用自動變為過期,且過期時刻清零。處於未分配和過期狀態的 IP 地址過期時刻為零,即沒有過期時刻。

對於收到的報文,設其收到的時刻為

。處理細節如下:

  1. 判斷接收主機是否為本機,或者為 *,若不是,則判斷類型是否為 Request,若不是,則不處理;
  2. 若類型不是 Discover、Request 之一,則不處理;
  3. 若接收主機為 *,但類型不是 Discover,或接收主機是本機,但類型是 Discover,則不處理。

對於 Discover 報文,按照下述方法處理:

  1. 檢查是否有占用者為發送主機的 IP 地址:
    • 若有,則選取該 IP 地址;
    • 若沒有,則選取最小的狀態為未分配的 IP 地址;
    • 若沒有,則選取最小的狀態為過期的 IP 地址;
    • 若沒有,則不處理該報文,處理結束;
  2. 將該 IP 地址狀態設置為待分配,占用者設置為發送主機;
  3. 若報文中過期時刻為 0 ,則設置過期時刻為
  1. ;否則根據報文中的過期時刻和收到報文的時刻計算過期時間,判斷是否超過上下限:若沒有超過,則設置過期時刻為報文中的過期時刻;否則則根據超限情況設置為允許的最早或最晚的過期時刻;
  2. 向發送主機發送 Offer 報文,其中,IP 地址為選定的 IP 地址,過期時刻為所設定的過期時刻。

對於 Request 報文,按照下述方法處理:

  1. 檢查接收主機是否為本機:
    • 若不是,則找到占用者為發送主機的所有 IP 地址,對於其中狀態為待分配的,將其狀態設置為未分配,並清空其占用者,清零其過期時刻,處理結束;
  2. 檢查報文中的 IP 地址是否在地址池內,且其占用者為發送主機,若不是,則向發送主機發送 Nak 報文,處理結束;
  3. 無論該 IP 地址的狀態為何,將該 IP 地址的狀態設置為占用;
  4. 與 Discover 報文相同的方法,設置 IP 地址的過期時刻;
  5. 向發送主機發送 Ack 報文。

  

  這題本身沒有太大的難度,主要困難在於靜下心來讀題。

  注意:每次處理報文時要先更新IP的狀態。

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <string>
  4 using namespace std;
  5 int t[10010];//接收到報文的時刻
  6 int N, T_def, T_max, T_min;
  7 string H = "";//主機名
  8 void settings()
  9 {
 10     char tmp;
 11 
 12     while ((tmp = getchar()) != ' ')
 13     {
 14         N *= 10;
 15         N += (tmp - '0');
 16     }
 17 
 18     while ((tmp = getchar()) != ' ')
 19     {
 20         T_def *= 10;
 21         T_def += (tmp - '0');
 22     }
 23 
 24     while ((tmp = getchar()) != ' ')
 25     {
 26         T_max *= 10;
 27         T_max += (tmp - '0');
 28     }
 29 
 30     while ((tmp = getchar()) != ' ')
 31     {
 32         T_min *= 10;
 33         T_min += (tmp - '0');
 34     }
 35 
 36     
 37     while ((tmp = getchar()) != '\n')
 38     {
 39         //printf("%d\n", H.size());
 40         H = H + tmp;
 41     }
 42 }
 43 
 44 int cnt;
 45 string S_H, R_H, kind;//發送主機、接收主機、報文類型
 46 int overdue_T;
 47 int IP;
 48 int overdue_IP[10010];//某IP地址的過期時刻
 49 int state[10010];//IP狀態    未分配、待分配、占用、過期————0、1、2、3
 50 string occupation[10010];//IP占用者
 51 
 52 void get_message()//接收報文
 53 {
 54     char tmp;
 55 
 56     //接收時刻
 57     while ((tmp = getchar()) != ' ')
 58     {
 59         t[cnt] *= 10;
 60         t[cnt] += (tmp - '0');
 61     }
 62 
 63     //發送主機
 64     S_H = "";
 65     while ((tmp = getchar()) != ' ')
 66     {
 67         S_H += tmp;
 68     }
 69 
 70     //接收主機
 71     R_H = "";
 72     while ((tmp = getchar()) != ' ')
 73     {
 74         R_H += tmp;
 75     }
 76 
 77     //報文類型
 78     kind = "";
 79     while ((tmp = getchar()) != ' ')
 80     {
 81         kind += tmp;
 82     }
 83 
 84     //IP
 85     IP = 0;
 86     while ((tmp = getchar()) != ' ')
 87     {
 88         IP *= 10;
 89         IP += (tmp - '0');
 90     }
 91 
 92     //過期時刻
 93     overdue_T = 0;
 94     while ((tmp = getchar()) != '\n')
 95     {
 96         overdue_T *= 10;
 97         overdue_T += (tmp - '0');
 98     }
 99 
100     //printf("%d %s %s %s %d %d\n", t[cnt], S_H.c_str(), R_H.c_str(), kind.c_str(), IP, overdue_T[cnt]);
101 }
102 
103 //更新IP的狀態
104 void update()
105 {
106     for (int i = 1; i <= N; i++)
107     {
108         if (state[i] == 1 && overdue_IP[i] <= t[cnt])
109         {
110             state[i] = 0;
111             overdue_IP[i] = 0;
112             occupation[i] = "";
113         }
114         else if (state[i] == 2 && overdue_IP[i] <= t[cnt])
115         {
116             state[i] = 3;
117             overdue_IP[i] = 0;
118         }
119     }
120 }
121 void discover()
122 {
123     update();
124     IP = 0;
125     int IP_0 = 0, IP_3 = 0;//最小未分配、最小過期
126     for (int i = 1; i <= N; i++)
127     {
128         if (occupation[i] == S_H)
129         {
130             IP = i;
131             break;
132         }
133         if (!IP_0 && state[i] == 0)
134             IP_0 = i;
135         if (!IP_3 && state[i] == 3)
136             IP_3 = i;
137     }
138     if (!IP)
139     {
140         if (!IP_0 && !IP_3)
141             return;//不處理該報文
142         if (IP_0)
143             IP = IP_0;
144         else
145             IP = IP_3;
146     }
147 
148     state[IP] = 1;
149     occupation[IP] = S_H;
150 
151     if (!overdue_T)
152         overdue_IP[IP] = t[cnt] + T_def;
153     else
154     {
155         if (overdue_T > t[cnt] + T_max)
156             overdue_IP[IP] = t[cnt] + T_max;
157         else if (overdue_T < t[cnt] + T_min)
158             overdue_IP[IP] = t[cnt] + T_min;
159         else
160             overdue_IP[IP] = overdue_T;
161     }
162     
163     //向發送主機發送 Offer 報文
164     cout << H << " " << S_H << " OFR " << IP << " " << overdue_IP[IP] << endl;
165 }
166 
167 void request()
168 {
169     update();
170     if (R_H != H)
171     {
172         for (int i = 1; i <= N; i++)
173         {
174             if (occupation[i] == S_H && state[i] == 1)//找到占用者為發送主機的所有 IP 地址
175             {
176                 //對於其中狀態為待分配的,將其狀態設置為未分配,並清空其占用者,清零其過期時刻
177                 state[i] = 0;
178                 occupation[i] = "";
179                 overdue_IP[i] = 0;
180             }
181         }
182         return;
183     }
184     if (IP <= N && occupation[IP] == S_H)//檢查報文中的 IP 地址是否在地址池內,且其占用者為發送主機
185     {
186         state[IP] = 2;
187 
188         if (!overdue_T)
189             overdue_IP[IP] = t[cnt] + T_def;
190         else
191         {
192             if (overdue_T > t[cnt] + T_max)
193                 overdue_IP[IP] = t[cnt] + T_max;
194             else if (overdue_T < t[cnt] + T_min)
195                 overdue_IP[IP] = t[cnt] + T_min;
196             else
197                 overdue_IP[IP] = overdue_T;
198         }
199         //向發送主機發送 Ack 報文
200         cout << H << " " << S_H << " ACK " << IP << " " << overdue_IP[IP] << endl;
201     }
202     else
203     {
204         //cout << overdue_T << ' ' << overdue_IP[IP] << endl;
205         //向發送主機發送 Nak 報文
206         cout << H << " " << S_H << " NAK " << IP << " 0" << endl;
207     }
208 }
209 
210 int main()
211 {
212     //printf("%s", H.c_str());
213     int n;
214     settings();
215     //printf("%d %d %d %d %s\n", N, T_def, T_max, T_min, H.c_str());
216     cin >> n;
217     getchar();
218     
219     for (cnt = 1; cnt <= n; cnt ++)
220     {
221         get_message();
222         if (R_H != H && R_H != "*" && kind != "REQ" || R_H == "*" && kind != "DIS" || R_H == H && kind == "DIS")
223             continue;
224         if (kind == "DIS")
225             discover();
226         else if(kind == "REQ")
227             request();
228     }
229     return 0;
230 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM