Неблокирующая обработка сокетов TCP - Как определить состояние блокировки перед записью в сокет? - PullRequest
1 голос
/ 05 февраля 2012

В настоящее время я пишу прокси-приложение, которое читает из одного сокета и пишет в другом. Оба они настроены как неблокирующие, что позволяет обрабатывать несколько пар сокетов.

Чтобы управлять правильным потоком между сокетами, приложение НЕ должно читать из исходного сокета, если запись в целевой сокет может блокироваться. Идея хорошая, однако я не нашел способа обнаружить состояние блокировки целевого сокета без предварительной записи в него ... и это не то, что нужно.

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

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

Ответы [ 2 ]

1 голос
/ 11 февраля 2012

Спасибо всем за отзыв.

Обобщение всех комментариев и ответов до сих пор:

Непосредственно к вопросу, единственный известный способ определить, что сокет блокируется при попытке записи в него, - это использовать select / poll / epoll.

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

  1. Использование SIOCOUTQ, чтобы узнать, сколько буфера осталось в целевом сокете и передать не более того. Как указывает @ ugoren , этот недостаток заключается в ненадежности, главным образом потому, что между чтением значения, его вычислением и попыткой записи фактического значения может измениться. Он также вводит некоторые проблемы ожидания в случае неправильного управления. Я полагаю, что если эта техника будет использоваться, за ней следует более надежная для полной защиты.

  2. Использование select / poll / epoll и добавление небольшого ограниченного буфера для каждого сокета чтения: изначально все сокеты чтения будут добавлены в poll, когда мы будем готовы к чтению, мы читаем он имеет ограниченный размер буфера, затем удаляет сокет чтения из poll и добавляет вместо него целевой сокет для записи, когда мы возвращаемся к poll и обнаруживаем, что целевой сокет готов к записи, мы пишем буфер, если все данные были приняты сокетом, мы удаляем целевой сокет из опроса и возвращаем сокет чтения обратно. Недостатком здесь является то, что мы увеличиваем количество системных вызовов (добавляем / удаляем к poll / select), и нам нужно сохранять внутренний буфер на сокет. Это кажется предпочтительным подходом, и можно добавить некоторую оптимизацию, чтобы попытаться сократить вызов системных вызовов (например, попытка записи сразу после чтения сокета чтения, и только если что-то осталось для выполнения вышеупомянутого).

Еще раз спасибо всем за участие в этом обсуждении, мне очень помогло упорядочить идеи.

1 голос
/ 05 февраля 2012

select или poll должны быть в состоянии предоставить вам информацию.
Я предполагаю, что вы уже используете один из них, чтобы определить, в каком из ваших сокетов чтения есть данные
Если у вас есть доступный для чтения сокет для чтения, замените его на соответствующий сокет для записи (но, конечно, поместите его в fds для записи) и снова вызовите select. Затем, если имеется гнездо для записи, вы можете читать и писать.

Обратите внимание, что возможно, что пишущий сокет готов к получению данных, но не так много, как вы хотите. Таким образом, вы можете прочитать 100 байтов и записать только 50.

...