2

Маленький веб

Серия Мы не пишем в техподдержку

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

Проект "Sandbird' в действии.

Проект "Sandbird' в действии.

Ода миниатюризации

Есть много причин, по которым миниатюрные девушк.. ээ реализации программ заслуживают внимания:

  • обучение — врядли получится разобраться как устроен вебсервер, перелопачивая исходники монстров вроде Apache или Nginx, спасет только миниатюрная реализация;

  • основа для собственных проектов — большой объем чужого исходного кода под капотом вашего проекта будет висеть гирей и отвлекать ресурсы на поддержку, в отличие от чего-то маленького и простого;

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

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

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

Еще с опытом приходит понимание, что любая программа — не более чем инструмент а реальную опасность всегда представляют живые люди, а не тупые машины.

Тестовое окружение

Не стал опять заморачиваться с *BSD, чтобы в третий раз не описывать специальную прослойку epoll-shim, позволяющую быстро и более-менее безболезненно портировать серверный софт с линукса. На этот раз в качестве тестового окружения выступает обычная Ubuntu Linux 25.10, хотя и с немного нестандартным ядром.

Компилятором выступит штатный же GCC, без изысков:

gcc version 15.2.0 (Ubuntu 15.2.0-4ubuntu4)

Sandbird

https://github.com/rxi/sandbird

Начнем погружение с весьма практичного проекта:

A tiny (~800sloc) embeddable HTTP server written in C89, compatible with Linux, OSX and Windows.

800 строк на чистом С, причем наиболее портабельного стандарта С89, c поддержкой Windows, Linux и MacOS — отличный набор для реального применения, например в качестве встроенного вебсервера для WiFi-роутера.

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

Вот так выглядит сборка примера:

cd example
gcc hello.c ../src/*.c -I../src -std=c89 -pedantic -Wall -Wextra -o hello

Так выглядит код тестового приложения с использованием этой библиотеки:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sandbird.h"

static int event_handler(sb_Event *e) {
if (e->type == SB_EV_REQUEST) {
printf("%s - %s %s\n", e->address, e->method, e->path);
sb_send_status(e->stream, 200, "OK");
sb_send_header(e->stream, "Content-Type", "text/plain");
sb_writef(e->stream, "Hello world");
}
return SB_RES_OK;
}

int main(void) {
sb_Options opt;
sb_Server *server;

memset(&opt, 0, sizeof(opt));
opt.port = "8000";
opt.handler = event_handler;
server = sb_new_server(&opt);

if (!server) {
fprintf(stderr, "failed to initialize server\n");
exit(EXIT_FAILURE);
}
printf("Server running at http://localhost:%s\n", opt.port);

for (;;) {
sb_poll_server(server, 1000);
}
sb_close_server(server);
return EXIT_SUCCESS;
}

Важный нюанс:

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

зато с неблокирующими сокетами:

..
static void set_socket_non_blocking(sb_Socket sockfd) {
#ifdef _WIN32
u_long mode = 1;
ioctlsocket(sockfd, FIONBIO, &mode);
#else
int flags = fcntl(sockfd, F_GETFL);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
#endif
}
..

webs

https://github.com/nicholascok/webs

Следующий интересный и даже в чем-то уникальный проект, с лаконичным описанием:

a simple websocket server library.

Реализует с помощью ~700 строк на С89.. серверные вебсокеты!

Вебсокеты это чаты, это «live-streaming», это например отображение логов в реальном времени и тому подобные динамические штуки.

Так это выглядит в действии:

Статику отдает пример из предыдущего проекта.

Статику отдает пример из предыдущего проекта.

Так выглядит сборка тестового приложения:

gcc -c *.c examples/test.c -Wall -Wextra -Wpedantic -Wno-overlength-strings -std=c89
gcc -o webs *.o -lpthread

Как нетрудно догадаться по -lpthread, тут уже используется многопоточность на базе POSIX threads:

..
static void* __webs_main(void* _srv) {
webs_server* srv = (webs_server*) _srv;
webs_client* user_ptr;
webs_client user;

for (;;) {
user.fd = __webs_accept_connection(srv->soc, &user);
user.srv = srv;
if (user.fd >= 0) {
user_ptr = __webs_add_client(srv, user);
pthread_create(&user_ptr->thread, 0, __webs_client_main,
user_ptr);
}
}
return NULL;
}
..

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

В случае реального использования, столь вольное использование malloc для обработки входящих пакетов быстро превратится в проблему:

..
/* deal with normal frames (non-fragmented) */
if (WEBSFR_GET_OPCODE(frm.info) != 0x0) {
/* read data */
if (data) free(data);
data = malloc(frm.length + 1);
..

Так выглядит сокращенная версия тестового сервера вебсокетов, c минимумом обработчиков:

#include "../webs.h"

int myFuncZ(webs_client* self) {
printf("server %ld: (id %ld) connected!\n",
self->srv->id, self->id);
webs_send(self, "greetings, salutations!");
return 0;
}
int myFunc2(webs_client* self) {
printf("server %ld: (id %ld) disconnected!\n",
self->srv->id, self->id);
return 0;
}
int main(void) {
webs_server* server1 = webs_start(7754);
if (!server1) {
printf("failed to initialise a server.\n");
return 1;
}
server1->events.on_open = myFuncZ;
server1->events.on_close = myFunc2;

webs_hold(server1);
webs_close(server1);
return 0;
}

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

Повторюсь, что весь этот проект, несмотря на всю свою интересность — сырой прототип и тащить в прод подобный код без переработки, "as-is" точно не стоит.

Собирается только для Linux и только с помощью gcc.

Прототип как он есть.

cpp-httplib

https://github.com/yhirose/cpp-httplib

Это уже куда более зрелая библиотека:

A C++ header-only HTTP/HTTPS server and client library

11к строк кода на C++11 и несколько зависимостей от внешних библиотек:

pthreads, brotli, OpenSSL, zlib

Реализуют весьма продвинутый HTTP/HTTPS сервер и клиент, причем фактически в одном файле. Есть поддержка сборки как на Linux так и Windows, причем для второй есть готовый проект для Visual Studio.

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

Так выглядит сборка тестового сервера, использующего эту библиотеку:

g++ -o server -O2 -std=c++11 -I.. -Wall -Wextra -pthread \
server.cc -DCPPHTTPLIB_OPENSSL_SUPPORT -lssl -lcrypto \
-DCPPHTTPLIB_ZLIB_SUPPORT -lz -DCPPHTTPLIB_BROTLI_SUPPORT \
-lbrotlicommon -lbrotlienc -lbrotlidec

По флагам вроде CPPHTTPLIB_ZLIB_SUPPORT можно заметить, что большая часть зависимостей на самом деле опциональна и отключаема.

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

Так выглядит серверный «Hello, world!»:

#include <httplib.h>
using namespace httplib;

int main(void) {
Server svr;

svr.Get("/hi", [](const Request & /*req*/, Response &res) {
res.set_content("Hello World!", "text/plain");
});

svr.listen("0.0.0.0", 8080);
}

Код в лучших традициях Django или Rails, тут есть маршрутизация запросов и привязка к URL — в данном случае обработчик связывается с урлом /hi.

Вот так выглядит обработка загрузки файлов с помощью формы и POST-запроса:

#include <fstream>
#include <httplib.h>
#include <iostream>
using namespace httplib;
using namespace std;

const char *html = R"(
<form id="formElem">
<input type="file" name="image_file" accept="image/*">
<input type="file" name="text_file" accept="text/*">
<input type="submit">
</form>
<script>
formElem.onsubmit = async (e) => {
e.preventDefault();
let res = await fetch('/post', {
method: 'POST',
body: new FormData(formElem)
});
console.log(await res.text());
};
</script>
)";

