【问题标题】:send() & recv() give me problemssend() 和 recv() 给我带来问题
【发布时间】:2014-06-29 23:52:04
【问题描述】:

我正在制作一个通过套接字传递结构的 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

我做错了什么?

【问题讨论】:

  • 将指针从一个进程发送到另一个进程并不是一个好主意。
  • 您是否为希望接收的数据分配了内存? char* msg 需要分配length 字节。还有其他需要处理的事情..
  • recv() 返回一些你必须注意的感兴趣的东西,即使它是积极的。 TCP 是一个字节流 - 它对您的 msg_t 结构一无所知,如果您不在 TCP 之上使用某些协议,它将被切片。
  • 几点:“对等连接重置”真的发生在recv上,还是一个错字?您是否使用SOCK_SEQPACKETSOCK_DGRAM 来保留消息边界(您应该,以及很可能sendmsg()/recvmsg())?最后,您的服务器很可能在您的rec_message() 呼叫之外的某个地方断开连接。失败的read()/recv() 不应该自己踩踏套接字。

标签: c sockets send recv


【解决方案1】:

这个问题有几个问题需要解决。也许最好首先关注 msg_t 结构本身。这是它可能看起来的模型;无论是在内存中,还是在传输时的“在线”中:

根据上面的说法,msg_t 是 40 字节长。这可以通过打印它的大小来确认:

     printf("sizeof(msg_t): %zd\n", sizeof(msg_t));

“那么所有的空白块是怎么回事?”

为了使事情在运行时更快,编译器在 CPU 寻址架构的“自然/原生”偏移处“对齐”“msg_t”结构中的每个字段。在我的 64 位系统上,这意味着每个结构字段将在 8 字节偏移上对齐;即使这意味着在结构中留下空的、未使用的空间。注意结构体字段的偏移量为:0、8、16、24、32;所有 8 字节的倍数。

在 32 位系统上,您可能会发现这些偏移量是 4 字节的倍数。

虽然结构字段的 8 字节对齐最适合内存访问,但在通过网络发送结构时就不是很好了。线路/协议结构最好以 1 字节对齐;从而消除结构中未使用的“填充”字节。

改变结构对齐方式的一种方法(支持我的许多编译器,但可能不是由 C 语言本身定义)是 '#pragma pack()';如下图所示:

#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)' 导致后面的结构是1字节对齐的。下一个“#pragma pack()”将编译器返回到其默认的 8 字节对齐默认值。这种“打包”结构如下所示:

接下来,检查结构中的字段。 'sender' 字段是一个 'char *'。 'char *' 是可以在“发送”机器(或端点)上找到发送者字符串的地址。 坦率地说,这个“地址”对“接收者”机器(或端点)根本没有价值;因为“接收者”无法访问“发送者”的内存。

'receiver' 字段也是如此;和“味精”字段。所有这些都是“发送者”机器上的字符串地址;这对“接收器”机器没有价值。

'intent' 很可能是发送实际的'sender'、'receiver' 和'msg' 字符串。为此,可以使用类似于以下的结构:

#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()

这个结构看起来像这样:

现在,实际的字符串在结构中;不仅仅是他们在内存中的地址。这将达到实际预期的效果。

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

请注意,每个“可变长度”字符串都以一个字节为前缀,表示后面字符串的长度。 (这就是字符串在 PASCAL 语言中的存储方式)。此字节允许以下字符串的长度为 0-255 个字节。电线上没有浪费空间。

不幸的是,这种“有线格式”不能直接使用 C 结构生成。

现在让我们来看问题中定义的结构;稍作修改:

typedef struct{
   char    type;           //message type
   char*   sender;         //sender
   char*   receiver;       //receiver
   char*   msg;            //text
} msg_t;

请注意,我已通过消除“#pragma pack()”内容将结构恢复为自然/原生 8 字节对齐方式。我还删除了“msgLength”字段(实际上并不需要)。

最有可能的是,结构的发送者、接收者和 msg 字段将被初始化为指向字符串(可能使用 malloc() 等分配)。使用上面的高效布局,通过网络发送此结构的方法是单独发送每个字段。

首先,发送一个字节的“类型”。然后发送发送方“字符串”的一字节长度[即:strlen(sender) + 1)。然后发送‘sender’字符串,后跟一个字节长度的receiver字符串,然后是‘receiver’字符串,然后是一个字节长度的‘msg’字符串,最后是‘msg’字符串。

在“接收器”端点上,您首先读取单字节“类型”(这会提示您后面将有三个“长度前”字符串)。读取下一个字节将告诉您以下字符串的大小(并允许您将 malloc() 内存分配到接收方端点的 msg_t 结构的“发送方”字段)。然后将“发送者”字符串读入大小合适的 malloc() 内存。读取接收器字符串长度和接收器字符串执行相同操作;最后,使用 msg 长度和字符串。

如果您发现 PASCAL 字符串(限制为 255 个字节)有点紧,请将长度前值从一个字节更改为多个字节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-05
    • 1970-01-01
    • 2019-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-22
    • 2014-03-02
    相关资源
    最近更新 更多