------------恢復內容開始------------
報文格式
為了便於實現,我們簡化地規定 DHCP 數據報文的格式如下:
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 地址過期時刻為零,即沒有過期時刻。
對於收到的報文,設其收到的時刻為
。處理細節如下:
- 判斷接收主機是否為本機,或者為
*
,若不是,則判斷類型是否為 Request,若不是,則不處理; - 若類型不是 Discover、Request 之一,則不處理;
- 若接收主機為
*
,但類型不是 Discover,或接收主機是本機,但類型是 Discover,則不處理。
對於 Discover 報文,按照下述方法處理:
- 檢查是否有占用者為發送主機的 IP 地址:
- 若有,則選取該 IP 地址;
- 若沒有,則選取最小的狀態為未分配的 IP 地址;
- 若沒有,則選取最小的狀態為過期的 IP 地址;
- 若沒有,則不處理該報文,處理結束;
- 將該 IP 地址狀態設置為待分配,占用者設置為發送主機;
- 若報文中過期時刻為 0 ,則設置過期時刻為
- ;否則根據報文中的過期時刻和收到報文的時刻計算過期時間,判斷是否超過上下限:若沒有超過,則設置過期時刻為報文中的過期時刻;否則則根據超限情況設置為允許的最早或最晚的過期時刻;
- 向發送主機發送 Offer 報文,其中,IP 地址為選定的 IP 地址,過期時刻為所設定的過期時刻。
對於 Request 報文,按照下述方法處理:
- 檢查接收主機是否為本機:
- 若不是,則找到占用者為發送主機的所有 IP 地址,對於其中狀態為待分配的,將其狀態設置為未分配,並清空其占用者,清零其過期時刻,處理結束;
- 檢查報文中的 IP 地址是否在地址池內,且其占用者為發送主機,若不是,則向發送主機發送 Nak 報文,處理結束;
- 無論該 IP 地址的狀態為何,將該 IP 地址的狀態設置為占用;
- 與 Discover 報文相同的方法,設置 IP 地址的過期時刻;
- 向發送主機發送 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 }