Skip to content

Instantly share code, notes, and snippets.

@codedokode
Created March 7, 2016 01:03
Show Gist options
  • Save codedokode/21a432321fe7bc435dab to your computer and use it in GitHub Desktop.
Save codedokode/21a432321fe7bc435dab to your computer and use it in GitHub Desktop.
Сокеты, асинхронное программирование, reactphp

Тут надо обратить внимание на такие моменты:

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

Асинхронный вызов чтения файла в программе выглядит примерно так:

readFileAsync(filename, onRead); 

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

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

Ну например, рассмотрим работу демона. Вначале он запускается и просит ОС начать слушать порт 1234. Во внутренний массив кладется указатель на функцию, допустим, onConnect. Так как делать больше нечего, программа обращается к ОС за уведомлениями. Уведомлений нет, программа блокируется в цикле ожидания событий. Он выглядит кстати примерно так:

инициализацияДемона();

while (true) {
уведомление = ожидатьУведомленияОтОС();
обработчик = найтиОБработчикДляУведомления(уведомление);
вызвать(обработчик);
}

Это называется event loop (цикл обработки событий). Функция ожидатьУведомления - единственная блокирующая функция тут. Если событий нет, именно в ней ждет программа. Если же происходит много событий, то event loop постоянно крутится, их обрабатывая, и не простаивает.

От клиента приходит запрос на соединение. ОС добавляет уведомление и будит нашу программу, передавая ей уведомление. Она вызывает onConnect. Та, допустим, решает принять запрос от клиента. Она вызывает асинхронную функцию чтения данных из соединения, и добавляет обработчик onRead, который будет вызван после получения порции данных. И на этом функция onConnect завершается, мы снова возвращаемся в event loop и блокируемся на ожидании событий.

Допустим через секунду от клиента приходит порция данных. Вызывается onRead, копирует эти данные в буфер и анализирует. Допустим, там только начало запроса. Тогда она снова запускает процесс чтения данных и возвращается в event loop. Приходит вторая часть запроса, вызывается onRead. Допустим в этот раз запрос собран полностью. OnRead обрабатывает его, формирует ответ и вызывает функцию отправки данных клиенту, а по завершении просит вызвать onEnd.

Когда данные будут отправлены клиенту, ОС пошлет уведомление, event loop его примет и вызвовет onEnd, а та например вызовет функцию закрытия соединения. Все, запрос обработан.

Это я привел пример обработки 1 запроса, но в случае нескольких параллельных соединений все конечно чуть сложнее.

Внешней "функцией" (или как это назвать?)

Наверно не внешней, а основной. Основа приложения - это event loop, который принимает от ОС уведомления о событиях и вызывает обработчики внутри программы (обработчик задается в момент начала выполнения какой-то операции).

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

Не умрет, а просто функция завершится и управление вернется назад в event loop.

у меня получилось что пока от ос не поступит хоть какого-то сигнала, скрипт будет простаивать

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

Ладно, попробую почитать исходники чтобы выяснить алгоритм. Хотя боюсь это непосильная задача, я быстро запутаюсь в непонятных вызовах.

Предлагаю найти event loop и можем обсудить этот код подробнее.

Ну если мое предположение о том, что какая-то функция выполняется в цикле верно, то соединение дохнет вместе с ней.

Нет, с чего бы?

Наверное нужно постоянное соединение, хотя я не до конца понимаю, как они работают.

нет

Работают они так. Обычно PHP скрипт при завершении освобождает память, уничтожает все переменные, закрывает все соединения, чтобы следующий скрипт запустился с чистого листа. Постоянные соединения - это соединения которые не закрываются при завершении скрипта, а остаются «висеть». Первый скрипт открывает соединение, а второй, запущенный после него, переиспользует его.

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

Подумай сам какие преимущества и недостатки у такого подхода перед обычными соединениями.

Каких еще дочерних процессов? У нас ведь он вроде только один в reactphp?

