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函数加上来。