引言:本篇笔记第一部分介绍socket套接字编程中常见的数据结构,第二部分介绍网络字节顺序、主机与网络字节顺序转换函数、IP地址转换函数。
下面介绍到的函数,都定义在netinet/in.h中,在ubuntu14.04下,其存储位置为/usr/include/netinet/in.h,可以cat打印其内容查看。
struct sockaddr是linux网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都会用到这个结构体,这个结构体是兼容Ipv4和Ipv6的。在实际编程中,这个结构体会被一个struct sockaddr_in 或者struct sockaddr_in6所填充。
struct sockaddr{
unsigned short sa_family; /* address族, AF_XXX*/
char sa_data[14]; /*14bytes的协议地址*/
}
typedef uint32_t in_addr_t; /*网络内部用来表示IP地址的类型*/
struct in_addr{
in_addr_t s_addr;
}
这个是Ipv4的标准格式,提供了方便的手段来访问socket address(struct sockaddr)结构中的每一个元素。
struct sockaddr_in
{
__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)];
};
_SOCKADDR_COMMON ( ):是一个宏定义结构,括号里面是它的参数,用以表示tcp/ip协议的版本,sin是ipv4, sin_6是ipv6。
unsigned char sin_zero:做减法为了保证这个结构体和struct sockaddr的大小一致。
这个是Ipv6的标准格式,提供了方便的手段来访问socket address(struct sockaddr)结构中的每一个元素。
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_);
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 sockaddr是linux网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都会用到这个结构体,这个结构体是兼容Ipv4和Ipv6的。在实际编程中,这个结构体会被一个struct sockaddr_in 或者struct sockaddr_in6所填充
在各种计算机体系中,对于字节、字等的存储机制有所不同,因为引发计算机通信领域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送。如果不达成一致的规则,通信双方将无法进行正 确的编/译码从而导致通信失败。关于大小端及验证方式,可以查看计算机存储的大小端模式解析。我们这里介绍网络字节顺序、主机与网络字节顺序转换函数、IP地址转换函数,为介绍网络编程核心API铺垫。
因为每一个机器内部对变量的字节存储顺序不同(大端:高位字节排放在内存的低地址端,低位字节存放在内存高地址端),而网络传输的数据大家是一定要统一的。所以对于内部字节表示顺序和网络字节顺序不同的机器,就一定要对数据进行转换,保证其可以正确解析。
对内部字节顺序和网络字节顺序相同的机器,同样调用转换函数,系统函数自己判定是否需要转换。
通常使用的有两种数据类型,短型(两个字节,如端口号)和长形(四个字节,如Ipv4地址)。在将数据发送到Internet之前,一定要把它的字节顺序从主机字节顺序,转换到网络字节顺序。下面这几款转换函数可以满足基本的需求:
在这些函数中,h理解为host主机、to即转换到、n为internet网络、s为short即短整形、l为long即整形。htons()即将一个短整形数据,从主机字节顺序转换到网络字节顺序。
我们习惯用点分十进制的表达方式来表示IP地址,如192.168.1.110,还是需要转换为一个无符号长整型,以便将其添加进表示IP地址的标准结构体,linux也提供了很多用于转换IP地址的函数,同样,可以查看其man手册:
这里选用了常用的几款,其测试代码如下:
/******************************************************
*时间:2018.02.05日
*内容:测试IP地址转换函数
*问题:
******************************************************/
#include
#include
#include
#include
#define MYIP "192.168.1.120"
int main(void)
{
/*******************************************************
*代码段1:测试IP转换函数:in_addr_t inet_addr(const char*p)
*结 果: 0x 78 01 a8 c0
* 120 1 168 192
*注 意:输出统一使用大端模式,即网络字节序
*******************************************************/
#if 0
in_addr_t addr = 0;
addr = inet_addr(MYIP);
printf("addr = 0x%x\n", addr);
#endif
/**********************************************************
*名 称 :inet_pton
*功 能 :convert IPv4 and IPv6 addresses from text to binary form
*函数原型:int inet_pton(int af, const char *src, void *dst);
*执行结果:0x78 01 a8 c0
*备 注:结构体初始化,需要注意{}
*********************************************************/
#if 0
struct in_addr addr = {0};
int ret = 0;
ret = inet_pton(AF_INET, MYIP, &addr);
if(ret != 1)
{
printf("inet_pton error!");
}
else
{
printf("addr = 0x%x\n", addr.s_addr);
}
#endif
/**********************************************************
*名 称 :inet_ntop
*功 能 :convert IPv4 and IPv6 addresses from binary to text form
*函数原型:const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);
*执行结果:ip addr = 192.168.1.120
*备 注:
***********************************************************/
struct in_addr addr = {0};
char buf[20] = {0};
addr.s_addr = 0x7801a8c0;
inet_ntop(AF_INET, &addr, buf, sizeof(buf));
printf("ip addr = %s.\n", buf);
return 0;
}
小结:本篇笔记介绍了与socket编程相关的数据结构及基本的转换函数。
参考内容:
1、linux man手册
2、《unix网络编程卷一》
纠错与建议
邮箱:[email protected]