Как сделать так, чтобы доступ к сокетам вел себя «асинхронно», не требуя цикла сообщений? - PullRequest
1 голос
/ 21 июля 2010

Моя программа использует объект NetworkOutput, который можно использовать для записи данных на удаленный сервер. Семантика заключается в том, что в случае, если объект в данный момент подключен (поскольку существует удаленный сервер), данные фактически отправляются через сокет. В противном случае он молча отбрасывается. Некоторые наброски кода:

class NetworkOutput
{
public:
  /* Constructs a NetworkOutput object; this constructor should not block, but it
   * should start attempting to the given host/port in the background.
   *
   * In case the connection gets closed for some reason, the object should immediately
   * try reconnecting.
   */
  NetworkOutput( const std::string &hostName, unsigned short port );

  /* Tells whether there is a remote client connected to this NetworkOutput object.
   * Clients can use this function to determine whether they need to both serializing
   * any data at all before calling the write() function below.
   */
  bool isConnected() const;

  /* Write data to the remote client, if any. In case this object is not connected
   * yet, the function should return immediately. Otherwise it should block until
   * all data has been written.
   *
   * This function must be thread-safe.
   */
  void write( const std::vector<char> &data );
};

Прямо сейчас у меня это реализовано с использованием неблокирующих сокетов. В конструкторе NetworkOutput я создаю сокет TCP, а также внутреннее вспомогательное окно. Затем я выполняю WSAAsyncSelect вызов на сокете. Это делает сокет неблокирующим, и это заставит сообщение волшебного окна (которое я зарегистрировал самостоятельно) отправлять во внутреннее вспомогательное окно в случае, если какое-либо интересное событие (такое как «соединение установлено» или «соединение закрыто») происходит в сокете , Наконец, я запускаю попытку подключения, используя WSAConnect . Это немедленно возвращается, и оконная процедура моего внутреннего вспомогательного окна получит уведомление, как только соединение установится успешно. Если соединение закрыто (потому что удаленный клиент ушел), будет вызвана процедура сообщения, и я попытаюсь восстановить соединение.

Эта система позволяет мне подключать и отключать удаленного клиента по желанию. Это работает довольно хорошо, но, к сожалению, это требует, чтобы у меня был запущен цикл сообщений. Без цикла сообщений уведомления, отправленные вызовом WSAAsyncSelect , по-видимому, не приходят в мое окно помощника.

Есть ли способ реализовать класс, как описано выше, не требуя цикла сообщений? Я возился с использованием блокирующих сокетов в потоке помощников, но пока не мог придумать ничего разумного. Я также подумал об использовании сокета UDP, чтобы мне вообще не нужно было подключаться, но я хотел бы знать, прослушивает ли удаленный клиент, чтобы в случае наличия нет удаленного клиента клиентам класса NetworkOutput не нужно выполнять сериализацию сложных объектов, прежде чем они смогут вызвать write().

Ответы [ 2 ]

4 голосов
/ 21 июля 2010

Вы можете использовать WSAEventSelect вместо WSAASyncSelect, который принимает идентификатор WSAEVENT вместо идентификатора сообщения, а затем использовать WSAWaitForMultipleEvents , чтобы дождаться, пока событие не будет сообщено.

Вместо WSAEVENT вы также можете использовать обычные события Win32, созданные с помощью CreateEvent, и обычные функции синхронизации , такие как WaitForMultipleObjects.

0 голосов
/ 21 июля 2010

Вы ищете функцию выбора:

http://support.sas.com/documentation/onlinedoc/sasc/doc750/html/lr2/select.htm

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

Когда ваш поток просыпается, обычно лучше, чтобы другой поток выполнял эту работу;что обычно происходит, когда вы создаете рабочий объект для каждого порта, у которого есть данные, ожидающие чтения, и добавляете их в очередь, где набор рабочих потоков начинает обрабатывать ввод.Как только это будет сделано, вы снова вызываете select (), чтобы дождаться большего ввода.

Примечание: вам не нужно этого делать, это можно сделать в одном потоке.

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