【问题标题】:Define msghdr length in sendmsg and recvmsg (unix sockets)在 sendmsg 和 recvmsg(unix 套接字)中定义 msghdr 长度
【发布时间】:2013-11-02 08:50:09
【问题描述】:

我正在尝试使用 sendmsgrecvmsg 函数(C 语言)通过 unix 域套接字发送和接收消息。

过程很简单:

  • 服务器使用sendmsg向客户端发送字符串(必须使用struct msghdr);
  • 字符串在 main (argv[1]) 中作为参数传递;
  • 客户端使用recvmsg接收字符串;
  • 客户端将字符串打印到标准输出。

我有以下问题:

1 - 当服务器发送字符串“test”时:

$ ./server test

2 - 客户端正确打印:

$ ./client
print data received > test

但是,

1- 当字符串较大时(> 7 个字符),如“hello_world”:

$ ./server hello_world

2 - 客户端打印奇怪的字符(好像没有'\0'):

$ ./client
print data received > hello_woh?!#

是不是和消息的长度有关?

如何定义消息的长度以便正确打印?

编辑:服务器源代码:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define SOCK_PATH "/tmp/usocket"

int send_msg_to_client(int socketfd, char* data) {

  struct msghdr msg;
  struct iovec iov;
  int s;

  memset(&msg, 0, sizeof(msg));
  memset(&iov, 0, sizeof(iov));

  msg.msg_name = NULL;
  msg.msg_namelen = 0;
  iov.iov_base = data;
  // replace sizeof(data) by strlen(data)+1
  iov.iov_len = strlen(data)+1;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = NULL;
  msg.msg_controllen = 0;
  msg.msg_flags = 0;

  printf("msg: %s\n", (char *) iov.iov_base);

  s = sendmsg(socketfd, &msg, 0);

  if(s < 0){
    perror("sendmsg");
    return 0;
  }

  return s;
}


int main(int argc, char* argv[])
{

        if (argc != 2) {
          printf("Usage: $ %s <data>\n",argv[0]);
          return 0;
        }

    int s, s2, len, slen;
    socklen_t t;
    struct sockaddr_un local, remote;
    char* data = argv[1];

    printf("print data: %s\n",data);

    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    memset(&local, 0, sizeof(local));
    local.sun_family = AF_UNIX;
    strcpy(local.sun_path, SOCK_PATH);

    unlink(local.sun_path);

    len = strlen(local.sun_path) + sizeof(local.sun_family);
    if (bind(s, (struct sockaddr *)&local, len) == -1) {
        perror("bind");
        exit(1);
    }

    if (listen(s, 5) == -1) {
        perror("listen");
        exit(1);
    }

    printf("Waiting for a connection...\n");

    t = sizeof(remote);
    if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) {
        perror("accept");
        exit(1);
    }

    printf("Connected.\n");

    slen = send_msg_to_client(s2, data);

    if(slen < 0)
        perror("send");

    printf("sent data length: %d\n", slen);

    close(s2);

    return 0;
}

编辑:客户端源代码:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define SOCK_PATH "/tmp/usocket"
#define MAX 100

int recv_msg_from_server(int socketfd, char data[MAX]) {

  struct msghdr msg;
  struct iovec iov;
  int s;

  memset(&msg, 0, sizeof(msg));
  memset(&iov, 0, sizeof(iov));

  msg.msg_name = NULL;
  msg.msg_namelen = 0;
  iov.iov_base = data;
  //replace sizeof(data) by MAX
  iov.iov_len = MAX;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = NULL;
  msg.msg_controllen = 0;
  msg.msg_flags = 0;


  s = recvmsg(socketfd, &msg, 0);

  if(s < 0){
    perror("recvmsg");
    return 0;
  }

  return s;
}


int main(void)
{
    int s, len, slen;
    struct sockaddr_un remote;
    char data[MAX];

    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(1);
        }

    printf("Trying to connect...\n");

    memset(&remote, 0, sizeof(remote));

    remote.sun_family = AF_UNIX;
    strcpy(remote.sun_path, SOCK_PATH);
    len = strlen(remote.sun_path) + sizeof(remote.sun_family);

    if (connect(s, (struct sockaddr *)&remote, len) == -1) {
            perror("connect");
            exit(1);
        }

    printf("Connected.\n");

    slen = recv_msg_from_server(s, data);

    if (slen < 0) {
        perror("recvmsg");
    }

    //data[MAX] = '\0';
    printf("print data received > %s\n", data);

    close(s);

    return 0;
}

【问题讨论】:

    标签: c sockets unix-socket


    【解决方案1】:

    乍一看,您似乎将有效负载的长度(iov.iov_len)设置为sizeof(data),即sizeof(char*)。假设您在 64 位机器上运行它,这个大小是 8 个字节,这就解释了为什么它确实适用于较短的字符串。

    你可能想要strlen(data) + 1

    【讨论】:

    • 感谢亚历山大的解释!我尝试了 strlen(data)+1 但客户端打印了前 2 个字符(将 strlen(data) 视为 1??)。然后我将其替换为 iov.iov_len = MAX;在客户端,它可以工作。但是,这不是最好的解决方案。
    • 为什么不在发送前打印iov_base 指向的iov_len 字节以查看正在发送的内容?假设数据指向一个大于 1 的以 NUL 结尾的有效字符串,则您的 iov_len 应该大于该值。
    • 嗯...怎么可能是4(对于字符串“test”)?它必须是 5。尝试调试它,看看你在 iov_len 中得到了什么。
    • 在服务器端,我没有问题。我在sendmsg 函数之前打印char* datastrlen(data)。并在sendmsg 函数之后打印msg.msg_iov-&gt;iov_basestrlen(msg.msg_iov-&gt;iov_base)。数值没问题!但是,在客户端,当我在recvmsg 函数之前打印char data[]strlen(data) 时,我得到一个奇怪的字符,strlen 的值为1。在recvmsg 函数之后,我得到了正确的值。
    • 对不起亚历山大,其他评论很混乱,所以我删除了它。 strlen("test") 给出 4 而不是 5。
    猜你喜欢
    • 1970-01-01
    • 2016-11-20
    • 2018-03-11
    • 1970-01-01
    • 1970-01-01
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    相关资源
    最近更新 更多