Клиент серверное приложение c: Клиент-серверное приложение на сокетах TCP в C# и .NET
C# клиент-серверное приложение — CodeRoad
Начнем с архитектуры клиент-сервер.
Предположим, вы окончательно определились с тем, что вам нужны клиент и сервер, но тщательно ли вы определились с архитектурой? Я имею в виду, какой тип сервера и какой тип клиента вы собираетесь создать?
Давайте посмотрим варианты здесь:
Сервер
1. Какой тип хостинга вы собираетесь использовать?
2. Какой тип и какую нагрузку должен обрабатывать ваш сервер?
Клиент
1. Тип потребителя ваших услуг
2.Нужно ли развертывать клиент на локальном компьютере или он должен быть веб -?
Очевидно, что существует больше проблем, чем указано выше. Первоначальный дизайн должен быть максимально гибким.
Итак, теперь давайте рассмотрим некоторые решения, касающиеся архитектуры.
Сервер:
1. Приложение, размещенное на сервере WCF: каждый раз, когда вам нужно управлять жизненным циклом сервера. Кроме того, это не масштабируемо. Поэтому, если вы ищете масштабируемую архитектуру, вам нужно искать больше.
2. IIS размещенный WCF сервер: это может быть хорошей идеей наряду с некоторыми проблемами архитектуры в соответствии с вашими потребностями.
3. Веб-метод: очевидно, это произошло после WCF, но WCF все еще находится на своем месте. Итак, основное различие заключается в том, в чем разница между веб-методом asp.net и сервисом wcf?
Теперь Клиент:
1. ASP.NET: это позволит использовать одно клиентское приложение для каждой платформы, очевидно, из-за HTML
2. WPF/WinForms: это будет немного сложно использовать в качестве клиента, так как вам нужно развернуть клиентское приложение на компьютере пользователя, и здесь возникает проблема безопасности данных. В первом случае вы можете напрямую использовать SSL или какой-либо другой способ отправки данных в браузер. В то время как в этом случае, если вы не используете WCF с HTTPS и есть некоторые проприетарные данные, идущие по проводам, это может быть проблемой.
Если вы ищете кросс-платформенное использование вашего сервера, вы можете использовать HTML.
Вывод:
Вы можете использовать сервер как размещенную службу WCF (либо в IIS, либо в автономном приложении), а клиент-как ASP.NET.
——————————
Если это недостаточно большое требование, то вы можете использовать ASP.NET в качестве сервера, а затем браузер в качестве клиента (нет необходимости создавать клиент).
—————————-
Вы можете создать сервер либо как WCF, либо как веб-методы и развернуть клиент на компьютере пользователя.
—————————-
WCF достаточно хорош, и он также может обрабатывать ваши собственные типы данных.
как правильно проектировать и разрабатывать web API
Рассказывает Владимир, веб-разработчик Noveo
Большинству разработчиков сайтов, веб-сервисов и мобильных приложений рано или поздно приходится иметь дело с клиент-серверной архитектурой, а именно разрабатывать web API или интегрироваться с ним. Чтобы не изобретать каждый раз что-то новое, важно выработать относительно универсальный подход к проектированию web API, основываясь на опыте разработки подобных систем. Предлагаем вашему вниманию объединенный цикл статей, посвящённых этому вопросу.
Приближение первое: Действующие лица
В один прекрасный момент, в процессе создания очередного веб-сервиса, я решил собрать все свои знания и размышления на тему проектирования web API для обслуживания нужд клиентских приложений и оформить их в виде статьи или серии статей. Разумеется, мой опыт не претендует на абсолют, и конструктивная критика и дополнения более чем приветствуются.
Чтиво получилось больше философское, нежели техническое, но и для любителей технической части здесь будет над чем поразмыслить. Сомневаюсь, что скажу в этой статье что-то принципиально новое, то, о чем вы никогда не слышали, не читали и о чем не думали сами. Просто попытаюсь уложить все в единую систему, в первую очередь в своей собственной голове, а это уже дорогого стоит. Тем не менее, буду рад, если мои измышления будут вам полезны в вашей практике. Итак, поехали.
Клиент и сервер
Сервером в данном случае мы считаем абстрактную машину в сети, способную получить HTTP-запрос, обработать его и вернуть корректный ответ. В контексте данной статьи совершенно не важны его физическая суть и внутренняя архитектура, будь то студенческий ноутбук или огромный кластер из промышленных серверов, разбросанных по всему миру. Нам в той же мере совершенно неважно, что у него под капотом, кто встречает запрос у дверей, Apache или Nginx, какой неведомый зверь, PHP, Python или Ruby выполняет его обработку и формирует ответ, какое хранилище данных используется: Postgresql, MySQL или MongoDB. Главное, чтобы сервер отвечал главному правилу — услышать, понять и простить ответить.
Клиентом тоже может быть все, что угодно, что способно сформировать и отправить HTTP-запрос. До определенного момента в этой статье нам также не особо будут интересны цели, которые ставит перед собой клиент, отправляя этот запрос, как и то, что он будет делать с ответом. Клиентом может быть JavaScript-сценарий, работающий в браузере, мобильное приложение, злой (или не очень) демон, запущенный на сервере, или слишком поумневший холодильник (уже есть и такие).
По большей части мы будем говорить о способе общения между выше перечисленными двумя, таком способе, чтобы они друг друга понимали, и ни у одного не оставалось вопросов.
Философия REST
REST (Representational state transfer) изначально был задуман как простой и однозначный интерфейс для управления данными, предполагавший всего несколько базовых операций с непосредственным сетевым хранилищем (сервером): извлечение данных (GET), сохранение (POST), изменение (PUT/PATCH) и удаление (DELETE). Разумеется, этот перечень всегда сопровождался такими опциями, как обработка ошибок в запросе (корректно ли составлен запрос), разграничение доступа к данным (вдруг этого вам знать не следует) и валидация входящих данных (вдруг вы написали ерунду), в общем, всеми возможными проверками, которые сервер выполняет перед тем, как выполнить желание клиента.
Помимо этого REST имеет ряд архитектурных принципов, перечень которых можно найти в любой другой статье о REST. Пробежимся по ним кратко, чтобы они были под рукой, и не пришлось никуда уходить:
Независимость сервера от клиента — серверы и клиенты могут быть мгновенно заменены другими независимо друг от друга, так как интерфейс между ними не меняется. Сервер не хранит состояний клиента.
Уникальность адресов ресурсов — каждая единица данных (любой степени вложенности) имеет свой собственный уникальный URL, который, по сути, целиком является однозначным идентификатором ресурса.
Пример: GET /api/v1/users/25/name
Независимость формата хранения данных от формата их передачи — сервер может поддерживать несколько различных форматов для передачи одних и тех же данных (JSON, XML и т.д.), но хранит данные в своем внутреннем формате, независимо от поддерживаемых.
Присутствие в ответе всех необходимых метаданных — помимо самих данных сервер должен возвращать детали обработки запроса, например, сообщения об ошибках, различные свойства ресурса, необходимые для дальнейшей работы с ним, например, общее число записей в коллекции для правильного отображения постраничной навигации. Мы еще пройдемся по разновидностям ресурсов.
Чего нам не хватает
Классический REST подразумевает работу клиента с сервером как с плоским хранилищем данных, при этом ничего не говорится о связанности и взаимозависимости данных между собой. Все это по умолчанию целиком ложится на плечи клиентского приложения. Однако современные предметные области, для которых разрабатываются системы управления данными, будь то социальные сервисы или системы интернет-маркетинга, подразумевают сложную взаимосвязь между сущностями, хранящимися в базе данных. Поддержка этих связей, т.е. целостности данных, находится в зоне ответственности серверной стороны, в то время, как клиент является только интерфейсом для доступа к этим данным. Так чего же нам не хватает в REST?
Вызовы функций
Чтобы не менять данные и связи между ними вручную, мы просто вызываем у ресурса функцию и «скармливаем» ей в качестве аргумента необходимые данные. Эта операция не подходит под стандарты REST, для нее не существует особого глагола, что заставляет нас, разработчиков, выкручиваться кто во что горазд.
Самый простой пример – авторизация пользователя. Мы вызываем функцию login, передаем ей в качестве аргумента объект, содержащий учетные данные, и в ответ получаем ключ доступа. Что творится с данными на серверной стороне – нас не волнует.
Еще вариант – создание и разрыв связей между данными. Например, добавление пользователя в группу. Вызываем у сущности группа функцию addUser, в качестве параметра передаем объект пользователь, получаем результат.
А еще бывают операции, которые вообще не связаны напрямую с сохранением данных как таковых, например, рассылка уведомлений, подтверждение или отклонение каких-либо операций (завершение отчетного периода etc).
В одной из следующих статей я постараюсь классифицировать эти операции и предложить варианты возможных запросов и ответов, основываясь на том, с какими из них мне приходилось сталкиваться на практике.
Множественные операции
Часто бывает так, и разработчики клиентов поймут, о чем я, что клиентскому приложению удобнее создавать/изменять/удалять/ сразу несколько однородных объектов одним запросом, и по каждому объекту возможен свой вердикт серверной стороны. Тут есть как минимум несколько вариантов: либо все изменения выполнены, либо они выполнены частично (для части объектов), либо произошла ошибка. Ну и стратегий тоже несколько: применять изменения только в случае успеха для всех, либо применять частично, либо откатываться в случае любой ошибки, а это уже тянет на полноценный механизм транзакций.
Для web API, стремящегося к идеалу, тоже хотелось бы как-то привести подобные операции в систему. Постараюсь сделать это в одном из продолжений.
Статистические запросы, агрегаторы, форматирование данных
Частенько бывает так, что на основе хранимых на сервере данных нам нужно получить статистическую выжимку или данные, отформатированные особым образом: например, для построения графика на стороне клиента. По сути это данные, генерируемые по требованию, в той или иной мере на лету, и доступные только для чтения, так что имеет смысл вынести их в отдельную категорию. Одной из отличительных особенностей статистических данных, на мой взгляд, является то, что они не имеют уникального ID.
Уверен, что это далеко не все, с чем можно столкнуться при разработке реальных приложений, и буду рад вашим дополнениям и коррективам.
Разновидности данных
Объекты
Ключевым типом данных в общении между клиентом и сервером выступает объект. По сути, объект – это перечень свойств и соответствующих им значений. Мы можем отправить объект на сервер в запросе и получить в результат запроса в виде объекта. При этом объект не обязательно будет реальной сущностью, хранящейся в базе данных, по крайней мере, в том виде, в котором он отправлен или получен. Например, учетные данные для авторизации передаются в виде объекта, но не являются самостоятельной сущностью. Даже хранимые в БД объекты склонны обрастать дополнительными свойствами внутрисистемного характера, например, датами создания и редактирования, различными системными метками и флагами. Свойства объектов могут быть как собственными скалярными значениями, так и содержать связанные объекты и коллекции объектов, которые не являются частью объекта. Часть свойств объектов может быть редактируемой, часть системной, доступной только для чтения, а часть может носить статистический характер и вычисляться на лету (например, количество лайков). Некоторые свойства объекта могут быть скрыты, в зависимости от прав пользователя.
Коллекции объектов
Говоря о коллекциях, мы подразумеваем разновидность серверного ресурса, позволяющую работать с перечнем однородных объектов, т.е. добавлять, удалять, изменять объекты и осуществлять выборку из них. Помимо этого коллекция теоретически может обладать собственными свойствами (например, максимальное число элементов на страницу) и функциями (тут я в замешательстве, но такое тоже было).
Скалярные значения
В чистом виде скалярные значения как отдельная сущность на моей памяти встречались крайне редко. Обычно они фигурировали как свойства объектов или коллекций, и в этом качестве они могут быть доступны как для чтения, так и для записи. Например, имя пользователя может быть получено и изменено в индивидуальном порядке GET /users/1/name
. На практике эта возможность пригождается редко, но в случае необходимости хотелось бы, чтобы она была под рукой. Особенно это касается свойств коллекции, например числа записей (с фильтрацией или без нее): GET /news/count
.
В одной из следующих статей я постараюсь классифицировать эти операции и предложить варианты возможных запросов и ответов, основываясь на том, с какими из них мне приходилось сталкиваться на практике.
Приближение второе: Правильный путь
В этом приближении я хотел бы отдельно поговорить о подходах к построению уникальных путей к ресурсам и методам вашего web API и о тех архитектурных особенностях приложения, которые влияют на внешний вид этого пути и его компоненты.
О чем стоит подумать, стоя на берегу
Версионность
Рано или поздно любая действующая система начинает эволюционировать: развиваться, усложняться, масштабироваться, усовремениваться. Для разработчиков REST API это чревато в первую очередь тем, что необходимо запускать новые версии API при работающих старых. Здесь я говорю больше не об архитектурных изменениях под капотом вашей системы, а о том, что изменяется сам формат данных и набор операций с ними. В любом случае версионность нужно предусмотреть как в изначальной организации исходного кода, так и в принципе построения URL. Что касается URL, здесь существует два наиболее популярных способа указания версии API, которой адресован запрос. Префиксация пути example-api.com/v1/
и разведение версий на уровне субдомена v1.example-api.com
. Использовать можно любой из них, в зависимости от потребности и необходимости.
Автономность компонентов
Web API сложных систем, поддерживающих несколько пользовательских ролей, зачастую требует разделения на части, каждая из которых обслуживает свой спектр задач. По сути, каждая часть может быть самостоятельным приложением, работать на разных физических машинах и платформах. В контексте описания API нам совершенно не важно, как сервер обрабатывает запрос и какие силы и технологии в этом замешаны. Для клиента API – система инкапсулированная. Тем не менее разные части системы могут обладать совершенно разной функциональностью, например, административная и пользовательская часть. И методология работы с одними и теми же, казалось бы, ресурсами может существенно отличаться. Поэтому такие части необходимо разделять на уровне домена admin.v1.example-api.com
или префикса пути example-api.com/v1/admin/
. Это требование не является обязательным, и многое зависит от сложности системы и её назначения.
Формат обмена данными
Самым удобным и функциональным, на мой взгляд, форматом обмена данными является JSON, но никто не запрещает использовать XML, YAML или любой другой формат, позволяющий хранить сериализованные объекты без потери типа данных. При желании можно сделать в API поддержку нескольких форматов ввода/вывода. Достаточно задействовать HTTP заголовок запроса для указания желаемого формата ответа Accept
и Content-Type
для указания формата переданных в запросе данных. Другим популярным способом является добавление расширения к URL ресурса, например, GET /users.xml
, но такой способ кажется менее гибким и красивым, хотя бы потому, что утяжеляет URL и верен скорее для GET-запросов, нежели для всех возможных операций.
Локализация и многоязычность
На практике многоязычность API чаще всего сводится к переводу сервисных сообщений и сообщений об ошибках на требуемый язык для прямого отображения конечному пользователю. Многоязычный контент тоже имеет место быть, но сохранение и выдача контента на разных языках, на мой взгляд, должна разграничиваться более явно, например, если у вас одна и та же статья существует на разных языках, то по факту это две разных сущности, сгруппированные по признаку единства содержания. Для идентификации ожидаемого языка можно использовать разные способы. Самым простым можно считать стандартный HTTP-заголовок Accept-Language
. Я встречал и другие способы, такие, как добавление GET-параметра language="en"
, использование префикса пути example-api.com/en/
или даже на уровне доменного имени en.example-api.com
. Мне кажется, что выбор способа указания локали зависит от конкретного приложения и задач, стоящих перед ним.
Внутренняя маршрутизация
Итак, мы добрались до корневого узла нашего API (или одного из его компонентов). Все дальнейшие маршруты будут проходить уже непосредственно внутри вашего серверного приложения, в соответствии с поддерживаемым им набором ресурсов.
Пути к коллекциям
Для указания пути к коллекции мы просто используем название соответствующей сущности, например, если это список пользователей, то путь будет таким /users
. К коллекции как таковой применимы два метода: GET (получение лимитированного списка сущностей) и POST (создание нового элемента). В запросах на получение списков мы можем использовать множество дополнительных GET параметров, применяемых для постраничного вывода, сортировки, фильтрации, поиска etc, но они должны быть опциональными, т.е. эти параметры не должны передаваться как часть пути!
Элементы коллекции
Для обращения к конкретному элементу коллекции мы используем в маршруте его уникальный идентификатор /users/25
. Это и есть уникальный путь к нему. Для работы с объектом применимы методы GET (получение объекта), PUT/PATCH (изменение) и DELETE (удаление).
Уникальные объекты
Во множестве сервисов существуют уникальные для текущего пользователя объекты, например профиль текущего пользователя /profile
, или персональные настройки /settings
. Разумеется, с одной стороны, это элементы одной из коллекций, но они являются отправной точкой в использовании нашего Web API клиентским приложением, и к тому же позволяют намного более широкий спектр операций над данными. При этом коллекция, хранящая пользовательские настройки может быть вообще недоступна из соображений безопасности и конфиденциальности данных.
Свойства объектов и коллекций
Для того, чтобы добраться до любого из свойств объекта напрямую, достаточно добавить к пути до объекта имя свойства, например получить имя пользователя /users/25/name
. К свойству применимы методы GET (получение значения) и PUT/PATCH (изменение значения). Метод DELETE не применим, т.к. свойство является структурной частью объекта, как формализованной единицы данных.
В предыдущей части мы говорили о том, что у коллекций, как и у объектов, могут быть собственные свойства. На моей памяти мне пригодилось только свойство count, но ваше приложение может быть более сложным и специфичным. Пути к свойствам коллекций строятся по тому же принципу, что и к свойствам их элементов: /users/count
. Для свойств коллекций применим только метод GET (получение свойства), т.к. коллекция – это только интерфейс для доступа к списку.
Коллекции связанных объектов
Одной из разновидностей свойств объектов могут быть связанные объекты или коллекции связанных объектов. Такие сущности, как правило, не являются собственным свойством объекта, а лишь отсылками к его связям с другими сущностями. Например, перечень ролей, которые были присвоены пользователю /users/25/roles
. По поводу работы с вложенными объектами и коллекциями мы подробно поговорим в одной из следующих частей, а на данном этапе нам достаточно того, что мы имеем возможность обращаться к ним напрямую, как к любому другому свойству объекта.
Функции объектов и коллекций
Для построения пути к интерфейсу вызова функции у коллекции или объекта мы используем тот же самый подход, что и для обращения к свойству. Например, для объекта /users/25/sendPasswordReminder
или коллекции /users/disableUnconfirmed
. Для вызовов функций мы в любом случае используем метод POST. Почему? Напомню, что в классическом REST не существует специального глагола для вызова функций, а потому нам придется использовать один из существующих. На мой взгляд, для этого больше всего подходит метод POST т.к. он позволяет передавать на сервер необходимые аргументы, не является идемпотентным (возвращающим один и тот же результат при многократном обращении) и наиболее абстрактен по семантике.
Надеюсь, что все более-менее уложилось в систему 🙂 В следующей части мы поговорим подробнее о запросах и ответах, их форматах, кодах статусов.
Приближение третье: Запросы и ответы
В предыдущих приближениях я рассказал о том, как пришла идея собрать и обобщить имеющийся опыт разработки web API. В первой части я постарался описать, с какими видами ресурсов и операций над ними мы имеем дело при проектировании web API. Во второй части были затронуты вопросы построения уникальных URL для обращения к этим ресурсам. А в этом приближении я попробую описать возможные варианты запросов и ответов.
Универсальный ответ
Мы уже проговаривали, что конкретный формат общения сервера с клиентом может быть любым на усмотрение разработчика. Для меня наиболее удобным и наглядным кажется формат JSON, хотя в реальном приложении может быть реализована поддержка нескольких форматов. Сейчас же сосредоточимся на структуре и необходимых атрибутах объекта ответа. Да, все данные, возвращаемые сервером, мы будем оборачивать в специальный контейнер — универсальный объект ответа, который будет содержать всю необходимую сервисную информацию для его дальнейшей обработки. Итак, что это за информация:
Success — маркер успешности выполнения запроса
Для того, чтобы при получении ответа от сервера сразу понять, увенчался ли запрос успехом, и передать его соответствующему обработчику, достаточно использовать маркер успешности «success». Самый простой ответ сервера, не содержащий никаких данных, будет выглядеть так:
POST /api/v1/articles/22/publish
{
"success": true
}
Error — сведения об ошибке
В случае, если выполнение запроса завершилось неудачей — о причинах и разновидностях отрицательных ответов сервера поговорим чуть позже, — к ответу добавляется атрибут «error», содержащий в себе HTTP-код статуса и текст сообщения об ошибке. Прошу не путать с сообщениями об ошибках валидации данных для конкретных полей. Правильнее всего, на мой взгляд, возвращать код статуса и в заголовке ответа, но я встречал и другой подход — в заголовке всегда возвращать статус 200 (успех), а детали и возможные данные об ошибках передавать в теле ответа.
GET /api/v1/user
{
"success": false,
"error": {
"code" : 401,
"message" : "Authorization failed"
}
}
Data — данные, возвращаемые сервером
Большинство ответов сервера призваны возвращать данные. В зависимости от типа запроса и его успеха ожидаемый набор данных будет разным, тем не менее атрибут«data» будет присутствовать в подавляющем большинстве ответов.
Пример возвращаемых данных в случае успеха. В данном случае ответ содержит запрашиваемый объект user.
GET /api/v1/user
{
"success": true,
"data": {
"id" : 125,
"email" : "[email protected]",
"name" : "John",
"surname" : "Smith",
}
}
Пример возвращаемых данных в случае ошибки. В данном случае содержит имена полей и сообщения об ошибках валидации.
PUT /api/v1/user
{
"success": false,
"error": {
"code" : 422,
"message" : "Validation failed"
}
"data": {
"email" : "Email could not be blank.",
}
}
Pagination — сведения, необходимые для организации постраничной навигации
Помимо собственно данных, в ответах, возвращающих набор элементов коллекции, обязательно должна присутствовать информация о постраничной навигации (пагинации) по результатам запроса.
Минимальный набор значений для пагинации состоит из:
- общего числа записей;
- числа страниц;
- номера текущей страницы;
- числа записей на странице;
- максимального числа записей на странице, поддерживаемого серверной стороной.
Некоторые разработчики web API также включают в пагинацию набор готовых ссылок на соседние страницы, а также первую, последнюю и текущую.
GET /api/v1/articles
Response:
{
"success": true,
"data": [
{
"id" : 1,
"title" : "Interesting thing",
},
{
"id" : 2,
"title" : "Boring text",
}
],
"pagination": {
"totalRecords" : 2,
"totalPages" : 1,
"currentPage" : 1,
"perPage" : 20,
"maxPerPage" : 100,
}
}
Работа над ошибками
Как уже упоминалось выше, не все запросы к web API завершаются успехом, но это тоже часть игры. Система информирования об ошибках является мощным инструментом, облегчающим работу клиента и направляющим клиентское приложение по правильному пути. Слово «ошибка» в этом контексте не совсем уместно. Здесь больше подойдёт слово исключение, так как на самом деле запрос успешно получен, проанализирован, и на него возвращается адекватный ответ, объясняющий, почему запрос не может быть выполнен.
Каковы же потенциальные причины получаемых исключений?
500 Internal server error — всё сломалось, но мы скоро починим
Это как раз тот случай, когда проблема произошла на стороне самого сервера, и клиентскому приложению остаётся только вздохнуть и уведомить пользователя о том, что сервер устал и прилёг отдохнуть. Например, утеряно соединение с базой данных или в коде завелся баг.
400 Bad request — а теперь у вас всё сломалось
Ответ прямо противоположный предыдущему. Возвращается в тех случаях, когда клиентское приложение отправляет запрос, который в принципе не может быть корректно обработан, не содержит обязательных параметров или имеет синтаксические ошибки. Обычно это лечится повторным прочтением документации к web API.
401 Unauthorized — незнакомец, назови себя
Для доступа к этому ресурсу требуется авторизация. Разумеется, наличие авторизации не гарантирует того, что ресурс станет доступным, но не авторизовавшись, вы точно этого не узнаете. Возникает, например, при попытке обратиться к закрытой части API или при истечении срока действия текущего токена.
403 Forbidden — вам сюда нельзя
Запрашиваемый ресурс существует, но у пользователя недостаточно прав на его просмотр или модификацию.
404 Not found — по этому адресу никто не живёт
Такой ответ возвращается, как правило, в трёх случаях: путь к ресурсу неверен (ошибочен), запрашиваемый ресурс был удалён и перестал существовать, права текущего пользователя не позволяют ему знать о существовании запрашиваемого ресурса. Например, пока просматривали список товаров, один из них внезапно вышел из моды и был удалён.
405 Method not allowed — нельзя такое делать
Эта разновидность исключения напрямую связана с использованным при запросе глаголом (GET, PUT, POST, DELETE), который, в свою очередь, свидетельствует о действии, которое мы пытаемся совершить с ресурсом. Если запрошенный ресурс не поддерживает указанное действие, сервер говорит об этом прямо.
422 Unprocessable entity — исправьте и пришлите снова
Одно из самых полезных исключений. Возвращается каждый раз, когда в данных запроса существуют логические ошибки. Под данными запроса мы подразумеваем либо набор параметров и соответствующих им значений, переданных методом GET, либо поля объекта, передаваемого в теле запроса методами POST, PUT и DELETE. Если данные не прошли валидацию, сервер в секции «data» возвращает отчет о том, какие именно параметры невалидны и почему.
Протокол HTTP поддерживает намного большее число различных статус-кодов на все случаи жизни, но на практике они используются редко и в контексте web API не несут практической пользы. На моей памяти мне не приходилось выходить за пределы вышеперечисленного списка исключений.
Запросы
Получение элементов коллекции
Одним из наиболее частотных запросов является запрос на получение элементов коллекции. Информационные ленты, списки товаров, различные информационные и статистические таблицы и многое другое клиентское приложение отображает посредством обращения к коллекционным ресурсам. Для осуществления этого запроса мы обращаемся к коллекции, используя метод GET и передавая в строке запроса дополнительные параметры. Как мы уже обозначили выше, в качестве ответа мы ожидаем получить массив однородных элементов коллекции и информацию, необходимую для пагинации — подгрузки продолжения списка или же конкретной его страницы. Содержимое выборки может быть особым способом ограничено и отсортировано с помощью передачи дополнительных параметров. О них и пойдёт речь далее.
Постраничная навигация
page — параметр указывает на то, какая страница должна быть отображена. Если этот параметр не передан, то отображается первая страница. Из первого же успешного ответа сервера будет ясно, сколько страниц имеет коллекция при текущих параметрах фильтрации. Если значение превышает максимальное число страниц, то разумнее всего вернуть ошибку 404 Not found.
GET /api/v1/news?page=1
perPage — указывает на желаемое число элементов на странице. Как правило, API имеет собственное значение по умолчанию, которое возвращает в качестве поля perPage в секции pagination, но в ряде случаев позволяет увеличивать это значение до разумных пределов, предоставив максимальное значение maxPerPage:
GET /api/v1/news?perPage=100
Сортировка результатов
Зачастую результаты выборки требуется упорядочить по возрастанию или убыванию значений определенных полей, которые поддерживают сравнительную (для числовых полей) или алфавитную (для строковых полей) сортировку. Например, нам нужно упорядочить список пользователей по имени или товары по цене. Помимо этого мы можем задать направление сортировки от A до Я или в обратном направлении, причём разное для разных полей.
sortBy — существует несколько подходов к передаче данных о сложной сортировке в GET параметрах. Здесь необходимо четко указать порядок сортировки и направление.
В некоторых API это предлагается сделать в виде строки:
GET /api/v1/products?sortBy=name.desc,price.asc
В других вариантах предлагается использовать массив:
GET /api/v1/products?
sortBy[0][field]=name&
sortBy[0][direction]=desc&
sortBy[1][field]=price&
sortBy[1][direction]=asc
В целом оба варианта равносильны, так как передают одни и те же инструкции. На мой взгляд, вариант с массивом более универсален, но тут, как говорится, на вкус и цвет…
Простая фильтрация по значению
Для того, чтобы отфильтровать выборку по значению какого либо поля, в большинстве случаев достаточно передать в качестве фильтрующего параметра имя поля и требуемое значение. Например, мы хотим отфильтровать статьи по ID автора:
GET /api/v1/articles?authorId=25
Усложнённые варианты фильтрации
Многие интерфейсы требуют более сложной системы фильтрации и поиска. Перечислю основные и наиболее часто встречаемые варианты фильтрации.
Фильтрация по верхней и нижней границе с использованием операторов сравнения from (больше или равно), higher (больше), to (меньше или равно), lower (меньше). Применяется к полям, значения которых поддаются ранжированию.
GET /api/v1/products?price[from]=500&price[to]=1000
Фильтрация по нескольким возможным значениям из списка. Применяется к полям, набор возможных значений которых ограничен, например, фильтр по нескольким статусам:
GET /api/v1/products?status[]=1&status[]=2
Фильтрация по частичному совпадению строки. Применяется к полям, содержащим текстовые данные или данные, которые могут быть приравнены к текстовым, например, числовые артикулы товаров, номера телефонов и т. д.
GET /api/v1/users?name[like]=John
GET /api/v1/products?code[like]=123
Именованные фильтры
В некоторых случаях, когда определенные наборы фильтрационных параметров часто употребимы и подразумеваются системой как нечто целостное, особенно если затрагивают внутреннюю, зачастую сложную механику формирования выборки, целесообразно сгруппировать их в так называемые именованные фильтры. Достаточно передать в запросе имя фильтра, и система построит выборку автоматически.
GET /api/v1/products?filters[]=recommended
Именованные фильтры могут также иметь свои параметры.
GET /api/v1/products?filters[recommended]=kidds
В этом подразделе я постарался рассказать о наиболее популярных вариантах и способах получения требуемой выборки. Скорее всего, в вашей практике наберется намного больше примеров и нюансов касаемо этой темы. Если у вас есть, чем дополнить мой материал, я буду только рад. Тем временем пост уже разросся до солидных масштабов, так что другие виды запросов мы разберём в следующем приближении.
За перевод материала выражаем благодарность международной IT-компании Noveo.
Взаимодействие Клиент-Сервер — Библиотека программиста
К содержанию
6. Взаимодействие Клиент-Сервер
Этот мир — клиент-серверный, малыш. Практически всё в сети происходит по клиент-серверной логике. Возьмём хоть Telnet. При подключении к удалённому узлу на 23 порт телнетом, программа на этом хосте (так называемый telnetd, сервер telnet) как бы просыпается, возвращается к жизни. Она обрабатывает входящее telnet-соединение, обрабатывает введённые вами логин и пароль, и т.д.
В этой диаграмме показан обмен данными между клиентом и сервером.
Обратим внимание, что клиент-серверная пара может «разговаривать» через SOCK_STREAM, SOCK_DGRAM, да и как угодно иначе — до тех пор, пока они говорят «на одном языке», то есть на одинаковом протоколе.
Некоторые хорошие примеры пар клиент-сервер: telnet/telnetd, FTP/FTPd, Firefox/Apache. Каждый раз, используя фтп, на другой стороне провода вы общаетесь с FTPD-сервером.
Обычно на машине запускается только один экземпляр сервера, который обрабатывает несколько клиентов, используя fork (). Основная процедура: сервер ждёт соединения, accetp () его и fork () — рождает дочерний процесс для обработки каждого соединения. Именно так и будет работать наш простой сервер из следующего раздела.
Простой TCP-сервер
Всё, что делает этот сервер — шлёт строку «Hello, World!n» через потоковое соединение. Всё, что вам нужно сделать для проверки этого сервера — запустить его в одном окне, а в другом запустить telnet и зайти на порт своего сервера:
telnet localhost 3490
Код сервера:
/*
** server.c — a stream socket server demo
*/#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>#define PORT «3490» // порт, на который будут приходить соединения#define BACKLOG 10 // как много может быть ожидающих соединенийvoid sigchld_handler(int s)
{
while(waitpid(— 1, NULL, WNOHANG) > 0);
}
// получаем адрес сокета, ipv4 или ipv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd, new_fd; // слушаем на sock_fd, новые соединения — на new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // информация об адресе клиента
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, «getaddrinfo: %sn», gai_strerror(rv));
return 1;
}
// цикл через все результаты, чтобы забиндиться на первом возможном
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == — 1) {
perror(«server: socket»);
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == — 1) {
perror(«setsockopt»);
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == — 1) {
close(sockfd);
perror(«server: bind»);
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, «server: failed to bindn»);
return 2;
}
freeaddrinfo(servinfo); // всё, что можно, с этой структурой мы сделали
if (listen(sockfd, BACKLOG) == — 1) {
perror(«listen»);
exit(1);
}
sa.sa_handler = sigchld_handler; // обрабатываем мёртвые процессы
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == — 1) {
perror(«sigaction»);
exit(1);
}
printf(«server: waiting for connections…n»);
while(1) { // главный цикл accept ()
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == — 1) {
perror(«accept»);
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf(«server: got connection from %sn», s);
if (!fork()) { // тут начинается дочерний процесс
close(sockfd); // дочернему процессу не нужен слушающий сокет
if (send(new_fd, «Hello, world!», 13, 0) == — 1)
perror(«send»);
close(new_fd);
exit(0);
}
close(new_fd); // а этот сокет больше не нужен родителю
}
return 0;
}
Весь код содержится в одной большой функции main () для большей синтаксической ясности. Если вам это кажется неудобным, разбейте код на функции поменьше.
(Новая функция — sigaction () — отвечает за подчисткой зомби-процессов, которые возникают после того, как дочерний (fork ()) процесс завершает работу. Если вы сделаете много зомби и не подчистите их, это не лучшим образом скажется на работе ОС).
Вы можете получить данные с сервера, используя написанный клиент, в следующем разделе.
Простой TCP-клиент
Эта штука ещё проще, чем сервер. Всё, что делает клиент — конектится к хосту, который вы укажете в командной строке, и к порту 3490. И примет строку, которую отошлёт сервер.
Код клиента:
/*
** client.c — a stream socket client demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define PORT «3490» // Порт, к которому подключается клиент
#define MAXDATASIZE 100 // максимальное число байт, принимаемых за один раз
// получение структуры sockaddr, IPv4 или IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,«usage: client hostnamen»);
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, «getaddrinfo: %sn», gai_strerror(rv));
return 1;
}
// Проходим через все результаты и соединяемся к первому возможному
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == — 1) {
perror(«client: socket»);
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == — 1) {
close(sockfd);
perror(«client: connect»);
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, «client: failed to connectn»);
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
s, sizeof s);
printf(«client: connecting to %sn», s);
freeaddrinfo(servinfo); // эта структура больше не нужна
if ((numbytes = recv(sockfd, buf, MAXDATASIZE— 1, 0)) == — 1) {
perror(«recv»);
exit(1);
}
buf[numbytes] = ‘;
printf(«client: received ‘%s’n»,buf);
close(sockfd);
return 0;
}
Обратите внимание, что если вы запустите клиент раньше сервера, connect () вернёт «Connection refused».
UDP-сокеты
Основы UDP-сокетов мы уже рассмотрели, когда обсуждали sendto и recvfrom, так что я просто приведу пару примеров — talker.c и listener.c
listener запущен на машине и ждёт входящие пакеты на порту 4950. talker посылает пакеты на это порт на указанной машине.
Код listener.c:
/*
** listener.c — a datagram sockets «server» demo
*/#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MYPORT «4950» // порт, к которому будет соединяться клиент
#define MAXBUFLEN 100
// получаем sockaddr, IPv4 или IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
char buf[MAXBUFLEN];
socklen_t addr_len;
char s[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // если нужен только IPv4, замените на AF_INET
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; // использовать мой IP
if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, «getaddrinfo: %sn», gai_strerror(rv));
return 1;
}
// цикл через все результаты, бинд на первый возможный
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == — 1) {
perror(«listener: socket»);
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == — 1) {
close(sockfd);
perror(«listener: bind»);
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, «listener: failed to bind socketn»);
return 2;
}
freeaddrinfo(servinfo);
printf(«listener: waiting to recvfrom…n»);
addr_len = sizeof their_addr;
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN— 1 , 0,
(struct sockaddr *)&their_addr, &addr_len)) == — 1) {
perror(«recvfrom»);
exit(1);
}
printf(«listener: got packet from %sn»,
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s));
printf(«listener: packet is %d bytes longn», numbytes);
buf[numbytes] = ‘;
printf(«listener: packet contains «%s«n», buf);
close(sockfd);
return 0;
}
Обратите внимание, что вызывая getaddrinfo () мы наконец-то используем SOCK_DGRAM. Также обратите внимание, что нет необходимости вызывать listen () или accept (). Это один из плюсов использования UDP!
Следующее — код talker.c:
/*
** talker.c — a datagram «client» demo
*/#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define SERVERPORT «4950» // порт для соединения
int main(int argc, char *argv[])
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
if (argc != 3) {
fprintf(stderr,«usage: talker hostname messagen»);
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, «getaddrinfo: %sn», gai_strerror(rv));
return 1;
}
// пробегаемся по результатам и создаём сокет
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == — 1) {
perror(«talker: socket»);
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, «talker: failed to bind socketn»);
return 2;
}
if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0,
p->ai_addr, p->ai_addrlen)) == — 1) {
perror(«talker: sendto»);
exit(1);
}
freeaddrinfo(servinfo);
printf(«talker: sent %d bytes to %sn», numbytes, argv[1]);
close(sockfd);
return 0;
}
Вот и всё, что необходимо сделать! Запустить «слушателя» на одной машине и «говорилку» на другой! И смотреть, как они общаются.
В принципе, вы не обязаны даже запускать сервер! Вы можете запустить только клиент, и он будет счастливо пулять пакеты в сеть, где они и сгинут, если никто на другом конце вызвать recvfrom (). Помните: UDP-сокет не даёт гарантии, что данные будут доставлены!
За исключением ещё одного случая, которую я уже не раз упоминал — UDP-сокета, устанавливающего соединение. Чтобы превратить наше приложение в устанавливающее соединение, talker должен вызывать connect () и указать адрес listener’а. с этого момента talker сможет отсылать и принимать данные только с адреса, указанного при connect (). И вместо sendto () и recvfrom () вы сможете использовать send () и recv ().
К содержанию
Клиент-сервер — Изучение веб-разработки | MDN
Теперь, когда вы знаете цель и потенциальные преимущества программирования на стороне сервера, мы подробно рассмотрим, что происходит, когда сервер получает «динамический запрос» от браузера. Поскольку большая часть серверного кода веб-сайта обрабатывает запросы и ответы аналогичным образом, это поможет вам понять, что нужно делать при написании большей части собственного кода.
Перед стартом: | Базовая компьютерная грамотность. Базовое понимание того, что такое веб-сервер. |
---|---|
Цель: | Изучить взаимодействие между клиентом и сервером на динамическом веб-сайте и, в частности, узнать, какие действия нужно произвести в коде серверной части. |
В обсуждении нет реального кода, поскольку мы ещё не выбрали, какой именно веб-фреймворк будем использовать для написания нашего кода! Тем не менее, это обсуждение всё ещё очень актуально, поскольку описанное поведение должно быть реализовано вашим серверным кодом независимо от того, какой язык программирования или веб-фреймворк вы выберите.
Веб-браузеры взаимодействуют с веб-серверами при помощи протокола передачи гипертекста (HTTP). Когда вы кликаете на ссылку на странице, заполняете форму или производите поиск, браузер отправляет на сервер HTTP-запрос.
Этот запрос включает:
- Путь (URL), который определяет целевой сервер и ресурс (например, HTML-файл, конкретная точка данных на сервере или запускаемый инструмент).
- Метод, который определяет необходимое действие (например, получить файл, сохранить или обновить какие-либо данные). Различные методы/команды и связанные с ними действия перечислены ниже:
GET
– получить определённый ресурс (например, HTML-файл, содержащий информацию о товаре или список товаров).POST
– создать новый ресурс (например, добавить новую статью на вики, добавить новый контакт в базу данных).HEAD
– получить метаданные об определённом ресурсе без получения содержания, как это делает запросGET
. Например, вы можете использовать запросHEAD
, чтобы узнать, когда ресурс в последний раз обновлялся, и только потом использовать (более «затратный») запросGET
, чтобы загрузить сам ресурс, если он был изменён.PUT
– обновить существующий ресурс (или создать новый, если таковой не существует).DELETE
– удалить определённый ресурс.TRACE
,OPTIONS
,CONNECT
,PATCH
– эти команды используются для менее популярных/более сложных задач, поэтому пока мы не будем их рассматривать.
- Дополнительная информация может быть закодирована в запросе (например, данные HTML-формы). Информация может быть закодирована как:
- URL-параметры:
GET
запросы зашифровывают данные в URL-адресе, который отправляется на сервер, путём добавления пар имя/значение в его конец, например,http://mysite.com?name=Fred&age=11
. В этом случае всегда ставится знак вопроса (?
), отделяющий основную часть URL-адреса от URL-параметров, знак равно (=), отделяющий каждое имя от соответствующего ему значения, и амперсанд (&), разделяющий пары. URL-параметры, по своей сути, «небезопасны», так как могут быть изменены пользователями и затем отправлены повторно. В результате, URL-параметры /GET
запросы не используются для запросов, которые обновляют данные на сервере. POST
данные.POST
запросы добавляют новые ресурсы, данные которых зашифрованы в теле самого запроса.- Куки-файлы клиентской части. Куки-файлы содержат данные сессий о клиенте, включая ключи, которые сервер может использовать для определения статуса его авторизации и разрешения/права доступа к ресурсам.
- URL-параметры:
Веб-серверы ожидают сообщений с запросами от клиентов, обрабатывают их, когда они приходят и отвечают веб-браузеру через сообщение с HTTP-ответом. Ответ содержит Код статуса HTTP-ответа, который показывает, был ли запрос успешным (например, «200 OK
» означает успех, «404 Not Found
» если ресурс не может быть найден, «403 Forbidden
», если пользователь не имеет права просматривать ресурс, и т. д.). Тело успешного ответа на запрос GET
будет содержать запрашиваемый ресурс.
После того как HTML-страница возвращена, она отрисовывается браузером. Во время этого браузер может обнаружить ссылки на другие ресурсы (например, HTML-страница обычно ссылается на JavaScript и CSS-файлы) и послать отдельные HTTP-запросы для загрузки этих файлов.
Как статические, так и динамические веб-сайты (речь о которых идёт в следующих разделах) используют точно такой же протокол/шаблоны обмена данными.
Пример GET запроса/ответа
Вы можете сформировать простой GET
запрос кликнув по ссылке или через поиск по сайту (такой как страница поисковой системы). Например, HTTP-запрос, отправленный во время выполнения запроса «client server overview» на сайте MDN, будет во многом похож на текст ниже (он не будет идентичным, потому что части сообщения зависят от вашего браузера/настроек).
Формат HTTP сообщения определён в «веб-стандарте» (RFC7230). Вам не нужно знать этот уровень детализации, но, по крайней мере, теперь вы знаете, откуда это появилось!
Запрос
Каждая строка запроса содержит информацию о запросе. Первая часть называется заголовок и содержит важную информацию о запросе, точно так же, как HTML head содержит важную информацию о HTML-документе (но не содержимое документа, которое расположено внутри тэга «body»):
GET https://developer.mozilla.org/en-US/search?q=client+server+overview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP/1.1 Host: developer.mozilla.org Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Referer: https://developer.mozilla.org/en-US/ Accept-Encoding: gzip, deflate, sdch, br Accept-Language: en-US,en;q=0.8,es;q=0.6 Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _gat=1; _ga=GA1.2.1688886003.1471911953; ffo=true
Первая и вторая строки содержат большую часть информации, о которой говорилось выше:
- Тип запроса (
GET
). - URL целевого ресурса (
/en-US/search
). - URL-параметры (
q=client%2Bserver%2Boverview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev
). - Целевой/хост-вебсайт (developer.mozilla.org).
- Конец первой строки также содержит короткую строку, идентифицирующую версию протокола (
HTTP/1.1
).
Последняя строка содержит информацию о клиентских куки — в данном случае можно увидеть куки, включающие id для управления сессиями (Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; ...
).
Оставшиеся строки содержат информацию об используемом браузере и о видах ответов, которые он может обработать. Например, здесь вы можете увидеть:
- Мой браузер (
User-Agent
) — Mozilla Firefox (Mozilla/5.0
). - Он может принимать информацию, упакованную в gzip (
Accept-Encoding: gzip
). - Он может принимать указанные кодировки (
Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7
) и языков (Accept-Language: de,en;q=0.7,en-us;q=0.3
). - Строка
Referer
идентифицирует адрес веб-страницы, содержащей ссылку на этот ресурс (то есть источник оригинального запроса,https://developer.mozilla.org/en-US/
).
HTTP-запрос может также содержать body, но в данном случае этого нет.
Ответ
Первая часть ответа на запрос показана ниже. Заголовок содержит следующую информацию:
- Первая строка содержит код ответа
200 OK
, говорящий о том, что запрос выполнен успешно. - Мы можем видеть, что ответ имеет
text/html
формат (Content-Type
). - Также мы видим, что ответ использует кодировку UTF-8 (
Content-Type: text/html; charset=utf-8
). - Заголовок также содержит длину ответа (
Content-Length: 41823
).
В конце сообщения мы видим содержимое body, содержащее HTML-код возвращаемого ответа.
HTTP/1.1 200 OK
Server: Apache
X-Backend-Server: developer1.webapp.scl3.mozilla.com
Vary: Accept,Cookie, Accept-Encoding
Content-Type: text/html; charset=utf-8
Date: Wed, 07 Sep 2016 00:11:31 GMT
Keep-Alive: timeout=5, max=999
Connection: Keep-Alive
X-Frame-Options: DENY
Allow: GET
X-Cache-Info: caching
Content-Length: 41823
<!DOCTYPE html>
<html lang="en-US" dir="ltr" data-ffo-opensanslight=false data-ffo-opensans=false >
<head prefix="og: http://ogp.me/ns#">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<script>(function(d) { d.className = d.className.replace(/\bno-js/, ''); })(document.documentElement);</script>
...
Остальная часть заголовка ответа содержит информацию об ответе (например, когда он был сгенерирован), сервере и о том, как он ожидает, что браузер обработает страницу (например, строка X-Frame-Options: DENY
говорит браузеру не допускать внедрения этой страницы, если она будет внедрена в <iframe>
на другом сайте).
Пример POST запроса/ответа
HTTP POST
создаётся, когда вы отправляете форму, содержащую информацию, которая должна быть сохранена на сервере.
Запрос
В приведённом ниже тексте показан HTTP-запрос, сделанный когда пользователь загружает новые данные профиля на этом сайте. Формат запроса почти такой же, как пример запроса GET
, показанный ранее, хотя первая строка идентифицирует этот запрос как POST
.
POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP/1.1
Host: developer.mozilla.org
Connection: keep-alive
Content-Length: 432
Pragma: no-cache
Cache-Control: no-cache
Origin: https://developer.mozilla.org
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: https://developer.mozilla.org/en-US/profiles/hamishwillee/edit
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8,es;q=0.6
Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; _gat=1; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _ga=GA1.2.1688886003.1471911953; ffo=true
csrfmiddlewaretoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT&user-username=hamishwillee&user-fullname=Hamish+Willee&user-title=&user-organization=&user-location=Australia&user-locale=en-US&user-timezone=Australia%2FMelbourne&user-irc_nickname=&user-interests=&user-expertise=&user-twitter_url=&user-stackoverflow_url=&user-linkedin_url=&user-mozillians_url=&user-facebook_url=
Основное различие заключается в том, что URL-адрес не имеет параметров. Как вы можете видеть, информация из формы закодирована в теле запроса (например, новое полное имя пользователя устанавливается с использованием: &user-fullname=Hamish+Willee
).
Ответ
Ответ от запроса показан ниже. Код состояния «302 Found
» сообщает браузеру, что сообщение обработано, и что необходим второй HTTP-запрос для загрузки страницы, указанной в поле Location
. В остальном информация аналогична информации для ответа на запрос GET
.
HTTP/1.1 302 FOUND
Server: Apache
X-Backend-Server: developer3.webapp.scl3.mozilla.com
Vary: Cookie
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
Date: Wed, 07 Sep 2016 00:38:13 GMT
Location: https://developer.mozilla.org/en-US/profiles/hamishwillee
Keep-Alive: timeout=5, max=1000
Connection: Keep-Alive
X-Frame-Options: DENY
X-Cache-Info: not cacheable; request wasn't a GET or HEAD
Content-Length: 0
На заметку: HTTP-ответы и запросы, показанные в этих примерах, были захвачены с помощью приложения Fiddler, но вы можете получить аналогичную информацию с помощью веб-снифферов (например, http://web-sniffer.net/) или с помощью расширений браузера, таких как HttpFox. Вы можете попробовать это сами. Воспользуйтесь любым из предложенных инструментов, а затем перейдите по сайту и отредактируйте информацию профиля, чтобы увидеть различные запросы и ответы. В большинстве современных браузеров также есть инструменты, которые отслеживают сетевые запросы (например, инструмент Network Monitor в Firefox).
Статический сайт — это тот, который возвращает тот же жёсткий кодированный контент с сервера всякий раз, когда запрашивается конкретный ресурс. Например, если у вас есть страница о товаре в /static/myproduct1.html
, эта же страница будет возвращена каждому пользователю. Если вы добавите ещё один подобный товар на свой сайт, вам нужно будет добавить ещё одну страницу (например, myproduct2.html
) и так далее. Это может стать действительно неэффективным — что происходит, когда вы попадаете на тысячи страниц товаров? Вы повторяли бы много кода на каждой странице (основной шаблон страницы, структуру и т. д.), И если бы вы захотели изменить что-либо в структуре страницы — например, добавить новый раздел «связанные товары» — тогда вам придётся менять каждую страницу отдельно.
На заметку: Статические сайты превосходны, когда у вас небольшое количество страниц и вы хотите отправить один и тот же контент каждому пользователю. Однако их обслуживание может потребовать значительных затрат по мере увеличения количества страниц.
Давайте вспомним, как это работает, снова взглянув на диаграмму архитектуры статического сайта, на которую мы смотрели в последней статье.
Когда пользователь хочет перейти на страницу, браузер отправляет HTTP-запрос GET
с указанием URL-адреса его HTML-страницы. Сервер извлекает запрошенный документ из своей файловой системы и возвращает HTTP-ответ, содержащий документ и код состояния HTTP Response status code 200 OK
(успех). Сервер может вернуть другой код состояния, например, «404 Not Found
», если файл отсутствует на сервере или «301 Moved Permanently
», если файл существует, но был перемещён в другое место.
Серверу для статического сайта нужно будет только обрабатывать GET-запросы, потому что сервер не сохраняет никаких модифицируемых данных. Он также не изменяет свои ответы на основе данных HTTP-запроса (например, URL-параметров или файлов cookie).
Понимание того, как работают статические сайты, тем не менее полезно при изучении программирования на стороне сервера, поскольку динамические сайты точно так же обрабатывают запросы для статических файлов (CSS, JavaScript, статические изображения и т. д.).
Динамический сайт — это тот, который может генерировать и возвращать контент на основе конкретного URL-адреса запроса и данных (а не всегда возвращать один и тот же жёсткий код для определённого URL-адреса). Используя пример сайта товара, сервер будет хранить «данные» товара в базе данных, а не отдельные HTML-файлы. При получении GET
-запроса для товара сервер определяет идентификатор товара, извлекает данные из базы данных и затем создаёт HTML-страницу для ответа, вставляя данные в HTML-шаблон. Это имеет большие преимущества перед статическим сайтом:
Использование базы данных позволяет эффективно хранить информацию о товаре с помощью легко расширяемого, изменяемого и доступного для поиска способа.
Использование HTML-шаблонов позволяет очень легко изменить структуру HTML, потому что это нужно делать только в одном месте, в одном шаблоне, а не через потенциально тысячи статических страниц.
Анатомия динамического запроса
В этом разделе представлен пошаговый обзор «динамического» цикла HTTP-запроса и ответа, основываясь на том, что мы рассмотрели в последней статье, с гораздо более подробной информацией. Чтобы не отдаляться от практики, мы будем использовать контекст веб-сайта менеджера спортивной команды, где тренер может выбрать имя своей команды и размер команды в HTML-форме и вернуться к предлагаемому «лучшему составу» для своей следующей игры.
На приведённой ниже диаграмме показаны основные элементы веб-сайта «team coach», а также пронумерованные ярлыки для последовательности операций, когда тренер обращается к списку «лучших команд». Частями сайта, которые делают его динамичным, являются веб-приложение (так мы будем ссылаться на серверный код, обрабатывающий HTTP-запросы и возвращающие HTTP-ответы), база данных, которая содержит информацию об игроках, командах, тренерах и их отношениях, и HTML-шаблоны.
После того, как тренер отправит форму с именем команды и количеством игроков, последовательность операций будет следующей:
- Веб-браузер отправит HTTP-запрос
GET
на сервер с использованием базового URL-адреса ресурса (/best
) и кодирования номера команды и игрока в форме URL-параметров (например,/best?team=my_team_name&show=11)
или как часть URL-адреса (например,/best/my_team_name/11/
). ЗапросGET
используется, потому что речь идёт только о запросе выборки данных (а не об их изменении). - Веб-сервер определяет, что запрос является «динамическим» и пересылает его в веб-приложение для обработки (веб-сервер определяет, как обрабатывать разные URL-адреса на основе правил сопоставления шаблонов, определённых в его конфигурации).
- Веб-приложение определяет, что цель запроса состоит в том, чтобы получить «лучший список команд» на основе URL (
/best/
) и узнать имя команды и количество игроков из URL-адреса. Затем веб-приложение получает требуемую информацию из базы данных (используя дополнительные «внутренние» параметры, чтобы определить, какие игроки являются «лучшими», и, возможно, определяя личность зарегистрированного тренера из файла cookie на стороне клиента). - Веб-приложение динамически создаёт HTML-страницу, помещая данные (из базы данных) в заполнители внутри HTML-шаблона.
- Веб-приложение возвращает сгенерированный HTML в веб-браузер (через веб-сервер) вместе с кодом состояния HTTP 200 («успех»). Если что-либо препятствует возврату HTML, веб-приложение вернёт другой код, например, «404», чтобы указать, что команда не существует.
- Затем веб-браузер начнёт обрабатывать возвращённый HTML, отправив отдельные запросы, чтобы получить любые другие файлы CSS или JavaScript, на которые он ссылается (см. шаг 7).
- Веб-сервер загружает статические файлы из файловой системы и возвращает их непосредственно в браузер (опять же, правильная обработка файлов основана на правилах конфигурации и сопоставлении шаблонов URL).
Операция по обновлению записи в базе данных будет обрабатываться аналогичным образом, за исключением того, что, как и любое обновление базы данных, HTTP-запрос из браузера должен быть закодирован как запрос POST.
Выполнение другой работы
Задача веб-приложения — получать HTTP-запросы и возвращать HTTP-ответы. Хотя взаимодействие с базой данных для получения или обновления информации является очень распространённой задачей, код может делать другие вещи одновременно или вообще не взаимодействовать с базой данных.
Хорошим примером дополнительной задачи, которую может выполнять веб-приложение, является отправка электронной почты пользователям для подтверждения их регистрации на сайте. Сайт также может выполнять протоколирование или другие операции.
Возвращение чего-то другого, кроме HTML
Серверный код сайта может возвращать не только HTML-фрагменты и файлы в ответе. Он может динамически создавать и возвращать другие типы файлов (текст, PDF, CSV и т. д.) или даже данные (JSON, XML и т. д.).
Идея вернуть данные в веб-браузер, чтобы он мог динамически обновлять свой собственный контент (AJAX) существует довольно давно. Совсем недавно «Одностраничные приложения» стали популярными, где весь сайт написан с одним HTML-файлом, который динамически обновляется по мере необходимости. Веб-сайты, созданные с использованием приложений такого рода, переносят большие вычислительные затраты с сервера на веб-браузер и приводят к тому, что веб-сайты, ведут себя больше как нативные приложения (очень отзывчивые и т. д.).
Веб-фреймворки на стороне сервера делают написание кода для обработки описанных выше операций намного проще.
Одной из наиболее важных операций, которые они выполняют, является предоставление простых механизмов для сопоставления URL-адресов для разных ресурсов/страниц с конкретными функциями обработчика. Это упрощает сохранение кода, связанного с каждым типом ресурса, отдельно от остального. Это также имеет преимущества с точки зрения обслуживания, поскольку вы можете изменить URL-адрес, используемый для доставки определённой функции в одном месте, без необходимости изменять функцию обработчика.junior/$’, потому что они используют метод сопоставления шаблонов под названием «регулярные выражения» (RegEx или RE). Вам не нужно знать, как работают регулярные выражения на этом этапе, кроме того, что они позволяют нам сопоставлять шаблоны в URL-адресе (а не жёстко закодированные значения выше) и использовать их в качестве параметров в наших функциях просмотра. В качестве примера, действительно простой RegEx может говорить «соответствовать одной заглавной букве, за которой следуют от 4 до 7 строчных букв».
Веб-фреймворк также упрощает функцию просмотра для получения информации из базы данных. Структура наших данных определяется в моделях, которые являются классами Python, которые определяют поля, которые должны храниться в основной базе данных. Если у нас есть модель с именем Team с полем «team_type», мы можем использовать простой синтаксис запроса, чтобы получить все команды, имеющие определённый тип.
В приведённом ниже примере представлен список всех команд, у которых есть точный (с учётом регистра) team_type
«junior» («младший») — обратите внимание на формат: имя поля (team_type
), за которым следует двойной знак подчёркивания, а затем тип соответствия для использования (в этом случае exact
(«точное»)). Существует много других типов соответствия, и мы можем объединить их. Мы также можем контролировать порядок и количество возвращаемых результатов.
from django.shortcuts import render
from .models import Team
def junior(request):
list_teams = Team.objects.filter(team_type__exact="junior")
context = {'list': list_teams}
return render(request, 'best/index.html', context)
После того, как функция junior()
получает список младших команд, она вызывает функцию render()
, передавая исходный HttpRequest
, HTML-шаблон и объект «context», определяющий информацию, которая должна быть включена в шаблон. Функция render()
— это функция удобства, которая генерирует HTML с использованием контекста и HTML-шаблона и возвращает его в объект HttpResponse
.
Очевидно, что веб-фреймворки могут помочь вам в решении многих других задач. В следующей статье мы обсудим намного больше преимуществ и некоторые популярные варианты веб-фреймворков.
На этом этапе вы должны хорошо ознакомиться с операциями, которые должен выполнять серверный код, и знать некоторые способы, с помощью которых веб-фреймворк на стороне сервера может сделать это проще.
В следующем модуле мы поможем вам выбрать лучший веб-фреймворк для вашего первого сайта.
Как сэкономить 30% на создании клиент-серверного приложения
Собираетесь сделать клиент-серверное приложение, а вам говорят, что придется раскошелиться? Вот уж нет! Есть люди, которые знают, как сократить бюджет на разработку на 30%, сделать задачу быстрее, а качество при этом будет выше. Звучит немного голословно, но это мы, и мы можем доказать, что такое бывает.
Если перед вами стоит задача разработать мобильное приложение, с 90-процентной вероятностью речь идет о клиент-серверном приложении. «Клиент-серверное» означает, что часть данных обрабатывается и хранится не на телефоне пользователя, а где-то на сервере.
Вот список критериев, указывающих на то, что приложение клиент-серверное:
- регистрация пользователей;
- авторизация в приложении с помощью соцсетей;
- поиск по материалам приложения;
- постоянно обновляемый контент;
- необходимость в интернет-подключении для работы.
У вас может создаться впечатление, что все приложения, которыми мы пользуемся — клиент-серверные. Верно, практически все. Не клиент-серверные только те, где пользователи никак не взаимодействуют с другими пользователями, например игра Angry Birds. Вы играете одни, других живых соперников нет. Но даже тут издатели используют альтернативу бэкэнду — например Game Center на iOS, чтобы пользователи могли видеть очки друг друга и появлялся соревновательный элемент. А также чтобы собирать данные о вас, конечно же. Еще пример — приложения, для работы которых не нужен интернет: фоторедакторы, калькуляторы, компас 🙂
Обычно команда состоит из фронтэнд- и бэкэнд-разработчика, дизайнера, тестировщика и проект-менеджера. Два человека нужно потому, что технологии и фреймворки для этих компонентов используются разные.
Фронтэнд-разработчик отвечает за ту часть приложения, с которой взаимодействует пользователь: что он видит у себя на экране, куда нажимает, что там происходит.
Бэкэнд-разработчик создает серверную часть приложения, работает с базами данных. И вот первая возможность для экономии: если серверная часть вашего приложения очень простая, а база данных — небольшая, можно обойтись без отдельного разработчика и использовать специальные сервисы для бэкэнда.
Функции дизайнера и тестировщика понятны, проект-менеджер же займется управлением и синхронизацией всей команды. Если приложение достаточно масштабное, можно привлекать несколько фронтэнд- и бэкэнд-разработчиков. Некоторые опытные разработчики могут писать и фронтэнд- и бэкэнд-компоненты, но не факт что их время стоит меньше, чем сумма тарифов отдельных разработчиков. Кроме того, уйдет больше времени на разработку, потому что работа ведется последовательно, а не одновременно.
Мы в «Культпросвете» разрабатываем клиент-серверные приложения вот уже несколько лет. Всегда стремимся оптимизировать рутинные процессы и найти самое эффективное решение задачи, чтобы сэкономить время и средства клиента. Так выше шанс, что клиент вернется снова.
В процессе создания очередного приложения нашей команде пришла в голову мысль: а что, если не писать одинаковые компоненты для каждого клиента с нуля, а сделать универсальный шаблон, который бы потом можно было кастомизировать под нужды конкретного проекта?
Мы составили список наиболее часто встречающихся компонентов всех клиент-серверных приложений и создали шаблоны для них:
- регистрация пользователей;
- авторизация в приложении с помощью соцсетей;
- поиск по материалам приложения;
- встроенный чат;
- звонки;
- расчерченная таблица базы данных пользователей;
- расчерченная таблица базы данных администраторов;
- методы управления пользователями;
- профиль пользователя и его редактирование;
- настройки приложения;
- управление уведомлениями;
- смена пароля;
- отображение версии приложения;
- выход из приложения.
Чтобы шаблоны были по-настоящему универсальны и продуманы, мы писали их с помощью фреймворка Laravel. В предыдущей статье мы рассказывали, почему считаем это решение на самом деле самым лучшим из всех, что существуют сегодня.
Для хостинга серверной части мы используем Amazon EC2, а Amazon S3 — в качестве хранилища больших файлов. Встроенные чаты и звонки делаем с помощью решения Twilio.
По самым скромным нашим подсчетам, функционал приложений, который поддается шаблонированию, занимает минимум 30% всего кода. Кастомизировать готовый шаблон куда быстрее, чем писать с нуля. Для сравнения, разработка функции логина в приложение с автоматической отправкой письма обычно занимала неделю. Неделю, Карл! А теперь, когда у нас есть шаблон, на его кастомизацию уходит максимум день.
Давайте приведем примеры приложений из наших предыдущих статей:
Стоимость разработки бэкэнда при обычном подходе | Стоимость разработки бэкэнда с шаблонами | |
---|---|---|
Tinder | $4000 | $2800 |
Kayak | $8500 | $5950 |
Uber | $15 000 | $10 000 |
Как видите, экономия заметная. Надеемся, другие компании последуют нашему примеру: оптимизируют рутину, напишут шаблоны и порадуют клиентов меньшим бюджетом на задачу.
БиномТех | Разработка мобильных приложений
Компания БиномТех является разработчиком нативных клиент-серверных приложений под Android и iOS. Нативная разработка является «родной» для операционных систем – Android, IOS. Такие мобильные приложения пишутся на «родных» языках под каждую конкретную платформу. Главное преимущество нативных приложений – они оптимизированы под конкретные операционные системы, а значит работают корректно, значительно быстрее ненативных и имеют экономный расход ресурсов телефона. Они имеют доступ к аппаратной части устройств, то есть могут использовать в своем функционале камеру смартфона, микрофон, акселерометр, геолокацию, адресную книгу, плеер и т.д.
Разработка мобильных приложений является для нас приоритетным направлением.
Мобильные приложения для Android разрабатываются в Android Studio. Исходные коды передаются заказчику на Java. Мобильные приложения для iOS разрабатываются на Objective-C или Swift.
Для того чтобы создать мобильное приложение наши специалисты составляют техническое задание на разработку мобильного приложения. Это необходимо для того, чтобы и Заказчик, и Исполнитель одинаково понимали объем выполняемых работ.
Компания имеет практический опыт разработки сервисов с геопозиционированием с использованием Google, Yandex и OpenStreetMap карт c вариантами использования отслеживания ресурсов. Быстрое построение кластеров при огромном количестве объектов, построение маршрутов на карте, предварительное кэширование участков карт. Компанией разработано приложение для дальнобойщиков по отображению местоположения объектов таких как АЗС, стоянки и т. д. Кнопка Тревога умеющая информировать рядом находящихся, что данному пользователю необходима помощь.
Разработан сервис с мобильным приложением для работы замерщиков и монтажников потолков. Сервис предназначен для рассылки оператором с веб-интерфейса задач на обработку для монтажников на мобильные устройства и контроль их выполнения в режиме реального времени. Данный сервис может быть легко адаптирован для постановки задач оператором и отслеживания их выполнения курьером.
Более подробное описание.
Мобильное приложение по работе со штрих-кодами. Приложение с мобильного телефона сканирует штрих-код и фотографирует товар. Информация размещается в базе данных. Приложение в Play Маркете
Одной из выполненных работ является агрегатор интернет-магазинов с товарами по спортивному питанию и их заказ. В приложении также присутствует дневник питания для планирования потребления ккал и дневник тренировок. Всевозможная статистика и графики
Онлайн игра с геопозиционированием и анимацией на карте. Отображение и взаимодействие нескольких пользователей на одной карте в режиме реального времени
В портфолио компании БиномТех есть сервис позволяющий управлять информацией по отслеживанию на карте владельца часов GPS-трекера. Сервис имеет возможность он-лайн слежения, истории передвижения, уведомление о разряде батареи, Push-уведомления при выходе или входе в определенную геозону
Мобильное приложение для водителей и транспортных компаний автоматизирующее взаимодействие их с диспетчерским центром и позволяет подбирать на рейс и отслеживать автомобили в учетной системе 1С.
Разработано 2 мобильных приложения, реализующих использование программ лояльности курортных городов, предназначенных для компаний, предоставляющих скидку туристам.
Приложение по управлению медицинскими светильниками. Приложение ищет подключаемое оборудование по Wi-Fi сети и по бинарному протоколу управляет настройками блоков освещения. Реализована функция управления, просмотра и записи встроенной в светильник видеокамеры.
В разработке мобильное приложение для проекта Маджорис который представляет собой социальную сеть для автолюбителей, с ключевым упором на госномера автомобилей. Приложение управляет личным кабинетом пользователя. Имеет встроенный чат, функционал публикации новостей с использованием камеры устройства, отображение фото и трансляцию видео. Отображение объектов на карте и построение маршрутов.
10.1.2 TCP и модель клиент/сервер
Читайте также
Обмен сообщениями и модель «клиент/сервер»
Обмен сообщениями и модель «клиент/сервер»
Представьте, что приложение читает данные из файловой системы. На языке QNX это значит, что данное приложение — это клиент, запрашивающий данные у сервера.Модель «клиент/сервер» позволяет говорить о нескольких рабочих
12.2. Клиент IPv4, сервер IPv6
12.2. Клиент IPv4, сервер IPv6
Общим свойством узла с двойным стеком является то, что серверы IPv6 могут выполнять обслуживание клиентов IPv4 и IPv6. Это достигается за счет преобразования адресов IPv4 к виду IPv6 (см. рис. А.6). Пример такого преобразования приведен на рис. 12.1.
Рис. 12.1.
12.3. Клиент IPv6, сервер IPv4
12.3. Клиент IPv6, сервер IPv4
Теперь мы поменяем протоколы, используемые клиентом и сервером в примере из предыдущего раздела. Сначала рассмотрим TCP-клиент IPv6, запущенный на узле с двойным стеком протоколов.1. Сервер IPv4 запускается на узле, поддерживающем только IPv4, и создает
4.2. Приложение типа клиент-сервер
4.2. Приложение типа клиент-сервер
Пример приложения модели клиент-сервер приведен на рис. 4.1. Именно на него мы будем ссылаться в тексте этой главы и главы 6 при необходимости проиллюстрировать использование программных каналов, FIFO и очередей сообщений System V.Клиент
6.7. Пример программы клиент-сервер
6.7. Пример программы клиент-сервер
Перепишем наш пример программы типа клиент-сервер из раздела 4.2 с использованием двух очередей сообщений. Одна из очередей предназначена для передачи сообщений от клиента серверу, а другая — в обратную сторону.Заголовочный файл svmsg.h
11.6.8.4. Пара клиент/сервер
11.6.8.4. Пара клиент/сервер
Пара клиент/сервер подобна паре драйвер/ядро, за исключением того, что часть ядра является запущенным в фоновом режиме демоном, от которого не требуется интерактивная работа и наличие собственного пользовательского интерфейса. Обычно демон
11.6.8.4. Пара клиент/сервер
11.6.8.4. Пара клиент/сервер
Пара клиент/сервер подобна паре драйвер/ядро, за исключением того, что часть ядра является запущенным в фоновом режиме демоном, от которого не требуется интерактивная работа и наличие собственного пользовательского интерфейса. Обычно демон
1.2 Понятие архитектуры клиент-сервер.
1.2 Понятие архитектуры клиент-сервер.
В подавляющем большинстве случаев локальная сеть используется для коллективного доступа к базам данных. Существует два подхода к организации коллективного доступа данным. Первый подход заключается в том , что файлы данных
2.2.9.1 Взаимодействие клиент-сервер
2.2.9.1 Взаимодействие клиент-сервер
Продукты INFORMIX построены на принципах архитектуры клиент/сервер. Это означает, что сервер INFORMIX-OnLine DS выполняется на одном компьютере, а клиентские приложения выполняются на других компьютерах, связанных с сервером сетью. При этом от
Клиент-сервер
Клиент-сервер
Средства локального доступа.* Локальная заглушка TCP/IP. Для многоуровневых серверных приложений и других клиентов доступ к локальному серверу на любой поддерживаемой платформе осуществляется через протокол TCP/IP: даже при отсутствии сетевой карты соединение
Характеристики СУБД клиент-сервер
Характеристики СУБД клиент-сервер
Масштабируемость
Появление сравнительно недорогих компьютерных сетей между 1980-ми и 1990-ми годами вызвало увеличенный спрос на масштабируемые информационные системы с дружественным пользователю интерфейсом. Программное обеспечение
Двухуровневая архитектура клиент-сервер
Двухуровневая архитектура клиент-сервер
На рис. 6.1 изображена гибкая система, где множество серверов Firebird выполняются на платформах с различными операционными и файловыми системами. Здесь присутствует смесь рабочих станций, на которых выполняются удаленные клиенты,
Динамические приложения клиент-сервер
Динамические приложения клиент-сервер
Во время выполнения программы приложениям часто бывают нужны операторы SQL, которые создаются или изменяются приложениями или вводятся пользователями. Приложения обычно предоставляют пользователю списки выбора, извлекаемые из
XSLT в архитектуре клиент-сервер
XSLT в архитектуре клиент-сервер
Многие из систем, применяющих XSLT, так или иначе, сводятся к клиент- серверной архитектуре, в которой клиент делает запрос, а сервер в качестве ответа возвращает некоторые данные. XSLT в таких решениях может использоваться для приведения
Программирование Назначение 1: Программирование розеток
Вернуться на домашнюю страницу CS 641
Цель проекта
В этом задании мы построим простую систему клиент-сервер,
где вы используете клиент для общения с фиктивным «математическим» сервером. В
протокол между клиентом и сервером выглядит следующим образом.
- Сервер сначала запускается на известном порту.
- Клиентская программа запущена (IP-адрес и порт сервера указаны в командной строке).
- Клиент подключается к серверу, а затем запрашивает у пользователя
Вход. Пользователь вводит простую строку арифматического выражения (например, «1 + 2», «5-6», «3 * 4»). В
ввод пользователя отправляется на сервер через подключенный сокет. - Сервер считывает ввод пользователя из клиентского сокета, оценивает выражение и отправляет результат обратно клиенту.
- Клиент должен отображать ответ сервера пользователю и предлагать пользователю ввести следующий ввод, пока пользователь не завершит клиентскую программу нажатием Ctrl + C.
Вам предоставляется клиент (исходный код). Вы напишете три версии сервера:
- Ваша серверная программа server1 будет отдельным сервером процессов, который может одновременно обрабатывать только одного клиента. Если второй клиент пытается поговорить с сервером, когда сеанс одного клиента уже выполняется, операции сокета второго клиента должны увидеть ошибку.
- Ваша серверная программа server2 будет многопроцессорным сервером, который будет разветвлять процесс для каждого нового полученного клиента.Несколько клиентов должны иметь возможность одновременно общаться с сервером.
- Ваша серверная программа «server3» будет одним сервером процесса, который
использует системный вызов select для обработки нескольких клиентов. Опять же, много
как и server2, server3 также сможет обрабатывать несколько клиентов
одновременно.
Как минимум, все ваши серверы должны уметь обрабатывать
операции сложения, умножения, вычитания и деления над двумя
целочисленные операнды. Вы также можете предположить, что два операнда
разделенные пробелом.Итак, несколько примеров тестов:
- Типы пользователей: 1 + 2, ответов сервера 3
- Типы пользователей: 2 * 3, ответов сервера 6
- Типы пользователей: 4-7, ответов сервера -3
- Типы пользователей: 30/10, ответов сервера 3
Обратите внимание, что фактические тестовые случаи, которые мы будем использовать, могут отличаться от
показанные выше: ваши серверы должны корректно работать с любыми
числа, а не только показанные выше, если они подтверждают
этот формат. Обработка нецелочисленных операндов, другие арифметические
операции или операции с более чем двумя операндами (например,г., «1 + 2 +
3 «) является необязательным.
Несколько хороших руководств и полезной документации по сокету
программирование и проектирование серверов для одновременных клиентов (с использованием обоих
fork и select) доступны в Интернете. Пожалуйста, используйте эти
ресурсы для самостоятельного изучения тонкостей программирования сокетов
во время этого задания.
Напишите свой код для этого задания на C / C ++. Пожалуйста
поговорите со мной, если у вас есть веская причина использовать любой другой язык.
Клиент
Вот копия клиентского источника
код.Ниже приведен пример запуска клиента. В этом примере
клиентский код сначала компилируется. Затем сервер запускается на порту 5000 в
другой терминал. Затем клиентской программе предоставляется IP-адрес сервера.
(в данном случае localhost 127.0.0.1) и порт (5000) в качестве командной строки
входы.
$ gcc client.c -o клиент $ ./client 127.0.0.1 5000 Подключено к серверу Введите сообщение на сервер: 22 + 44 Сервер ответил: 66 Введите сообщение на сервер: 3 * 4 Сервер ответил: 12 ... ...
Параллельно вот как выглядит вывод на сервере вот так
(вы можете распечатать более или менее отладочную информацию).Обратите внимание, что
вывод сервера, показанный ниже, предназначен только для иллюстрации, и мы
не оценивать вас на основе результатов отладки вашего сервера. Мы будем в первую очередь
оценивать вас на основе того, возвращает ли ваш сервер правильные ответы на
клиенты или нет.
$ gcc server1.c -o server1 $ ./server1 5000 Подключен к клиентскому сокету номер 4 Клиентский сокет 4 отправил сообщение: 22 + 44 Отправка ответа: 66 Клиентский сокет 4 отправил сообщение: 3 * 4 Отправка ответа: 12 ... ...
Серверы
Часть 1: Сервер с одним процессом
Сначала вы напишете простой
server в файле с именем «server1.c «. Программа server1 должна принимать
номер порта из командной строки и запустить прослушивающий сокет на
эту командную строку. Каждый раз, когда клиентский запрос поступает в этот сокет,
сервер должен принять соединение, прочитать запрос клиента,
и вернем результат. После ответа на одно сообщение сервер
затем следует дождаться чтения следующего сообщения от клиента, пока
клиент хочет поболтать. После завершения работы клиента (чтение сокета
терпит неудачу), сервер должен вернуться в режим ожидания другого клиента.В
сервер должен завершить работу по Ctrl + C.
Ваш простой server1 НЕ должен обрабатывать несколько клиентов одновременно
(только один за другим). То есть, когда server1 работает с
клиент, другой клиент, который пытается общаться с тем же сервером, должен
увидеть сообщение об ошибке. Однако второй клиент должен быть успешным, если
первый клиент уволился.
Часть 2: Многопроцессорный сервер
Примечание: Для частей 2 и 3 ниже ваш сервер должен вести себя
как любой реальный сервер.Он должен иметь возможность обрабатывать несколько клиентов.
одновременно. Он должен работать нормально, поскольку клиенты приходят и уходят. Ваш сервер
должен всегда работать (до тех пор, пока не будет остановлен с помощью Ctrl + C), и должен
не уходить по какой-либо другой причине. Если вы сомневаетесь в чем-либо
функциональность сервера, подумайте, что будет делать настоящий сервер, и
используйте это как руководство для своей реализации.
Для части 2 вы напишете server2.c, чтобы иметь возможность обрабатывать несколько
клиентов симулированно, создавая отдельный процесс для каждого
клиент.Вы должны иметь возможность повторно использовать большую часть кода из server1.c,
но ваш server2.c должен компилироваться и запускаться независимо от вашего кода в
Часть 1.
Часть 3: Параллельный сервер с «select»
Теперь напишете
server3.c для обработки нескольких вызовов с помощью системного вызова «select». В
отличие от части 2 в том, что вы не будете использовать fork () для создания
новый процесс для каждого клиента, но вы будете обрабатывать всех клиентов
розетки в одном процессе. Поведение сервера относительно
для правильной работы с несколькими клиентами то же самое, что и
спецификация в части 2 выше.
Инструкции по подаче
Вы должны выполнять проект в группах по одному или два человека. Если вы работаете с
другой студент, вы оба должны вносить равный вклад в
присвоение (т.е. не оставлять одного человека для написания кода
один).
Чтобы отправить свой PA, создайте папку для отправки, имя которой
объединение номеров бросков членов вашей команды, разделенных
подчеркивать («_»). Например, имя вашей папки может быть
«15000001_15000002». Поместите все свои файлы в эту папку, затем создайте
сжатый tar-архивом, в имени которого указаны все номера рулонов (например,грамм.,
«15000001_15000002.tgz») и отправьте в Moodle. Например, перейдите в
каталог с папкой для отправки и выполните «tar -zcvf
15000001_15000002.tgz 15000001_15000002 «.
Ваша папка для отправки должна содержать следующие файлы.
- server1.c, server2.c, server3.c, как описано выше. Пожалуйста, следите
строго соблюдайте правила для имен файлов. - Необязательный сценарий make / build для компиляции вашего кода. В противном случае будет использоваться простой gcc, как показано в примере вывода выше.
- testcases.txt с подробным описанием различных сценариев, которые вы
протестировали ваши три сервера с помощью. Особо выделите любые необязательные
случаев (например, операции с 3 операндами, например «1 + 2 + 3»), которые вы
протестировали ваш код с помощью.
Примечание: Пожалуйста, задокументируйте свой код должным образом. Поскольку мы будем читать
через ваш код во время оценивания, четко написанный и хорошо документированный
код принесет вам больше оценок в дополнение к выполнению работы оценщика
легко.
Тестирование
Мы проведем следующие тесты, чтобы оценить ваше задание.это
настоятельно рекомендуется проводить на своих серверах такие же тесты, как
задолго до подачи. Пожалуйста, укажите, какие из этих тестов у вас есть
успешно протестирован в вашем представлении testcases.txt. Кроме того,
перечислите любые дополнительные тесты, которые вы могли придумать на своем
собственный.
- [TEST1]: для сервера 1 мы запустим одного клиента, подключимся к
сервер и проверьте все 4 арифметические операции (+, -, *, /) с двумя
операнды каждый. Мы проверим, что результаты, возвращаемые сервером
клиенту верны. - [TEST2]: для server1 мы запустим клиента, посчитаем
операций (например, TEST1), затем завершите работу клиента, запустите второй
клиент и убедитесь, что второй клиент может общаться с сервером как
Что ж. - [TEST3]: для сервера 1 мы попытаемся подключить второго клиента, когда
первый все еще подключен, и проверьте, что его операции с сокетом
провал. - [TEST4]: Для сервера 2 мы проверим правильность арифметики
операции для одного клиента, как в TEST1. - [TEST5]: для server2 мы проверим, что несколько клиентов могут
одновременное подключение и чат с сервером правильно. - [TEST6]: для server2 мы подключим клиента, затем подключимся и
отключите второго клиента. Первый клиент должен продолжить
работают правильно. - [TEST7], [TEST8], [TEST9]: повторяет три вышеуказанных теста для
server3 тоже. - [TEST10]: мы проверим, что server2 запускает несколько процессов для
несколько клиентов, а server3 — нет.
Оценка
Ниже представлена схема выставления оценок за это задание. Это задание
несет 25 баллов (с учетом 10% оценки).
- 5 баллов за проверку кода (читаемость, документация, правильное следование спецификации).
- 10 баллов за 10 тестовых случаев, перечисленных выше.
- 10 баллов за письменный тестовый вопрос.
Обратите внимание, что оценка отправки кода и письменного теста не
независимый. Например, если вы плохо сдали письменный тест, отметки
выделенный для вашего кода также будет наказан, так как это будет
предполагается, что вы не решали задание самостоятельно.
Удачи!
Вернуться на домашнюю страницу CS 641
Как создать простое соединение клиент-сервер в C Часть-1 «Null Byte :: WonderHowTo
Привет, я здесь новичок, и, поскольку я не вижу много руководств на C, я подумал о том, чтобы внести свой вклад относительно просто.Это будет короткая серия.
Это руководство предназначено для тех, кто уже знает, как программировать на C и имеет базовое представление о том, как работают сети … но, тем не менее, я буду объяснять это шаг за шагом.
Эта серия предназначена для использования в Linux или других Unix-подобных системах.
В этой части я не буду представлять код, потому что сначала мне нужно будет поговорить о некоторых ключевых концепциях. Во второй части мы начнем строить наш базовый сервер на C, а в третьей или четвертой части — клиент.
Сетевая розетка
Итак, что такое сетевая розетка? Network-Socket — это конечная точка взаимодействия клиент-сервер или наоборот. В основном это то, что мы используем для отправки данных на сервер или клиент, или, другими словами, это двусторонний канал связи между двумя программами. Сокет привязан к порту, поэтому уровень TCP может определить приложение, которому предназначены данные.
Пример:
Сервер обычно ожидает соединений от клиента. Они делают это, слушая сокет.
Когда клиент делает запрос на соединение (зная имя хоста и порт, который прослушивает сервер), ему необходимо идентифицировать себя для сервера, чтобы он привязался к номеру локального порта, который он будет использовать во время этого соединения. Это назначается системой.
Если все идет хорошо, сервер принимает его соединение и создает новый сокет для того же порта. Также его удаленная конечная точка настроена на адрес и порт клиента. Ему нужен новый сокет, чтобы он мог продолжать прослушивать исходный сокет для запросов на соединение, одновременно обслуживая клиента.
Если соединение принято, на стороне клиента создается сокет, чтобы он мог взаимодействовать с сервером.
Следовательно, и клиент, и сервер могут сообщать чтение или запись в свои сокеты.
Типы сокетов
Потоковые сокеты: для связи используется протокол TCP. Если мы хотим отправить сообщение с помощью этого сокета, указанное сообщение будет отправлено на сервер по одному символу сообщения за раз. Это то, что мы будем использовать в этой серии.
Датаграммные сокеты: этот сокет использует протокол UDP для связи и сразу отправляет все сообщение на сервер.
Дескрипторы
Дескриптор — это целое число низкого уровня для идентификации открытого сокета или других источников ввода / вывода (файлов и т. Д.) На уровне ядра в Linux и других Unix-подобных системах. Эти дескрипторы содержатся в структуре процесса.
Пример:
Если мы откроем файл, ОС создаст запись в таблице для представления этого файла и сохранит содержимое. Эта запись представлена целыми числами (1,2,3,4, …). Это целое число является дескриптором, в данном случае файловым дескриптором.В розетках тоже самое. Когда мы открываем сокет, создается запись, содержимое сохраняется, и у нас есть дескриптор сокета.
Окончание части 1
А пока это все, ребята. Надеюсь, вам понравилась моя первая часть этой серии, я впервые пишу что-то подобное, поэтому, если я где-то допустил ошибку, не стесняйтесь исправить меня, это будет очень признательно.
Хотите начать зарабатывать деньги как хакер в белой шляпе? Начните свою хакерскую карьеру с помощью пакета обучения Premium Ethical Hacking Certification Bundle 2020 в новом магазине Null Byte и получите более 60 часов обучения от профессионалов в области кибербезопасности.
Купить сейчас (скидка 90%)>
Другие выгодные предложения, которые стоит проверить:
Модель клиент-серверного приложения — Документация по системе запросов на устранение неполадок 9.1
Модель клиент-серверного приложения представляет собой комбинацию следующих элементов:
- Программный компонент с пользовательским интерфейсом на клиентском компьютере
- Программный компонент на сервере, который обрабатывает и хранит информацию
- Механизм связи между клиентом и сервер.
Пользователь работает на клиентском компьютере, который «обслуживается» сервером.
Простая модель клиент-сервер
Как правило, клиентские и серверные компоненты находятся в разных компьютерных системах, а механизм связи основан на какой-либо сетевой технологии, но можно использовать и клиент, и сервер. один и тот же хост просто обменивается данными между процессами.
Чтобы упростить разработку клиент-серверных приложений, используется интерфейс прикладного программирования (API).Обычно сначала разрабатывается сервер, а его функции структурированы в виде набора «команд». Эти команды запрограммированы в API, который становится «языком» для взаимодействия с сервером. Когда клиент хочет, чтобы сервер что-то сделал, он делает запрос через API. API обрабатывает все функции связи и операционной системы, заставляет сервер выполнять задачу и возвращает соответствующую информацию клиенту. При разработке клиентского программного обеспечения программисту не нужно знать какие-либо детали серверной среды или механизмов связи.
Сервер BMC Remedy AR System имеет полностью определенный API, который является общим для всех серверных платформ, как Windows, так и UNIX. Все клиентские инструменты BMC Remedy AR System используют этот API для взаимодействия с серверами. Все они говорят на одном языке и полностью взаимозаменяемы. Клиент на любой платформе может работать с сервером на любой платформе. Пока клиент может подключаться к серверу BMC Remedy AR System, он может передавать все свои запросы и получать ответы на этом общем языке.Клиенту не нужно знать, на какой платформе работает сервер. Ему также не нужно знать о других клиентах, использующих тот же сервер.
Системный API BMC Remedy AR определяется строгим набором программных функций и структур данных, которые задокументированы в функциях и структурах данных BMC Remedy AR System C API. API реализован в виде библиотеки C и связанных файлов, которые можно связать с вашими программами. Сторонние программы, связанные с библиотекой BMC Remedy AR System API, становятся клиентами сервера BMC Remedy AR System.
BMC Remedy AR System API
Программирование сокетов
на Python: клиент, сервер и одноранговый узел
Сокеты (также известные как программирование сокетов) позволяют программам в любой момент отправлять и получать данные в двух направлениях. В этом руководстве рассказывается, как вы можете отправлять данные с устройства на устройство, от клиента к серверу…
В этом руководстве рассказывается, как можно отправлять данные с устройства на устройство , от клиента к server и наоборот с использованием программирования сокетов на Python.
Больше наглядного ученика? Посмотрите наше программирование сокетов в видеоуроке по Python ниже.
Готовы к строительству? Давайте прыгнем!
Что такое программирование сокетов?
Сокеты (также известная как программирование сокетов) — это программа, которая позволяет двум сокетам отправлять и получать данные, в двух направлениях, , в любой момент.
Он работает, соединяя два сокета (или узла) вместе и позволяя им обмениваться данными в реальном времени, и является отличным вариантом для создания множества приложений.
Зачем использовать сокеты для отправки данных?
Подключенные к Интернету приложения, которые должны работать в реальном времени, значительно выиграют от реализации сокетов в их сетевом коде . Вот несколько примеров приложений, использующих программирование сокетов:
Python, в отличие от JavaScript, — это язык, который выполняется синхронно. Вот почему был разработан asyncio — чтобы сделать Python более надежным, особенно для природы программирования сокетов.
С потоковыми сокетами данные можно отправлять или получать в любое время.Если ваша программа Python находится в процессе выполнения некоторого кода, другие потоки могут обрабатывать новые данные сокета. Такие библиотеки, как asyncio, реализуют несколько потоков, поэтому ваша программа Python может работать асинхронно.
Python Socket Programming Tutorial
Изначально Python предоставляет класс сокетов, поэтому разработчики могут легко реализовать объекты сокетов в своем исходном коде. Мы можем начать реализацию сокетов в нашей программе с трех простых шагов:
Импорт библиотеки сокетов
Чтобы использовать объект сокета в своей программе, начните с импорта библиотеки сокетов.Нет необходимости устанавливать его с помощью диспетчера пакетов, он поставляется с Python прямо из коробки.
Build Socket Objects
Теперь мы можем создавать объекты сокетов в нашем коде.
Открытие и закрытие соединения
После того, как у нас есть инициализированный объект сокета, мы можем использовать некоторые методы, чтобы открыть соединение , отправить данные , получить данные и, наконец, закрыть соединение .
import socket
sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
Этот код создает объект сокета, который мы сохраняем в переменной «sock». Конструктору предоставляются параметры семейства и типа соответственно. Для параметра семейства задано значение по умолчанию, которым является Address Format Internet .
Параметр типа установлен на Socket Stream , также значение по умолчанию, которое включает «последовательные, надежные, двусторонние байтовые потоки на основе соединения» по TCP 1 .
## Подключиться к IP с портом, может быть URL носок.подключить (('0.0.0.0', 8080)) ## Отправьте данные, этот метод можно вызывать несколько раз sock.send («Двадцать пять байтов для отправки») ## Получить до 4096 байт от однорангового узла sock.recv (4096) ## Закройте соединение сокета, больше нет передачи данных sock.close ()
Python Socket Client Server
Теперь, когда мы знаем несколько методов передачи байтов, давайте создадим клиентскую и серверную программу с Python.
импортная розетка serv = сокет.сокет (socket.AF_INET, socket.SOCK_STREAM) serv.bind (('0.0.0.0', 8080)) serv.listen (5) в то время как True: conn, addr = serv.accept () from_client = '' в то время как True: data = conn.recv (4096) если не данные: перерыв from_client + = данные распечатать from_client conn.send ("Я СЕРВЕР
") conn.close () напечатать 'клиент отключен'
Как это работает?
Этот код создает объект сокета и связывает его с портом локального хоста 8080 как сервер сокета .Когда клиенты подключаются к этому адресу через сокет, сервер прослушивает данные и сохраняет их в переменной «data».
Затем программа регистрирует данные клиента, используя «print», а затем отправляет клиенту строку: Я СЕРВЕР .
Давайте посмотрим на клиентский код, который будет взаимодействовать с этой серверной программой.
Python Socket Client
Вот демонстрационный код сокета клиента .
импортная розетка клиент = сокет.сокет (socket.AF_INET, socket.SOCK_STREAM) client.connect (('0.0.0.0', 8080)) client.send ("Я КЛИЕНТ
") from_server = client.recv (4096) client.close () распечатать from_server
Как это работает?
Этот клиент открывает соединение через сокет с сервером, но только в том случае, если серверная программа в настоящее время работает . Чтобы проверить это самостоятельно, вам нужно будет использовать 2 окна терминала одновременно.
Затем клиент отправляет некоторые данные на сервер: Я КЛИЕНТ
Затем клиент получает некоторые данные, которые он ожидает от сервера.
Готово! Теперь вы можете начать потоковую передачу данных между клиентами и серверами , используя базовое сетевое программирование Python.
Как вы отправляете данные между клиентами?
Отправить данные между 2 или более клиентскими устройствами через Интернет сложно. Из-за защиты, реализованной с помощью сетевой безопасности, не все устройства, подключенные к всемирной паутине, имеют общедоступный IP-адрес.
Это означает, что реализованный нами код Python не будет на 100% надежным для отправки одноранговых данных в нашем приложении реального времени.
Итак, как добиться надежности и скорости при передаче одноранговых данных ?
Это может быть выполнено с помощью сервера в середине :
- Клиентские устройства, использующие Интернет, могут подключаться к серверу с общедоступным IP-адресом (или доменом веб-сайта).
- Затем этот посредник может передавать сообщения, маршрутизируемые одному или нескольким клиентам.
PubNub делает это лучше всего с помощью API Pub / Sub .Это быстро, надежно, безопасно и легко реализовать на на любом клиентском устройстве .
Независимо от того, есть ли у вас сервер Python, веб-сайт JavaScript или что-то среднее между ними, вы можете использовать PubNub для отправки данных любому пользователю в менее чем за 250 мс .
С One-to-Many , One-to-One или Many-to-Many PubNub автоматически масштабируется для поддержки любой нагрузки приложения. Использование API открывает мгновенное постоянное соединение между всеми клиентами, имеющими ключи API Pub / Sub.Это выполняет те же задачи, что и соединение через сокет.
PubNub и Python с подключением SSL
Вот пример одноранговых данных , которые отправляются с PubNub по одному каналу с SSL . Вы можете думать об этом как об отправке данных через TCP-сокет.
Когда вы регистрируете бесплатную учетную запись PubNub, вы можете использовать практически бесконечное количество каналов для отправки сообщений в реальном времени. Прежде чем попробовать код, убедитесь, что создали бесплатную учетную запись PubNub.
Клиент 1
из pubnub.callbacks import SubscribeCallback из pubnub.enums импорт PNStatusCategory из pubnub.pnconfiguration import PNConfiguration из pubnub.pubnub импорт PubNub время импорта импорт ОС pnconfig = PNConfiguration () pnconfig.publish_key = 'здесь публикует ваш pubnub ключ' pnconfig.subscribe_key = 'здесь ваш ключ подписки pubnub' pnconfig.ssl = Верно pubnub = PubNub (pnconfig) def my_publish_callback (конверт, статус): # Проверяем, успешно ли выполнен запрос или нет если не статус.is_error (): проходить класс MySubscribeCallback (SubscribeCallback): def присутствие (self, pubnub, присутствие): проходить статус def (self, pubnub, status): проходить сообщение def (self, pubnub, message): напечатать "с устройства 2:" + message.message pubnub.add_listener (MySubscribeCallback ()) pubnub.subscribe (). channels ("chan-1"). execute () ## опубликовать сообщение в то время как True: msg = raw_input ("Введите сообщение для публикации:") если msg == 'exit': os._exit (1) pubnub.publish (). channel ("chan-1"). message (str (msg)). pn_async (my_publish_callback)
Клиент 2
Для этих двух клиентских программ в командной строке можно вводить строки. Максимальный размер сообщения для публикации PubNub — 32 КБ. Используйте 2 окна терминала, чтобы опробовать код!
из pubnub.callbacks import SubscribeCallback из pubnub.enums импорт PNStatusCategory из pubnub.pnconfiguration import PNConfiguration из pubnub.pubnub импорт PubNub время импорта импорт ОС pnconfig = PNConfiguration () pnconfig.publish_key = 'здесь публикует ваш pubnub ключ' pnconfig.subscribe_key = 'здесь ваш ключ подписки pubnub' pnconfig.ssl = Верно pubnub = PubNub (pnconfig) def my_publish_callback (конверт, статус): # Проверяем, успешно ли выполнен запрос или нет если не status.is_error (): проходить класс MySubscribeCallback (SubscribeCallback): def присутствие (self, pubnub, присутствие): проходить статус def (self, pubnub, status): проходить сообщение def (self, pubnub, message): напечатать "с устройства 1:" + сообщение.сообщение pubnub.add_listener (MySubscribeCallback ()) pubnub.subscribe (). channels ("chan-1"). execute () ## опубликовать сообщение в то время как True: msg = raw_input ("Введите сообщение для публикации:") если msg == 'exit': os._exit (1) pubnub.publish (). channel ("chan-1"). message (str (msg)). pn_async (my_publish_callback)
Завершение программирования сокетов в Python
Весь код в этом посте размещен на GitHub в репозитории Python Socket Demo, если вы хотите, чтобы все это было в одном месте.
Надеемся, вам понравится наше руководство по программированию сокетов. Надеюсь, вы сможете использовать его для создания чего-то удивительного. Это может быть потрясающая умная домашняя система безопасности или приложение для чата в реальном времени! Возможности безграничны. Дайте нам знать, что вы создаете!
PubNub полностью бесплатен до 1 миллион сообщений в месяц . Чтобы узнать о дополнительных возможностях API, ознакомьтесь с документацией по PubNub Python v4 SDK или с любым из 75+ клиентских SDK PubNub.
Как программировать на C # Socket
C # упрощает сетевое программирование благодаря своим пространствам имен, таким как System.Net и System.Net.Sockets . Сокет — это конечная точка соединения «Кому» и «От» (двунаправленная) между двумя программами (серверной программой и клиентской программой), работающими в одной сети. Нам нужны две программы для взаимодействия с приложением сокета на C #. Программа сокета сервера (сервер) и программа сокета клиента (клиент).
Пример сокета C #
Программа серверного сокета C #: Программа серверного сокета C #, работающая на компьютере, имеет сокет, привязанный к номеру порта на том же компьютере и прослушивающий входящие запросы клиента.
Программа сокета клиента C #: Программа сокета клиента C # должна знать IP-адрес (имя хоста) компьютера, на котором находится программа сокета сервера C #, и номер порта, назначенный для прослушивания запроса клиента.
После установления соединения между сервером и клиентом они могут связываться (читать или писать) через свои собственные сокеты.
Существует два типа протоколов связи, используемых для программирования сокетов в C #: TCP / IP, (протокол управления передачей / Интернет-протокол), связь и UDP / IP, (протокол дейтаграмм пользователя / Интернет-протокол).
В следующем разделе мы собираемся взаимодействовать с программой сокета сервера C # и программой сокета клиента C # с использованием протокола связи TCP / IP.
На приведенном выше рисунке показаны интерфейсы связи сервера и клиента на C #.
Программа серверных сокетов C #:
Программа серверных сокетов выполняется с помощью приложения на основе консоли C #. Здесь сервер прослушивает запрос клиента, и когда сервер C # получает запрос от клиентского сокета, сервер отправляет ответ клиенту.Щелкните следующую ссылку, чтобы просмотреть подробную информацию о программе сокетов сервера C #.
Программа клиентского сокета C #:
Программа клиентских сокетов C # — это приложение для Windows. Когда программа C # Client выполняется, она устанавливает соединение с программой C # Server и отправляет запрос на сервер, в то же время он также получает ответ от C # Server. Щелкните следующую ссылку, чтобы просмотреть подробную информацию о программе клиентских сокетов C #.
Как запустить эту программу?
Программа C # Socket состоит из двух разделов.
1. Программа серверных сокетов C #
2. Программа клиентского сокета C #
Когда вы закончите кодирование и построите серверную и клиентскую программу, сначала вам нужно запустить C # Server Socket Program из DOS приглашение , затем вы получите сообщение «Сервер запущен» на экране DOS, где запущена серверная программа.
Следующим шагом будет запуск программы клиентских сокетов C # на том же компьютере или других компьютерах в той же сети. Когда вы запускаете клиентскую программу, она устанавливает соединение с сервером и на экране клиента появляется сообщение « Client Started », в то же время вы можете увидеть сообщение на экране сервера «Accept connection from client».
Теперь ваша программа сокета сервера C # и программа сокета клиента C # подключены и обмениваются данными.