send() в НЕ-блокируемых сокетах

Тема в разделе "LANGS.C", создана пользователем AlannY, 9 янв 2009.

  1. AlannY

    AlannY New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2008
    Сообщения:
    41
    Раньше пользовался WinAPI, и оттуда функциями WSASend и WSAGetOverlappedResult. Теперь тоже самое нужно реализовать силами POSIX: socket, send и т.п.

    У меня сокет НЕ блокируемый, поэтому вызов send(), как я думаю, вернёт -1 и номер ошибки в <errno>, которым могут быть EAGAIN или EWOULDBLOCK (проверить пока не могу, так что извините). После этого, можно ждать, пока сокет будет доступен на запись (соответственно, если доступен на запись, то данные (прошлые) ушли и можно писать заного). Но! Мне нужно узнать, сколько реально байт данных ушло, т.к. send() может отправить не то кол-во данных, которое ему подсовываешь :-(

    Пока набросал такой код:

    Код (Text):
    1. switch (errno)
    2.   {
    3.     case EWOULDBLOCK:
    4.        while (1)
    5.          {
    6.             tv.tv_sec = timeout; /* мой таймаут */
    7.             tv.tv_usec = 0;
    8.  
    9.             /* ждать, пока сокет будет доступен на запись */
    10.             FD_ZERO (&writefds);
    11.             FD_SET (priv->socket, &writefds);
    12.             rc = select (priv->socket+1, NULL, &writefds, NULL, &tv);
    13.  
    14.              if (rc < 0)
    15.                 {
    16.                   /* ошибка в select() */
    17.                   return 1;
    18.                 }
    19.               else if (rc==0)
    20.                   {
    21.                      /* таймаут - продолжать ждать, но уже новым select() */
    22.                      continue;
    23.                   }
    24.                else if (rc > 0)
    25.                   {
    26.                      /* сокет доступен на запись, видимо, данные ушли */
    27.                      /* ЗДЕСЬ нужно определить, сколько байт реально ушло :-) */
    28.                      break;
    29.                   }
    30.              }
    31.  
    32.           break;
     
  2. perez

    perez Member

    Публикаций:
    0
    Регистрация:
    25 апр 2005
    Сообщения:
    502
    Адрес:
    Moscow city
    А зачем тебе знать, сколько байт? Если есть EAGAIN, значит, надо переслать заново. Если есть ошибка при отправке - не прошло. Если ни то ни другое - все байты ушли. Точнее скорее всего байты перенеслись в некий буфер, функция возвратила управление, а система в фоне выслала все данные.
    Если select сообщает, что можно писать, значит все ушло.

    На тот случай, если твоих данных слишком много, вот что говорит man:

    If the message is too long to
    pass atomically through the underlying protocol, the error EMSGSIZE is
    returned, and the message is not transmitted.

    P.S. Очень рекомендую задавать подобные вопросы на www.opennet.ru
     
  3. AlannY

    AlannY New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2008
    Сообщения:
    41
    Вот здесь: http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#sendall автор пишет, что данные могут уйти не все:

    Поэтому и спрашиваю о том, что делать если не все данные ушли :-(
     
  4. AlannY

    AlannY New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2008
    Сообщения:
    41
    Вот ещё с сайта мелкомягких: http://msdn.microsoft.com/en-us/library/ms740149(VS.85).aspx

     
  5. AlannY

    AlannY New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2008
    Сообщения:
    41
    И там задам :)
     
  6. AlannY

    AlannY New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2008
    Сообщения:
    41
    Искал по инэту, в общем, пришёл к выводу: send() всегда блокирует, даже если сокет НЕ блокируемый. Результат EAGAIN означает, что нужно повторить попытку отправки, а не, как я думал, то, что нужно подождать отправки данных, как это делается с WSASend. Или я чего-то фундаментального не понимаю, или WSASend и send работают кардинально по-разному: одна адаптирована для не блокируемых операций, другая не адаптирована.

    Всем спасибо.
     
  7. perez

    perez Member

    Публикаций:
    0
    Регистрация:
    25 апр 2005
    Сообщения:
    502
    Адрес:
    Moscow city
    У тебя кстати в примере и есть блокирующий код =) Даже если send не блокируется, твоя прога блокируется, так как ждет в цикле.

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

    При событии на главном сокете:
    if(FD_ISSET(sock, &fdset_r) || FD_ISSET(sock, &fdset_w))
    означает, что новое соединение. Обрабатываем его, добавляем новый сокет в массив.

    При событии на дочерних:
    for(map<int, client_info> ::iterator Iter = map_clients.begin(), End = map_clients.end(); Iter != End; Iter++){
    int sclient = Iter->first;
    if(FD_ISSET(sclient, &fdset_r)){....}
    ....
    }
    Обрабатывается ввод - вывод с дочерними сокетами в блокирующем режиме. Ничего обычно не виснет, так как сокет уже готов на ввод-вывод.
    (Тока вместо массива использовался std::map в этом коде)
     
  8. phprus

    phprus New Member

    Публикаций:
    0
    Регистрация:
    18 авг 2006
    Сообщения:
    16
    AlannY
    Почему? Если send обнаружит, что может заблокировать неблокируйщий сокет, то он возвращает -1, а в errno будет записано EAGAIN или EWOULDBLOCK. В случае если send что-то отправил, то количество реально отправленных байт он вернет. При том реально может быть отправлено меньше байт, чем было передано в качестве параметров.
    http://www.opennet.ru/man.shtml?topic=send&category=2&russian=0
     
  9. AlannY

    AlannY New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2008
    Сообщения:
    41
    perez
    Будете смеяться, но у меня клиент :)
     
  10. AlannY

    AlannY New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2008
    Сообщения:
    41
    phprus
    Ясно :)