int main(void) {
Server svr;

svr.Get("/", [](const Request & /*req*/, Response &res) {
res.set_content(html, "text/html");
});

svr.Post("/post", [](const Request &req, Response &res) {
const auto &image_file = req.form.get_file("image_file");
const auto &text_file = req.form.get_file("text_file");

cout << "image file length: " << image_file.content.length() << endl
<< "image file name: " << image_file.filename << endl
<< "text file length: " << text_file.content.length() << endl
<< "text file name: " << text_file.filename << endl;
{
ofstream ofs(image_file.filename, ios::binary);
ofs << image_file.content;
}
{
ofstream ofs(text_file.filename);
ofs << text_file.content;
}

res.set_content("done", "text/plain");
});

svr.listen("localhost", 1234);
}

Так это выглядит в действии:

Разумеется подобный функционал — не откровение, особенно на 2026й год, но блин:

11к строк кода на всю красоту!

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

tiny_ftpserver

https://github.com/adamwym/tiny_ftpserver

Вот такой «игрушечный» проект:

a tiny FTP server written in C++

Всего 1.5к строк на С++11 от безвестного китайского автора реализуют полнофункциональный FTP/FTPS‑сервер — с chroot, поддержкой локальных и анонимных юзеров и всем прочим.

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

В работе:

Проект использует известную библиотеку libnet, о чем автор забыл сообщить и которую необходимо установить до сборки:

apt install libnet1-dev

Сама сборка происходит с помощью обычного cmake:

mkdir build && cd build
cmake ..

Реализация FTP-сервера тут максимально классическая — используется chroot а использование 21 и 20 портов зашито в код:

..
int socketfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_port = htons(21);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
..

Все это означает, что запускать сервер придется от суперпользователя:

sudo ./tiny_ftpserver ../tiny_ftpserver.conf

У этого проекта есть один неожиданный нюанс:

на самом деле это.. студенческая работа.

