众所周知小端就是低地址存低位数,高地址存高位数。
如0x01020304 在小端存储中是 0x04 , 0x03, 0x02 ,0x01 (左边低地址右边高地址), 读数从右往左读。
大端反之, 0x01020304则为 0x01, 0x02 ,0x03 , 0x04 (左边低地址右边高地址),读数从左往右读。
看起来好像位序不受影响,影响顺序的最小单位为字节。
typedef struct {
unsigned char b0:1, b1:1,b2:1,b3:1,b4:1,b5:1,b6:1,b7:1;
} bit8s;
typedef union {
unsigned char v;
bit8s b;
} byte;
typedef union {
unsigned short v;
byte c[2];
} byte2;
typedef union {
unsigned int v;
byte c[4];
} byte4;
void test_bytes_order(unsigned int num)
{
byte4 x;
x.v = num;
printf("Unsigned Num: %08x\n", x.v);
int i = 0;
for (i = 0; i < 4; i++) {
printf("%p : %02x \n", &x.c[i], x.c[i].v);
}
printf("\n");
}
int main()
{
test_bytes_order(0x01020304);
return 0;
}
低地址存低位,高地址存高位
root@ubuntu:~/temp# ./a.out
Unsigned Num: 01020304
0x7fff603f8804 : 04
0x7fff603f8805 : 03
0x7fff603f8806 : 02
0x7fff603f8807 : 01
低地址存高位,高地址存低位
root@debian-mips:~/temp# ./a.out
Unsigned Num: 01020304
0x7f8b723c : 01
0x7f8b723d : 02
0x7f8b723e : 03
0x7f8b723f : 04
降上述代码稍加修改,增加为输出
void test_bytes_order(unsigned int num)
{
byte4 x;
x.v = num;
printf("Unsigned Num: %08x\n", x.v);
int i = 0;
for (i = 0; i < 4; i++) {
printf("%p : %02x ", &x.c[i], x.c[i].v);
printf("%d%d%d%d%d%d%d%d\n", x.c[i].b.b0, x.c[i].b.b1,x.c[i].b.b2,x.c[i].b.b3,
x.c[i].b.b4,x.c[i].b.b5,x.c[i].b.b6,x.c[i].b.b7);
}
printf("\n");
}
root@ubuntu:~/temp# ./a.out
Unsigned Num: 01020304
0x7ffcb76dd074 : 04 00100000
0x7ffcb76dd075 : 03 11000000
0x7ffcb76dd076 : 02 01000000
0x7ffcb76dd077 : 01 10000000
root@debian-mips:~/temp# ./a.out
Unsigned Num: 01020304
0x7f9b44cc : 01 00000001
0x7f9b44cd : 02 00000010
0x7f9b44ce : 03 00000011
0x7f9b44cf : 04 00000100
可以看出,同一个32位数,在大端和小端机器上不仅有相反的字节序,也有相反的位序。那么就有一个问题,在网络传输中,不同端的这个位序能保证吗?
#include
#include
#include
#include
#include
#include
#include "end.h"
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s \n" , argv[0]);
exit(1);
}
int port = atoi(argv[1]);
int server_fd, client_fd, len, recv_len;
struct sockaddr_in server_addr, client_addr;
char buffer[BUFFER_SIZE];
// 创建socket
if ((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(1);
}
// 初始化服务器地址结构体
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
// 绑定端口
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
exit(1);
}
printf("UDP server is listening on port %d...\n", port);
unsigned int num = 0;
while(1) {
len = sizeof(client_addr);
// 接收消息
if ((recv_len = recvfrom(server_fd, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &len)) == -1) {
perror("recvfrom");
exit(1);
}
if (recv_len == 4) {
num = *(unsigned int *)&buffer[0];
printf("Receiv a num\n");
test_bytes_order(num);
num = ntohl(num);
printf("After ntohl\n");
test_bytes_order(num);
}
//printf("Received message: %s", buffer);
// 发送响应消息
if (sendto(server_fd, buffer, recv_len, 0, (struct sockaddr*)&client_addr, len) == -1) {
perror("sendto");
exit(1);
}
}
close(server_fd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include "end.h"
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("Usage: %s \n" , argv[0]);
return 1;
}
const char* server_ip = argv[1];
int server_port = atoi(argv[2]);
uint32_t data = strtoul(argv[3], NULL, 16); // 将十六进制字符串转换为无符号整数
int sockfd;
struct sockaddr_in server_addr;
// 创建UDP套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
return 1;
}
// 设置服务器地址信息
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
if (inet_pton(AF_INET, server_ip, &(server_addr.sin_addr)) <= 0) {
perror("invalid server address");
return 1;
}
printf("Send a num\n");
test_bytes_order(data);
data = htonl(data);
printf("After htonl\n");
test_bytes_order(data);
// 发送数据
if (sendto(sockfd, &data, sizeof(data), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("sendto failed");
return 1;
}
printf("Data sent successfully.\n");
// 关闭套接字
close(sockfd);
return 0;
}
Makefile
all: server client
server: udpserver.c end.c end.h
gcc -g udpserver.c end.c -o server
client: udpclient.c end.c end.h
gcc -g udpclient.c end.c -o client
clean:
rm -f *.o
rm -f server client
小端机器启动服务,大端机器启动客户端发送数据
root@debian-mips:~/temp# ./client 172.0.1.53 5555 0x01020304
Send a num
Unsigned Num: 01020304
0x7fd00fd4 : 01 00000001
0x7fd00fd5 : 02 00000010
0x7fd00fd6 : 03 00000011
0x7fd00fd7 : 04 00000100
After htonl
Unsigned Num: 01020304
0x7fd00fd4 : 01 00000001
0x7fd00fd5 : 02 00000010
0x7fd00fd6 : 03 00000011
0x7fd00fd7 : 04 00000100
Data sent successfully.
root@ubuntu:~/temp# ./server 5555
UDP server is listening on port 5555...
Receiv a num
Unsigned Num: 04030201
0x7ffc5497a624 : 01 10000000
0x7ffc5497a625 : 02 01000000
0x7ffc5497a626 : 03 11000000
0x7ffc5497a627 : 04 00100000
After ntohl
Unsigned Num: 01020304
0x7ffc5497a624 : 04 00100000
0x7ffc5497a625 : 03 11000000
0x7ffc5497a626 : 02 01000000
0x7ffc5497a627 : 01 10000000
发送的字节顺序和接收到的一致。01 02 03 04发出,收到也是01 02 03 04, 经过ntohl转换,获得 04 03 02 01, 即小端的0x01020304。
发送的位序和接收到的位序不一致。发出00000001 ,收到10000000,似乎硬件底层已经帮助完成了位序的转换。
Number 0x01020304
#大端 0x01 0x02 0x03 0x04
#大端 00000001 00000010 00000011 00000100
#小端 00100000 11000000 01000000 10000000
#小端 0x04 0x03 0x02 0x01
可以看完整位序正好相反,完全镜像。
大端和小端,对于基本数字类型不仅字节序不同,位序也不同。
数字在网络传输中可以通过htons, htonl等方法转换字节序;
位序会在硬件层面自动完成转换,如果需要访问某几个位,那需要做额外处理,就像IP头中的版本号和头长度
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error "Please fix "
#endif
__u8 tos;
__be16 -tot_len;
__be16 -id;
__be16 -frag_off;
__u8 ttl;
__u8 protocol;
__be16 -check;
__be32 -saddr;
__be32 -daddr;
};
常见的这个字节 0x45, 4为ipv4, 5为ip header长度, 5x4 = 20 bytes
当主机收到IP包时,包里的每一个字节都已经是当前主机的位序。
大端 01000101, 前 4bits 4 (version), 后 4bits 5 (ihl)
小端 10100010, 前 4bits 5 (ihl), 后 4bits 4 (version).
参考了https://blog.csdn.net/gsls200808/article/details/79113864,做了一些简化
在ubuntu虚拟机上安装QEMU
sudo apt install qemu
下载debian mips镜像
#下载地址:
https://packages.baidu.com/app/qemu-mips/
## https://people.debian.org/~aurel32/qemu/ 已经404,只有百度这里还有资源
#镜像和内核
debian_wheezy_mips_standard.qcow2
vmlinux-3.2.0-4-4kc-malta
启动虚拟机
qemu-system-mips -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -nographic
#账号密码
root/root
#修改源
cat /etc/apt/sources.list
deb http://mirrors.aliyun.com/debian-archive/debian/ wheezy main non-free contrib
deb http://mirrors.aliyun.com/debian-archive/debian/ wheezy-proposed-updates main non-free contrib
deb-src http://mirrors.aliyun.com/debian-archive/debian/ wheezy main non-free contrib
deb-src http://mirrors.aliyun.com/debian-archive/debian/ wheezy-proposed-updates main non-free contrib
#apt-get install gcc 即可
虚拟机网络禁ping,但是可以直接TCP/UDP访问出去。