linux網路程式設計基礎(一)
一、資料儲存順序:大端和小端
高位位元組儲存高位元組稱為小端模式,通常都計算機採用這個模式儲存。而網路則採用大端傳輸。所以需要轉換
面試有時會出這麼個題:寫一個程式判斷程式的儲存是大端還是小端?
程式的原理見下圖:
#include<stdio.h> #include<stdlib.h> union word{ int a; char b; }c; int check(){ c.a=1; return (c.b == 1)? 0:1; } int main(){ if(check()){ printf("big\n"); }else{ printf("Little\n"); } }
利用Union的特點,判斷出了大端還是小端。
還有一道經典的面試題是 :
#include<stdio.h>
int main(){
int a[]={1,2,3,4};
int *ptr1 = (int *)(&a+1);
int *ptr2 = (int *)((int)a+1);
printf("%x , %x\n",ptr1[-1],*ptr2);
}
結果是:2 , 2000000 。
第一個值的計算請參考 中的第四題。
第二個值的計算如下圖:
將a轉換為整形,然後增加1,再轉回指標型,相當於只移動了一個bit。由於是小端儲存,會將下一個數的末尾讀進來。
二、位元組序的處理。
因為存在大端小端的問題,所以就要進行統一的轉換。
注意字串是不用轉換的,因為一個字元正好佔一位元組。儲存順序不影響值。而浮點數也不用轉換,因為浮點數的讀取規則是在cpu中定義的,是一致的。
轉換所用的函式為:
htons(),htonl(); 主機轉為網路位元組序,s為short , l為long
ntohs(),ntohl(); 網路轉為主機位元組序。
三、地址格式的轉換
通常情況下,都是用點分十進位制(如:202.134.23.145)來表示IP地址。是個字串。但是程式中處理時用到的是一個二進位制的值。所以要進行轉換。
對於IPV4有以下4個函式:
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
int main(){
//ip地址字串
char* sa="202.30.45.11";
//記錄ip地址的結構體
struct in_addr addr,ret;
//是網路地址型別
in_addr_t at;
//將點分十進位制字串轉換為32位網路位元組序的IP
at=inet_addr(sa);
//十六進位制輸出
printf("inet_addr:0x%x \n",at);
//將點分十進位制字串轉換為32位主機位元組序,與網路位元組序應該是反過來的
printf("inet_network:0x%x \n",inet_network(sa));
//結構體中記錄IP地址的資料成員
addr.s_addr=at;
//網路位元組序轉換為點分十進位制數
printf("inet_ntoa:%s \n",inet_ntoa(addr));
//點分十進位制數轉換為網路位元組序,引數為結構體
inet_aton(sa,&ret);
printf("inet_aton:0x%x \n",ret.s_addr);
}
編譯執行的結果:
[[email protected] 400]$ ./addr
inet_addr:0xb2d26ca
inet_network:0xca262d0b
inet_ntoa:202.30.45.11
inet_aton:0xb2d26ca
[[email protected] 400]$
而對於結構不同的IPV6就不行了。但有兩個函式可以通用於IPV4和IPV6:
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(){
struct in_addr ret;
//IPV6的ip結構體
struct in6_addr ret6;
//儲存轉換的IP地址
char buf[32];
//IPV4
char* sa="202.128.47.168";
//IPV6
char* sa6="fe80::20c:29ff:fe5f:8760";
//點分十進位制轉為網路位元組序,第一個引數表示IPV4
inet_pton(AF_INET,sa,&ret);
printf("pton: 0x%x\n",ret.s_addr);
//網路位元組序轉為點分十進位制,第二個引數為要轉換的值,第三個引數為轉換後儲存的位置,第四個引數為字串的最大長度
inet_ntop(AF_INET,&ret,buf,32);
printf("ntop: %s\n",buf);
//IPV6的用法一樣只是第一個引數改為AF_INET6
inet_pton(AF_INET6,sa6,&ret6);
printf("pton: 0x%x\n",ret6.s6_addr);
inet_ntop(AF_INET6,&ret6,buf,32);
printf("ntop: %s\n",buf);
}
四、域名與IP資訊的解析
IP地址不好記,所以便有了域名。
可以通過返回主機資訊:
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<netdb.h>
int main(int argc,char **argv){
char *ptr,**pptr;
struct hostent *hptr;
char addr[32];
if(argc>1){
//將資訊存入結構體中
if((hptr=gethostbyname(argv[1]))== NULL){
printf("gethostbyname error!:%s\n",argv[1]);
exit(1);
}
//輸出主機名
printf("h_name: %s\n",hptr->h_name);
//主機備選名稱,以NULL結尾的列表
for(pptr=hptr->h_aliases;*pptr!=NULL;pptr++){
printf("alias: %s\n",*pptr);
}
//主機網路地址,以NULL結尾的列表
for(pptr=hptr->h_addr_list;*pptr!=NULL;pptr++){
inet_ntop(hptr->h_addrtype,*pptr,addr,sizeof(addr));
printf("addr :%s \n",addr);
}
}
}
編譯輸出為:
[[email protected] 400]$ ./gethost www.baidu.com
h_name: www.a.shifen.com
alias: www.baidu.com
addr :119.75.217.56
addr :119.75.218.45
[[email protected] 400]$