本章主要說明:
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