linux ip相關結構體和轉換函數

1.結構體

1.IPv4: struct sockaddr_in, 16個字節

#include <netinet/in.h>
#include <arpa/inet.h>

struct in_addr {
    unsigned long s_addr;          // 4 bytes load with inet_pton()	按照網絡字節順序存儲IP地址
};
struct sockaddr_in {
	short int sin_family;              /* AF_INET */
	unsigned short int sin_port;       /* Port number存儲端口號(使用網絡字節順序) */
	struct in_addr sin_addr;           /* Internet address存儲IP地址,使用in_addr這個數據結構 */
	unsigned char sin_zero[8];         /* Same size as struct sockaddr */
};//16字節

注意:sin_port和sin_addr都必須是網絡字節序(NBO),一般可視化的數字都是主機字節序(HBO)。

示例:

struct sockaddr_in addr_dst;
//addr_dst.sin_addr.s_addr =  htonl(3232235887);		//方式一
addr_dst.sin_addr.s_addr =  inet_addr("192.168.1.111");	//方式二
addr_dst.sin_port = htons(80);
addr_dst.sin_family = AF_INET;

2.IPv6: struct sockaddr_in6, 28個字節

struct sockaddr_in6 {
    sa_family_t sin6_family;    /* AF_INET6 */
    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 */
};
struct in6_addr {
    union {
        uint8_t u6_addr8[16];
        uint16_t u6_addr16[8];
        uint32_t u6_addr32[4];
    } in6_u;

    #define s6_addr                 in6_u.u6_addr8
    #define s6_addr16               in6_u.u6_addr16
    #define s6_addr32               in6_u.u6_addr32
};

3.通用結構體1: struct sockaddr, 16個字節

#include <sys/socket.h>

typedef unsigned short	sa_family_t;
struct sockaddr {  
     	sa_family_t sin_family;	/* 地址族address family, AF_xxx	*/
    	char sa_data[14]; 		/* 14 bytes of protocol address	*/
 }; 

4.通用結構體2: struct sockaddr_storage,128個字節

/* Structure large enough to hold any socket address 
(with the historical exception of AF_UNIX). 128 bytes reserved.  */

#if ULONG_MAX > 0xffffffff
# define __ss_aligntype __uint64_t
#else
# define __ss_aligntype __uint32_t
#endif
#define _SS_SIZE        128
#define _SS_PADSIZE     (_SS_SIZE - (2 * sizeof (__ss_aligntype)))

struct sockaddr_storage
{
    sa_family_t ss_family;      /* Address family */
    __ss_aligntype __ss_align;  /* Force desired alignment.  */
    char __ss_padding[_SS_PADSIZE];
};

示例:根據socket fd獲取對端地址

/**@brief IP地址:包括IPv4和IPv6格式的地址
 */
typedef struct
{		/* 24 bytes */
	struct in_addr	v4;							///< IPv4地址
	struct in6_addr	v6;							///< IPv6地址
	unsigned char	res[4];
}U_IN_ADDR;

/**@brief: 從fd中獲取對端地址,該地址是網絡地址
 *@param[in] fd socket描述符
 *@param[out] U_IN_ADDR *ip 網絡地址
 *@return 成功0     失敗 -1
*/
int get_peer_addr(int fd, U_IN_ADDR *ip)
{
	socklen_t namelen = sizeof(struct sockaddr_storage);
	struct sockaddr_in peer4;
	struct sockaddr_in6 peer6;
	struct sockaddr_storage peer;

	bzero(&peer4, sizeof(struct sockaddr_in));
	bzero(&peer6, sizeof(struct sockaddr_in6));
	bzero(&peer, sizeof(struct sockaddr_storage));

	if ((fd <= 0) || (nullptr == ip))
	{
		return -1;
	}
	
	if(getpeername(fd, (struct sockaddr*)&peer, &namelen))
	{
		return -1;
	}
	if(AF_INET == peer.ss_family)
	{
		memcpy(&peer4, &peer, sizeof(struct sockaddr_in));
		ip->v4 = peer4.sin_addr;
	}
	else if(AF_INET6 == peer.ss_family)
	{
		memcpy(&peer6, &peer, sizeof(struct sockaddr_in6));
		
		if(IN6_IS_ADDR_V4MAPPED(&peer6.sin6_addr))
		{
			memcpy(&ip->v4.s_addr, (char *)peer6.sin6_addr.s6_addr+12, 4);
		}
		else
		{
			ip->v6 = peer6.sin6_addr;
		}
	}
	//printf("peer_addr: %s\n", inet_ntoa(ip->v4));
	
	return 0;
}

