我正在制作一个通过socket
传递结构的C程序这是我的结构
typedef struct{
char type; //message type
char* sender; //sender
char* receiver; //receiver
unsigned int msglen; //msg length
char* msg; //text
} msg_t;
这是我的发送功能:
void send_message(int socket, char* msg)
{
msg_t message;
bzero(&message,sizeof(message));
message.msg = msg;
if(send(socket,&message,sizeof(msg_t),0) < 0)
{
perror("ERROR: send fail\n");
}
}
这是我的接收功能:
msg_t rec_message(int socket)
{
msg_t buff;
bzero(&buff,sizeof(buff));
if(recv(socket,&buff,sizeof(buff),0) < 0)
{
perror("ERROR: receive failed\n");
}
return buff;
}
当我发送类似字符串的消息时,一切正常,但当我切换到结构时,客户端似乎发送消息然后给我这个:
ERROR: receive failed: connection reset by peer
和服务器:
ERROR: receive failed: invalid argument
我做错了什么?
答案 0 :(得分:1)
这个问题有几个问题需要解决。也许最好首先关注msg_t结构本身。这是一个它可能是什么样子的模型;无论是在记忆中还是在电线上都是如此。因为传播:
根据以上所述,msg_t长40个字节。这可以通过打印它的大小来确认:
printf("sizeof(msg_t): %zd\n", sizeof(msg_t));
&#34;那么所有空白块都是什么?&#34;
为了在运行时快速完成任务,编译器会对齐&#39; &#39; msg_t&#39;中的每个字段结构在&#34;自然/本地&#34; CPU寻址架构的偏移量。在我的64位系统上,这意味着每个结构字段将在8字节偏移上对齐;即使这意味着在结构中留下空的,未使用的空间。请注意,结构字段的偏移量为:0,8,16,24,32;所有8个字节的倍数。
在32位系统上,您可能会发现这些偏移量是4个字节的倍数。
虽然结构字段的8字节对齐对于存储器访问是最佳的,但是当通过线路发送结构时,它并不是那么好。线/协议结构最好以1字节对齐;从而消除了未使用的填料#39;结构中的字节数。
改变结构对齐的一种方法(支持我的许多编译器,但可能不是由C语言本身定义)是#pragma pack()&#39 ;;使用如下所示:
#pragma pack(1)
typedef struct{
char type; //message type
char* sender; //sender
char* receiver; //receiver
unsigned int msglen; //msg length
char* msg; //text
} msg_t;
#pragma pack()
在上面的结构定义中,第一个#pragma pack(1)&#39;导致以下结构为1字节对齐。下一个#pragma pack()&#39;将编译器返回到其默认的8字节对齐默认值。这个&#34;包装&#34;结构看起来像这样:
接下来,检查结构中的字段。发件人&#39;字段是&#39; char *&#39;。 A&#39; char *&#39;是一个地址,可以在发送&#39;发现&#39;机器(或端点)。 说实话,这个地址&#39;对接收者而言毫无价值。机器(或端点);作为接收者&#39;无法访问&#39;发件人&#39;。
的记忆&#39;接收器&#39;也是如此。领域;并且&#39; msg&#39;领域。所有这些都是&#39;发件人&#39;上的字符串地址。机;这对接收者来说没什么价值。机。
最有可能的是,&#39;意图&#39;是发送实际的&#39;发送者&#39;接收者&#39;和&#39; msg&#39;字符串。为此,可以使用类似于以下的结构:
#pragma pack(1)
typedef struct{
char type; //message type
char sender[15]; //sender
char receiver[15]; //receiver
char msg[30]; //text
} msg_t;
#pragma pack()
此结构如下所示:
现在,实际的字符串在结构中;不只是他们在记忆中的地址。这将完成实际意图。
不幸的是,它确实限制了每个字符串的长度;它还包含大量未使用/浪费的空间。也许删除该限制并允许更多灵活性会很好。发送这些字段可能更好:
请注意,每个&#39;可变长度&#39; string的前缀是一个字节,表示后面的字符串的长度。 (这是字符串存储在PASCAL语言中的方式)。该字节允许以下字符串长度为0-255个字节。电线上没有浪费的空间。
不幸的是,这种电线格式&#39;不能使用C结构直接生产。
让我们看看问题中定义的结构;稍加修改:
typedef struct{
char type; //message type
char* sender; //sender
char* receiver; //receiver
char* msg; //text
} msg_t;
请注意,我已经通过消除&#39; #pragma pack()&#39;将结构返回到它的自然/本机8字节对齐方式。东西。我还删除了“msgLength”&#39;字段(它不是真的需要)。
最有可能的是,结构的发送方,接收方和消息字段将被初始化为指向字符串(可能使用malloc()等分配)。使用上面的有效布局,通过线路发送此结构的方法是单独发送每个字段。
首先,发送一个字节&#39; type&#39;。然后发送一个字节长度的发件人&#39;字符串&#39; [即:strlen(发件人)+ 1)。然后发送&#39;发件人&#39;字符串,后跟接收器字符串的一个字节长度,接着是接收器&#39;字符串,后跟一个字节长度的&#39; msg&#39;字符串,然后是&#39; msg&#39;字符串。
在&#39;接收器&#39;端点,你首先读取单字节&#39;类型&#39; (这会让你知道将会有三个&#39;长度先前的&#39;字符串要遵循)。读取下一个字节将告诉您以下字符串的大小(并允许malloc()内存到接收器端点的msg_t结构的&#39; sender&#39;字段)。然后阅读发件人&#39;字符串到完全正确的malloc()ed内存。同样要读取接收器字符串长度和接收器字符串;最后,使用msg长度和字符串。
如果发现PASCAL字符串(限制为255字节)有点紧,请将长度先前的值从一个字节更改为多个字节。