И подобных проектов на Github оказалось великое множество. Так что далеко не везде в 2026м году забыли что такое высшее техническое образование, что не может не радовать.

fineFTP Server

https://github.com/eclipse-ecal/fineftp-server

Следующий интересный и весьма редкий проект:

FineFTP is a minimal FTP server library for Windows and Unix flavors.

~2k строк на С++14 реализуют FTP-сервер в виде.. библиотеки!

В этом и есть основная фишка этого проекта — возможность встроить FTP (причем сервер а не клиент) в ваше собственное приложение.

Так выглядит код примера, с внедрением FTP-сервера:

#include <fineftp/server.h>
#include <thread>

int main() {
// Create an FTP Server on port 2121. We use 2121 instead of the default port
// 21, as your application would need root privileges to open port 21.
fineftp::FtpServer ftp_server(2121);

// Add the well known anonymous user. Clients can log in using username
// "anonymous" or "ftp" with any password. The user will be able to access
// your C:\ drive and upload, download, create or delete files. On Linux just
// replace "C:\\" with any valid path. FineFTP is designed to be cross-platform.
ftp_server.addUserAnonymous("C:\\", fineftp::Permission::All);

// Start the FTP Server with a thread-pool size of 4.
ftp_server.start(4);

// Prevent the application from exiting immediately
for (;;) std::this_thread::sleep_for(std::chrono::milliseconds(100));
return 0;
}

Зачем и для чего такое может быть нужно — другой вопрос, все же обычно работу с файлами в конечном приложении (например обновление прошивки) стараются реализовать в виде клиента и через HTTP/HTTPS.

Но с точки зрения использования все отлично работает:

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

Старого цирка с chroot и 21м портом тут нет, поэтому все работает без привилегий суперпользователя — на скриншоте выше как раз видно использование нестандартного порта для работы.

Поддерживается сборка для Linux, MacOS и Windows c приоритетом для последней.

Отличный проект, но далеко не последний в сегодняшней подборке.

rcpd

https://github.com/tenox7/rcpd

Этот проект — привет из далекого и славного прошлого:

This is a modern re-implementation of rcp (remote copy protocol) daemon, originally part berkeley r-commands.

Да, это самый настоящий сервер RCP, всего 300 строк на Golang от знаменитого в узких кругах компьютерных реконструкторов автора Tenox.

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

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

Возвращаясь к проекту:

rcpd от Tenox реализует серверную сторону — сервер RCP

К этому серверу подключаются клиенты для загрузки или скачивания файлов. Так выглядит процесс копирования файла, в качестве клиента тут rcp из пакета GNU Inetutils:

Как видите и клиент и сервер запускаются от суперпользователя — такие были времена, RCP использует 514 порт, который нельзя занять без привилегий.

Сборка:

go build -o rcpd .

Кстати в Makefile проекта есть поддержка кроссплатформенных сборок, с весьма богатым выбором:

Стоит добавить, что протокол RCP сам по себе очень простой и не менялся весь период своего существования, что позволяет подключаться к этому RCP-серверу 21 века даже с помощью клиентских программ из 80х и 90х.

За кадром

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

url.c

https://github.com/cozis/url.c/

Парсер строк URL на чистом С и без зависимостей:

This is a small library to parse and manipulate URLs in conformance to RFC 3986 and (most of) the WHATWG specification.

Что умеет:

  • No allocations

  • No dependencies

  • The ability to switch between RFC 3986 and WHATWG with a flag

  • Relative reference parsing and resolution

  • URL normalization

  • Doesn't rely on null-terminated strings

LightFTP

https://github.com/hfiref0x/LightFTP

Еще один интересный проект миниатюрного FTP-сервера:

Small x86-32/x64 FTP Server

Поддерживается сборка под Linux/Windows/Mac, сам проект не мертвый и судя по коммитам — развивается.

uftpserver

https://github.com/cpopp/MicroFTPServer

Весьма специфичная реализация FTP-сервера для встраиваемых систем:

Minimal FTP Server that can run on an ESP8266 with MicroPython

Так я впервые узнал о существовании «микропетона» — давно существующего проекта, с огромным количеством поддерживаемого железа и широким функционалом.

Ну и в качестве финального аккорда:

https://github.com/mtheall/ftpd

FTP Server for 3DS/Switch/Linux.

Реализация FTP-сервера для.. Nintendo Switch!

Это такая игровая консоль для гиков если кто вдруг не в курсе:

P.S.

Статья была опубликована на Хабре, оригинал как обычно в нашем блоге.

Лига программистов

2.3K постов12K подписчика

Правила сообщества

- Будьте взаимовежливы, аргументируйте критику

- Приветствуются любые посты по тематике программирования

- Если ваш пост содержит ссылки на внешние ресурсы - он должен быть самодостаточным. Вариации на тему "далее читайте в моей телеге" будут удаляться из сообщества

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества