Работа с сетью (TCP-сокеты)
Прямой доступ к системным сетевым функциям (POSIX) через тот же FFI-механизм,
что используется для графики (подключить сеть;, аналогично графика.код).
Нет отдельного, встроенного HTTP-клиента — вместо этого даны сами
"кирпичики" (сокеты), из которых HTTP собирается на самой Сфере, тем же
способом, каким Список/Словарь — просто библиотечный код, а не магия
компилятора.
подключить сеть;
функция главная(): целое
целое32 сокет = создать_сокет(целое32(2), целое32(1), целое32(0));
текст адрес_сервера = построить_адрес_сервера("93.184.216.34", целое32(80));
подключиться(сокет, адрес_сервера, целое32(16));
текст запрос = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
отправить_данные(сокет, запрос, длина(запрос), целое32(0));
текст буфер = выделить_память(4096);
целое получено = получить_данные(сокет, буфер, 4090, целое32(0));
записать_целое32(буфер, получено, целое32(0)); // нуль-терминатор
вывести(прочитать_текст_по_смещению(буфер, 0));
закрыть_сокет(сокет);
вернуть 0;
конец
⚠️ Важно про получить_данные (recv) — TCP не гарантирует, что все
отправленные сервером данные придут за один вызов. Для полного ответа
нужно читать в цикле, пока получить_данные не вернёт 0
(соединение закрыто — гарантированно произойдёт, если запрос содержал
Connection: close):
текст полный_ответ = "";
верно продолжать = да;
пока (продолжать)
текст буфер = выделить_память(4096);
целое получено = получить_данные(сокет, буфер, 4090, целое32(0));
если (получено больше 0)
записать_целое32(буфер, получено, целое32(0));
полный_ответ = полный_ответ + прочитать_текст_по_смещению(буфер, 0);
иначе
продолжать = нет;
конец
конец
⚠️ построить_адрес_сервера принимает только числовой IP-адрес, не
доменное имя ("93.184.216.34", не "example.com") — настоящий DNS-резолвинг
(превращение домена в IP) пока не реализован, это честно названный
следующий шаг.
Серверная сторона — простой HTTP-сервер
bind/listen/accept — то, что нужно, чтобы не только подключаться к
чужим серверам, но и принимать подключения самому:
подключить сеть;
// AF_INET = 2, SOCK_STREAM = 1 (TCP), SOL_SOCKET = 1, SO_REUSEADDR = 2.
функция главная(): целое
целое32 слушающий_сокет = создать_сокет(целое32(2), целое32(1), целое32(0));
текст включено = выделить_память(4);
записать_целое32(включено, 0, целое32(1));
настроить_сокет(слушающий_сокет, целое32(1), целое32(2), включено, целое32(4));
текст адрес = построить_адрес_сервера("0.0.0.0", целое32(8080));
привязать_сокет(слушающий_сокет, адрес, целое32(16));
слушать_сокет(слушающий_сокет, целое32(10));
целое32 клиент = принять_подключение(слушающий_сокет, нулевой_указатель_сети(), нулевой_указатель_сети());
текст буфер_запроса = выделить_память(2048);
получить_данные(клиент, буфер_запроса, 2047, целое32(0));
текст тело = "<html><body><h1>Привет!</h1></body></html>";
текст ответ = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: "
+ целое_в_текст(длина(тело)) + "\r\n\r\n" + тело;
отправить_данные(клиент, ответ, длина(ответ), целое32(0));
закрыть_сокет(клиент);
закрыть_сокет(слушающий_сокет);
вернуть 0;
конец
Полный, рабочий пример (с параллельной обработкой каждого подключения в
своём потоке и простой маршрутизацией по пути — разные страницы для
разных адресов, честная страница 404 для остального) —
examples/веб/http_сервер.код. Проверено объективно: несколько
НАСТОЯЩИХ одновременных запросов через curl (в фоне, параллельно)
получают разные, корректные HTTP-ответы, не дожидаясь друг друга по
очереди.
⚠️ Пока нет URL-декодирования — маршруты держатся на ASCII (/about, не
кириллические), так как браузеры и curl всегда %-кодируют не-ASCII
символы в пути запроса — это следующий, отдельный шаг в сторону
настоящего веб-сервера.