主机字节序和网络字节序

现代CPU的累加器一次都能装载 (至少) 4字节(这里考虑32位机,下同),即-一个整数。那么这4字节在内存中排列的顺序将影响它被累加器装载成的整数的值。这就是字节序问题。字节序分为大端字节序(big endian)和小端字节序(itle endian)。

大端字节序是指一个整数的高位字节(23 ~ 31 bit)存储在内存的低地址处,低位字节(0~ 7bit)存储在内存的高地址处。小端字节序则是指整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。

//查看机器字节序
#include <cstdio>void byteOrder()
{union {short value;char unionBytes[sizeof(short)];} test;test.value = 0x0102;if ((test.unionBytes[0] == 1) && (test.unionBytes[1] == 2)) {printf("big endian\n");} else if ((test.unionBytes[0] == 2) && (test.unionBytes[1] == 1)) {printf("little endian\n");} else {printf("unknown...\n");}
}int main(void)
{byteOrder();return 0;
}


现代PC大多采用小端字节序,因此小端字节序又被称为主机字节序。

当格式化的数据(比如32bit整型数和16bit短整型数)在两台使用不同字节序的主机之间直接传递时,接收端必然错误地解释之。解决问题的方法是:发送端总是把要发送的数据转化成大端字节序数据后再发送,而接收端知道对方传送过来的数据总是采用大端字节序,所以接收端可以根据自身采用的字节序决定是否对接收到的数据进行转换(小端机转换,大端机不转换)。因此大端字节序也称为网络字节序,它给所有接收数据的主机提供了一个正确解释收到的格式化数据的保证。

需要指出的是,即使是同一台机器上的两个进程(比如一个由C语言编写,另一个由JAVA编写)通信,也要考虑字节序的问题(JAVA虚拟机采用大端字节序)。

Linux提供了如下4个函数来完成主机字节序和网络字节序之间的转换:

#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

它们的含义很明确,比如htonl表示“host to network long”,即将长整型(32 bit)的主
机字节序数据转化为网络字节序数据。这4个函数中,长整型函数通常用来转换IP地址,短
整型函数用来转换端口号(当然不限于此。任何格式化的数据通过网络传输时,都应该使用
这些函数来转换字节序)。

通用socket地址

socket网络编程接口中表示socket地址的是结构体sockaddr,其定义如下:

#include <bits/socket.h>
struct sockaddr
{sa_family_t sa_family;char sa_data[14];
}

sa_family成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。

常见的协议族(protocol family,domain)和对应的地址族:

宏PF_*和AF_*都定义在bits/socket.h头文件中,且后者与前者有完全相同的值,所以两者通常混用。

sa_data成员用于存放socket地址值。但是,不同的协议族的地址值具有不同的含义和长度。

由表5-2可见,14 字节的sa_ data 根本无法完全容纳多数协议族的地址值。因此,Linux定义了下面这个新的通用socket地址结构体:

#include <bits/socket.h>
struct sockaddr_storage
{sa_family_t sa_family;unsigned long int __ss_align;char __ss_padding[128 - sizeof(__ss_align)];
};

这个结构体不仅提供了足够大的空间用于存放地址值,而且是内存对齐的(这是
align成员的作用)。

专用socket地址

上面这两个通用socket地址结构体显然很不好用,比如设置与获取IP地址和端口号就需要执行烦琐的位操作。所以Linux为各个协议族提供了专门的soket地址结构体。

UNIX本地域协议族使用如下专用socket地址结构体:

#include <sys/un.h>
struct sockaddr_un
{sa_family_t sa_family;//地址族:AF_UNIXchar sun_path[108];//文件路径名
};

TCP/IP协议族有sockaddr_ in 和sockaddr_ in6 两个专用socket地址结构体,它们分别用
于IPv4和IPv6:

//IPv4
struct in_addr
{u_int32_t s_addr; //IPV4地址,网络字节序表示
};struct sockaddr_in
{sa_family_t sin_family; //地址族:AF_INETu_int16_t sin_port; //端口号,网络字节序表示struct in_addr sin_addr; //IPV4地址结构体
};//IPv6
struct in6_addr
{unsigned char sa_addr[16]; //IPV6地址,网络字节序表示
};struct sockaddr_in
{sa_family_t sin6_family; //地址族:AF_INET6u_int16_t sin6_port; //端口号,网络字节序表示u_int32_t sin6_flowinfo; //流消息,设置为0struct in6_addr sin6_addr; //IPV6地址结构体u_int32_t sin6_scope_id; //scope ID
};

