**C語言編程實(shí)現(xiàn)對(duì)IPV4地址的合法性判斷**
> 有了解過我的朋友,可能有點(diǎn)印象,我在N年前的博客中,就寫了這個(gè)主題,當(dāng)時(shí)確實(shí)是工作中遇到了這個(gè)問題。本想著等工作搞完之后,就把這個(gè)問題的解決代碼補(bǔ)上,結(jié)果一鴿,就是好幾年,真是慚愧。現(xiàn)在把這部分代碼公開,歡迎大家來下載測試。
@[toc]
# 1 寫在前面
有了解過我的朋友,可能有點(diǎn)印象,我在N年前的博客中,就寫了這個(gè)主題,當(dāng)時(shí)確實(shí)是工作中遇到了這個(gè)問題。本想著等工作搞完之后,就把這個(gè)問題的解決代碼補(bǔ)上,結(jié)果一鴿,就是好幾年,真是慚愧。現(xiàn)在把這部分代碼公開,歡迎大家來下載測試。
如果你發(fā)現(xiàn)代碼有問題,歡迎與我私信聯(lián)系。
# 2 需求分析
其實(shí),本專題的需求很簡單,就是輸入一段字符串,判斷它是不是合法的IPv4地址。僅僅從功能上看,似乎很簡單,但是真正要做到很完美,也是需要下點(diǎn)功夫的。不信,你看看下文的拆解。
![IPV4 的圖像結(jié)果](https://img-blog.csdnimg.cn/img_convert/994e3109304e34a6bbd59db8247d37c6.jpeg)
# 3 簡單版本
我們先上一個(gè)簡單版本,直接看代碼:
```c
#include
#include
#include
#include
int is_valid_ipv4(const char *ip_address)
{
int num, dots = 0;
char *ptr;
if (ip_address == NULL) {
return 0;
}
ptr = strtok((char *)ip_address, ".");
if (ptr == NULL) {
return 0;
}
while (ptr) {
if (!isdigit(*ptr)) {
return 0;
}
num = atoi(ptr);
if (num < 0 || num > 255) {
return 0;
}
ptr = strtok(NULL, ".");
if (ptr != NULL) {
dots++;
}
}
if (dots != 3) {
return 0;
}
return 1;
}
int check_is_valid_ipv4(const char *ip)
{
int ret = 0;
ret = is_valid_ipv4(ip);
return ret;
}
int main(int argc, const char *argv[])
{
const char *ip = argv[1];
printf("check %sn", ip);
printf("ret %dn", check_is_valid_ipv4(ip));
}
```
編譯運(yùn)行一下,輸入一個(gè)常見的ipv4地址是沒有問題,比如 "192.168.0.1";同時(shí),非法的字符輸入也是會(huì)報(bào)錯(cuò)的。
```c
~/ipv4]$gcc -o test ipv4.c
~/ipv4]$./test 192.168.0.1
check 192.168.0.1
ret 1
~/ipv4]$./test
check 192.168.w.2
ret 0
```
但是,如果我加一個(gè)限制:如何判斷一個(gè)IPV4地址是一個(gè)合法的 **主機(jī)地址** 呢?
比如這個(gè):“238.171.84.41”,它的判斷還是合法的哦,實(shí)際上這不是合法的 **主機(jī)地址** 。
```c
~/ipv4]$./test 238.171.84.41
check 238.171.84.41
ret 1
```
另外一個(gè),上面的代碼還檢測不到,諸如此類的輸入:“0192.168.1.1”
```c
~/ipv4]$./test 0192.168.1.1
check 0192.168.1.1
ret 1
```
所以,我們需要優(yōu)化一下。
# 4 進(jìn)階一下
正如上面的分析,我們需要對(duì)代碼進(jìn)行優(yōu)化:
首先得判斷按照 "." 分割后的數(shù)字段,不能以 "0" 字符開頭。
```c
while (ptr) {
if (!isdigit(*ptr)) {
return 0;
}
if (*ptr == '0') { //check start with '0'
return 0;
}
num = atoi(ptr);
if (num < 0 || num > 255) {
return 0;
}
ptr = strtok(NULL, ".");
if (ptr != NULL) {
dots++;
}
}
~/ipv4]$./test 0192.168.1.1
check 0192.168.1.1
ret 0
```
根據(jù)IPv4地址的分類:
- A類:(1.0.0.1-126.255.255.254)(默認(rèn)子網(wǎng)掩碼:255.0.0.0或0xFF000000)第一個(gè)字節(jié)為網(wǎng)絡(luò)號(hào),后三個(gè)字節(jié)為主機(jī)號(hào),表示為網(wǎng)絡(luò)--主機(jī)--主機(jī)--主機(jī)。該類IP地址的最前面為“0”,所以地址的網(wǎng)絡(luò)號(hào)取值于1~126之間。共有16777214個(gè)主機(jī)地址,一般用于大型網(wǎng)絡(luò)。
- B類:(128.1.0.1-191.254.255.254)(默認(rèn)子網(wǎng)掩碼:255.255.0.0或0xFFFF0000)前兩個(gè)字節(jié)為網(wǎng)絡(luò)號(hào),后兩個(gè)字節(jié)為主機(jī)號(hào)。該類IP地址的最前面為“10”,所以地址的網(wǎng)絡(luò)號(hào)取值于128~191之間。共有65534個(gè)主機(jī)地址,一般用于中等規(guī)模網(wǎng)絡(luò)。
- C類:(192.0.1.1-223.255.254.254)(子網(wǎng)掩碼:255.255.255.0或0xFFFFFF00)前三個(gè)字節(jié)為網(wǎng)絡(luò)號(hào),最后一個(gè)字節(jié)為主機(jī)號(hào)。該類IP地址的最前面為“110”,所以地址的網(wǎng)絡(luò)號(hào)取值于192~223之間。共有254個(gè)主機(jī)地址,一般用于小型網(wǎng)絡(luò)。
- D類:是多播地址。(224.0.0.1-239.255.255.254) 該類IP地址的前面4位為“1110”,所以網(wǎng)絡(luò)號(hào)取值于224~239之間;后面28位為組播地址ID。這是一個(gè)專門保留的地址。它并不指向特定的網(wǎng)絡(luò),目前這一類地址被用在多點(diǎn)廣播(Multicasting)中。多點(diǎn)廣播地址用來一次尋址一組計(jì)算機(jī),它標(biāo)識(shí)共享同一協(xié)議的一組計(jì)算機(jī)。
- E類:是保留地址,為將來使用保留。(240.0.0.0---255.255.255.254) 該類IP地址的最前面為“1111”,所以網(wǎng)絡(luò)號(hào)取值于240~255之間。
可知,如果要符合一個(gè)正常的IPv4主機(jī)地址,只能是A、B、C類,而不能是D、E類。
所以在判斷時(shí),我們應(yīng)該增加IPv4地址的類別判斷。
在上面的判斷返回前,增加一個(gè)判斷:
```c
if (atoi(ip_address) >= 1 && atoi(ip_address) <= 126) {
printf("This is a Class A IP address.n");
return 1;
} else if (atoi(ip_address) >= 128 && atoi(ip_address) <= 191) {
printf("This is a Class B IP address.n");
return 1;
} else if (atoi(ip_address) >= 192 && atoi(ip_address) <= 223) {
printf("This is a Class C IP address.n");
return 1;
} else {
printf("This is not a Class A, B, or C IP address.n");
return 0;
}
```
完整的代碼如下:
```c
#include
#include
#include
#include
int is_valid_ipv4(const char *ip_address)
{
int num, dots = 0;
char *ptr;
if (ip_address == NULL) {
return 0;
}
ptr = strtok((char *)ip_address, ".");
if (ptr == NULL) {
return 0;
}
while (ptr) {
if (!isdigit(*ptr)) {
return 0;
}
if (*ptr == '0') { //check start '0'
return 0;
}
num = atoi(ptr);
if (num < 0 || num > 255) {
return 0;
}
ptr = strtok(NULL, ".");
if (ptr != NULL) {
dots++;
}
}
if (dots != 3) {
return 0;
}
if (atoi(ip_address) >= 1 && atoi(ip_address) <= 126) {
printf("This is a Class A IP address.n");
return 1;
} else if (atoi(ip_address) >= 128 && atoi(ip_address) <= 191) {
printf("This is a Class B IP address.n");
return 1;
} else if (atoi(ip_address) >= 192 && atoi(ip_address) <= 223) {
printf("This is a Class C IP address.n");
return 1;
} else {
printf("This is not a Class A, B, or C IP address.n");
return 0;
}
return 1;
}
int check_is_valid_ipv4(const char *ip)
{
int ret = 0;
ret = is_valid_ipv4(ip);
return ret;
}
int main(int argc, const char *argv[])
{
const char *ip = argv[1];
printf("check %sn", ip);
printf("ret %dn", check_is_valid_ipv4(ip));
}
```
這個(gè)時(shí)候,我們?cè)僭囈幌轮暗姆茿/B/C類的IPv4地址:
```c
~/ipv4]$./test 238.171.84.41
check 238.171.84.41
This is not a Class A, B, or C IP address.
ret 0
~/ipv4]$./test 192.168.2.3
check 192.168.2.3
This is a Class C IP address.
ret 1
```
至此,基本得到了比較完美的判斷,但有沒有漏洞呢?留給讀者自己去思考吧。
# 5 高階版本
有經(jīng)驗(yàn)的程序一定會(huì)發(fā)現(xiàn),上面的各個(gè)判斷真的號(hào)麻煩啊!
每個(gè)case都需要這樣去比較判斷,那得多費(fèi)勁啊!
有沒有更加清爽一點(diǎn)的高階方法啊?
答案當(dāng)然是有的,這個(gè)時(shí)候你就需要了解一下:**正則表達(dá)式** 了。
很多主流的編程語言都有標(biāo)準(zhǔn)庫來支持正則表達(dá)式,那么C語言里面有沒有呢?
其實(shí)C語言里面也是可以用正則表達(dá)式的,這個(gè)先留個(gè)懸念,且聽下回分解。
歡迎打擊提前預(yù)習(xí)下:[正則表達(dá)式語言 - 快速參考 | Microsoft Learn](https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/regular-expression-language-quick-reference)
# 6 完整測試用例
本小節(jié)給大家補(bǔ)充一下各種測試用例,希望對(duì)大家測試代碼有幫助:
```c
合法的測試輸入
192.168.0.1
10.0.0.1
172.16.0.1
255.255.255.255
非法的測試輸入
256.0.0.1
192.168.0.0.1
192.168.0
192.168.0.1.2
非法的測試輸入
256.0.0.1
192.168.0.0.1
192.168.0
192.168.0.1.2
300.300.300.300
1.2.3
1.2.3.4.5
1.2.3.4.
.1.2.3.4
1..2.3.4
```
測試用例是不斷豐富的,歡迎大家來補(bǔ)充。
審核編輯黃宇
-
C語言
+關(guān)注
關(guān)注
180文章
7601瀏覽量
136251 -
IPv4
+關(guān)注
關(guān)注
0文章
142瀏覽量
19869
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論