1. 程式人生 > >linux網路程式設計基礎(一)

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]$