Комментарии 16
Очень просто. Я бы сказал что чересчур :)
Во-первых, используемый вами протокол TCP безусловно хорош, но сервер который принимает только одно соединение, извлекает из него всё что в него отправили и тут же закрывается это немножко малоюзабельно. Что-то на уровне
> Как в языке Python сделать Hello World? Всего лишь print("Hello World")! Ничего другого не надо!
То есть это прямо скажем не сервер, а "Как прослушать порт, и принять из него что-нибудь", притом его легко полностью парализовать не завершая соединение :)
Первое что имеет любой сервер:
- Некоторый менеджер соединений. В него мы добавляем клиентов.
- Некоторые абстракции над sock'ами, условные клиенты, которыми мы такие, желательно в неблокирующем виде, принимаем все присланные данные всеми sock'ами (сколько прислали столько неблокирующе и считываем, и если сообщение не закончено то переходим к следующему), отключаем клиентов по таймаутам, проверяем что они нам что-то прислали и выполняем на присланные сообщения какие-нибудь callback'и.
- Некоторый абстрактный формат сообщений. Протокол TCP ориентирован не на сообщения, а на потоки, и ему нужен дополнительный формат для разделения сообщений, вы же не хотите использовать одно целое соединение чтобы прислать одно сообщение, когда их может быть пачка? Простейший формат сообщений -- message + "\r\n", и эти "\r\n" работают разделителями сообщений. Но у него проблема с самими данными, их приходится паковать в какой-нибудь base64 чтобы из-за тела сообщения содержащего эти самые "\r\n" мессага не распилилась, поэтому чуть более сложный вариант, например
text = "abcdef"
formatted = '{0:04d}{1:s}'.format(len(text), text)
> '0006abcdef'
Приведёт к уникальной возможности указывать длину сообщения перед самим сообщением, и корректно их сепарировать по длине, принимая и отправляя их сразу пачками (хотя появляется лимит на длину, которую можно указать произвольной, но она внесёт некоторый оверхед). Я сам использую похожие прикладные протоколы для своих небольших нужд, где HTTP слишком тяжеловесен, и что-то похожее используется в самом HTTP, только там чуть более комплексный заголовок, содержащий не только длину а сразу множество опций, и само тело там может быть а может не быть, но это уже частности.
И вот чего-то подобного хотелось бы видеть в статье а не только "Мы открыли сокет! Мы отправили в него строчку и приняли! Ура!". Статья неплоха как вводная часть, перед дальнейшей кухней вроде той что я описал, но ПОСЛЕ теоретической части которой тоже не хватает: что такое сервер? что такое сокет? что такое порт? что такое tcp/udp/ip? Почему localhost и что он означает, во что трансформируется и почему? Ваше описание избыточно упрощено, и мало имеет отношения к действительности :)
Ну и в третьих, хаб "Серверное администрирование" немножко кажется тоже лишним, здесь примерно ничего про администрирование, про роутинг, пробитие или использование портов, фаерволы и так далее :)
Присоединяюсь насчёт сокета. Всё делается через сокет, а что такое сокет -- непонятно.
Спасибо большое за критику, в дальнейшем буду развивать эту ветку статей.
Получить подключение, прочитать запрос и отправить ответ это и есть принцип работы, наверное, самого распространенного типа серверов - веб серверов.
В текущем сценарии менеджер соединений не обязателен, потому что нет параллельной работы с соединениями. А на уровне получить запрос от клиента и подождать пока сервер примет соединение оно реализовано на уровне ОС.
Вообще КМК архитектура веб-серверов плавно ползет именно к таким супер простым серверам запущенным в нескольких копиях с балансировкой нагрузки через reverse proxy.
Но теории добавить стоит, конечно. И дампы траффика разобрать.
Предлагаю усложнять сервер в следующих статьях шаг за шагом. В чем косяк этого сервера - если подключиться еще одним клиентом, то он будет получать от сервера ответ только когда первый отключится... Соответственно, нужно переключать сервер между клиентами, например, используя опрос сокетов через select. Высший пилотаж - aiohttp:)
Как два байта переслать! (с)
Ошибка статьи в том, что вы берете слишком низкоуровневый примитив и ожидаете, что поверх него пользователь будет строить какой-то велосипед для передачи данных. В реальности уже давно написаны практически всевозможные обертки над голыми сокетами, и крайне маловероятно что кому-то понадобится ещё одна.
А ведь сервер - это не просто socket.recv(). Это дофига всего - ААА, сериализация, валидация, и много-чего-другого-ция. Да и даже socket.recv() уже мало кому интересен - слишком медленно и тяжко с асинхронностью.
TL;DR: статья называется "как построить простой дом", но начинаем с добычи глины для лепки кирпичей.
Ну и тут выше был вопрос про сокет - поэтому отвечу вкратце.
Сокет в переводе с английского языка - разъем или розетка, проще говоря - дырка в которую можно вставить кабель. Как и в случае с обычной розеткой в стене - сама по себе она мало чем ценна, в неё нужно что-то включить. Отсюда возникает разделение на серверные (слушающие, Listening) сокеты и клиентские сокеты. И если серверный сокет - это условная розетка в стене, то клиентский сокет - это пальцы, которые мы в неё вставляем.
Фактически серверный сокет - это такой механизм в ОС, который ждет входящего подключения. И как только к нему подключаются - сразу вызывает какой-то Ваш код, который обрабатывает это подключение. Ну то есть нормальное состояние серверного сокета - ничего не делать и ждать, он потому и называется слушающим.
Клиентский же сокет - это механизм в ОС, который подключается к серверному сокету, и если подключение произошло удачно - то можно писать в сокет данные и читать из сокета данные.
Технически эта вся история реализована при помощи семейства интернет-протоколов TCP\IP. IP - это про адресацию (те самые IP-адреса, да), бывает версии 4 и 6. TCP - это про надежность доставки (там есть два варианта - TCP медленнее и с гарантией и UDP быстро, но как повезет). На уровне TCP сделано ещё много чего полезного - например Congestion Control, которые определяют честность разделения лимитированного по ширине канала между несколькими одновременными соединениями.
Чтобы не запихивать весь этот функционал в какую-то одну огромную библиотеку - умные люди придумали модель OSI. Это такая референсная и недостижимая (как социализм) штука, идея которой заключается в том, что сетевое взаимодействие между двумя системами делится на абстрактные "уровни", каждый из которых полностью реализует свою задачу. Условно говоря - протоколу TCP на уровне L3 по идее не важно, что у него за физический носитель на уровне L1 - медный кабель, оптоволокно или Wi-Fi. (на самом деле это совсем не так, но для 70-х годов прошлого века идея тотальной унификации всего на свете выглядела прогрессивной). Много полезной инфы об этом можно почерпнуть из старого курса Cisco ICND, и несмотря на его почтенный возраст он имхо обязателен к прочтению всем, кто пытается что-то соорудить из сокетов.
Ответ хороший, но суть в том, что по хорошему он должен быть в теле статьи :)
Поправлю немного, протокол TCP "сидит" на L4, если мы говорим о модели OSI. А на L3 как раз таки IP. Получив src и dst порты, PDU спускается на L3 дабы обрасти src и dst IP адресами
Спасибо за развернутый и понятный ответ!
Почему все прицепились к какому-то сокету...
Изначально проведена не самая корректная аналогия: фургон с булками - это БД, сервер - повар.
Не очень удачная абстракция с фургоном и поваром. Имхо, если любите покушать, то - ресторан.
![](https://habrastorage.org/getpro/habr/upload_files/68a/776/ac2/68a776ac2c87e330240585fa0601efa6.png)
Кажется пропущен момент создания сокета (откуда взялось s
)
Пишем простой сервер на Python