【Анимация】Когда мы читаем и пишем Socket, что мы читаем и пишем?

Redis база данных сервер программист
【Анимация】Когда мы читаем и пишем Socket, что мы читаем и пишем?

Сокет Сокет — это понятие, хорошо знакомое большинству программистов, оно лежит в основе программирования компьютерных сетей, и TCP/UDP использует его для отправки и получения сообщений. Знакомый нам веб-сервер опирается на него внизу, а реляционная база данных MySQL и база данных в памяти Redis, которые мы используем, полагаются на него внизу. Мы также полагаемся на WeChat, чтобы общаться с другими людьми, мы полагаемся на него, когда играем в онлайн-игры, и читатели могут прочитать эту статью, потому что он незаметно поддерживает сетевое общение за кулисами.

простой процесс

Когда клиент и сервер взаимодействуют с использованием протокола TCP, клиент инкапсулирует req объекта запроса, сериализует req объекта запроса в массив байтов, а затем отправляет массив байтов на сервер через сокет сокета, и сервер использует сокет socket.Прочитайте массив байтов, десериализуйте его в объект запроса req и обработайте его.После обработки сгенерируйте ответ, соответствующий req, сериализуйте res объекта ответа в массив байтов, а затем отправьте свой собственный массив клиенту через socket. , клиент считывает свой собственный массив через сокет socket, а затем десериализует его в объект ответа.

Коммуникационная среда часто может скрывать процесс сериализации.Мы видим, что, как показано на рисунке выше, объект запроса req и объект ответа res перемещаются туда и обратно между клиентом и сервером.

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

Многолетний опыт работы в сфере обслуживания интернет-технологий говорит мне, что если вы не понимаете лежащего в основе механизма, вы не поймете, почему возникают различные странные и послушные проблемы при чтении и записи сокетов, почему иногда они блокируются, иногда не блокируются ,а иногда сообщает об ошибках.Почему возникает проблема залипания и половинной упаковки?Что конкретно такое NIO,и это особо новая технология? Понимание этих проблем требует от вас понимания лежащих в их основе механизмов.

детальный процесс

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

Сокет, который мы обычно используем, на самом деле является просто ссылкой (идентификатором объекта), а объект сокета фактически помещается в ядро ​​операционной системы. Внутри объекта сокета есть две важные буферные структуры, одна — буфер чтения (read buffer), другая — буфер записи (write buffer), все они представляют собой структуры массива ограниченного размера.

Когда мы записываем массив байтов в сокет клиента (сериализованный объект сообщения запроса req), массив байтов копируется в буфер записи объекта сокета в области ядра, а у сетевого модуля ядра за это будет отвечать отдельный поток , Данные буфера записи постоянно копируются на оборудование сетевой карты, а оборудование сетевой карты затем отправляет данные по сетевому кабелю, проходит через серию коммутаторов маршрутизатора и, наконец, достигает оборудования сетевой карты сервера.

Аналогично сетевой модуль ядра сервера также будет иметь отдельный поток, который постоянно копирует полученные данные в буфер чтения сокета и ждет, пока пользовательский слой их прочитает. Пользовательский процесс конечного сервера копирует данные из буфера чтения в память пользовательской программы с помощью метода чтения, на который ссылается сокет, и десериализует их в объект запроса для обработки. Затем сервер отправляет обработанный объект ответа клиенту в обратном процессе, который здесь подробно описываться не будет.

блокировать

Мы заметили, что пространство буфера записи ограничено, поэтому, если приложение записывает в сокет слишком быстро, это пространство будет заполнено. После заполнения операции записи будут блокироваться до тех пор, пока для этого пространства не будет доступно достаточно места. Однако при NIO (неблокирующий ввод-вывод) операция записи также может быть неблокирующей. Сколько может быть записано, определяется возвращаемым значением. Программа пользователя будет кэшировать содержимое, которое не было записано, и продолжит повторите попытку позже, напишите.

Также мы заметили, что содержимое буфера чтения может быть пустым. Таким образом, операция чтения сокета (обычно чтение массива байтов фиксированной длины) также будет блокироваться и не будет возвращаться, пока в буфере чтения не будет достаточно содержимого (заполнение массива байтов). С NIO вы можете читать столько, сколько хотите, без блокировки. Если чтения недостаточно, последующие будут продолжать пытаться читать.

ack

На приведенном выше рисунке показан весь процесс сокета? Явно нет, процесс подтверждения (ack) данных вообще не отображается. Например, когда содержимое буфера записи копируется на сетевую карту, скопированное содержимое не будет удалено из буфера записи сразу, а не будет удалено до тех пор, пока не придет акк другой стороны. Если состояние сети неудовлетворительное, подтверждение будет задержано, и буфер записи скоро будет заполнен.

Баотоу

Внимательные учащиеся могут заметить, что сообщение req на картинке при копировании на сетевую карту становится REQ в верхнем регистре, почему? Потому что эти две вещи не совсем одно и то же. Сетевой модуль ядра будет передавать сообщения в буфере блоками, если содержимое буфера слишком велико, оно будет разбито на несколько независимых небольших пакетов сообщений. А также добавьте дополнительную информацию заголовка к каждому пакету сообщения, такую ​​как адрес исходной сетевой карты и адрес сетевой карты назначения, серийный номер сообщения и др. Когда принимающей стороне необходимо изменить порядок этих пакетов сообщений и собрать заголовки, они будут выброшены в буфер чтения. Эти сложные детали очень сложно показать в анимации.

ставка

Другой вопрос, что делать, если буфер чтения переполнен, и что делать сетевой карте при получении сообщения от другой стороны? Общей практикой является отказ от подтверждения и не передача его другой стороне.Если другая сторона обнаружит, что подтверждение не пришло, она отправит сообщение повторно. Почему буфер заполнен? Это связано с тем, что получатель сообщения обрабатывает медленно, а отправитель создает сообщение слишком быстро.В это время протокол TCP будет иметь алгоритм динамической настройки окна, чтобы ограничить скорость отправки отправителя, чтобы эффективность отправки и получения соответствовала . Если это протокол udp, сообщение будет полностью потеряно, если оно потеряно.

Есть более сложные детали внутренней реализации сетевого протокола, которые требуют дальнейшего изучения, поэтому давайте продолжим их анализ позже. Добро пожаловать, чтобы обратить внимание на мой публичный аккаунт «Code Cave», давайте вместе учиться и добиваться успехов на пути написания кода.