При чем тут reactphp? Эта фича была там задолго до его появления. Более того, зачем эта фича нужна reactphp, если он работает не завершаясь? Она для обычных скриптов, которые завершаются после отдачи страницы клиенту.

Да обыкновенный чат, господи, неужели даже там можно так все усложнить?

Я не думаю что это сложно. Если действительно хочется разобраться, разберешься.

Кстати, если ты найдешь event loop, ты можешь сделать в нем отладочный вывод, чтобы например процесс в консоль писал все пришедшие от ОС уведомления.

а вот тема асинхронности конкретно в php не раскрыта.

Давай изучать код.

Я смутно понимаю, что такое вообще сокет,

Сокет = идентификатор соединения. Есть 2 вида сокетов:

  • слушающий (серверный) сокет - определяется для TCP только IP адресом интерфейса и портом
  • сокет соединения - определяется для TCP 2 IP адресами и 2 портами. Например 1.2.3.4:8080 <-> 2.3.4.5:9090

Программа-сервер открывает слушающий сокет и ждет соединений. Каждый раз, когда новый клиент подсоединяется к нужному порту, ОС устанавливает TCP-соединение и выдает программе-серверу сокет нового соединения.

Обрати внимание, число слушающих сокетов ограничено числом портов на одном интерфейсе (65535). Число возможных сокетов соединений намного больше так как определяется комбинацией 4 параметров.

тем более не знаю чем отличается "интернет-сокет" от "доменного сокета Unix". "Интернет-сокет" это наверное они так перевели WebSocket, а "доменный" это обычный юниксовый.

Интернет-сокет (AF_INET) это для соединений, работающих по протоколу TCP. unix domain (AF_UNIX) это специальный вид соединений, работающих только внутри одного компьютера. Это скорее оптимизация, чтобы не задействовать всю мощь протокола TCP когда нам всего лишь надо связать 2 локальных программы. unix domain не исплоьзует IP адреса и порты. Вместо этого слушатель (сервер) создает на диске специальный файл-сокет, например /tmp/mysql-socket, а клиент подсоединяется, указывая имя файла-сокета в качестве параметра.

Например, mysql открывает 2 слушающих сокета: для TCP соединений и для unix-domain соединений, с адресом типа /var/lib/mysql/socket. Я впрочем не уверен что в наше время это дает какой-то ощутимый выигрыш, мне кажется сейчас и TCP быстро работает.

http://www.vr-online.ru/content/imenovannye-kanaly-i-sokety-unix-prosto-o-slozhnom-1902

Вот тебе в помощь паста про TCP (2 штуки):

Функции для работы с сокетами (это просто перенесенные на PHP сокеты Беркли, которые являются чем-то вроде негласного стандарта и поддерживаются во многих языках): http://php.net/manual/ru/book.sockets.php

"Интернет-сокет" это наверное они так перевели WebSocket

Нет, неверно. Websocket это протокол работающий поверх TCP-соедиения. Это не новый вид сокетов, а обычный прикладной протокол.

Если я правильно понял, то в юниксах любой поток данных считается "файлом",

Да, есть такое. Файловый дескриптор (идентификатор потока ввода-вывода) может указывать на реальный файл, TCP-сокет, unix-domain-сокет, на pipe (трубу для передачи данных между процессами: ls | head), на поток ввода-вывода от устройства (мыши или клавиатуры).

Это позволяет использовать системные вызовы read/write (в PHP аналоги - fread/fwrite) для чтения/записи данных в любой вид потока. Кстати в PHP fread/fwrite тоже работают и с файлами, и с сокетами.

А вот поверх этого сделаны еще потоки (streams), о них ниже.

В виндовсе файлами являются только куски данных на жестком диске, или нет?

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

Так вот, функцией вроде fopen мы открываем соединение с реальным "куском данных хранящемся на диске",

Да

тогда как сокет это разновидность юниксового "файла" фактически просто идентификатор какого-то потока.

