发送()& recv()给我带来了问题

时间:2014-05-12 14:42:22

标签: c sockets send recv

我正在制作一个通过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

我做错了什么?

1 个答案:

答案 0 :(得分:1)

这个问题有几个问题需要解决。也许最好首先关注msg_t结构本身。这是一个它可能是什么样子的模型;无论是在记忆中还是在电线上都是如此。因为传播:

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;结构看起来像这样:

packed msg_t

接下来,检查结构中的字段。发件人&#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()

此结构如下所示:

functional msg_t

现在,实际的字符串在结构中;不只是他们在记忆中的地址。这将完成实际意图。

不幸的是,它确实限制了每个字符串的长度;它还包含大量未使用/浪费的空间。也许删除该限制并允许更多灵活性会很好。发送这些字段可能更好:

non-fixed-size packet

请注意,每个&#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字节)有点紧,请将长度先前的值从一个字节更改为多个字节。