c - send()和recv()给我带来问题

标签 c sockets send recv

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


我究竟做错了什么?

最佳答案

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



根据以上所述,msg_t为40个字节长。可以通过打印出尺寸来确认:

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


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

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

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

虽然结构字段的8字节对齐方式最适合用于内存访问,但是当通过有线发送结构时,它并不是那么好。导线/协议结构最好对齐为1字节;从而消除了结构中未使用的“填充”字节。

更改结构对齐方式的一种方法是“ #pragma pack()”(受许多编译器支持,但可能不是C语言本身定义的)。用法如下所示:

#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字节对齐默认值。这种“打包”结构如下所示:



接下来,检查结构中的字段。 “发件人”字段为“字符*”。 “ char *”是一个地址,可以在“发送”计算机(或端点)上找到发送方字符串。
直言不讳,这个“地址”对于“接收者”机器(或端点)根本没有任何价值。因为“接收方”无法访问“发送方”的内存。

“接收者”字段也是如此。和“ msg”字段。所有这些都是“发送者”计算机上字符串的地址;这对“接收器”机器毫无价值。

最有可能的是,“意图”是发送实际的“发送者”,“接收者”和“ 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'字段(实际上不是必需的)。

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

首先,发送一个字节的“类型”。然后发送发送者“字符串”的一字节长度[即:strlen(sender)+ 1)。然后发送“发送者”字符串,然后发送接收者字符串的一个字节长度,然后发送“接收者”字符串,然后发送“ msg”字符串的一个字节长度,然后发送“ msg”字符串。

在“接收器”端点上,您首先读取一个字节的“类型”(这可能会提示您将跟随三个“长度在前”的字符串)。读取下一个字节将告诉您以下字符串的大小(并允许您将内存分配给接收方端点msg_t结构的'sender'字段)。然后将“发送方”字符串读入大小正确的malloc()内存中。进行相同的操作以读取接收器字符串的长度以及接收器字符串;最后,加上味精长度和字符串。

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

关于c - send()和recv()给我带来问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23612062/

相关文章:

c - 程序将不再找到 math.h

c - FFmpeg - pts 和 dts 对于视频没有正确增加,但对于音频则正确

c# - SMTP 发送获取 SmtpFailedRecipientException

sockets - 原始套接字问题: Are TCP packets passed to a raw socket?

Java ServerSocket和Socket实现IM系统

arrays - MPI - 发送数组段

c++ - Winsock API - 在两个线程中同时发送和接收数据

c - 列出数组中不重复的字符

c - 在 C 中保留 RAM

sockets - 调用accept()后出现 “Illegal Seek”错误