【網路程式設計】TCPIP_3_地址族與資料序列

李柱明發表於2021-08-20


前言

說明:

  • demo 基於 Linux。

3. 地址族與資料序列

// 	呼叫 bind 函式分配IP地址和埠號。
//	成功時返回0,失敗時返回-1
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);

/* 	補充 client 部分 */
//	成功時返回0,失敗時返回-1
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

3.1 分配給套接字的 IP 地址與埠號

參考: 李柱明部落格園

3.2 引數 IP 地址

由於原生的引數 struct sockaddr 型別不夠直觀,所以採用型別轉換的 struct sockaddr_in 只要保證其位元組大小一樣,然後把其當做一段微記憶體即可。

3.2.1 IPV4 地址的結構體

// 原生,不建議直接用
struct sockaddr {
	sa_family_t	sa_family;	/* address family, AF_xxx	*/
	char		sa_data[14];	/* 14 bytes of protocol address	*/
};

// 格式轉換後,推薦使用
struct sockaddr_in
{
    sa_family_t sin_family;  //地址族(Address Family)
    uint16_t sin_port;       //16 位 TCP/UDP 埠號
    struct in_addr sin_addr; //32位 IP 地址
    char sin_zero[8];        //為了保證其大小一致,目前不使用
};

struct in_addr
{
    in_addr_t s_addr; //32位IPV4地址
}

3.2.2 地址族(Address Family)

成員 sa_family_t sin_family;

具體地址族型別參考 地址族(附件)

地址族(Address Family) 含義
AF_INET IPV4用的地址族
AF_INET6 IPV6用的地址族
AF_LOCAL 本地通訊中採用的 Unix 協議的地址族

3.2.3 埠號

成員 uint16_t sin_port;

16 byte。
以網路位元組序儲存。(CPU 分大小端,為了統一,網路協同使用一種位元組序來保證資料正確,到對端後再按需轉換即可)

小知識

  • 可以這樣區分 IP 和 埠號:一個主機可以共用一個 IP,埠號 用來區分主機程式。
  • 知名埠是要把該埠分配給特定的應用程式,範圍是 0~1023 ,HTTP 的埠號是 80 ,FTP 的埠號是20和21。

3.2.4 IP 地址

成員 struct in_addr sin_addr;

32 byte。
以網路位元組序儲存。

小知識

  • 127.0.0.1 表示本機 IP。協議規定的。

3.2.5

成員 char sin_zero[8];

為了讓sockaddr與sockaddr_in兩個資料結構保持大小相同而保留的空位元組,目前沒有其它用途。

3.3 例項參考

注意:客戶端建立socket後使用的函式是connect()。

#define cHOST "192.168.112.128" // 伺服器端IP
#define cPORT 8080              // 伺服器程式埠號

// 客戶端
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; // IPV4
serv_addr.sin_addr.s_addr = inet_addr(cHOST); // 地址
serv_addr.sin_port = htons(cPORT); //埠
//呼叫 connect 函式向伺服器傳送連線請求
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
	error_handling("connect() error!");

3.4 網路位元組序

3.4.1 位元組序

得先了解大小端的問題。
本人的快速記憶是 小高高,意思是 位元組放在位。
因為CPU有大小端的差異,如果不統一網路位元組序就會導致資料互動錯亂,如圖:

網路位元組序統一使用大端。
所以在收發前,先對資料進行檢查、轉換,再進行網路傳輸。

3.4.2 位元組序轉換相關函式

前字尾:

  • h:表示 host ,主機位元組。
  • n:表示 network,網路位元組。
  • s:表示 short。
  • l:表示 long。
unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);

3.5 字串轉為網路位元組序的整數形

3.5.1 inet_addr()

函式 inet_addr()

inet_addr是一個計算機函式,功能是將一個點分十進位制的IP轉換成一個長整數型數(u_long型別)等同於inet_addr()。

注意:一位元組的範圍為 [0:255]

// 返回:若字串有效則將字串轉換為32位二進位制網路位元組序的IPV4地址,否則為INADDR_NONE
#include <arpa/inet.h> // Linux
in_addr_t inet_addr(const char* strptr);

// 例子:
unsigned long conv_addrA = inet_addr("1.2.3.4"); // conv_addrA 為 0x4030201

unsigned long conv_addrB = inet_addr("1.2.3.256"); // conv_addr ERR,因為範圍為 [0:255]

3.5.2 inet_aton()

函式 inet_aton()

函式 inet_aton() 和 函式 inet_addr() 在功能上是一樣的,也是將字串形式的IP地址轉換成整數型的IP地址。
不同的是該函式得到的結果儲存到 in_addr 結構體引數裡面了,這樣就可以判斷有沒有轉換成功。