这两个专用socket地址结构体各字段的含义都很明确,我们只在右边稍加注释。

所有专用socket地址(以及sockaddr_storage)类型的变量在实际使用时都需要转化为通用socket地址类型sockaddr(强制转换即可),因为所有socket编程接口使用的地址参数的类型都是sockaddr。

IP地址转换函数

通常,人们习惯用可读性好的字符串来表示IP地址,比如用点分十进制字符串表示IPv4地址,以及用十六进制字符串表示IPv6地址。但编程中我们需要先把它们转化为整数(二进制数)方能使用。而记录日志时则相反,我们要把整数表示的IP地址转化为可读的字符串。下面3个函数可用于用点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间的转换:

#include <arpa/inet.h>
//将用点分十进制字符串表示的 IPV4 地址转换成网络字节序整数表示的 IPV4 地址,
//失败返回 INADDR_NONE
in_addr_t inet_addr(const char* strptr);
//与 inet_addr 功能一致,将结果存入 inp 指向的结构体中
//失败返回 0,成功返回 1
int inet_aton(const char* cp, struct in_addr* inp);
//将网络字节序整数表示的 IPV4 地址转化为用点分十进制表示的 IPV4 地址
//内部使用静态变量存储转化结果,返回值指向该静态内存,不可重入
//失败返回 0,成功返回 1
char* inet_ntoa(struct in_addr in);

inet_addr函数将用点分十进制字符串表示的 IPV4 地址转换成网络字节序整数表示的 IPV4 地址,失败返回 INADDR_NONE。

inet_aton函数完成和inet_addr同样的功能,但是将转化结果存储于参数inp指向的地址结构中。它成功时返回1,失败则返回0。

inet_ntoa函数将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址。但需要注意的是,该函数内部用一个静态变量存储转化结果,函数的返回值指向该静态内存,因此inet_ ntoa 是不可重入的。

//代码清单5-2 揭示了其不可重入性
#include <cstdio>
#include <arpa/inet.h>int main() {in_addr addr1, addr2;addr1.s_addr = inet_addr("1.2.3.4");addr2.s_addr = inet_addr("10.194.71.60");char *szValue1 = inet_ntoa(addr1);char *szValue2 = inet_ntoa(addr2);printf("address 1: %s\n", szValue1);printf("address 2: %s\n", szValue2);return 0;
}


下面这对更新的函数也能完成和前面3个函数同样的功能,并且它们同时适用于IPv4地址和IPv6地址:

#include <arpa/inet.h>
//10进制点分字符串的IPV4地址或16进制IPV6地址转换成网络字节序整数的IP地址,并存入dst内存
//af:对应的协议族 AF_INET 或 AF_INET6
//失败返回 0,成功返回 1
int inet_pton(int af, const char* src, void* dst);//与inet_pton函数相反,socklen_t 指定目标存储单元的大小见后续宏
//成功返回目标存储单元地址,失败返回NULL并设置errno
const char* inet_ntop(int af, const char* src, char* dst, socklen_t cnt);

inet_pton函数将用字符串表示的IP地址src(用点分十进制字符串表示的IPv4地址或用十六进制字符串表示的IPv6地址)转换成用网络字节序整数表示的IP地址,并把转换结果存储于dst指向的内存中。其中,af 参数指定地址族,可以是AF_ INET 或者AF_ INET6。inet_pton成功时返回1,失败则返回0并设置errmo。

inet_ ntop 函数进行相反的转换,前三个参数的含义与inet_ pton的参数相同,最后一个参数cnt指定目标存储单元的大小。下面的两个宏能帮助我们指定这个大小(分别用于IPv4和IPv6):

#include <netinet/in.h>
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46

inet_ntop成功时返回目标存储单元的地址,失败则返回NULL并设置errno。

