近日在工作中遇到一个奇怪问题,A程序发送一个结构给B程序,在B程序接收后,B程序按照A程序的结构进行解析,但始终不对。看过结构定义后,虽然知道有可能会出现字节对齐问题,一直怀疑是他们消息传输构造不对。经过GDB确认A程序发出的结构是正确的,在B程序接收处也同样没问题。当调试到转化处时才发现确实是字节对齐导致的。下面举例说明(32bit Linux Gcc)。
server.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#pragma pack(1)
typedef struct
{
unsigned int a;
short int b;
long long int c;
unsigned int d;
} type1;
#pragma pack()
#define PORT 8888
int main(int argc, char *argv[])
{
int fd = 0;
int len = 0;
int err = 0;
char *buff = NULL;
char recvbuf[1024]={0};
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
type1 *C = NULL;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
printf("Socket create failure");
return -1;
}
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
len = sizeof(struct sockaddr_in);
err = bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (-1 == err)
{
printf("bind error\n");
return -1;
}
while (1)
{
recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&client_addr, &len);
C = (type1 *)recvbuf;
printf("C={0x%x,0x%x,0x%llx,0x%x}\n", C->a, C->b, C->c, C->d);
}
return 0;
}
//输出
[root@smart Desktop]# ./server
C={0x11223344,0x5566,0x5678901234560019,0x43211234}
client.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#pragma pack(4)
typedef struct
{
unsigned int a;
short int b;
long long int c;
unsigned int d;
} type1;
#pragma pack()
#define PORT 8888
int main(int argc, char *argv[])
{
int fd = 0;
int len = 0;
int err = 0;
char *buff = NULL;
char recvbuf[1024]={0};
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
type1 A = {0x11223344, 0x5566, 0x1234567890123456LL, 0x87654321};
printf("sizeof(A)=%d,A={0x%x,0x%x,0x%llx,0x%x}\n", sizeof(A), A.a, A.b, A.c, A.d);
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
printf("Socket create failure");
return -1;
}
buff = (char *)&A;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
len = sizeof(struct sockaddr_in);
sendto(fd, buff, sizeof(type1), 0, (struct sockaddr *)&server_addr, len);
return 0;
}
//输出
[root@smart Desktop]# ./client
sizeof(A)=20,A={0x11223344,0x5566,0x1234567890123456,0x87654321}
[root@smart Desktop]#
从上面这个示例可以看出,输出的结果与我们预期的不一样。这是因为在32位系统下,数据存储将按结构体成员的最大字节数对齐,并且最大字节数为4。 上面的用例采用了手工设置对齐方式来测试该问题。从上述的运行结果看,我们需要在两个程序间保持一致,最好手动设置一样的字节对齐方式。
pragma pack(1)表示按一字节对齐,#pragma pack()表示取消前面的字节对齐,按默认方式对齐。
另外在编码时我们就应该考虑结构设计,尽量避免会引入需要考虑字节对齐的问题,如将short int 改为int,就无需考虑该问题。
–质量高的代码会将问题消灭在编码阶段,修复bug将会让你效率更低。
Juyin@2017/1/14