5.項目U_IN_ADDR結構體包括IPv4和IPv6格式的地址,24個字節

/**@brief IP地址:包括IPv4和IPv6格式的地址
 */
typedef struct
{	/* 24 bytes */
	struct in_addr	v4;							///< IPv4地址,4 bytes
	struct in6_addr	v6;							///< IPv6地址,16 bytes
	unsigned char	res[4];
}U_IN_ADDR;

6.項目SOCKET_ADDR_T結構體兼容IP地址結構體,28個字節

/**@brief	兼容IP地址結構體
 */
typedef struct _ADDR_T
{   /* 28 bytes */
    union
    {
        struct sockaddr_in sin4;    /**< IPV4 地址*/
        struct sockaddr_in6 sin6;  	/**< IPV6 地址*/
    }SA;
}SOCKET_ADDR_T;

1.2 sockaddr和sockaddr_in的區別

https://blog.csdn.net/will130/article/details/53326740
https://www.cnblogs.com/zihaowang/p/4649548.html

程序員不應操作sockaddr,sockaddr是給操作系統用的。

程序員應使用sockaddr_in來表示地址,sockaddr_in區分了地址和端口,使用更方便。

2.系統函數轉換

1.網絡字節順序與本地字節順序之間的轉換函數:

htonl() “Host to Network Long” // 將主機的無符號長整形數轉換成網絡字節順序
htons() “Host to Network Short” // 將主機的無符號短整形數轉換成網絡字節順序
ntohl() “Network to Host Long” // 將一個無符號長整形數從網絡字節順序轉換爲主機字節順序
ntohs() “Network to Host Short” // 將一個無符號短整形數從網絡字節順序轉換爲主機字節順序

注意:主機序(小端)和網絡序(大端)是兩種不同的存儲方式,與系統架構有關;
注意:本質上執行 htonl() 和 ntohl() 是一樣的,同理 htons() 和 ntohs();
注意:在小端序主機上,執行一次 htonl()、htons()、ntohl()、ntohs()就進行一次轉換;
注意:在大端序主機上,執行 htonl()、htons()、ntohl()、ntohs()本質上無效,始終等於原值;

linx系統下,htonl() htons() ntohl() ntohs()的頭文件及函數定義如下:

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong); 
uint16_t htons(uint16_t hostshort); 
uint32_t ntohl(uint32_t netlong); 
uint16_t ntohs(uint16_t netshort);

https://blog.csdn.net/ktpd_pro/article/details/56276994

2.網絡地址轉換函數

點十進制字符串IP—>整數形式:
inet_add() 將點分十進制IP字符串轉化爲一個網絡字節序的整數值(二進制位的大端存儲);
inet_network() 將點分十進制IP字符串轉化爲一個主機字節序的整數值(二進制位小端存儲);
inet_aton() 將點分十進制IP字符串轉化爲一個網絡字節序的整數值(二進制位的大端存儲);

區別1:inet_addr和inet_aton轉換後的整數是網絡字節序;
inet_network轉換後的整數是主機字節序;

區別2:inet_addr和inet_network將255.255.255.255作爲無效的IP地址
inet_aton將255.255.255.255作爲有效的IP地址(大多數路由器認爲255.255.255.255是有效地址)

整數形式–>點十進制字符串:
char *inet_ntoa(unsigned in_addr addr);//此函數的返回值會被下次調用返回值覆蓋

其他:
inet_pton()
inet_ntop()

IP地址有兩種不同的格式:十進制點分形式 和 32位二進制形式
https://www.cnblogs.com/shijingxiang/articles/4693208.html

三種給socket賦值地址的方法:
inet_aton(server_addr_string,&myaddr.sin_addr);
myadd.sin_addr.s_addr = inet_addr(“10.175.25.100”);
INADDR_ANY轉不轉NBO隨便
myadd.sin_addr_s_addr = htonl(INADDR_ANY);
myadd.sin_addr_s_addr = INADDR_ANY;

3.inet_addr()函數

將點分十進制IP轉化爲網絡字節序(二進制位的大端存儲);

Linux下,用inet_aton代替;

函數聲明:

unsigned long inet_addr(const char* cp);
in_addr_t inet_addr(const char *strptr);//??哪個是??

返回值:
成功:返回32位二進制的網絡字節序地址;
失敗:返回INADDR_NONE;

/* Address indicating an error return. */
#define	INADDR_NONE		((unsigned long int) 0xffffffff)

注意:inet_addr失敗時返回INADDR_NONE,但這個值會和255.255.255.255這個合法地址混淆