socket地址API相关推荐

  1. 一文了解websocket全双工通信java实现socket地址404问题解决

    websocket介绍 1.websocket介绍 1.1注解介绍 2.demo 2.1 后端代码 2.2 前端代码 2.3 效果 附录:socket地址404问题解决 1.websocket介绍 W ...

  2. C++使用socket的api创建简单服务器

    使用Socket API建立简易的TCP服务端 目录 建立一个socket 绑定接受客户端连接的端口bind 监听网络端口listen 等待接受客户端连接accept 向客户端发送一条数据send 关 ...

  3. 4.11、socket地址

    4.11.socket地址 1.通用 socket 地址 2.专用socket地址 1.通用 socket 地址 socket 网络编程接口中表示 socket 地址的是结构体 sockaddr,其定 ...

  4. Python零基础速成班-第12讲-Python获取网络数据Socket,API接口,网络爬虫Crawler(制作弹幕词云)

    Python零基础速成班-第12讲-Python获取网络数据Socket,API接口,网络爬虫Crawler(制作弹幕词云) 学习目标 获取网络数据Socket API接口 网络爬虫Crawler(制 ...

  5. Socket基础API介绍

    文章目录 1 Socket基础API介绍 1 Socket基础API介绍 我们先来看下使用Socket API建立简易TCP服务端和客户端的步骤: 用Socket API建立简易TCP服务端: 建立一 ...

  6. linux网络编程Internet Socket地址,套接字,和函数

    文章内容节选<linux/UNIX 系统网络编程> Internet domain socket地址有两种:IPv4 IPv6 IPv4被存储在结构体中, 该结构体在 netinet/in ...

  7. php 调用微信收货地址,php版微信自动获取收货地址api用法示例

    微信公众平台现在是越来越强大了,我们可以通过各种api接口来与平台对接获取对应的数据了,下面来看一个由php实现的微信自动获取收货地址api程序,具体如下. 关于接口的说明我就不介绍了,在官方可以看到 ...

  8. linux 高性能读书笔记之通用socket地址

    ####socket网络编程接口 socket的地址是结构体sockaddr 代码如下 struct sockaddr{ sa_family_t sa_family; char sa_data[14] ...

  9. php 调用微信收货地址,php微信自动获取收货地址api用法实例详解

    这篇文章主要介绍了php版微信自动获取收货地址api用法,结合实例形式分析了php版微信API接口调用与使用技巧,需要的朋友可以参考下 微信公众平台现在是越来越强大了,我们可以通过各种api接口来与平 ...

最新文章

  1. 干货 | 深度学习检测小目标常用方法
  2. K-d tree 算法
  3. 测试php程序运行时间
  4. Delphi中流对象 TStream
  5. IO多路复用之epoll总结
  6. How To Replace The Firefox Icon With Your Logo
  7. xmind使用教程思维导图
  8. macOS 输入法快速切换工具 —— KeyboardHolder
  9. php运算符包括,php运算符有哪些 - php完全自学手册 - php中文网手册
  10. R:基于每股权益的量化分析 —— PEG估值法
  11. C语言应用(3)——Base64编码/解码
  12. sox处理mp3,使用SoX将mp3文件拆分为TIME秒
  13. 构建自己的GAFATA
  14. 计算机联锁想系统包括哪几层,计算机联锁系统技术_习题.ppt
  15. 《ROS2机器人建模URDF》8.4控制移动机器人轮子运动
  16. 关于JDK8安装遇到1335问题
  17. h5 实现简单的png icon 换颜色效果
  18. IKAnalyzer 配置文件介绍
  19. php仿携程网站,仿携程网手机端
  20. FE_CSS 页面布局之定位

热门文章

  1. 核心网upf作用_高性能5G核心网,动力从何而来? 核心网,是整个通信网络的大脑,是不可或缺的重要组成部分。 网络的管理控制、鉴权认证等关键功能,主要由核心网负责。核心网的... - 雪球...
  2. HTTPS协议详解:TLS/SSL握手过程
  3. 转, C# 如何在MVC3中取消备用控制器的选择
  4. 第八次立会顺利召开!
  5. 关于使用Tomcat服务器出现413错误的解决办法(Request Entity Too Large)
  6. C/C++ 变量的初始化
  7. IBATIS的优缺点
  8. Unity3D 游戏引擎之实现平面多点触摸(二)
  9. JS:ES6-4 简化对象与箭头函数
  10. 【博客项目】—密码加密( 六)