concurrency-ladder.js
Loading canvas…
Цикл select() на сервере Winsock: процесс в user space пересобирает набор наблюдения, один раз засыпает в ядре, просыпается со списком готовых сокетов, принимает нового клиента через listener и читает готового без ожидания. Показывает, где живут дескрипторы (kernel space), где — состояние парсера (user space), и что accept() создаёт новый дескриптор ровно в момент вызова.
Шестая ступень лестницы concurrency: вместо потока на клиента сервер отдаёт ядру весь набор сокетов и один раз засыпает до первого интересного события. Схема показывает, где что живёт: дескрипторы — в kernel space, состояние разбора — в user space, и один цикл событий обслуживает всех.
| Нода | Роль |
|---|---|
loop | Цикл событий: собирает набор, зовёт select(), разбирает готовых |
parser | Состояние incremental parser, своё на каждого клиента |
readyset | Набор наблюдения (fd_set) — появляется, когда его собрали |
listener | Слушающий сокет и его backlog |
fd-a, fd-b, fd-c | Дескрипторы принятых соединений в ядре |
FD_ZERO плюс FD_SET на listener и каждый живой fd. Набор
появляется на схеме: до этого момента его не существует.accept() снял соединение из
backlog и выдал новый дескриптор: кубик fd клиента C появляется ровно в
момент вызова.recv() забирает то, что уже лежит в буфере, и отдаёт
парсеру состояния этого клиента.recv() вернул 0: peer закрыл направление, slot
освобождается.Набор пересобирается каждый цикл: после возврата select() в fd_set
остаются только готовые. Readable не значит «есть данные» — recv() == 0 это
orderly close, а SOCKET_ERROR с WSAECONNRESET — обрыв, и оба закрывают slot
ровно один раз. На writable подписываются только при pending output, иначе
ожидание вырождается в busy poll.
У select() стандартный FD_SETSIZE равен 64, поэтому listener и все клиенты
должны в него помещаться. WSAPoll() снимает лимит, но требует проверять
revents на POLLERR, POLLHUP и POLLNVAL.
Winsock: select, WSAPoll, FD_SET и FD_ISSET.
Введите числа или выберите пресет