Можно ли закрыть сокет из другого потока, когда происходит отправка / запись в тот же сокет? - PullRequest
13 голосов
/ 28 августа 2010

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

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

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

Ответы [ 5 ]

5 голосов
/ 06 января 2015

В Linux закрытие сокета не проснется recv().Также, как @ jxh говорит:

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

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

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

3 голосов
/ 29 августа 2010

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

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

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

2 голосов
/ 24 мая 2013

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

1 голос
/ 14 февраля 2014

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

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

0 голосов
/ 29 августа 2010

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

...