UNIX網絡編程卷一 第三章 地址定義及相關函數

本章主要說明:

1. 5中不同的套接字地址,以及其結構體定義,和使用說明。

2. 網絡地址的結構體(數字)和字符串相互轉換函數。

3. 字節序測試,調整函數。

4. 內存按字節操作函數。


一、 套接字地址結構

這些結構體地址很多,感覺很難記,可能要以後多實踐吧。。

所有網絡地址都是以sockaddr_ 開頭的結構體組成,包括IPV4的 struct sockaddr_in{}, IPV6 的 struct sockaddr_in6;,UNIX域 struct sockaddr_un, 

數據鏈路 struct sockaddr_dl , 通用套接字地址有兩個, struct sockaddr(舊) ,以及struct sockaddr_storage(新)

1.  IPV4套接字地址結構體

IP地址結構體:

struct in_addr{

in_addr_t  s_addr;

};

通常 in_addr_t 是 由一個unsigned int 類型typedef而來, 爲何struct in_addr 結構體裏面只有一個地址成員, 卻還定義了一個結構體?

這是有歷史原因的,原來把這個結構定義爲多種結構體的union, 爲了方便訪問地址有ABC分類中的2個16位值,但是目前這種地址分類方法被淘汰了,所以這種結構體演變成這樣。


struct sockaddr_in{

uint8_t  sin_len;

sa_family_t sin_family;

in_port_t sin_port;

struct in_addr sin_addr;

char sin_zero[8];

};

這個套接字結構體包含了 協議族,端口號, IP地址,POSIX, sin_len規範不要求這個字段,填寫該結構體時可以直接忽略他。


2. IPV6套接字地址結構體

IP地址結構體:

struct in6_addr{

uint8_t s6_addr[16];

};

外觀上相比v4就多了個6, 分配的地址有16個字節,v4一般就4個字節。

struct sockaddr_in6{

...

};

其中成員前面部分和IPV4幾乎是一致的, 所有成員都以 sin6_打頭。


3. UNIX域套接字

struct  sockaddr_un{
  sa_family_t sun_family; /*PF_UNIX或AF_UNIX */
  char sun_path[UNIX_PATH_MAX]; /* 路徑名 */

  };

這個套接字用於本地進程間通信,也就沒有IP地址這些了,所以結構也不一樣


4. 數據鏈路 struct sockaddr_dl

這個不知道。。。


5. 通用套接字地址

舊:

struct sockaddr{

uint8_t sa_len;

sa_family_t sa_family;

char sa_data[14];

};

因爲當時還沒有void * 指針,所以才需要定義這個結構體。

通用套接字的作用主要用來進行強制類型轉換,因爲在bind , connect等函數中都是使用通用地址結構體指針作爲參數。

如bind 函數原型: int bind(int, struct sockaddr *, socklen_t);

由於IPV6的引入,這個通用地址結構體容納不了這麼大空間,因此有了新的通用地址結構。

struct sockaddr_storage{

uint8_t ss_len;

sa_family_t ss_family;

...

};

這個地址類型必須強制轉換成ss_family對應的結構體後才能訪問其中的其餘成員。


以上五中地址,前兩種套接字地址結構是固定長度的,unix 域和數據鏈路套接字是可變長的,因此需要一個len 字段。


二、 地址轉換函數


1.  ipv4 使用的轉換函數

inet_addr_t inet_addr(const char * strptr);   這個函數已經被廢棄,用下面的函數替代。


int inet_aton(const char *strptr, struct in_addr  *addrptr );

成功返回1, 失敗返回0

轉換結果保存在addrptr中


char * inet_ntoa(struct in_addr inaddr);

需要注意的是這個函數的參數是結構體,而不是指針這不知道爲什麼要這樣設計;而且返回的字符串是保存在靜態內存中,因此這個函數不可重入(線程不安全)。


這兩個函數在現有代碼中應該很常見,新代碼可以用inet_pton 和inet_ntop來替代,兼容IPV4和IPV6


2. ipv6 使用的轉換函數

int inet_pton(int family, const char * strptr, void * addrptr);

成功返回1, 失敗返回<0, 輸入表達式無效返回0

 const char * inet_ntop(int family, const void *addrptr,  char * strptr, size_t len);

若成功則返回指向結果的指針,失敗返回NULL


n 表示number,整數表示地址, p 表示point ,點分十進制表示地址

 

三、字節序測試函數


網絡字節序是大端字節序,主機字節序一般是小端字節序。

我一般記憶的是小端序:低低高高

低地址 存放低位數據,高地址存放高位數據。

如0x0102存放在數組a[2]中,其中a[1]地址比a[0]要高。

那麼,如果a[0] (低地址)裏面放的是 0x02(低位)那麼就是小端,如果放的是0x01那麼就是大端。

1. 測試主機字節序例子

#include "unp.h"

int
main(int argc, char **argv)
{
union {
 short  s;
      char   c[sizeof(short)];
    } un;


un.s = 0x0102;
printf("%s: ", CPU_VENDOR_OS);
if (sizeof(short) == 2) {
if (un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
else
printf("unknown\n");
} else
printf("sizeof(short) = %d\n", sizeof(short));


exit(0);
}

2. 調整字節序函數

uint16_t htons(uint16_t host16bitvalue);

uint32_t htonl(uint32_t host32bitvalue);

uint16_t  ntohs(uint16_t host16bitvalue);

uint32_t ntohs(uint32_t host32bitvalue);

h 表示 host

n 表示network

s 表示short   看作16位

l 表示long 看作32位


4. 內存操作函數

一般使用ANSI   C函數即 mem開頭的那幾個函數,如memset, memcpy,memcpy

還有一種b開頭的函數, bzero    bcopy, bcmp



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章