1. C++ / Говнокод #20825

    +322

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    void worker(int id)
    {
        while (true) {
            std::unique_lock<std::mutex> lock(connPoolMutex);
            connPoolCond.wait(lock, [] { return !connectionsPool.empty(); });
    
            ClientConnection conn = connectionsPool.front();
            connectionsPool.pop();
            lock.unlock();
    
            TCPSocket sock(conn);
            sock.setReadTimeout(READ_TIMEOUT);
    
            char buffer[MAX_PACKET_LEN] = {};
            int sz = sock.recv(buffer, sizeof(buffer));
    
            // [...]
        }
    }
    
    // [...]
    
    int main()
    {
        // [...]
        TCPSocket server("0.0.0.0:1234");
        server.bind();
        server.listen(1000);
    
        while (true) {
            ClientConnection conn = server.accept();
    
            std::unique_lock<std::mutex> l(connPoolMutex);
            connectionsPool.push(conn);
    
            connPoolCond.notify_one();
        }
    }

    Современный, многопоточный и масштабируемый сервер на C++!

    TCPSocket - очень тонкая обёртка над голым сокетом.

    Запостил: gost, 17 Августа 2016

    Комментарии (86) RSS

    • Какой багор (хоть он вас всех и заебал) )))
      Ответить
    • > "0.0.0.0:"
      > захардкожено
      -2
      Ответить
      • Что не так? Слушать все ипы же.
        Ответить
        • Именно это.
          Ответить
          • А порт захаржкоженый тебя не смущает?
            Ответить
            • порт захардкоженый не так пробематично - если кастомыры проинформированы. и часто это тоже фича, если сервис публичный.

              а вот слушание на 0.0.0.0 часто попадает в продакшн, и вот тот самопальный тестовый сокет сервер резко становится доступным почти всем пользователям интернета. (если я не ошибаюсь это был один из элементов с которых SCADA security фиаско и началось. Сименс это относительно быстро пофиксил, но было уже поздно потому что хацкеры уже начали глубже копать. и просто убирание INADDR_ANY, после того как осознали потенциал, что покойнику припарка.)
              Ответить
              • >>доступным почти всем пользователям ин

                man iptables
                Ответить
                • ага. по воробьям из пушки.

                  вместо что бы тупой баг пофиксить - давайте фаервол сделаем обязательным.
                  Ответить
                  • лол, а бывают публичные сервера без файрволов?

                    но на самом деле я согласен: серверов без настрйоки IP адреса не бывает в природе
                    это наколенная поделка
                    Ответить
                    • > лол, а бывают публичные сервера без файрволов?

                      сомневаюсь.

                      на самом деле в интернет такое подключается редко, и это больше нужно как локальная секурити фича, что бы локальные пользователи не могли чего-то случайно/преднамерено сделать.
                      Ответить
                • Т.е. любому оператору, настраивающему сраный сервант для какой-нибудь маловажной фигни надо ещё давать права на редактирование iptables?
                  Ответить
                  • >>редактирование iptables?

                    неправильно так говорить

                    настраивают netfilter, а iptables это просто клиентская тулза
                    Ответить
                    • Всем пхуй, прыщемакака. Ты еще тут про ЖМУ/Пинус позаливай.
                      Ответить
                      • какой же ты дурак) не удивительно что у тебя работы нет, ты же ничего не умеешь
                        Ответить
                        • А ты кем работает? Питухом в помещении без окон?
                          Ответить
              • Там неизменяемые дефолтовые админские пароли были, помимо всего прочего.
                Ответить
                • это не такая большая проблема - если посторонние до вводу пароля и доступиться не могут.

                  насколько я помню там говно было на многих уровнях, включая то что Сименская обслуживающая софтина через фаервол и доступится не могла к девайсам - она требовала что бы девайсы были в той же сети. софтина стоит на ПЦ, ПЦ стоит в корпоративной сете == девайсы стоят в корпоративной сети. тушите свет.
                  Ответить
                  • Что такое в твоем понимании фаерволл и как софтина должна уметь с ним работать? o_O
                    Ответить
                    • >>Что такое фаерволл

                      ого, как всё запущено) Информатику в школе прогуливал?
                      Ответить
                      • >и как софтина должна уметь с ним работать?
                        В лужу бзданул. Теперь выпутывайся.
                        Ответить
                        • не тормози. пакеты которые проходят через фаервол не принимаются софтиной, и наоборот.
                          Ответить
                          • Я нихуя не понял.

                            >она требовала что бы девайсы были в той же сети.
                            В том же широковещательном домене?
                            Ответить
                            • > Я нихуя не понял.

                              Железо/софт слали/принимали слегка кривые пакеты, которые фаерволы дропают, потому что они кривые.

                              > В том же широковещательном домене?

                              Подробностей не видел.
                              Ответить
            • Смущает, но меньше. Порт -- обычное говно, а вот невозможность убрать слушание всех айпи -- уже пиздец. Тем более, мы не знаем что это за сервант и что он делает.
              Ответить
    • А в чем собственно говнокод?
      Ответить
      • Ну, если не учитывать убогие выньсокеты и общую кривизну решения, грабли тут зарыты в "connPoolCond.notify_one();", а именно:
        Notify one
        Unblocks one of the threads currently waiting for this condition.
        
        If no threads are waiting, the function does nothing.

        Таким образом, если все обрабатывающие потоки будут заняты обработкой запроса, новые соединения будут игнорироваться, причём не до тех пор, пока какой-то поток освободится, а ещё и до тех пор, пока не придут другие соединения.
        Ответить
        • Ерунду какую-то ты написал. Если все потоки заняты, то их и не надо будить. Когда какой-то поток закончит с обработкой запроса, он дойдет до connPoolCond.wait и сразу выйдет из него не засыпая, потому что там условие !connectionsPool.empty().
          В общем-то в этом коде написан классический producer-consumer. Можешь запомнить этот паттерн - пригодится.
          Ответить
          • > Когда какой-то поток закончит с обработкой запроса, он дойдет до connPoolCond.wait и сразу выйдет из него не засыпая, потому что там условие !connectionsPool.empty().
            Это не условие как таковое, это защита от ложных пробуждений, т.е. connPoolCond.wait(pred) эквивалентно
            while (!pred()) {
                wait(lock);
            }

            (http://en.cppreference.com/w/cpp/thread/condition_variable/wait).

            Поэтому, когда какой-то поток закончит с обработкой запроса, он просто уйдёт в спячку, поскольку очередной notify_one() был проигнорирован всеми.
            Ответить
            • ты тормозишь: наколько долго условие `!pred() == false`, он не вызовет `wait(lock)` и не будет спать вообще. буквально: он возьмет лок, проверит предикат, и сразу же бросит лок и вернется.

              PS я крестовой версией еще не пользовался, но сомневаюсь что они от pthread_cond_wait() (с которого это API сдиралось) далеко отклонились.
              Ответить
            • Не знаю, что еще сказать. Почитай внимательно код и свою же ссылку и подумай.
              Ответить
            • > Поэтому, когда какой-то поток закончит с обработкой запроса, он просто уйдёт в спячку, поскольку очередной notify_one() был проигнорирован всеми.

              Ты сам не понял тот код, который выложил, gost.
              Ответить
              • > Ты сам не понял тот код, который выложил, gost.
                Всё ещё хуже: я не понял код, который сам и написал...
                Ответить
        • И что не так с сокетами? У сокетов беркли точно такой же интерфейс. Буст асио тоже его повторяет.
          Ответить
        • > общую кривизну решения
          Вот еще до этого тезиса доебусь. На этом "кривом решении" основано дохуя серверов даже сейчас. Люди начали использовать тредпул с общей очередью запросов, когда догадались, что не обязательно спавнить тред/форкаться на каждый запрос. И это был основной подход в серверах до тех пор, пока не случилось c10k и не стали актуальны всякие еполлы.
          Ответить
          • да и даже сейчас, с10к не для всех сервисов и актуально.

            когда я последний раз (на перле!) делал тупой форк сервер, пока он еще был в зачаточной форме (буквально - accept + fork) я попробовал найти лимит соединений в секунду. но обломался. потому что как выяснилось все тестовые проги которые были под рукой (включая глючную версию нетката) были еще большее тормозными. на как минимум 1к в секунду на процессор/коре можно с легкостью полагаться. с глючными тестовыми прогами я доходил до 1.5к соединений в секунду - но судя по нагрузке системной, можно было бы лоад в 4-5 раз еще поднять. еще раз: тупой форк сервак на перле.
            Ответить
            • показать все, что скрытоКстати, с появлением го тредпулы могут вернуть свое. Просто потоки теперь зеленые. Я не знаю, как у них там идиоматично писать сервачки, но надеюсь, что они не спавнят по горутине на запрос.
              Ответить
              • по моему опыту зеленые потоки и сетевые сервера плохо мешаются.

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

                как по мне, все языки которые сокеты/этц предоставляют, должны просто включать в стандартную библиотеку полноценную реализацию сокет сервака. в случае С/С++, есть кучи мелких граблей - но в случае managed языков с GC, большинства этих проблем нет, и я не вижу причин почему нет. (хотя исторический пример жабы, где все через Ж шло, не вдохновляет.)
                Ответить
                • > но если тебе надо на диск или в базу ходить, тогда зеленые потоки могут (и по личному опыту: будут) подсасывать.

                  Чем это объясняется? Чем доступ к базе из зелёного потока принципиально отличается от доступа из обычного потока, при условии правильной реализации клиента?
                  Ответить
                  • реализация клиента не играет роли.

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

                    не все сисколлы легко перекрыть (например: специальная семантика с обработкой сигналов или restartable логика какая есть. или просто повторно вызывать нельзя.)

                    но самая главная проблема это то что они не могут знать сколько долго системный вызов будет блокировать - и будет ли он вообще блокировать.

                    в тривиальном случае, если зеленые потоки глупо будут исходить из того что системный вызов всегда блокирует, это будет приводить к серьёзной потере производительности из-за излишнего скедулинга. (в прошлом я такое видел как 100% CPU load на простом сетевом пинг-понге (echo server))

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

                    это как бы и была главная причина почему народ в 90х массивно пытался с зеленых потоков на нативные переходить :-)
                    Ответить
                    • Ничего не понял.

                      Чем принципиально отличается лапша из коллбэков и таймеров, которую ты напишешь на еполле/бусть.ио от аналогичной лапши, но генирируемой автоматически из простых прямолинейных исходников?
                      Ответить
                      • Я не уверен в чем именно твой вопрос.

                        > автоматически из простых прямолинейных исходников?

                        (а) Где?

                        (б) Никакая автоматика не способна догадаться на 100% что именно в исходниках происходит, и как именно оно на практике должно работать.

                        > лапша из коллбэков и таймеров, которую ты напишешь на еполле/бусть.ио

                        В этой лапше разраб явно описывает семантику того что будет происходить и как на это надо реагировать.

                        ... Я может пропустил там какие-то передовые технологии в этом вашем Go. Теоретически, на уровне языка можно больше интересного сделать в этом направлении. Практически, это не помогает, потому что чем больше ты отдаляешь код от нативного интерфейса, тем больше потенциальных граблей создаешь.
                        Ответить
                        • > В этой лапше разраб явно описывает семантику того что будет происходить и как на это надо реагировать.

                          Ну вот в бусте ты говоришь что-то вроде "прочитай из вот этого сокета N байт и вызови вот эту функцию, когда закончишь".
                          Чем это отличается от рантайма, который суспендит зелёный тред на "блокирующем" вызове, сам создаёт нужный обработчик асинхронного вызова и будит тред обратно, когда данные пришли? Где тут возможность использовать "дополнительную" семантику?
                          Ответить
                          • > Чем это отличается от рантайма, который суспендит зелёный тред на "блокирующем" вызове, сам создаёт нужный обработчик асинхронного вызова и будит тред обратно, когда данные пришли? Где тут возможность использовать "дополнительную" семантику?

                            Наконец таки. Возвращаемся к тому что я написал ранее: И как твой рантайм догадывается что вызов "блокирующий"?

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

                            В абсрактной программе, "socket.recv()" может значить кучу различных вещей. Включая что я ожидаю прочитать 0 байт, ака EOF. И даже это может блокировать. (Блин, даже "socket.close()" может блокировать.)
                            Ответить
                            • > И как твой рантайм догадывается что вызов "блокирующий"?

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

                              Есть определённый хардкорчик с FFI, но это всегда отдельная песня.
                              Ответить
                              • ... я ж вроде не с жабистом тут разговариваю, который думает что стандартная либа полна магии.

                                расскажи мне когда блокируют recv()/read(). (теже яйца на виндах, только с другой стороны: опросить можно все, но опросы не сильно торопливые (включая загадочные случаи где WaitSingleObject(0ms) медленнее чем ReadFile()).)
                                Ответить
                                • > стандартная либа полна магии

                                  Стандартная библиотека содержит ровно столько магии, сколько в неё положили.

                                  > расскажи мне когда блокируют recv()/read()

                                  Расскажи лучше, как прочитать из сокета с таймаутом в юниксе.
                                  Ответить
                                  • > Расскажи лучше, как прочитать из сокета с таймаутом в юниксе.

                                    libev/libevent/libuv или чего там нынче актуально - и они это делают за тебя. или даже тот же QSocketNotifier.

                                    на живом POSIX это делается - poll()'ом с POLLIN флагом. `pollfd pfd; pfd.fd = sockDes; pfd.events = POLLIN; rc = poll( &pfd, 1, timeOutMillis )`. если poll() вернул 0 - то таймаут. если 1 - то можно читать гарантировано без блокировки.

                                    те библиотеки (и тот же QSocketNotifier) это просто оболочки для главного цикла приложения вокруг poll() вызова, которые автоматизируют за тебя работу с массивом pollfd структур (или структур/этц для других API типа epoll).
                                    Ответить
                                    • > на живом POSIX это делается - poll()

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

                                        обычно или epoll или kqueue, нет?
                                        Ответить
                                      • > поэтому доводы типа "блокирующий быстрее, полл тормозит" как-то не очень убедительно смотрятся.

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

                                        с другой стороны случай на который я выше ссылался это как раз то что происходит в зеленых потоках: ОС знает о событии, но так как нет потока/задачи/процесса (ОС объекта) который заблокирован на ожидании этого события, ОС ничего с этим событием (кроме как поставить в очередь) сделать не может. событие проморгал - не заметишь его до тех пор пока активно еще раз ОС про события не спросишь.
                                        Ответить
                                        • показать все, что скрытоА по-моему это зависит от интенсивности потока.

                                          Иной раз блокировки происходят так редко и на такой маленький срок что люди вообще делают спин-лок. Правда это в ядре
                                          Ответить
                                          • > что люди вообще делают спин-лок. Правда это в ядре

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

                                            и в ядре эта тематика - ожидания событий - выглядит на изнанку. пришел пакет, подняли прерывание, достали данные из буфера, разбудили IP стэку что есть работа, IP стэк проснулся и переложил данные TCP стэку и его разбудили, TCP стэк проснулся, мапнул данные на соединение/сокет, сложил данные данные в буфер сокета, и когда кончил, (если есть epoll) положил/обновил сообщение в очереди событий, разбудил приложени(е,я) которые ждут.

                                            тут как раз с романом спор то и идет. он склоняется/предпочитает к простой последовательной обработке данных (почему хотят потоки). в юзерспэйс это работает - тут можно делать что угодно. в то время как в кернел спэйс все безжалостно event-driven, и альтернатив никаких нет.
                                            Ответить
                                            • показать все, что скрыто>> они не совместимы с ожиданием событий.
                                              внешних событий -- да, потому что с точки зрения ядра могут пройти многие годы, прежде чем придет пакет

                                              Я имел ввиду что способ зависит от частоты прихода данных
                                              Ответить
                  • вообщем: зеленые потоки не лучший вариант, если тебе нужно IO делать.

                    цель это не избегать нативных потоков. цель это их полноценно загружать работой.

                    лучший вариант это когда стандартная либа просто реализует какой гибкий IO мультиплексинг, который позволяет приложению работать с произвольным количеством нативных потоков. к этому как правило сводятся все такого типа приложения. например boost.asio.
                    Ответить
                    • Буст асио - это тащемто кал. За неимением лучшего едим, что дают.
                      Если бы кто запилил в буст нормальные файберы с примитивами синхронизации и ио, я бы пользовался.
                      Ответить
                      • ASIO мне тоже не нравится.

                        но если тебе нужно работать асинхронно с файлами - то альтернатив мало (я по крайней мере других не знаю).

                        с другой стороны, если речь идёт только про сокеты, то там все проще и есть тучи библиотек которые это делают (libev/libuv/libevent/Qt и все остальное с "main loop" в названии).
                        Ответить
                        • показать все, что скрытоА какие средства для работы с файлами есть в асио? Чето не вижу.
                          Ответить
                          • никаких. асия - днище. может в асинхронность только по сети, никаких файлов. линукс тоже днище. в асинхронную работу с файлами типа оверлейпед ио не может
                            Ответить
                            • показать все, что скрыто>>линукс
                              что не так с epoll?

                              а) ты типа про виндовфый FILE_FLAG_OVERLAPPED?
                              Ответить
                              • показать все, что скрытоВ линаксе файловые дескрипторы, которые файлы на диске, всегда готовы для чтения, т.е. еполл на них не блокируется. А уже когда делаешь read, он блокируется и тянет байты с диска, если надо.
                                Ответить
                                • > блокируется и тянет байты с диска
                                  Прошу заметить, синхронно! А это днище царя гороха. Упоротоши-красноглазики! Дма уже как в восмидесятых завезли.

                                  Вон 8ка-10ка свои асинхронные апи запилила на все, и это я не говорю о древнем аверлапед ио
                                  Ответить
                                  • показать все, что скрытопричем тут 80е?
                                    Конечно же контроллер диска читает в память блоками сам.
                                    Илиты думаешь там процесссор по 8 байтиков MOVает?
                                    Ответить
                                    • > Конечно же контроллер диска читает в память блоками сам.

                                      Ну и нахуй тогда тебе системный поток для этого, если ПДП все стянет с диска и просигнализирует прерыванием разбудить зеленый поток? Нахрен тебе синхронность для чтения файлов? А тупые посиксные линуксоиды, одержимые кампиляцией ведра не осилили, зато ходят довольные с красными глазами
                                      Ответить
                                • показать все, что скрытода, с диском не работает, я вкурсе
                                  Ответить
                            • что-то может - но это очень эзотерическая фича.

                              http://stackoverflow.com/questions/378515/whats-the-deal-with-boost-asio-and-file-i-o

                              AOI на юнихах используется на столько редко что шансов у него выйти из ниши очень мало.

                              хороший источних примеров должен быть libtorrent - http://blog.libtorrent.org/2012/10/asynchronous-disk-io/
                              Ответить
    • в чем именно говно? потому что я не уверен что ты сам знаешь в чем говно ;-)

      ЗЫ с вероятностью 90% говна тут нет. а последние 10% это такой редкий случай. с другой стороны, это совместимо с убогими выньсокетами.
      Ответить
    • Нет, ну вы посмотрите. Пришел и все минусанул долбоеб.
      Ответить
    • Зелёные потоки лучший вариант, если тебе надо ио. Гугли async io, overlaped io.
      Главное чтоб с зелеными потоками была интеграция у ио
      Ответить
      • >Зелёные потоки
        Потоки, не загрязняющие естественную среду?
        Ответить
        • показать все, что скрытоЭто еще один базворд для кооперативной многозадачности когда вместо потоков ОСи используются "потоки" внутри рантайма, и шедулер про них ничего не знает

          а ты пиши на дельфях дальше, не отвлекай людей от серьеных бесед
          Ответить
          • Ок. Спасибо за инфу.
            Ответить
            • Ты чтоли запилил капчераспознователь? 139 ботов то...
              Ответить
              • Нет. Вручную регал, когда-то давно.
                Ответить
                • >> Ты чтоли запилил капчераспознователь? 139 ботов то...
                  > Нет. Вручную регал, когда-то давно.
                  Лол. Для тебя пару дней назад (в соседнем треде рассказывал)- это когда-то давно? Ведь точно регал 139 ботов, чтоб разрушить империю говнокода, но потом стал п ушистым и хорошим

                  Ух щит, уже 152 бота...
                  Ответить
                  • >Ведь точно регал 139 ботов, чтоб разрушить империю говнокода, но потом стал п ушистым и хорошим

                    Не стал. Я так и остался ебанутым существом троллем и спамером.
                    На сайте vbbook, где я когда-то обитал, модератором был толстый, пухлый мальчик. Он очень запаривался со мной и однажды, запарившись со мной бороться, сказал с горечью : "Тролль так и останется троллем..."
                    Ответить

    Добавить комментарий