什么是大小端
计算机的最小存储单位为字节(8bit),对于单一的字节(byte),大部分cpu以相同的顺序处理位元(bit),因此单字节的存放方法和传输方式一般相同,但当cpu处理多字节数据时(int),就会有字节的存储顺序问题,一般有两种方式: 大端(big endian) 小端(little endian)
存储方式
以数据 0x01020304 为例,下面是它在两种平台上的存储图示:
- 大端序 (ppc64)
地址增长方向 ---------------> -------------------------------- 0x01 | 0x02 | 0x03 | 0x04 --------------------------------
- 小端序 (x86_64)
地址增长方向 ---------------> -------------------------------- 0x04 | 0x03 | 0x03 | 0x01 --------------------------------
- 程序示例
#include <stdio.h>
int main(int argc, char *argv[])
{
union {
int i;
char c[4];
} u;
u.i = 0x01020304;
fprintf(stdout, "%.2x %.2x %.2x %.2x\n",
u.c[0], u.c[1], u.c[2], u.c[3]);
return 0;
}
- x86_64(小端)上面运行:
04 03 02 01
- ppc64(大端)上面运行:
01 02 03 04
网络传输时的问题
网络协议通常规定使用大端方式传输数据,如果在不同的平台间传输数据时不注意大小端,就会产生问题
- 发送一个char
发送程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int sock_fd;
struct sockaddr_in addr;
if ((sock_fd = socket(AF_INET, SOCK_DGRAM,
IPPROTO_UDP)) == -1) {
perror("socket");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
if (inet_aton(argv[1], &addr.sin_addr) == 0) {
close(sock_fd);
fprintf(stderr, "inet_aton: server address error!\n");
exit(-1);
}
char c = 1;
fprintf(stdout, "send: %d\n", c);
if (sendto(sock_fd, &c, sizeof(c), 0,
(const struct sockaddr *)&addr, sizeof(addr)) == -1) {
close(sock_fd);
perror("sendto");
exit(-1);
}
return 0;
}
接收程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int sock_fd;
struct sockaddr_in addr;
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock_fd, (const struct sockaddr *)&addr,
sizeof(addr)) == -1) {
close(sock_fd);
perror("bind");
exit(-1);
}
char c;
if (recv(sock_fd, &c, sizeof(c), 0) == -1) {
close(sock_fd);
perror("recv");
exit(-1);
}
fprintf(stdout, "recv: %d\n", c);
return 0;
}
运行结果:
x86_64(小端)上运行发送程序:
send: 1
ppc64(大端)上运行接收程序:
recv: 1
可见在发送单字节数据时,是不需要注意大小端问题的
- 发送一个int
发送程序(将char改为int):
int c = 1;
fprintf(stdout, "send: %d\n", c);
if (sendto(sock_fd, &c, sizeof(c), 0,
(const struct sockaddr *)&addr, sizeof(addr)) == -1) {
close(sock_fd);
perror("sendto");
exit(-1);
}
接收程序(将char改为int):
int c;
if (recv(sock_fd, &c, sizeof(c), 0) == -1) {
close(sock_fd);
perror("recv");
exit(-1);
}
fprintf(stdout, "recv: %d\n", c);
运行结果:
x86_64(小端)上运行发送程序:
send: 1
ppc64(大端)上运行接收程序:
recv: 16777216
- 正确的发送一个int
发送程序(交换字节序):
int c = 0x01020304;
char * p = (char *)&c;
char buf[4];
buf[0] = p[3];
buf[1] = p[2];
buf[2] = p[1];
buf[3] = p[0];
fprintf(stdout, "send: %d\n", c);
if (sendto(sock_fd, &buf, sizeof(buf), 0,
(const struct sockaddr *)&addr, sizeof(addr)) == -1) {
close(sock_fd);
perror("sendto");
exit(-1);
}
运行结果:
x86_64(小端)上运行发送程序:
send: 16909060
ppc64(大端)上运行接收程序:
recv: 16909060