首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >发送()& recv()给我问题

发送()& recv()给我问题
EN

Stack Overflow用户
提问于 2014-05-12 14:42:21
回答 1查看 964关注 0票数 0

我正在做一个通过socket传递结构的C程序

这是我的结构

代码语言:javascript
复制
typedef struct{
    char    type;           //message type
    char*   sender;         //sender
    char*   receiver;       //receiver
    unsigned int msglen;    //msg length
    char*   msg;            //text
} msg_t;

这是我的发送功能:

代码语言:javascript
复制
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");
    }
}

这是我的接收功能:

代码语言:javascript
复制
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;

}

当我像发送字符串一样发送消息时,一切正常,但是当我切换到结构时,客户端似乎发送了消息,然后给我以下内容:

代码语言:javascript
复制
ERROR: receive failed: connection reset by peer

而服务器这个:

代码语言:javascript
复制
ERROR: receive failed: invalid argument

我做错了什么?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-05-13 08:00:12

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

根据以上所述,msg_t的长度为40字节。这可以通过打印出它的大小来确认:

代码语言:javascript
复制
     printf("sizeof(msg_t): %zd\n", sizeof(msg_t));

“那么,这些空白砖是怎么回事呢?”

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

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

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

更改结构对齐(支持我的许多编译器,但可能不是C语言本身定义的)的一种方法是‘#杂注包();它用于如下所示:

代码语言:javascript
复制
#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()

在上面的结构定义中,第一个‘#杂注包(1)’导致以下结构对齐1字节。下一个‘#杂注包()’将编译器返回到默认的8字节对齐默认值。这种“打包”的结构如下所示:

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

“接收者”字段和“msg”字段也是如此。所有这些都是“发件人”机器上字符串的地址,对“接收者”机器没有任何价值。

最有可能的是,“意图”是发送实际的“发送方”、“接收方”和“msg”字符串。为此,可以使用类似于以下结构的结构:

代码语言:javascript
复制
#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结构生成。

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

代码语言:javascript
复制
typedef struct{
   char    type;           //message type
   char*   sender;         //sender
   char*   receiver;       //receiver
   char*   msg;            //text
} msg_t;

请注意,我通过删除‘#杂注包()’的内容,将结构返回到它的自然/本地8字节对齐。我还删除了“msgLength”字段(它并不是真正需要的)。

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

首先,发送一个字节“type”。然后发送发送方'string‘的一个字节长度[即:strlen(发送方)+ 1)。然后发送‘发件人’字符串,然后是接收方字符串的一个字节长度,然后是‘接收者’字符串,然后是'msg‘字符串的一个字节长度,然后是'msg’字符串。

在“接收方”端点上,您首先读取一个字节的“type”(这将提示您,后面将有三个“长度优先”字符串)。读取下一个字节将告诉您以下字符串的大小(并允许您将malloc()内存转到接收端点的msg_t结构的“发送方”字段)。然后将“发件人”字符串读入正确大小的malloc()ed内存中。同样读取接收方字符串长度和接收方字符串;最后,读取msg长度和字符串。

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23612062

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档