да, сокет это что-то вроде идентификатора соединения.

То есть сокет это что-то вроде подготовленного соединения, точка входа, куда можно присоединиться

Да, хорошее сравнение. Сокет может быть в разных состояниях: только что создан, слушает порт, устанавливает соединение, соединение завершено/разорвано.

Так вот, stream_socket_server как раз и создает сокет

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

Также обрати внимание на на префикс stream - это функции для работы с потоками, в PHP попытались унифицировать и абстрагировать реализацию потоков на разных ОС во что-то единое: http://php.net/manual/ru/book.stream.php

Также, заметь в PHP есть еще stream wrappers, которые позволяют работать с данными других источников единообразно: http://php.net/manual/ru/wrappers.php

Этого в линуксе в чистом виде нет (например читать содержимое сжатого файла или поток данных через протокол HTTP), это уже надстройка со стороны PHP.

Есть также более низкоуровневые функции для работы именно с сокетами: http://php.net/manual/ru/book.sockets.php

Фактически вызов stream_socket_server скрывает в себе socket_create, socket_bind, socket_listen.

Сокет задается тремя параметрами - протоколом://айпи-адресом:портом, например "tcp://127.0.0.1:8080".

Да, в данном случае это такой синтаксис, придуманный в PHP. Вот тут описаны разные виды префиксов: http://php.net/manual/ru/transports.php

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

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

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

Стоит еще на это обратить внимание. Почему несколько реализаций event loop? дело в том, что при асинхронной работе можно получать уведомления от ОС несколькими способами. Системный вызов select (stream_select в PHP) - самый древний, работает везде, но не масштабируется на большое число соединений. Дело в том что в нем ты передаешь список сокетов, для которых хочешь получить события, и когда сокетов много (сотни, тысячи), составление и обход этого списка занимают значительное время. То есть он неэффективен если у нас много сокетов.

Ну если ты почитаешь мануал по функции ты наверно увидишь сам это.

Потому были придуманы более новые механизмы передачи уведомлений от ОС процессу. Список (скопировал не читая) технологий: /dev/poll, kqueue(2), event ports, POSIX select(2), Windows select(), poll(2), and epoll(4). (POSIX select - это как раз старый механизм). Там механизм такой, что твоя программа открывает что-то вроде потока и ядро через этот поток передает информацию о новых событиях на сокетах и файлах (пришли данные/сокет готов к передаче следующей порции данных/ошибка). То есть тут нет затрат на постоянное составление списка сокетов и его обход.

Описаны тут подробно на английском, сложно: http://www.kegel.com/c10k.html

Для работы с этими механизмами придуманы библиотеки на Си: http://libuv.org/ http://software.schmorp.de/pkg/libev.html http://libevent.org/

Часть из них доступна в PHP через расширения. Потому Factory выбирает наиболее эффективный из доступных механизмов, а если не один не доступен, откатывается к самому медленному, но везде работающему select.

Метод addReadStream берет сокет (в нем тип resource или int? походу простой int идентификатор), кладет его в приватное свойство-список readStreams, а колбек-обработчик кладет в приватное свойство readListeners.

Скорее всего resource. Идентификатор потока в PHP это resource.

Это как раз есть та штука, которая запоминает соответствие между потоком и функцией-обработчиком событий на этом потоке.

Ничего, щас почитаю про эти sql-классы, должно проясниться. Но уже сейчас понятно, что у нас в объекте $loop внутри свойства timers хранятся какие-то периодические события (вывод потребления памяти).

SplObjectStorage это просто объектно-ориентированный и оптимизированный для хранения объектов массив (умеет искать объект в себе за O(1) и делать его ключом в отличие от обычного массива).

При каких условиях наступит running=false пока не понятно, дальше видно будет.

Наверно там есть функция типа finishEventLoop()

Итак, внутри цикла какие-то "тики": Как это перевести?

Тиканье часов. tick = 1 шаг цикла event loop. Туда можно положить функцию которая будет вызываться на следующем шаге event loop.

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

