The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]




Версия для распечатки Пред. тема | След. тема
Новые ответы [ Отслеживать ]
send и select, !*! drone, 03-Апр-11, 02:24  [смотреть все]
Не могу никак разобраться.
Использую неблокирующие сокеты и select.
Программа принимает соединения, читает и пишет в клиентские сокеты, все работает.
Но всплыла проблема:
если попробовать отослать относительно большой объем данных (150 кб),
то отсылается примерно 33864 байт, а далее шлется маленькими кусочками
байт по 100 с интервалом по несколько секунд.
С чем может быть связано такое поведение?

На всякий случай:
вот синтаксис select:

select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
         struct timeval *timeout);

по событию на readfds для клиентского сокета я читаю данные, все нормально.
по событию на writefds для клиентского сокета я ничего не делаю.
Просто в произвольном месте программы вызываю send(sclient, buffer, 150*1024, 0).
Это правильно?

  • send и select, !*! C, 02:44 , 03-Апр-11 (1)
    TCP ?
    читать про алгоритм нагла?
  • send и select, !*! drone, 04:11 , 03-Апр-11 (3)
    Не знаю, на сколько правильно решение, сделал так:

    когда нужно заслать большой объем, пишу

    fcntl(sock, F_SETFL, O_DIRECT);
    int bytes_sent = send(sock, buffer_send, sz_buf_send, 0);
    fcntl(sock, F_SETFL, O_NONBLOCK);

    Вроде работает, по несколько мегабайт пуляет нараз.

    • send и select, !*! C, 18:00 , 03-Апр-11 (4)
      еще раз говорю
      man алогоритм нагла

      и отключается он через ioctl
      но прежде чем это делать
      изучите внимательно все статьи в интернете на эту тему

      • send и select, !*! drone, 20:13 , 06-Апр-11 (5)
        При чем здесь алгоритм нагла?
        Он как я понял склеивает мелкие сообщения.
        А тут большой объем сразу нормально не шлется.

        Прочитал, отключается он так:

        const int on = 1 ;
        setsockopt(sclient, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));


        Сделал так, ничего не изменилось.
        При выставлении флага:

        fcntl(sock, F_SETFL, O_DIRECT);

        Все отправляется быстро в локалке.
        Однако так я перевожу сокет в блокирующмй режим, основной цикл сервера приостанавливается.
        Есть какие0нибудь идеи еще?

        • send и select, !*! guest, 22:39 , 06-Апр-11 (6)
          > Есть какие0нибудь идеи еще?

          Вы для начала определитесь блокирующие у вас сокеты или нет.

          > fcntl(sock, F_SETFL, O_DIRECT);

          Первый раз такое вижу, в чем смысл O_DIRECT для сокета? где почитать?

          • send и select, !*! drone, 11:45 , 07-Апр-11 (7)
            FreeBSD:

            man fcntl
            ....................
                 O_NONBLOCK   Non-blocking I/O; if no data is available to a read(2) sys-
                              tem call, or if a write(2) operation would block, the read
                              or write call returns -1 with the error EAGAIN.

                 O_APPEND     Force each write to append at the end of file; corresponds
                              to the O_APPEND flag of open(2).

                 O_DIRECT     Minimize or eliminate the cache effects of reading and writ-
                              ing.  The system will attempt to avoid caching the data you
                              read or write.  If it cannot avoid caching the data, it will
                              minimize the impact the data has on the cache.  Use of this
                              flag can drastically reduce performance if not used with
                              care.

                 O_ASYNC      Enable the SIGIO signal to be sent to the process group when
                              I/O is possible, e.g., upon availability of data to be read.
            ....................


            У меня неблокирующие, один цикл, опрос сокетов по select. Стандартная схема.
            Но чтобы большой файл передался быстро (см первое сообщение), временно приходится переводить в блокирующий режим.

            O_NONBLOCK | O_DIRECT
            Не дает эффекта. Работает как неблокирующий по старому, и проблема остается.

            • send и select, !*! guest, 12:19 , 07-Апр-11 (8)
              > FreeBSD:

              Дык маны, я и сам читать умею... и в курсе как он работает для дискового I/O, но причем тут сокеты?

              > У меня неблокирующие, один цикл, опрос сокетов по select. Стандартная схема.

              Вы там пишите:
              >по событию на writefds для клиентского сокета я ничего не делаю.

              А зачем тогда селект насилуете?
              >Просто в произвольном месте программы вызываю send(sclient, buffer, 150*1024, 0).

              Кода то вы не показываете. Да и спрашиваете как-то мутно, можно только гадать.
              Видимо вас удивляет почему при O_NONBLOCK этот send() не отсылает все сразу?

              > Но чтобы большой файл передался быстро (см первое сообщение), временно приходится переводить
              > в блокирующий режим.

              O_NONBLOCK на скорость передачи сам по себе никак не влияет.

              • send и select, !*! drone, 13:24 , 07-Апр-11 (9)
                >> Видимо вас удивляет почему при O_NONBLOCK этот send() не отсылает все сразу?

                Не держите меня за дурака =)
                Я прекрасно понимаю, что оно не должно сразу.
                Повторю, схема стандартная, прога рабоает уже несколько лет.
                Просто больших данных за раз не передавали.
                При попытке отослать большие данные шлется не просто медленно, а присходит
                какая-то аномалия. После пересылки n-го количества килобайт начинает слать
                байт по 100 с интервалами по несколько секунд. И это в локалке.
                То есть точно не в скорости передачи дело.

                >>  O_NONBLOCK на скорость передачи сам по себе никак не влияет.

                Это понятно. Но оно устраняет вышеописанныу аномалию.

                >>>>по событию на writefds для клиентского сокета я ничего не делаю.
                >>А зачем тогда селект насилуете?

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

                Потом просто пишу с помощью send.

                Код не привожу, потому что большой. Резать кучу надо.
                Могу привести крд начала цикла:


                    while(true)
                    {
                        //////// POLLING SOCKETS ////////

                        FD_ZERO(&fdset_r);
                        FD_ZERO(&fdset_w);
                        FD_SET(sock, &fdset_r);
                        FD_SET(sock, &fdset_w);

                        int max_fd = sock;

                        for(map<int, client_info> ::iterator Iter = map_clients.begin(), End = map_clients.end(); Iter != End; ++Iter)
                        {
                            int sclient = Iter->first;
                            FD_SET(sclient, &fdset_r);
                            if(sclient > max_fd) max_fd = sclient;
                        }

                        timeval poll_timeout;
                        bzero(&poll_timeout, sizeof(timeval));
                        poll_timeout.tv_sec = poll_timeout_param;
                        int ret = select(max_fd + 1, &fdset_r, &fdset_w, NULL, &poll_timeout);

                        if(ret > 0)
                        {
                            if(FD_ISSET(sock, &fdset_r) || FD_ISSET(sock, &fdset_w))
                            {
                                int sclient = accept(sock, NULL, NULL);
                                if(sclient > 0)
                                {
                // собственно появилось новое соединение
                ........... куча кода .........
                // далее проверяем, есть ли байты на клиентских сокетах и читаем:

                            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))
                                {
                                    FD_CLR(sclient, &fdset_r);
                // данные есть, можно читать



                • send и select, !*! guest, 14:42 , 07-Апр-11 (10)
                  > Код не привожу, потому что большой. Резать кучу надо.

                  У вас проблемы с отправкой, но показываете вы accept и проверку к готовности recv()
                  с таким же успехом парсинг командной строки могли бы выложить...

                  > Могу привести крд начала цикла:

                  Это ни о чем (ну кроме того, что пихать listen() сокет в fdset_w бессмысленно)

                  • send и select, !*! drone, 15:38 , 07-Апр-11 (11)
                    >> Код не привожу, потому что большой. Резать кучу надо.
                    > У вас проблемы с отправкой, но показываете вы accept и проверку к
                    > готовности recv()
                    > с таким же успехом парсинг командной строки могли бы выложить...
                    >> Могу привести крд начала цикла:
                    > Это ни о чем (ну кроме того, что пихать listen() сокет в
                    > fdset_w бессмысленно)

                    Повторяю, я просто в коде делаю send(sclient, ...)
                    И все.
                    Вот и не выкладывал код, потому что не надо.
                    fdset_w добавил на всякий случай, ничего не делаю по нему.

                    • send и select, !*! guest, 15:58 , 07-Апр-11 (12)
                      При дефолтном SO_SNDBUF и O_NONBLOCK ваш send() за раз отправить 150кб не в состоянии, соответственно передача рвется на куски, показать нужную часть кода вы категорически отказываетесь и предлагаете угадать, а как же оно у вас там устроено... Давайте гадать:
                      Когда send() позовут следующий раз?
                      Варианта два (код то вы не показываете)
                      1 хз знает когда, т.е. в момент когда select() сработает на чтение либо вывалиться по таймауту.
                      2 у вас там жрущий проц цикл вокруг send() с проверкой на EAGAIN
                      • send и select, !*! drone, 23:24 , 07-Апр-11 (14)
                        > При дефолтном SO_SNDBUF и O_NONBLOCK ваш send() за раз отправить 150кб не
                        > в состоянии, соответственно передача рвется на куски, показать нужную часть кода
                        > вы категорически отказываетесь и предлагаете угадать, а как же оно у
                        > вас там устроено... Давайте гадать:
                        > Когда send() позовут следующий раз?
                        > Варианта два (код то вы не показываете)
                        > 1 хз знает когда, т.е. в момент когда select() сработает на чтение
                        > либо вывалиться по таймауту.
                        > 2 у вас там жрущий проц цикл вокруг send() с проверкой на
                        > EAGAIN

                        Да господи! Читайте первый пост. Процитирую:
                        >> Просто в произвольном месте программы вызываю send(sclient, buffer, 150*1024, 0).

                        Обычно это после того, как пришли данные на сокет и обработались. Шлется ответ.
                        Ничего там не жрется.
                        То что оно не может отослать 150 кб за раз это я тоже понимаю, админю и прогаю почти 10 лет. Вопрос не в том, почему частями, а в том, откуда взялись эти дикие задержки.

                        • send и select, !*! C, 23:46 , 07-Апр-11 (15)
                          > тоже понимаю, админю и прогаю почти 10 лет. Вопрос не в

                          тоесть если у вас такой большой опыт
                          вы сможете набросать код на несколько строк
                          клиент-сервера
                          который демонстрируют такую особенность?

                          скомпилять и запустить каждый у себя локально сможет! )


                        • send и select, !*! drone, 00:05 , 08-Апр-11 (16)
                          >> тоже понимаю, админю и прогаю почти 10 лет. Вопрос не в
                          > тоесть если у вас такой большой опыт
                          > вы сможете набросать код на несколько строк
                          > клиент-сервера
                          > который демонстрируют такую особенность?
                          > скомпилять и запустить каждый у себя локально сможет! )

                          Надеялся обойтись без этого, работы много.
                          Думал, может кто сталкивался.
                          Освобожусь, напишу обязательно.

                        • send и select, !*! guest, 10:00 , 08-Апр-11 (17)
                          > вы сможете набросать код на несколько строк
                          > клиент-сервера
                          > который демонстрируют такую особенность?

                          человек напрочь не понимает, что на не блокируемом сокете с дефолтными буферами его
                          send(,,150*1024,) никогда не вернет 150*1024 т.к. за 10лет не дочитал man send

                        • send и select, !*! drone, 04:14 , 09-Апр-11 (19)
                        • send и select, !*! guest, 10:23 , 08-Апр-11 (18)
                          > Да господи! Читайте первый пост. Процитирую:
                          >>> Просто в произвольном месте программы вызываю send(sclient, buffer, 150*1024, 0).

                          Да не нервничайте вы так.


                          > Обычно это после того, как пришли данные на сокет и обработались. Шлется
                          > ответ.
                          > Ничего там не жрется.
                          > То что оно не может отослать 150 кб за раз это я
                          > тоже понимаю, админю и прогаю почти 10 лет. Вопрос не в
                          > том, почему частями, а в том, откуда взялись эти дикие задержки.

                          Ответ был, повторю: следующий вызов send() происходит через случайное время, а именно либо по готовности одного из дескрипторов к чтению либо по истечению таймаута select()
                          Или вы все-таки не понимаете, что ваш send(sclient, buffer, 150*1024, 0) всегда возвращает <= размера SO_SNDBUF?

                        • send и select, !*! drone, 04:20 , 09-Апр-11 (20)
                          >[оверквотинг удален]
                          >> ответ.
                          >> Ничего там не жрется.
                          >> То что оно не может отослать 150 кб за раз это я
                          >> тоже понимаю, админю и прогаю почти 10 лет. Вопрос не в
                          >> том, почему частями, а в том, откуда взялись эти дикие задержки.
                          > Ответ был, повторю: следующий вызов send() происходит через случайное время, а именно
                          > либо по готовности одного из дескрипторов к чтению либо по истечению
                          > таймаута select()
                          > Или вы все-таки не понимаете, что ваш send(sclient, buffer, 150*1024, 0) всегда
                          > возвращает <= размера SO_SNDBUF?

                          ---------------------

        • send и select, !*! C, 21:47 , 07-Апр-11 (13)
          > fcntl(sock, F_SETFL, O_DIRECT);

          нет такой стратегии O_DIRECT для сокета
          она только для fs работает

          может по O_DIRECT срабатывает какая то дефолтовая стратегия
          которая сбрасывает принудительно буфферы
          но желания колупать код бсд что бы проверить, у меня нет


  • send и select, !*! drone, 05:11 , 09-Апр-11 (21)
    Всем спасибо за ответы, я разобрался.
    Проблема была в не правильном понимании работы неблокирующих сокетов.
    Я думал, что вызвав send, _все_ данные положатся во внутренний буфер и сами зашлются, а занчение, которое возвращает send - это количество данных, отправленное на данный момент.
    Для меня было новостью, что после этого оно ничего слать никуда не будет. А хвост моего буфера останется не отосланным.
    Я подохревал подвох, поэтому написал в своем первом сообщении:
    >> по событию на writefds для клиентского сокета я ничего не делаю.

    Теперь я понимаю, что в этом месте нужно досылать не отосланные данные.
    Видимо криво излагаю свои мысли, раз такая дискуссия разрослась.
    Посыпаю свою голову пеплом и еще раз всем спасибо =)

    • send и select, !*! guest, 08:38 , 09-Апр-11 (22)
      > Я подохревал подвох, поэтому написал в своем первом сообщении:
      >>> по событию на writefds для клиентского сокета я ничего не делаю.

      В вашем исходном посте смутило, да и сейчас смущает это:
      >то отсылается примерно 33864 байт, а далее шлется маленькими кусочками
      >байт по 100 с интервалом по несколько секунд.

      Совершенно не понятно откуда берутся кусочки по 100 байт если send() на клиента все-таки один. Если вы с этим разобрались может расскажите?

      • send и select, !*! drone, 13:04 , 09-Апр-11 (23)
        >> Я подохревал подвох, поэтому написал в своем первом сообщении:
        >>>> по событию на writefds для клиентского сокета я ничего не делаю.
        > В вашем исходном посте смутило, да и сейчас смущает это:
        >>то отсылается примерно 33864 байт, а далее шлется маленькими кусочками
        >>байт по 100 с интервалом по несколько секунд.
        > Совершенно не понятно откуда берутся кусочки по 100 байт если send() на
        > клиента все-таки один. Если вы с этим разобрались может расскажите?

        Там от клиента приходили сообщения с таким интервалом видимо.
        Тоже асинхронно, и сервер на них лтвечал, поэтому я предположил, что это те данные досылаются.
        Факт прихода этих данных я наблюдал на клиентской машине.
        Так как данных много, я о конца передачи не дожидался.
        Весь прием замораживался на клиенте, потому что передача осуществляется
        посредством некого протокола. Когда большие данные начали передаваться, первым пошел заголовок с CRC и общим размером пакета.
        В итоге часть паета пришла, далее стало дополняться данными от маленьких запорстов байт по 100. Как-то так.




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру