LINUX.ORG.RU

Застревает старт UDS-сервера

 ,


0

1

Написал тестовую программу UDS сервера и клиента, UDS = Unix Domain Sockets. Иногда сервер стартует и принимает какие то искаженные данные, это нарушает процесс обмена, и сам сервер виснет и клиент повисает ожидая данные

Сценарий работы прост - клиент подключается к серверу, пишут в сервер данные и получает ответ, затем еще раз пишет данные и получает снова ответ. Если все запускать начисто, холодный старт - то ноль проблем. А вот если повторно, зачастую возникает описанный выше затык

Код примера 2in1, сервер запускается с аргументом, клиент без: http://paste.org.ru/?dsi7xi

Не помогает ни двойной unlink ни remove. Может можно просто при открытии сокета куда то всё вычитать в /dev/null. Почему подобное может возникать и как с это решить?

Что-то подобное, как если бы TCP сервер крэшнулся и некоторое время не позволял бы биндить сокет, просто в UDS это какая-то специфичная проблема, с которой не знаю как бороться. И ведь при старте UDS сервера оно спокойно всё всегда открывает, но при первом же взаимодействии обнаруживаются повисшие данные почему то

Эм… А почему у тебя struct sockaddr вместо struct sockaddr_un? У первого же размер — 16 байт. Ты когда в него пишешь, перетираешь другие переменные в стеке.

И когда в bind передаёшь размер, должно быть sizeof(struct sockaddr_un), а не вычисление через strlen().

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Это я в примерах смотрел, везде только так было. На холодную работает стабильно, ничего не перетирается вроде. А вообще да, странно, там sa_data это всего жалких 14 байт, и его размер вместе с sa_family это константа

Завтра попробую с sockaddr_un. Но у меня короткие адреса, не должно перетирать, я понимаю эту проблему

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

Вот, я тебе поправил:

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

void run_server(void);
void run_client(void);

int main(int ac, char *av[]) {
  int server = 0;
  if (ac > 1)
    server = 1;
  if (server) {
    run_server();
  } else {
    run_client();
  }
  return 0;
}

#define SOCKET_NAME "/tmp/testuds"

void run_server(void) {
  char buffer[1024];
  struct sockaddr_un server, client;
  int server_socket = 0;
  int res, name_len, i;

  unlink(SOCKET_NAME);
  server_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
  if (server_socket < 0) {
    printf("server: ERROR: server socket failed\n");
    return;
  }

  server.sun_family = AF_UNIX;
  strcpy(server.sun_path, SOCKET_NAME);
  if (bind(server_socket, (struct sockaddr *)&server, sizeof(server)) < 0) {
    printf("server: ERROR: bind failed \"%s\", errno = %d\n", SOCKET_NAME,
           errno);
    return;
  }

  printf("server: OK\n");

  while (1) {
    name_len = sizeof(client);
    res = recvfrom(server_socket, buffer, sizeof(buffer), 0,
                   (struct sockaddr *)&client, &name_len);
    if (res < 0) {
      printf("server: ERROR: recvfrom failed, errno = %d\n", errno);
      return;
    }
    if (client.sun_family != AF_UNIX) {
      printf("server: unexpected client family %d\n", client.sun_family);
      return;
    }
    printf("server: from client= \"%.*s\", client.sun_path= %s\n", (int)res,
           buffer, client.sun_path);

    strcpy(buffer, "replyfromserver");
    res = sendto(server_socket, buffer, strlen(buffer), 0,
                 (struct sockaddr *)&client, name_len);
    printf("server: reply res= %d\n\n", res);
  }
}

void run_client() {
  char buffer[1024];
  struct sockaddr_un server, client, reply;
  int client_socket = 0;
  int res, name_len;

  client_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
  if (client_socket < 0) {
    printf("client: ERROR: client socket failed, errno = %d\n", errno);
    return;
  }

  client.sun_family = AF_UNIX;
  sprintf(client.sun_path, "/tmp/pid%d", getpid());
  unlink(client.sun_path);

  if (bind(client_socket, (struct sockaddr *)&client, sizeof(client)) < 0) {
    printf("client: ERROR: bind failed \"%s\", errno = %d\n", client.sun_path,
           errno);
    return;
  }

  printf("client: bind OK, \"%s\"\n", client.sun_path);

  server.sun_family = AF_UNIX;
  strcpy(server.sun_path, SOCKET_NAME);
  strcpy(buffer, "datafromclient");
  res = sendto(client_socket, buffer, strlen(buffer), 0,
               (struct sockaddr *)&server, sizeof(server));
  printf("client: sent to server %d bytes, len= %d\n", res, strlen(buffer));

  name_len = sizeof(reply);
  res = recvfrom(client_socket, buffer, sizeof(buffer), 0,
                 (struct sockaddr *)&reply, &name_len);
  if (res < 0) {
    printf("client: ERROR: recvfrom failed, errno = %d\n", errno);
    return;
  }

  printf("client: from server= \"%.*s\"\n", (int)res, buffer);

  strcpy(buffer, "anotherdata");
  res = sendto(client_socket, buffer, strlen(buffer), 0,
               (struct sockaddr *)&server, sizeof(server));
  printf("client: sent to server %d bytes, len= %d\n", res, strlen(buffer));

  name_len = sizeof(reply);
  res = recvfrom(client_socket, buffer, sizeof(buffer), 0,
                 (struct sockaddr *)&reply, &name_len);
  if (res < 0) {
    printf("client: ERROR: recvfrom failed, errno = %d\n", errno);
    return;
  }
  printf("client: second reply= \"%.*s\"\n", (int)res, buffer);

  close(client_socket);
  unlink(client.sun_path);
}

В процессе правок нашёлся ещё один дефект: ты неправильно передавал размер в recvfrom. Последний параметр там in-out, тебе нужно сначала установить его в размер буфера, а функция его подправит до реально используемого размера. Вообще там ещё нужно проверять, что вернулось значение не больше размера буфера, потому что если так, данные обрезаны, а буфер нужно было предоставлять побольше.

У меня работает нормально, ничего не подвисает, мусорных данных нет.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Благодарю за глубокое содействие!

Действительно, перейдя на sockaddr_un и внеся исправления для bind и name_len, работа стала стабильной! Я видел примеры с sockaddr_un ранее, но почему-то казалось что разницы особой нет

Unix Domain Sockets это не только в 7 раз более высокая производительность чем TCP loopback, но и более простая адресация, и пакеты ходят сплошным куском, как мне и надо (по 75 килобайт)

В Qt 5 я так понимаю есть QLocalServer и QLocalSocket для этого, легко переделывать если надо повысить скорость локального обмена. Эти UDS полагаю по сути peer-to-peer, сначала не работало потому что не делал bind, казалось странным bind для клиента но не сервера

Вообще, идеально для IPC. Но адресация немного странная, приходится использовать pid для уникальности имен одинаковых по сути клиентов. Мне это нужно было чтобы заменить QNX-механизмы обмена для их работы на Linux с целью более удобной отладки

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

казалось странным bind для клиента но не сервера

Ты же используешь датаграммы, пересылку данных без установления соединения. Чтобы «клиент» смог принять датаграмму, он должен быть сервером. Поэтому нужен bind().

Но адресация немного странная, приходится использовать pid для уникальности имен одинаковых по сути клиентов

Есть SOCK_SEQPACKET, с установлением соединения, но при этом сохраняющий границы пакетов.

i-rinat ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.