Работа с сетью (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 символы в пути запроса — это следующий, отдельный шаг в сторону настоящего веб-сервера.