Может ли сокет быть закрыт из другого потока, когда происходит отправка /recv в одном и том же сокете?

Может ли сокет быть закрыт из другого потока при отправке /recv в том же сокете?

Предположим, что один поток блокирует вызов recv, а другой поток закрывает один и тот же сокет, будет ли поток в вызове recv знать это и выйти безопасно?

Я хотел бы знать, будет ли поведение отличаться между различными ОС/платформами. Если да, то как он будет себя вести в Solaris?

Ответ 1

Я не знаю реализацию сетевого стека Solaris, но я выброшу свою теорию/объяснение того, почему она должна быть безопасной.

  • Thread A вводит некоторый системный вызов блокировки, например read(2), для данного данного сокета. В буфере приема сокетов нет данных, поэтому поток A снимается с процессора, помещенного в очередь ожидания для этого сокета. Здесь не инициируются события сетевого стека, состояние подключения (при условии TCP) не изменилось.
  • Thread B выдает close(2) в сокете. В то время как структура сокета ядра должна быть заблокирована, когда поток B обращается к ней, ни один другой поток не удерживает эту блокировку (поток A освободил блокировку, когда она была поставлена ​​в режим ожидания ожидания). Предполагая, что в буфере отправки сокетов нет выдающихся данных, отправляется пакет FIN, и соединение входит в состояние FIN WAIT 1 (опять я предполагаю TCP здесь, см. диаграмма состояния соединения)
  • Я предполагаю, что изменение состояния соединения сокета приведет к пробуждению для всех потоков, заблокированных для данного сокета. Это поток A будет вводить состояние runnable и обнаруживать, что соединение закрывается. Ожидание может быть повторно введено, если другая сторона не отправила свой собственный FIN, иначе системный вызов вернется с eof в противном случае.

В любом случае внутренние структуры ядра будут защищены от ненадлежащего одновременного доступа. Это не значит, что это хорошая идея для ввода/вывода сокетов из нескольких потоков. Я бы посоветовал изучить неблокирующие сокеты, государственные машины и фреймворки, такие как libevent.

Ответ 2

При закрытии linux сокет не пробуждается recv(). Кроме того, поскольку @jxh говорит:

Если поток заблокирован в recv() или send(), когда сокет закрыт другим потоком, заблокированный поток получит ошибку. Однако, трудно определить правильное исправление после получая ошибку. Это связано с тем, что номер дескриптора файла связанные с гнездом, возможно, были подобраны еще поток, и заблокированный поток теперь проснулся на ошибке для "действительный" сокет. В этом случае разбуженная нить не должна вызывать close().

Просветленная нить должна каким-то образом различать, ошибка была вызвана соединением (например, сетевой ошибкой), которая требует, чтобы он вызывал close(), или если ошибка была сгенерирована другая нить, вызвавшая на нем функцию close(), и в этом случае она должна просто выйдите из строя, не делая ничего дальше сокета.

Таким образом, лучший способ избежать обеих проблем - вызвать shutdown() вместо close(). shutdown() сделает файловый дескриптор еще доступным, поэтому он не будет назначен другим дескриптором, также проснется recv() с ошибкой, и поток с вызовом recv() может закрыть сокет обычным способом, например, произошла нормальная ошибка.

Ответ 3

Для меня, shutdown() сокет из другого потока выполняет задание в Linux

Ответ 4

Если поток заблокирован в recv() или send(), когда сокет закрыт другим потоком, заблокированный поток получит ошибку. Однако после получения ошибки трудно определить правильное исправление. Это связано с тем, что номер дескриптора файла, связанный с сокетом, возможно, был выбран другим потоком, и заблокированный поток теперь проснулся при ошибке для "действительного" сокета. В этом случае разбуженный поток должен не вызывать close() сам.

Просветленный поток должен каким-то образом различать, была ли ошибка вызвана соединением (например, сетевой ошибкой), которая требует, чтобы он вызывал close(), или если ошибка была сгенерирована другим потоком с именем close() на нем, и в этом случае он должен просто отключиться, не делая ничего дальше сокета.

Ответ 5

Да, нормально закрыть сокет из другого потока. Любые заблокированные/занятые потоки, которые используют этот сокет, сообщают о подходящей ошибке.