queue->dequeue() это метод стандартного объекта SplQueue или он возвращает какую-то нашу колбек-функцию?

Он снимает объект из массива и возвращает его. Вроде array_pop.

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

Да. Он их удаляет чтобы они выполнились только один раз.

Таким образом в $this->futureTickQueue->tick() скорее всего мы добавляем в очередь обработчик вида "спросить у операционной системы, не докачался ли файлик? если скачался, сделать с ним то что нужно, если нет снова добавить в очередь переспросить потом у ОС статус этой операции".

Вряд ли. Это неэффективно, постоянно проверять статус операции (polling = опрос). Это для чего-то другого используется. Я погуглил и нашел:

Each time you call nextTick or futureTick the passed callback is pushed into a SplQueue. Once the event-loop is done handling the current outstanding I/O it will start processing the tick queues.

Когда вы вызываете nextTick/futureTick, ваш коллбек добавляется в очередь. Когда event loop обработает все накопившиеся уведомления (о завершении асинх. операций) от ОС, он вызовет эти обработчики.

Ticks come in two flavours: next and future. Next will continue running until all callbacks on the SplQueue are executed. While future will only execute the callbacks that are on the SplQueue when it begins to process them.

Очередь nextTick вызовет все хранящиеся в ней обработчики, в том числе те которые будут добавлены в процессе вызова предыдущих. futureTick вызовет только те обработчики которые там были на момент начала операции (остальные вызовутся но в следующий раз).

То есть если ты в обработчике nextTick каждый раз ставишь новый обработчик то event loop будет выполнять только nextTick, а таймеры и ввод-вывод не будут обрабатываться.

То есть ты разбиваешь тяжелую задачу например на 100 маленьких действий и делаешь их через futureTick, как описано тут: https://github.com/reactphp/react/blob/master/examples/next-tick.php

После этих трех тиков в цикле проверяется длинное условие: 11. if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty())

Это условие проверяет есть ли у нас какие-то обработчики, которые не связаны с вводом-выводом и которые должны быть вызваны в будущем. Если их нет, мы можем ждать уведомлений от ОС вечно. Если они есть, мы не можем ждать вечно, мы например подождем секунду (и обработаем пришедшие уведомления если они есть), а потом будем вызывать таймеры/nextTick/futureTick.

  1. $this->waitForStreamActivity(0);
  2. $this->streamSelect(), которая является оберткой над

Это и есть функция которая обращается к ОС за уведомлениями о готовности сокетов/дескрипторов к примему/передаче данных или ошибках. То есть за уведомлениями о результатах асинхронных операций.

Заметь что в ней есть таймаут. Мы можем ждать событий вечно, а можем не более определенного времени. Это нужно для реализации таймеров.

Внимательно изучи мануал по stream_select. Это тут ключевая функция, именно она внутри делает системный вызов select к ядру ОС.

Ну то есть короче если хоть один поток из $this->readStreams или $this->writeStreams (смотри пункт 4) проявляет активность, то ... А фиг его знает, я устал и уже ничего не понимаю. Зачем нужна эта функция?

Она обращается за уведомлениями о завершившихся операциях к ОС. Как только на сокете/сокетах происходит событие (пришли данные по сети/сокет готов передать следующий блок данных/ошибка), stream_select вернется и сообщит нам идентификаторы потоков где произошли события.

Тут метод run должен по идее завершиться. Означает ли это конец всей работы скрипта? У меня спеклись мозги, уже не в состоянии что-то думать.

Метод run завершится только когда будет выставлен running = false. По моему он выставляется функцией $loop->stop().

603447

Зависит от сервера. Из-за оверхеда на заголовки пакетов полезных данных будет передаваться не 100 мбит, а может 85-90.

Где-то выше ты мне предлагал отлавливать сигналы системы и выводить их в консоль, пока не понимаю, где именно к этим данным можно получить доступ. Вроде бы через функцию stream_select.