建議:linux下,可以用inet_aton代替,vs2005裏沒有這個函數。

4.inet_network()函數

功能:將點分十進制IP轉化爲主機字節序(二進制位小端存儲)。

函數聲明:

int inet_network(const char *StrIP)

返回值:
失敗:返回-1; //-1還是0xffffffff
成功:返回主機字節序對應的數;

注意:當IP是255.255.255.255時,會認爲這是個無效的IP地址

5.inet_aton()函數

功能:將點分十進制IP轉化爲網絡字節序存放在addr中,並返回該網絡字節序對應的整數。

頭文件:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

聲明:

/* 返回1:串有效,返回0:串出錯 */
int inet_aton(const char *strptr, struct in_addr *addrptr);

返回值:
失敗返回0,成功返回網絡字節序對應的數;///////////////到底返回1代表成功,還是返回網絡數??

6.inet_ntoa()函數

功能:該函數將一個網絡字節順序的IP地址轉換爲它所對應的點分十進制串。常用於打印ip地址信息。

頭文件:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

聲明:

char* inet_ntoa(struct in_addr in);

返回值:
返回指向點分十進制字符串的指針。

注意:對inet_aton的調用傳遞的是指向結構的指針,而對inet_ntoa的調用傳遞的是結構本身;

示例:

struct sockaddr_in sock;
sock.sin_family = AF_INET;
//將字符串轉換爲in_addr類型
sock.sin_addr.S_un.S_addr =  inet_addr("192.168.1.111");
sock.sin_port = htons(5000);

//將in_addr類型轉換爲字符串
printf("inet_ntoa ip = %s\n",inet_ntoa(sock.sin_addr));

//結果輸出:
//inet_ntoa ip = 192.168.1.111



以下兩個較新的函數:inet_pton和inet_ntop對IPv4和IPv6地址都能進行處理,字母p代表presentation,字母n代表numeric;
https://www.cnblogs.com/warren-liuquan/p/3555945.html

7.inet_pton()函數

頭文件:#include<arpa/inet.h>
原型:int inet_pton(int family, const char *strptr, void *addrptr);
功能:
參數:int family:AF_INET6/AF_INET;
返回值:若函數成功,則返回1;若輸入不是有效的格式,則函數返回0;若處理失敗,函數返回-1

示例:

if(inet_pton(AF_INET, (char *)ddns_cfg.serverAddr, &ip) > 0)
{
	
}
else if(inet_pton(AF_INET6, (char *)ddns_cfg.serverAddr, &ipv6) > 0)
{

}
else
{ 
	p_ddns->addr_type = PSIA_ADDR_TYPE_DOMAIN; /* host name */ 
}

原先使用inet_addr函數進行轉換,inet_addr函數無法區別255.255.255.255和錯誤值,且錯誤時返回值不一定爲-1,inet_pton無此問題 */
ret = inet_pton(AF_INET, (const char *)ip, (void *)&addr);
if(1 != ret)

8.inet_ntop()函數

頭文件:#include<arpa/inet.h>
原型:const char* inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
功能:該函數將一個網絡字節順序的IP地址轉換爲它所對應的點分十進制串。
參數:注意:對inet_aton的調用傳遞的是指向結構的指針,而對inet_ntoa的調用傳遞的是結構本身。
返回值:	返回指向點分十進制字符串的指針。
如果len太小,無法容納表達格式結果(包括終止的空字符),則返回一個空指針,並置errno爲ENOSPC。

示例:

inet_pton()和inet_ntop()兩個函數的參數family既可以是AF_INET,也可以是AF_INET6。如果以不被支持的地址族作爲family參數,兩個函數都返回錯誤,並將errno置爲EAFNOSUPPORT。

2.2 inet_addr與inet_aton比較

inet_addr與inet_aton不同在於,他的返回值爲轉換後的32位網絡字節序二進制值,而不是作爲出參返回,這樣存在一個問題,他的返回值返回的有效IP地址爲0.0.0.0到255.255.255.255,如果函數出錯,返回常量值INADDR_NONE(這個值一般爲一個32位均爲1的值),這意味着點分二進制數串255.255.255.255(IPv4的有限廣播地址)不能由此函數進行處理。

參考資料:
https://www.cnblogs.com/tanghuimin0713/p/3425936.html
https://blog.csdn.net/c_base_jin/article/details/60469622
https://www.cnblogs.com/warren-liuquan/p/3555945.html
https://blog.csdn.net/u013827488/article/details/53905936

connct等socket函數加上來。
connct等socket函數加上來。
connct等socket函數加上來。

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