Домен Unix: connect(): нет такого файла или каталога

как указано в заголовке, мой вызов connect() к сокету типа домена unix с соответствующим адресом приводит к ошибке ENOENT: нет такого файла или каталога.

Два сокета правильно инициализированы, и файлы сокетов созданы и привязаны соответствующим образом. Серверные и клиентские сокеты выполняются в разных процессах, хотя клиентский процесс обрабатывается fork() и execl(). Точно так же я анализирую адрес клиентского и серверного сокетов, которые я использую для настройки клиентского сокета. Серверный процесс использует pthreads.

Вот моя попытка connect():

struct sockaddr_un address;
address.sun_family = AF_UNIX;
memcpy(address.sun_path, filepath.c_str(), filepath.length());
address.sun_path[filepath.length()] = '\0';

if(-1 == connect(this->unix_domain_descriptor_.descriptor(),       \
                (struct sockaddr*)&address,                       \
                size))
{
    global::ExitDebug(-1, "connect() failed", __FILE__, __LINE__);
    return -1;
}

Я пробовал разные значения размера, например:

//  this is from unix(7) man page. It doesn't work neither with nor without "+1"
socklen_t size =  offsetof(struct sockaddr_un, sun_path);
          size += strlen(address.sun_path) + 1;

//  this is from one of my books about linux programming
socklen_t size = sizeof(address);

//  this is from a sample code which I found at the internet
socklen_t size = sizeof(address.sun_family) + strlen(address.sun_path);

//  Update 1: 
socklen_t size = SUN_LEN(&address);

//  this is what I tried out after looking into the declaration
//  of struct sockaddr_un
socklen_t size = strlen(address.sun_path);

Удивительно, но все инициализации, кроме последней, приводят к ошибке EINVAL: недопустимый аргумент для connect(), и я получаю ENOENT: нет такого файла или каталога только с последним. Я даже пробовал целые примеры из Интернета, но безуспешно. И очевидно, что замена socklen_t на size_t или int ничего не меняет.

Я уже проверил это:

  • address.sun_path содержит правильный путь к файлу сокета, начиная с корневого каталога
  • address.sun_path имеет длину 61 символ.
  • address.sun_family имеет значение AF_UNIX/AF_LOCAL.
  • address.sun_family имеет размер 2 байта
  • нет ошибок при создании и привязке обоих сокетов
  • серверный сокет находится в состоянии прослушивания
  • sizeof(address) возвращает 110, как и должно быть

Теперь мне было интересно, почему не работает пример man-страницы и были ли изменения, которые не были обновлены на linux.die.net или www.kernel.org . Моя ОС — Debian Squeeze, если это актуально.

Любые идеи, что я делаю неправильно? И как это решить? Если вам нужно больше кода или у вас есть вопросы, то не стесняйтесь спрашивать меня (хотя, вероятно, мне не нужно это заявлять, но это мой первый пост здесь >.‹).

кстати, извините за мой плохой английский

Обновление 2

Решено. Я опубликую это в дополнительном ответе ниже из ясности.


person xQuare    schedule 24.07.2012    source источник
comment
Как насчет strcpy вместо memcpy и ручного размещения нулевого терминатора?   -  person Jonathon Reinhart    schedule 24.07.2012
comment
Спасибо за ответ. Я попробовал это только сейчас, и это не изменилось. Между прочим, нулевой терминатор уже установлен вручную.   -  person xQuare    schedule 24.07.2012
comment
die.net — ужасный сайт с устаревшей информацией и кучей рекламы. Выживает за счет тяжелого SEO — и все. Я бы рекомендовал вам никогда не использовать его.   -  person    schedule 24.07.2012
comment
Спасибо. В любом случае я предпочитаю www.kernel.org. Не могли бы вы порекомендовать какие-либо другие страницы? Может там что-то найду...   -  person xQuare    schedule 24.07.2012
comment
Твоя фраза "кроме последнего" уже не держится после обновления, не так ли?   -  person MvG    schedule 24.07.2012
comment
Вы можете попробовать strace клиент, посмотреть, не напечатает ли он что-нибудь неожиданное во время connect. Однако это выстрел в темноте.   -  person MvG    schedule 24.07.2012
comment
Да, это не так. Я стараюсь поддерживать его в актуальном состоянии, насколько это возможно. Спасибо за терпеливость. Я собираюсь попробовать strace сейчас, хотя сначала мне нужно немного прочитать об этом, так что это займет некоторое время.   -  person xQuare    schedule 24.07.2012
comment
Я либо не знаю, как использовать strace, либо он неправильно отслеживает дочерние процессы. Вместо создания сокета он внезапно начинает слушать/выбирать/принимать, что никогда не вызывается в дочернем процессе. Я что-то упускаю, когда использую его? strace -o файл вывода -ttT -f -ff файл выполнения   -  person xQuare    schedule 24.07.2012


Ответы (2)


Выяснив, что я правильно обрабатываю сокеты, я немного изменил свой код для connect(), и теперь он работает. Я просто добавил эту строку после объявления моей переменной:

memset(&address, 0, sizeof(struct sockaddr_un));

Кто-нибудь знает, почему мне нужно установить всю переменную в 0, чтобы она работала? Спросить в новой теме или можно здесь?

person xQuare    schedule 25.07.2012
comment
В struct sockaddr_un есть поля, которые могут быть прочитаны функциями, которым они переданы. Если эти значения не инициализированы, произойдет неопределенное поведение. Кроме того, многие из этих значений по умолчанию равны 0 и могут иметь недопустимые значения, если они не инициализированы должным образом. Или, если они являются указателями, функция будет разыменовывать указатель NULL. - person Iharob Al Asimi; 09.04.2016
comment
Да, именно поэтому я устанавливал все переменные, которые мне были нужны, даже значения по умолчанию. Но хорошо, что вы прямо указываете на это :D - person xQuare; 10.04.2016

Цитата из руководства glibc:

Вы должны вычислить параметр LENGTH для адреса сокета в локальном пространстве имен как сумму размера компонента sun_family и длины строки (не размера выделения!) строки имени файла. Это можно сделать с помощью макроса SUN_LEN:

  • Макрос: int SUN_LEN (_struct sockaddr_un *_ PTR)
    Макрос вычисляет длину адреса сокета в локальном пространстве имен.

В приведенном ниже примере используется вычисление, для которого вы говорите, что это терпит неудачу для вас:

size = (offsetof (struct sockaddr_un, sun_path)
       + strlen (name.sun_path) + 1);

Но вы должны попробовать этот макрос. Если что-то изменилось или пример неправильный, есть большая вероятность, что этот макрос работает так, как задумано. Если это так, вы можете посмотреть на его внутренности. На первый взгляд мне кажется, что в макросе отсутствует часть + 1, используемая во всех примерах. Что соответствует предупреждению из руководства использовать «не размер выделения!» Как говорится в вашем посте, без + 1 это тоже не сработало, хотя шансы невелики.

Из любопытства, какова длина пути? Вы проверили, достаточно ли велико поле, представленное в структуре, чтобы вместить его? Что такое sizeof(address.sun_path) в вашей реализации? Интересно, возможно, вы копируете в незарезервированную память, и часть пути перезаписывается при следующем вызове функции.

person MvG    schedule 24.07.2012
comment
Использование первого моего первого кода инициализации без +1 возвращает то же значение, которое запрошено в вашем руководстве (которое я уже пробовал раньше). SUN_LEN возвращает точно такое же значение. Хотя спасибо за ответ, знание о SUN_LEN облегчит задачу в будущем. - person xQuare; 24.07.2012
comment
Длина пути составляет 61 символ. sizeof(address.sun_path) возвращает 108. Я не резервирую память динамически, поэтому сомневаюсь, что копирую что-либо в незарезервированную память. - person xQuare; 24.07.2012