Избегайте повторного использования одного и того же номера fd в многопоточном сокетном приложении - PullRequest
5 голосов
/ 01 октября 2009

У меня есть асинхронное приложение, выполняющее несколько потоков, выполняющих операции через сокеты, где операции планируются, а затем выполняются асинхронно.

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

Проблема возникает потому, что (accept (); close (); accept ()) возвращает одинаковое значение fd в обоих методах accept (), что может привести к описанной выше ситуации.

Я не вижу способа избежать этого.

любой намек?

Ответы [ 6 ]

3 голосов
/ 01 октября 2009

Хорошо, нашел ответ.

Лучший способ здесь - вызвать accept () и получить наименьший доступный fd, продублировать его с номером, известным вам как dup2 (6,1000), и закрыть (6) теперь у вас есть контроль над диапазоном fd, который вы используете.

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

Поскольку принятие выполняется всегда в одном и том же потоке, а dup2 и close не дорогие по сравнению с принятием, которое всегда делается там, это идеально подходит длянеобходимо.

2 голосов
/ 01 октября 2009

Как вы управляете розетками? Похоже, у вас есть несколько потоков, любой из которых может:

  1. accept входящее соединение
  2. close существующее соединение
  3. создать новое исходящее соединение

Похоже, вам нужен способ обеспечить доступ к различным плавающим сокетам. Рассматривали ли вы возможность ассоциировать каждый сокет с мьютексом, который предотвращает закрытие сокета, пока он еще используется, или, возможно, поместить каждый дескриптор сокета в структуру с атомарным счетчиком ссылок, который будет препятствовать тому, чтобы другие потоки закрывали его, пока все потоки не будут его использовать?

1 голос
/ 02 октября 2009

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

На вашем месте, учитывая ограничения, на которых вы настаиваете, я бы использовал dup () (не dup2 ()) внутри мьютекса. (Может быть, мьютексы per-fd, если вас это беспокоит.)

1 голос
/ 01 октября 2009

Отличный вопрос! Я даже не осознавал, что такая проблема может возникнуть.

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

1 голос
/ 01 октября 2009

сокет представляет собой 5-локальный {local-addr, local-port, remote-addr, remote-port, proto}, поэтому, если вы можете использовать эти свойства вместо fd для маршрутизации событий / обработчиков, вы можете избежать FD Clash.

другой вариант - сериализовать все операции close () / accept () (приоритеты?), Чтобы они не могли смешиваться

0 голосов
/ 01 октября 2009

Вести счетчик ожидающих операций (чтение / запись и т. Д.) Для каждого сокета, а также наличие ожидающего закрытия запроса на сокет. Там, где раньше вы звонили close, сначала проверьте, есть ли ожидающие операции. Если есть, вместо этого вызовите shutdown, а затем вызывайте close только тогда, когда количество ожидающих операций достигнет 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...