【Linux网络编程学习】预备知识(网络字节序、IP地址转换函数、sockaddr数据结构)
此為??蚅inux C++課程和黑馬Linux系統編程筆記。
1. 網絡字節序
我們已經知道,內存中的多字節數據相對于內存地址有大端和小端之分。
磁盤文件中的多字節數據相對于文件中的偏移地址也有大端小端之分。網絡數據流同樣有大端小端之分,那么如何定義網絡數據流的地址呢?發送主機通常將發送緩沖區中的數據按內存地址從低到高的順序發出,接收主機把從網絡上接到的字節依次保存在接收緩沖區中,也是按內存地址從低到高的順序保存,因此,網絡數據流的地址應這樣規定:先發出的數據是低地址,后發出的數據是高地址。
TCP/IP協議規定,網絡數據流應采用大端字節序,即低地址高字節。例如:發送主機中地址0-1存儲16位的源端口號,這個端口號是1000(0x3e8),則地址0是0x03,地址1是0xe8,也就是先發0x03,再發0xe8,這16位在發送主機的緩沖區中也應該是低地址存0x03,高地址存0xe8。但是,如果發送主機是小端字節序的,則地址0存儲0xe8,地址1存儲0x03,接收方接收到的端口號就成了59395(0xe803)而不是1000。因此,發送主機把1000填到發送緩沖區之前需要做字節序的轉換。同樣地,接收主機如果是小端字節序的,接到16位的源端口號也要做字節序的轉換。如果主機是大端字節序的,發送和接收都不需要做轉換。同理,32位的IP地址也要考慮網絡字節序和主機字節序的問題。
為使網絡程序具有可移植性,使同樣的C代碼在大端和小端計算機上編譯后都能正常運行,可以調用以下庫函數做網絡字節序和主機字節序的轉換。
#include <arpa/inet.h> uint32_t htonl(uint32_t hostlong); // 32位,host轉為net,用于主機向網絡發送IP時的轉換 uint16_t htons(uint16_t hostshort); // 16位,host轉為net,用于主機向網絡發送IP時的轉換 uint32_t ntohl(uint32_t netlong); // 32位,net轉為host,用于主機向網絡發送端口號時的轉換 uint16_t ntohs(uint16_t netshort); // 16位,net轉為host,用于主機向網絡發送端口號時的轉換h表示host,n表示network,l表示32位長整數(IP地址為32位),s表示16位短整數(端口號位16位)。
如果主機是小端字節序,這些函數將參數做相應的大小端轉換然后返回,如果主機是大端字節序,這些函數不做轉換,將參數原封不動地返回。
2. IP地址轉換函數
通常,人們習慣用可讀性好的字符串來表示 IP 地址,比如用點分十進制字符串表示 IPv4 地址,以及用
十六進制字符串表示 IPv6 地址。但編程中我們需要先把它們轉化為整數(二進制數)方能使用。而記錄
日志時則相反,我們要把整數表示的 IP 地址轉化為可讀的字符串。
af:地址族: AF_INET(表示ipv4) AF_INET6(表示ipv6)
src:需要轉換的點分十進制的IP字符串
dst:轉換后的結果保存在這個里面
af:地址族: AF_INET(表示ipv4) AF_INET6(表示ipv6)
src: 要轉換的ip的整數的地址
dst: 轉換成IP地址字符串保存的地方
size:第三個參數的大小(數組的大小)
返回值:返回轉換后的數據的地址(字符串),和 dst 是一樣的
3. socket 地址
socket 網絡編程接口中表示 socket 地址的是結構體 sockaddr,其定義如下:
#include <bits/socket.h> struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; typedef unsigned short int sa_family_t;很多網絡編程函數誕生早于 IPv4 協議,那時候都使用的是 struct sockaddr 結構體,為了向前兼容,現在sockaddr 退化成了類似(void *)的作用,傳遞一個地址給函數,至于這個函數是 sockaddr_in 還是sockaddr_in6,由地址族確定,然后函數內部再強制類型轉化為所需的地址類型
TCP/IP 協議族有 sockaddr_in 和 sockaddr_in6 兩個專用的 socket 地址結構體,它們分別用于 IPv4 和IPv6:
#include <netinet/in.h> struct sockaddr_in { sa_family_t sin_family; /* __SOCKADDR_COMMON(sin_) */ in_port_t sin_port; /* Port number. */ struct in_addr sin_addr; /* Internet address. */ /* Pad to size of `struct sockaddr'. */ unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (in_port_t) - sizeof (struct in_addr)]; }; struct in_addr { in_addr_t s_addr; }; struct sockaddr_in6 { sa_family_t sin6_family; in_port_t sin6_port; /* Transport layer port # */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* IPv6 scope-id */ }; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef uint16_t in_port_t; typedef uint32_t in_addr_t; #define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))sin_family:指定你所使用哪種協議,ipv4或者ipv6.
AF_INET : ipv4
AF_INET6 : ipv6
AF_UNIX, AF_LOCAL : 本地套接字通信(進程間通信)
sin_port: 端口號
所有專用 socket 地址(以及 sockaddr_storage)類型的變量在實際使用時都需要轉化為通用 socket 地址類型 sockaddr(強制轉化即可),因為所有 socket 編程接口使用的地址參數類型都是 sockaddr。
總結
以上是生活随笔為你收集整理的【Linux网络编程学习】预备知识(网络字节序、IP地址转换函数、sockaddr数据结构)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 做试管婴儿植入能放几个胚胎
- 下一篇: 输卵管炎症的症状有什么