/*	
	string: 含有需要轉換的IP地址資訊的字串地址值
	addr: 將儲存轉換結果的 in_addr 結構體變數的地址值
	成功時返回 1 ,失敗時返回 0
*/
#include <arpa/inet.h>
int inet_aton(const char *string, struct in_addr *addr);

3.5.3 inet_ntoa()

函式 inet_ntoa()

該函式更好和 inet_aton() 相反,n -> a。
把網路位元組整數型序轉換為地址點分字串形式。

特別注意

  • 該函式特點:其返回值為指標,實際記憶體實在函式裡面申請的,該函式的實現裡,只申請一次,下次呼叫時使用同樣的已申請了的記憶體,(而不是重新申請,返回新的指標),所以下次呼叫該函式時,會把上次資料覆蓋。
  • 使用注意:獲取到返回的指標後,在下次呼叫該函式前,必須使用完畢或者把資料拷貝到新的記憶體裡慢慢享受。
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr adr);

3.6 網路地址初始化參考

struct sockaddr_in addr;
char *serv_ip = "192.168,1.2";          	//宣告IP地址族
char *serv_port = "8080";                  	//宣告埠號字串
memset(&addr, 0, sizeof(addr));            	//結構體變數 addr 的所有成員初始化為0
addr.sin_family = AF_INET;                 	//制定地址族
addr.sin_addr.s_addr = inet_addr(serv_ip); 	//基於字串的IP地址初始化
addr.sin_port = htons(atoi(serv_port));    	//基於字串的IP地址埠號初始化

地址族(附件)

/* Supported address families. */
#define AF_UNSPEC	0
#define AF_UNIX		1	/* Unix domain sockets 		*/
#define AF_LOCAL	1	/* POSIX name for AF_UNIX	*/
#define AF_INET		2	/* Internet IP Protocol 	*/
#define AF_AX25		3	/* Amateur Radio AX.25 		*/
#define AF_IPX		4	/* Novell IPX 			*/
#define AF_APPLETALK	5	/* AppleTalk DDP 		*/
#define AF_NETROM	6	/* Amateur Radio NET/ROM 	*/
#define AF_BRIDGE	7	/* Multiprotocol bridge 	*/
#define AF_ATMPVC	8	/* ATM PVCs			*/
#define AF_X25		9	/* Reserved for X.25 project 	*/
#define AF_INET6	10	/* IP version 6			*/
#define AF_ROSE		11	/* Amateur Radio X.25 PLP	*/
#define AF_DECnet	12	/* Reserved for DECnet project	*/
#define AF_NETBEUI	13	/* Reserved for 802.2LLC project*/
#define AF_SECURITY	14	/* Security callback pseudo AF */
#define AF_KEY		15      /* PF_KEY key management API */
#define AF_NETLINK	16
#define AF_ROUTE	AF_NETLINK /* Alias to emulate 4.4BSD */
#define AF_PACKET	17	/* Packet family		*/
#define AF_ASH		18	/* Ash				*/
#define AF_ECONET	19	/* Acorn Econet			*/
#define AF_ATMSVC	20	/* ATM SVCs			*/
#define AF_RDS		21	/* RDS sockets 			*/
#define AF_SNA		22	/* Linux SNA Project (nutters!) */
#define AF_IRDA		23	/* IRDA sockets			*/
#define AF_PPPOX	24	/* PPPoX sockets		*/
#define AF_WANPIPE	25	/* Wanpipe API Sockets */
#define AF_LLC		26	/* Linux LLC			*/
#define AF_IB		27	/* Native InfiniBand address	*/
#define AF_MPLS		28	/* MPLS */
#define AF_CAN		29	/* Controller Area Network      */
#define AF_TIPC		30	/* TIPC sockets			*/
#define AF_BLUETOOTH	31	/* Bluetooth sockets 		*/
#define AF_IUCV		32	/* IUCV sockets			*/
#define AF_RXRPC	33	/* RxRPC sockets 		*/
#define AF_ISDN		34	/* mISDN sockets 		*/
#define AF_PHONET	35	/* Phonet sockets		*/
#define AF_IEEE802154	36	/* IEEE802154 sockets		*/
#define AF_CAIF		37	/* CAIF sockets			*/
#define AF_ALG		38	/* Algorithm sockets		*/
#define AF_NFC		39	/* NFC sockets			*/
#define AF_VSOCK	40	/* vSockets			*/
#define AF_KCM		41	/* Kernel Connection Multiplexor*/
#define AF_QIPCRTR	42	/* Qualcomm IPC Router          */
#define AF_SMC		43	/* smc sockets: reserve number for
				 * PF_SMC protocol family that
				 * reuses AF_INET address family
				 */

參考:

相關文章