Надо разобраться как она работает сначала. КАк возвращается информация о событиях на сокетах? Мануал нам никак не помог. Что же делать?

  1. глянем аналогичную функцию socket_select, тем более что на нее стоит ссылка: http://php.net/manual/ru/function.socket-select.php

Внимание On exit, the arrays are modified to indicate which socket resource actually changed status.

Похоже что функция оставляет в переданных массивах только те идентификаторы сокетов, на которых произошли события (то есть сокет завершил предыдущие операции и готов к примему/передаче данных) указанного типа (read/write/except).

Кстати сразу же вопрос - read/write - это понятно, а что такое except? Что за вид событий?

То есть работают асинхронные (неблокирующие) сокеты примерно так:

  • создаем сокет, ставим его в неблокирующий режим, устанавливаем соединение

  • пробуем записать в сокет мегабайт данных через fwrite

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

  • если сейчас попробовать снова вызвать fwrite, она скорее всего вернет 0, то есть буфер заполнен и больше данных она принять не готова.

  • вызываем stream_select, передавая ей наш сокет. Она блокируется до наступления одного из событий:

  • закончился таймаут, если он не бесконечный

  • наш процесс получил сигнал и ожидание было прервано (что такое сигнал?). В этом случае надо просто вызвать stream_select снова.

  • сокет отправил все данные из буфера и снова готов к приему данных

(как различить эти случаи?)

В третьем случае мы берем непереданные данные, снова вызываем fwrite, она берет сколько может этих данных и кладет во внутренний буфер, и так далее.

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

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

В общем, асинхронные fread/fwrite кладут/берут данные из буфера в ядре и не блокируют выполнение. Синхронные же операции делают то же самое, но блокируют выполнение если сокет не готов. То есть синхронный fwrite блокируется до тех пор пока буфер не станет свободным и не удастся положить в него хоть 1 байт. А синхронный fread блокируется до прихода хотя бы 1 пакета с данными.

Какой объем этого буфера в ядре? Я не знаю, зависит от настроек ОС наверно. Твой код должен работать с любым, даже если это 1 байт.

Как я сказал выше, было бы полезно написать программку для асинхронной работы с файлами/сокетами. И полезно было бы там натыкать echo, чтобы она писала все операции которые пытается делать, столько-то байт записано, столько-то прочитано.

Можно сделать программу которая например слушает сокет и записывает пришедшие данные в файл. Можно сделать TCP-прокси: программа слушает порт, а при попытке соединения она сама соединеяется с удаленным сервером. например с какой-нибудь википедией, и передает все пришедшие данные туда. И конечно записывает в файл все пришедшие и ушешие данные. И еще в консоли пишет все, что происходит. Как тебе идея?

В случае если все верно сделано, ты сможешь в браузере набрать http://localhost:12345/ и увидеть википедию.

но откуда я возьму ip яндекса или википедии?

Нужно воспользоваться сервисом DNS, отправить запрос на DNS сервер. Почитай про функции

  • getprotobyname
  • gethostbyname
  • gethostbynamel
  • getservbyname
  • dns_get_record

