socket最开始的含义是一个IP地址和端口队(ip,port) 。它唯一地表示了使用TCP通信的一端 。这就是socket地址 。
主机字节序和网络字节序现在CPU的累加器一次都能装载(至少)4字节(这里考虑32位机器,下同),即一个整数 。那么这4个字节在内存中排列的顺序将影响它被累加器装载成的整数的值 。这就是字节序的问题 。
字节序分为大端字节序(big endian)和小端字节序(little endian) 。大端字节序是指一个整数的高位字节(23 ~ 31 bit)存储在内存的地址处,低位字节(0~7 bit)存储在内存的高地址处 。小端字节序则指整数的高位字节序存储在内存的高地址处,而低位字节序则存在在内存的低地址处 。
下面的代码是检查机器的字节序:
#include <stdio.h>void byteorder(){union{short value;char union_bytes[sizeof(short)];}test;test.value = https://www.isolves.com/it/wl/zs/2020-07-28/0x0102;if((test.union_bytes[0] == 1) && (test.union_bytes[1] == 2)){printf("big endiann");}else if((test.union_bytes[0] == 2) && (test.union_bytes[1] == 1)){printf("little endiann");}else{printf("unknownn");}}int main(int argc, char const *argv[]){byteorder();return 0;}
当格式化的数据(比如32bit整型数和16bit短型数)在两台使用不同字节序的主机之间传递时,接收端必然错误地解释之 。
解决问题的方法是:发送端总是把要发送的数据转化成大端字节序再发送,而接收端知道对方传送过来的数据总是采用大端字节序,所以接收端可以根据自身采用的字节序决定是否对接收到的数据进行转换(小端机转换,大端机不转换) 。因此大端字节序也称为网络字节序,它给所有接受数据的主机提供了一个正确解释收到的格式化数据的保证 。
需要指出的是,即使是同一台机器上的两个进程(比如一个由C语言,另一个JAVA编写)通信,也要考虑字节序的问题(JAVA虚拟机采用大端字节序) 。
linux提供了4个函数来完成主机字节序和网络字节序之间的转换 。
#include <netinet/in.h>它们的含义很明确,比如htonl表示“host to network long",即将长整型(32bit)的主机字节序转换为网络字节序数据 。这四个函数中,长整型函数通常用来转换IP地址,短整型函数用来转换端口号 。(当然不限于此 。任何格式化的数据通过网络传输时,都应该使用这些函数来转化字节序) 。
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);
通用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)和对应的地址族如下表:
![Linux网络API - socket地址API](http://img.jiangsulong.com/220419/0133444308-0.jpg)
文章插图
宏PF_*和AF_*都定义在bits/socket.h头文件中,且后者与前者有完全相同的值,所以二者通常混用 。
sa_data成员用于存放socket地址值 。但是不同的协议族的地址值具有不同的含义和长度 。如下表所示:
![Linux网络API - socket地址API](http://img.jiangsulong.com/220419/0133445H6-1.jpg)
文章插图
由此可以发现,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)];};
这个结构体不仅提供了足够大的空间用于存放地址值,而且是内存对齐的(这是__ss_align成员的作用) 。专用socket地址上面这两个通用socket地址结构体显然很不好用,比如设置与获取IP地址和端口号就需要执行烦琐的位操作 。所以Liunx为各个协议族提供了专门的socket地址结构体 。
UNIX本地域协议族使用如下专用socket地址结构体:
#include <sys/un.h>struct sockaddr_un{sa_family_t sin_family; /*地址族: AF_UNIX*/char sun_path[108];/*文件路径名*/};
TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用socket地址结构体,它们分别用于IPv4和IPv6:struct sockaddr_in{sa_family_t sin_family;/*地址族:AF_INET*/u_int16_t sin_port;/*端口号,要用网络字节序表示*/struct in_addr sin_addr;/*IPv4地址结构体*/};struct in_addr{u_int32_t s_addr;/*IPv4地址,要用网络字节序表示*/};struct sockaddr_in6{sa_family_t sin6_family;/*地址族:AF_INET6*/u_int16_t sin6_port;/*端口号,要用网络字节序表示*/u_int32_t sin6_flowinfo;/*流信息,应设置为0*/struct in6_addr sin6_addr;/*IPv6地址结构体*/u_int32_t sin6_scope_id;/*scope ID, 尚处于实验阶段*/};struct in6_addr{unsigned char sa_addr[16];/*IPv6地址,要用网络字节序表示*/};
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Kali linux 安装教程
- Linux操作系统的作业调度和进程调度
- 开启天文之路的 4 个 Python 工具 | Linux 中国
- 寒露是什么意思,网络白茶是什么意思
- linux xshell 免密登录及排错
- Linux操作系统中常用调度算法
- Linux 常用命令大汇集
- 网络扫描利器Fing之Linux版本使用教程
- linux安装mysql启动不起来总结
- GCNN 使用Keras构建具有自定义结构和层次的图卷积神经网络