【UNIX網絡編程(一)】套接字地址結構、網絡字節順序和地址轉換功能

介紹:應該用在網絡編程實現每一個套接字地址結構。因此主套接字地址結構後前提網絡計劃編制,地址結構能夠在兩個方向上發送:從工藝到內核和內核處理。構中的二進制值之間進行轉換。編程


大多數套接字函數都需要一個指向套接字地址結構的指針做爲參數。不一樣協議都有本身的套接字地址結構。網絡

通用的套接字地址結構是sockaddr。IPv4套接字地址結構是定義在頭文件<netinet/in.h>中的sockaddr_in,其POSIX定義例如如下:socket

struct in_addr{
	in_addr_t		s_addr;		/*32-bit IPv4 address*/
};							/*network byte ordered*/
struct sockaddr_in{
	unit8_t		sin_len;		/*length of structure(16)*/
	sa_family_t	sin_family;	/*AF_INET*/
	in_port_t		sin_port;		/*16-bit TCP or UDP port number*/
							/*network byte ordered*/
	struct in_addr	sin_addr;		/*32-bit IPv4 address, network byte ordered*/
	char			sin_zero[8];	
};
POSIX規範僅僅需要結構中的3個字段:sin_family、sin_addr和sin_port。

它們的數據類型也都在上面給出了。函數

注意:32位IPv4地址存在兩種不一樣的訪問方法。舉例來講,假設serv定義爲某個網絡套接字地址結構,那麼serv.sin_addr將按in_addr結構引用當中的32位IPv4地址。而serv.sin_addr.s_addr將按in_addr_t(通常是一個無符號的32位整數)引用同一個32位IPv4地址。所以,必須正確使用IPV4地址。尤爲是將它做爲函數的參數時。因爲編譯器對傳遞結構和傳遞整數的處理時全然不一樣的。spa

通用套接字地址結構sockaddr定義在<sys/socket.h>頭文件裏。指針

struct sockaddr{
	unit8_t	sa_len;		/*address family:AF_xxx value*/
	sa_family_t	sa_family;	/*protocol-specific address*/
};
套接字函數被定義爲以指向通用套接字地址結構的指針做爲參數。如int bind(int, struct sockaddr *, socklen_t);所以。調用這些函數時必須將指向特定於協議的套接字地址結構的指針進行強制類型轉換,變成指向某個通用套接字地址結構的指針。如struct sockaddr_in serv;  bind(sockfd, (struct sockaddr *)&serv, sizeof(serv));該技巧差點兒用在了所有套接字函數中,必定要熟練掌握。

套接字地址結構有5種:IPv四、IPv六、Unix域、數據鏈路和存儲。code


地址轉換函數,在ASCII字符串與網絡字節序的二進制值之間轉換網際地址。blog

inet_aton、inet_addr(已經不用了)和inet_ntoa在點分十進制數串(好比「206.168.112.96」)與它長度爲32位的網絡字節序二進制值間轉換IPv4地址。而inet_pton和inet_ntop對於IPv4和IPv6地址都適用。ip

#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);  /*返回:若字符串有效則爲1,不然爲0*/
char  *inet_ntoa(struct in_addr inaddr);			/*返回:指向一個點分十進制數串的指針*/
inet_aton將strptr所指字符串轉換爲一個32位的網絡字節序二進制值,並經過指針addrptr來存儲。若成功爲1,不然爲0。inet_ntoa函數將一個32位的網絡字節序二進制IPv4地址轉換成對應的點分十進制數串,由該函數的返回值所指向的字符串駐留在靜態內存中。注意,該函數以一個結構而不是該結構的指針做爲其參數。

#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

兩個函數的family參數既可以是AF_INET,也可以是AF_INET6。假設以不被支持的地址族做爲family參數。這兩個函數就返回一個錯誤,並將errno置位EAFNOSUPPORT。內存

第一個函數嘗試轉換由strptr指針所指的字符串,並經過addrptr指針存放二進制結果。若成功則返回值爲1。不然假設對所指定的family而言輸入的字符串不是有效的表達格式,返回0。inet_ntop進行相反的轉換,從數值格式轉換爲表達式。


既然上面提到了網絡字節序,那麼就要說一說網絡字節序與主機字節序的差異。

內存中存儲兩個字節有兩種方法:一種是將迪許字節存儲在起始地址,稱爲小端字節序;還有一種是將高端字節存儲在起始地址。稱爲大端字節序。

主機字節序是系統所用的字節序,可以用下列程序測試:

#include <stdio.h>
#include <stdlib.h>

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

	un.s = 0x0102;
	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("unkonwn\n");
	}else
			printf("sizeof(short) = %d\n", sizeof(short));
	exit(0);
}
網絡字節序和主機字節序之間相互轉換的函數例如如下

#include <netinet/in.h>

unit16_t htons(unit16_t host16bitvalue);

unit32_t htons(unit32_t host32bitvalue);/*返回網絡字節序的值*/

unit16_t ntohs(unit16_t net16bitvalue); 

unit32_t ntohs(unit32_t net32bitvalue); /*返回主機字節序的值*/

應該把s視爲一個16位的值(好比TCP或UDPport號)。把l視爲一個32位的值(好比IPv4地址);
除了協議首部中各個字段的字節序問題外,還有網絡分組中所含數據的字節序問題。


補充:字節操縱函數

操縱多字節字段的函數有兩組:它們既不正確數據作解釋,也不若是數據是以空字符結束的字符串。

#include <string.h>

void bzero(void *dest, size_t nbytes);

void bcopy(const void *src, void *dest, size_t bytes);

int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);

#include <string.h>

void *memset(void *dest, int c, size_t len);

void *memcpy(void *dest, const void *src, size_t nbytes);

int memcmp(const void *ptr1, const void *ptr2, size_t nbytes);

相關文章
相關標籤/搜索