Определите длину msghdr в sendmsg и recvmsg (сокеты unix)

Я пытаюсь отправлять и получать сообщения через сокеты домена unix, используя функции sendmsg и recvmsg (язык C).

Процесс очень прост:

  • сервер отправляет строку клиенту с помощью sendmsg (необходимо использовать struct msghdr);
  • строка передается в качестве аргумента в main (argv[1]);
  • клиент получает строку с помощью recvmsg;
  • клиент выводит строку на стандартный вывод.

У меня есть следующая проблема:

1 — Когда сервер отправляет строку «тест»:

$ ./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;
}

person rmgoncalo    schedule 23.10.2013    source источник


Ответы (1)


На первый взгляд, похоже, что вы устанавливаете длину полезной нагрузки (iov.iov_len) на sizeof(data), что равно sizeof(char*). Предполагая, что вы запускаете его на 64-битной машине, этот размер составляет 8 байт, что объясняет, почему он работал для более коротких строк.

Вы, вероятно, хотите сделать это strlen(data) + 1.

person Alexander L. Belikoff    schedule 23.10.2013
comment
Спасибо Александр за объяснение! Я попробовал strlen(data)+1, но клиент печатает первые 2 символа (рассматривает strlen(data) как 1??). Затем я заменил это на iov.iov_len = MAX; на стороне клиента, и это работает. Но, это не лучшее решение. - person rmgoncalo; 24.10.2013
comment
Почему бы вам не распечатать iov_len байтов, на которые указывает iov_base, прямо перед отправкой, чтобы увидеть, что отправляется? Предполагая, что данные указывают на допустимую строку с завершением NUL, длиннее 1, ваш iov_len должен быть больше этого. - person Alexander L. Belikoff; 24.10.2013
comment
Хм... Как это может быть 4 (для теста строки)? Должно быть 5. Попробуйте отладить его, чтобы увидеть, что вы получите в iov_len. - person Alexander L. Belikoff; 24.10.2013
comment
На стороне сервера у меня проблем нет. Я печатаю char* data и strlen(data) перед функцией sendmsg. И печать msg.msg_iov->iov_base и strlen(msg.msg_iov->iov_base) после функции sendmsg. Значения в порядке! Но на стороне клиента, когда я печатаю char data[] и strlen(data) перед функцией recvmsg, я получаю странный символ и значение 1 для strlen. После функции recvmsg я получаю правильные значения. - person rmgoncalo; 24.10.2013
comment
Извините, Александр, другой комментарий был очень запутанным, поэтому я его удалил. strlen("test") дает 4, а не 5. - person rmgoncalo; 24.10.2013