Обрати внимание что они все блокирующие. Обрати внимание что процесс резолва имени в IP довольно сложен. на линуксах:

  • (нововведение) читается файл /etc/nsswitch.conf ( https://en.wikipedia.org/wiki/Name_Service_Switch#nsswitch.conf ) хранящий правила разрешения имен пользователей, настройки DNS и тд. Это появилось не так давно, сколько-то лет назад.

http://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html

  • читается /etc/hosts (имя может быть переопределено) и ищется там
  • читается файл /etc/resolv.conf, хранящий праивла и порядок разрешения имен. Там вроде бы хранятся IP 2 серверов DNS. В случае использования DHCP (автоматическое получение IP адреса) информация о DNS серверах могут передаваться DHCP-сервером.
  • делается DNS запрос на DNS сервер за записью типа A (IP адрес) для данного домена.
  • выбирается один из возвращенных Ip адресов

Это если мы используем только IPv4 и не хотим проверить, есть ли у сервера IPv6 адрес.

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

dig example.com 
host -v example.com

(советую сделать если есть линукс, если нет то можно например тут http://manytools.org/network/query-dns-records-online/ )

Как ты видишь процесс довольно сложный, подразумевает чтение файлов, выполнение запросов. Описанные выше функции по этой причине блокирующие и ты не должен использовать их внутри event loop.

А что делать внутри event loop? Искать, может кто-то добрый написал асинхронный DNS клиент для PHP. Я нагуглил это, спасибо добрым людям: https://github.com/reactphp/socket-client#dns-resolution

Но тебе для тестовой программки я думаю можно использовать блокирующее разрешение имени хоста.

В общем, сделай DNS запрос например для яндекса или гугла и разбери пришедший ответ.

604539

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

Хотя нет. Я думаю дело в том что на твоем компьютере нет интерфейса с адресом 93.158.134.3

Ты можешь увидеть список сетевых интерфейсов так:

(win) ipconfig /all
(линукс) ifconfig 

Иногда надо писать /sbin/ifconfig или /usr/sbin/ifconfig иногда нужно дописать sudo.

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

Ты либо указываешь IP адрес конкретного интерфейса либо указываешь '0.0.0.0' что значит «слушать на всех интерфейсах». IP адрес здесь всего лишь способ идентифицировать конкретный интерфейс. Ведь у компьютера может быть много сетевых карт, включенных в разные сети.

На практике обычно выбор сводится к 0.0.0.0 vs 127.0.0.1

для сервера нужно использовать только ip своей локальной сети

Только 0.0.0.0 либо IP одного из сетевых интерфейсов. Просмотреть их можно через ipconfig/ifconfig, команды есть где-то выше.

loopback

Его лучше никак не переводить. loopback тут скорее всего какой-то термин из электроники, когда выход устройства подключали кабелем ко входу. Судя по https://en.wikipedia.org/wiki/Loopback

loopback interface это виртуальный сетевой интерфейс с адресом 127.x.x.x который не отправляет сетевые пакеты в сеть. Он используется для установки локальных TCP соединений внутри компьютера.

Говорит, что это число не может быть больше некой системной настройки SOMAXCONN, хз где его найти и посмотреть/увеличить.

Это размер очереди куда система будет складывать информацию о пришедших запросах на установление соединения. При переполнении очереди дальнейшие запросы будут получать отказ. Ты вынимаешь запрос из этой очереди вызовом socket_accept.

Настраивается в разных ОС по разному.

Ты знаешь как протестировать свой hello world? В windows можно подсоединиться через telnet IP-сервера порт, в linux установи netcat и делай nc -vv ip port

Если надо после соединения что-то отправить, используй echo или cat:

echo "Yes" | nc ....
cat /dev/random | nc ....

Кстати, nc умеет еще и слушать порт, по моему так: nc -vv -l -p 12345. Принятые данные будут отображаться в консоли. полезная утилита для тестирования. Если там изучить мануал, на ней можно даже примитивный сервер сделать.

Также, в линуксе есть интересная программа socat, которая умеет устанавливать двунаправленные каналы между разными файлами и устройствами. можешь как-нибудь почитать мануал, вдруг пригодится.

Также, есть так называемый «суперсервер» xinetd который позволяет сделать сервер из любого скрипта или команды, получающего данные на вход и выдающих ответ на выход.

Увидеть список открытых портов и соединений можно командой netstat -abn под windows.

набрать стаж года 4

как-то не оптимистично. Я уверен, ты быстрее разберешься.

Чтобы лучше понимать протокол TCP можно еще почитать его описание, чтобы знать процесс установления соедиенния, что такое RST/FIN/ACK. Я тут нашел какие-то ссылки, может там что полезное есть:

Ну и еще есть толстенный учебник Олифера по сетевым технологиям.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment