#c #boost #boost-asio
#c #boost #boost-asio
Вопрос:
Я использую Boost 1.45 ASIO для обработки некоторых подключений к сокетам в приложении, которое работает как на Windows, так и на Mac. В Windows следующий код не вызывает никаких ошибок, и мои сокеты закрыты чисто. Однако на Mac как завершение работы, так и (если я закомментирую это) функции закрытия выдают мне ошибки «Плохого дескриптора файла». До тех пор, пока я не вызову этот код, сокеты работают нормально. Но как только я вызываю shutdown или close, я получаю сообщение об ошибке. Есть идеи, что может происходить?
if(socket.is_open())
{
socket.shutdown(socket.both);
socket.close();
}
Комментарии:
1. Было бы полезно, если бы вы могли прояснить цель этого фрагмента кода. Обычно
~socket()
dtor закрывает базовый тип собственного дескриптора. Есть ли причина, по которой вы явно закрываете его?2. Я думал, что вам действительно нужно закрыть сокеты — эта ошибка, похоже, не вызывает никаких проблем (кроме самой ошибки), поэтому, если мне не нужно явно закрывать сокет, тогда я с радостью устраню это и продолжу свою жизнь. Итак, чтобы уточнить, мне не нужно вызывать shutdown или close?
3. Я бы не советовал игнорировать эту ошибку. «Неправильный файловый дескриптор» на
close
обычно означает, что файловый дескриптор уже закрыт, что является потенциально серьезной ошибкой, поскольку дескрипторы используются повторно. Например, если ваша программа ошибочно закрывает (скажем) дескриптор 5 дважды, и вы (или другой поток!) случается, что между ними создается новый дескриптор, новый дескриптор также будет равен 5, и ошибочныйclose
закроет его. Итак, хотя я не знаю ответа на ваш вопрос, я настоятельно рекомендую определить основную причину, а не игнорировать ее или «подметать ее под ковер» с помощью деструктора.
Ответ №1:
«Неправильный файловый дескриптор» включен close
обычно означает, что дескриптор уже закрыт. Часто это происходит из-за ошибки двойного закрытия в каком-то совершенно не связанном разделе программы.
Такая ошибка может быть заразной. Если ваша программа дважды закрывает один и тот же дескриптор, и он тем временем переназначается, второй close
закроет дескриптор какого-либо несвязанного объекта из-под них. И затем, когда этот объект закрывает свой дескриптор, он фактически может закрывать дескриптор другого объекта… И так далее, пока последний в строке не получит ошибку «плохой файловый дескриптор».
Это побочный эффект того, что (а) дескрипторы находятся в глобальном состоянии и (б) требование Unix, чтобы любой вызов open/socket/ etc. назначал неиспользуемый дескриптор с наименьшим номером.
Единственный известный мне способ отладить это — отслеживать создание и уничтожение всех файловых дескрипторов с помощью такого инструмента, как strace
(в Linux) или dtrace
(на Mac). (Ну, может быть, не единственный способ. Однажды я написал запутанный LD_PRELOAD
хак, чтобы перехватывать каждый вызов open
и close
, чтобы выяснить, какой поток дважды закрывал свой дескриптор, потому что второе закрытие уничтожало дескриптор, используемый другим потоком …)
Удачи.
Ответ №2:
if(socket.is_open())
{
socket.shutdown(socket.both);
socket.close();
}
Если у вас нет веской причины для этого, я предлагаю позволить socket::~socket()
деструктору закрыть базовый собственный файловый дескриптор. Если вы обеспокоены утечкой дескрипторов, используйте такой инструмент, как valgrind, для анализа вашей программы.
Комментарии:
1. На самом деле это не игнорирование ошибки. Библиотека asio абстрагирует собственный базовый тип дескриптора и не требует явного закрытия для предотвращения утечки ресурсов. Если в программе есть ошибка при двойном закрытии дескрипторов, скорее всего, она в другом месте.
2. Ошибка может быть в другом месте, но ошибка возникает здесь… И я совершенно уверен, что люди boost не будут создавать никаких исключений из своих деструкторов. Итак, ваше предложение игнорирует проблему, а не устраняет проблему. (Невозможность обнаружить ошибки при close() — вот почему полагаться на деструктор — плохой стиль в целом, IMO. В этом случае это определенно игнорирует реальную проблему, какой бы она ни была.)
3. Я принимаю этот ответ немного неохотно — я согласен с Nemo в том, что где-то еще может быть ошибка, но после детального просмотра моей программы я не могу ее найти — это единственное закрытие, которое у меня там было, поэтому, если я выну это и позволю деструктору обработать это, я не буду делать никаких явных закрытий. Для моей программы игнорирование этого является разумным, потому что на данный момент программа все равно завершает работу, но для потомков, прежде чем перейти к этому маршруту, проверьте наличие дополнительных закрытий, как упоминает Nemo.
Ответ №3:
У меня была та же проблема: в Windows все было в порядке, а в Linux возникло исключение в зависимости от IIRC состояния сокета.
Альтернативой ответу Сэма является использование фиктивного error_code для молчаливого игнорирования исключения, если оно возникает. Смотрите перегрузки при закрытии и завершении работы в документации asio.