Сокеты tcp ip. Java. HTTP протокол и работа с WEB. Обработка нового клиентского соединения

Viber OUT 18.03.2019
Viber OUT

Сеть Frame Relay является сетью с коммутацией кадров или сетью с ретрансляцией кадров, ориентированной на использование цифровых линий связи. Ретрансляция кадров (frame relay) - это метод доставки данных в сетях с коммутацией пакетов, а также и с коммутацией каналов и сообщений. Frame Relay поддерживает физический и канальный уровни OSI. Данная технология применяет при передаче данных технику коммутируемых и постоянных виртуальных соединений. Скорость передачи данных до 44 Мбит/с, но без гарантии целостности данных и достоверности их доставки. Обычно применяется при построении территориально распределённых корпоративных сетей, а также в составе решений, связанных с обеспечением гарантированной пропускной способности канала передачи данных (IP-телефония, видеоконференции и прочее).

В технологии Frame Relay на канальном уровне выполняется мультиплексирование потока данных в кадры. При этом осуществляется мультиплексирование в одном канале связи нескольких потоков данных. Кадры при передаче через коммутатор не подвергаются преобразованиям, поэтому сеть получила название ретрансляции кадров. Следовательно, сеть коммутирует уже не пакеты, а кадры.

Отдельный кадр FR имеет в себе минимальное управляющей информации, что приводит к достаточно высокой эффективности передачи данных. При этом изначально допускается, что каналы передачи данных должны быть достаточно надежными, а функции управления потоком выполняются протоколами верхних уровней. Это обусловлено тем, что технология FR не оснащена встроенными функциями контроля доставки и управления потоком кадров. Основу сети Frame Relay образуют специализированные коммутаторы - FRAD (Frame Relay Access Device, устройство доступа к сети с ретрансляцией кадров). Коммутаторы FR применяют технологию сквозной коммутации, когда каждый кадр посылается на следующий транзитный узел сразу же по прочтении адресной информации. В случае возникновения ошибок, коммутаторы FR не пытаются их восстановить кадры и отбраковывают. Отправленные данные восстанавливаются оборудованием конечного пользователя, и промежуточное оборудование освобождено от нужды выявления и исправления ошибок. При этом накладные расходы по обработке данных из расчета на кадр уменьшаются, что существенно повышает пропускную способность.

Изначально информационное взаимодействие технологии FR реализовывалось лишь на физическом и канальном уровне. Принципиальное отличие технологии FR от ранее имевшихся технологий построения сетей заключалось именно в отсутствии сетевого уровня взаимодействия и заключается. На физическом уровне FR поддерживает интерфейсы по стандартам как ITU-T, так и ANSI: X.21, V.24, V.35, DDN, DDS, интерфейсы BRI и PRI сетей ISDN, полные и дробные протоколы T1/E1, SDH. На уровне звена данных используется процедура доступа LAPF, стандартизованная Рекомендацией Q.922 ITU-T. Столлингс Вильям: Компьютерные сети, протоколы и технологии Интернета / Кондукова Е.Ф. М.: BHV, 2005 г. - 832 с. Стек протоколов FR передает кадры при установленном виртуальном соединении по протоколам физического и канального уровней. Функции сетевого уровня перемещены на канальный уровень, поэтому необходимость в сетевом уровне отпала. Тем самым, указанные особенности и обеспечивают преимущества сетей, которые построены по технологии FR.

Кадр протокола FR содержит минимально необходимое количество служебных полей. Структура и формат кадра FR состоит из следующих элементов: Пятибратов А.П. Вычислительные системы, сети и телекоммуникации: учебное пособие. - М.: Кнорус, 2013. - 376 с.

HEADER (2-4 Byte)

Данные (переменный размер)

Поле FLAG. Каждый кадр начинается и замыкается "флагом" - последовательностью "01111110". Данное поле выполняет функцию обрамления кадра. Чтобы предотвратить случайную имитацию последовательности "флаг" внутри кадра, в процессе его передачи проверяется всё его содержание между двумя флагами и после каждой последовательности, состоящей из пяти идущих подряд бит "1", вставляется бит "0". Данная процедура неукоснительно должна выполняться при формировании любого кадра FR, при приёме эти биты "0" отбрасываются. Принцип формирования поля FLAG в кадре FR соответствует принципам формирования поля FLAG в кадре других технологий. Узлам сети FR разрешено уничтожать искаженные кадры, не уведомляя об этом пользователя. Искаженным считается кадр, которому присущ какой-либо из следующих признаков:

Наблюдается менее пяти октетов между флагами;

Искажено поле адреса (для случая, когда проверка не выявила ошибки в FCS);

Имеется ошибка в FCS;

Отсутствует корректное ограничение флагов;

Отсутствует целое число октетов после удаления бит обеспечения прозрачности;

Превышен допустимый максимальный размер;

Имеется несуществующий DLCI.

Поле HEADER. Поле заголовка кадра содержит информацию, которая используется для управления виртуальными соединениями и процессами передачи данных в сети Frame Relay. Данное поле кадра FR, помимо собственно адресной информации, содержит также и дополнительные поля управления потоком данных и уведомлений о перегрузке канала (Приложение А): Пятибратов А.П. Вычислительные системы, сети и телекоммуникации: учебное пособие. - М.: Кнорус, 2013. - 376 с.

Механизмы уведомления о возникновении перегрузок и их устранения реализуются посредством битов FECN и BECN в заголовке кадра LAPF. Информационные биты FECN и BECN выставляются в момент попадания кадра в затор трафика. Если узел сети FR обнаруживает, что его входные очереди начинают переполняться, предыдущему узлу (источнику трафика) передается пакет с установленным битом BECN, чтобы приостановить поступление данных до разрешения ситуации. Маршрутизаторы с интерфейсом FR могут расшифровать значения этих битов и активизировать управление потоком на базе протокола верхлежащего уровня, например, - TCP/IP. При этом представленный механизм не вписался бы в концепцию регламентирования пропускной способности сети, поддерживаемую FR, без введения соглашения о согласованной скорости передачи информации (Committed Information Rate, CIR). Столлингс Вильям: Компьютерные сети, протоколы и технологии Интернета / Кондукова Е.Ф. М.: BHV, 2005 г. - 832 с.

Поле данных. Поле полезной нагрузки в кадре Frame Relay имеет переменную длину и предназначено для переноса блоков данных протоколов верхних уровней. Поле полезной нагрузки имеет минимальную длину в 1 октет, максимальную согласно стандарту - 1600 октетов, тем не менее в реализациях некоторых производителей FR-оборудования допускается превышение максимального размера (до 4096 октетов).

Поле FCS. Проверочная последовательность кадра (FCS) применяется для выявления возможных ошибок при его передаче и состоит из 2 октетов. FCS формируется аналогично циклическому коду HDLC. Пескова, С.А.Сети и телекоммуникации: учеб. пособие для вузов / Пескова, С.А., Кузин, А.В., Волков, А.Н. - М.: Академия, 2008. - 350 с.

Лекция 6. Принципы построения сетей Frame Relay и АТМ

Сети Frame Relay. Сеть Frame Relay является сетью с коммутацией кадров или сетью с ретрансляцией кадров, ориентированной на использование цифровых линий связи. Первоначально технология Frame Relay была стандартизирована как служба в сетях ISDN со скоростью передачи данных до 2 Мбит/с. В дальнейшем эта технология получила самостоятельное развитие. Frame Relay поддерживает физический и канальный уровни OSI. Технология Frame Relay использует для передачи данных технику виртуальных соединений (коммутируемых и постоянных).

Стек протоколов Frame Relay передает кадры при установленном виртуальном соединении по протоколам физического и канального уровней. В Frame Relay функции сетевого уровня перемещены на канальный уровень, поэтому необходимость в сетевом уровне отпала. На канальном уровне в Frame Relay выполняется мультиплексирование потока данных в кадры.

Frame Relay осуществляет мультиплексирование в одном канале связи нескольких потоков данных. Кадры при передаче через коммутатор не подвергаются преобразованиям, поэтому сеть получила название ретрансляции кадров. Таким образом, сеть коммутирует кадры, а не пакеты. Скорость передачи данных до 44 Мбит/с, но без гарантии целостности данных и достоверности их доставки.

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

Технология Frame Relay в основном используется для маршрутизации протоколов локальных сетей через общие (публичные) коммуникационные сети. Frame Relay обеспечивает передачу данных с коммутацией пакетов через интерфейс между оконечными устройствами пользователя DTE (маршрутизаторами, мостами, ПК) и оконечным оборудованием канала передачи данных DCE (коммутаторами сети типа "облако").

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

На рисунке 6.1 представлена структурная схема сети Frame Relay, где изображены основные элементы:

DTE (data terminal equipment) – аппаратура передачи данных (маршрутизаторы, мосты, ПК);

DCE (data circuit-terminating equipment) – оконечное оборудование канала передачи данных (телекоммуникационное оборудование, обеспечивающее доступ к сети).

Рисунок 6.1 – Cтруктурная схема сети Frame Relay

Физический уровень Frame Relay. На физическом уровне FR используют цифровые выделенные каналы связи, протокол физического уровня I.430/431.

Канальный уровень Frame Relay. Протокол канального уровня LAP-F в сетях Frame Relay имеет два режима работы – основной (core) и управляющий (control). В основном режиме, который практикуется в сегодняшних сетях FR, кадры передаются без преобразования и контроля, как и в коммутаторах локальных сетей. За счет этого сети FR обладают высокой производи- тельностью, так как кадры в коммутаторах не подвергаются преобразованию, а сеть не передает квитанции подтверждения между коммутаторами на каждый пользовательский кадр. Структура стека (рисунок 6.2) отражает происхождение технологии FR в недрах технологии ISDN, так как сети FR заимствуют многое из стека протоколов ISDN (процедуры установления SVC).

Основу технологии составляет протокол LAP-F core, который является упрощенной версией протокола LAP-D.

Протокол LAP-F (стандарт Q.922 ITU-T) работает на любых каналах сети ISDN, а также на каналах типа Т1/Е1. Терминальное оборудование посылает в сеть кадры LAP-F в любой момент времени, считая что виртуальный канал в сети коммутаторов проложен. При использовании PVC оборудованию Frame Relay нужно поддерживать только протокол LAP-F core. Протокол LAP-F contol является необязательной надстройкой над LAP-F core, которая выполняет функции контроля доставки кадров и управления потоком. При этом control сетью реализуется служба frame switching.

Рисунок 6.2 – Стек протоколов Frame Relay

Технология ATM. В 80-е годы во многих промышленных развитых странах началась разработка широкополосной цифровой сети с интеграцией служб (B-ISDN). Создание такой сети позволяет организовать такие службы, как высококачественная видеотелефония, видеоконференции, высокоскорост- ная передача данных, передача телевизионных программ высокого качества, поиск видеоинформации и ряд других. Для этого требуются скорости передачи, превышающие 2 Мбит/с, являющуюся максимальной скоростью, предостав- ляемой пользователю узкополосной ISDN. В результате исследований, проводившихся с середины 80-х годов, МККТТ (ныне МСЭ-Т) принял в 1988 г. рекомендацию I.121, определившую общие принципы B-ISDN. Наиболее важный из них – использование асинхронного режима переноса информации (АТМ), реализующего процессы передачи и коммутации выше физического уровня. Решающее значение при выборе АТМ имело то, что большинство источников информации работают в прерывистом режиме. Например, коэффициент активности речи составляет 0,3 – 0,4, еще меньше он в интер- активных системах передачи данных, весьма разнообразна видеоинформация и т. д. Поэтому применение синхронного режима переноса (STM), при котором выделяется постоянная полоса пропускания, соответствующая наивысшей мгновенной скорости передачи информации, оказывается весьма неэффек- тивным. В то же время асинхронный режим переноса, основанный на статистических (пакетных) методах, позволяет гибко распределять полосу пропускания, обеспечивая совместную работу разнообразных служб в условиях изменения параметров служб и нагрузки.

В соответствии с определениями рекомендаций I.113 и I.121 термин АТМ обозначает специфический пакетно-ориентированный режим переноса информации, использующий метод асинхронного временного разделения, при котором поток информации организуется в блоки фиксированной длины, называемые ячейками. Для прояснения терминологии следует заметить, что согласно рекомендации G.803 различают термины "передача" (transmission), обозначающий физический процесс распространения сигнала по каналу связи, и "перенос" (transfer) – процесс перемещения информации по сети. Ячейка (cell) имеет длину 53 байта, из которых 48 байтов – информация пользователя и 5 байт – заголовок. Основное назначение заголовка – идентификация ячеек, принадлежащих одному и тому же виртуальному каналу. АТМ является методом, ориентированным на установление соединений. До начала передачи информации между пользователями должен быть организован виртуальный канал. Сигнальная и пользовательская информация передаются по отдельным виртуальным каналам. Группа виртуальных каналов, проходящих на некоторых участках сети по одному и тому же направлению, может объединяться в виртуальный тракт. Поскольку АТМ предполагает использование высоко- скоростных и обладающих высокой помехозащищенностью цифровых систем передачи (как правило, на основе волоконно-оптических линий), повышение верности осуществляется только в оборудовании пользователей. Отказ от повышения верности в узлах коммутации значительно упрощает алгоритм их функционирования и позволяет применять в них аппаратные средства, имеющие значительно более высокое быстродействие, чем программируемые микропроцессоры. Высокая пропускная способность трактов передачи, быстродействие коммутационных устройств и короткая длина ячеек обеспечивают, как правило, быструю доставку ячеек по сети. Контроль за их доставкой осуществляется в оконечном оборудовании пользователей. По своей сути метод АТМ представляет собой разновидность метода коммутации пакетов – так называемую быструю коммутацию пакетов – наиболее близкую по своим пользовательским характеристикам методу коммутации каналов.



Сеть АТМ способна не только быть основой для организации самых разнообразных служб в рамках B-ISDN, предназначенных для передачи данных, изображений и т.д. Она также может служить транспортной средой для телефонной сети, узкополосной ISDN, связи городских сетей передачи данных (MAN) и др. (рисунок 6.3).

Рисунок 6.3 – Логическая схема возможного использования сети АТМ

Использования технологии АТМ позволяет строить гибкие сети, эффективно использующие пропускную способность трактов передачи за счет их статистического мультиплексирования. Универсальность АТМ состоит еще и в том, что это первая технология, которая может использоваться в сетях любого масштаба: локальных (LAN), городских (MAN) и территориальных (WAN).

Упрощенная архитектура сети АТМ представлена на рисунке 6.4.

Рисунок 6.4 – Структура сети АТМ

Она состоит из связанных между собой АТМ коммутаторов. Находящееся за пределами сети оборудование пользователя взаимодействует с коммутаторами через интерфейс пользователь – сеть (UNI). Для взаимодей- ствия коммутаторов между собой служит интерфейс сетевого узла (NNI). МСЭ-Т стандартизировал в рекомендации I.432 два типа интерфейса UNI: на скоростях 155 и 622 Мбит/с (это скорости 1-го и 4-го уровней SDH). Подготовлены стандарты по использованию технологии АТМ на первичной скорости европейской иерархии 2 Мбит/с.

Стек протоколов ATM (рисунок 6.5) включает уровень адаптации ATM и физический уровень.

Рисунок 6.5 – Стек протоколов АТМ

Уровень адаптации АТМ (AAL) осуществляет преобразование пользовательской информации в информационные поля ячеек и наоборот. Именно наличие AAL придают АТМ присущую ей способность переносить разнообразную пользовательскую информацию в стандартных ячейках. Следует подчеркнуть, что процедуры ААL реализуются вне пределов сети АТМ в оконечном оборудовании пользователя. Уровень адаптации может использовать для своих нужд до 4 байт в пределах 48-байтного информационного поля ячейки, оставляя, таким образом, непосредственно для полезной информации пользователей 44 байта. AAL делится, в свою очередь, на два подуровня: подуровень конвергенции (CS) и подуровень разборки и сборки (сегментации и реассемблирования) (SAR).

Верхний из них – CS – получает информацию от пользователя и разбивает ее на протокольные единицы данных этого подуровня, длина которых определяется конкретным типом уровня адаптации. Далее к ним добавляются заголовок и окончание, содержащие служебную информацию о виде передаваемого трафика и размере протокольной единицы, а также позволяющие осуществлять контроль и исправление ошибок на приеме. При необходимости этот подуровень обеспечивает также синхронизацию. Подуровень разборки и сборки принимает полученные протокольные единицы CS и разбивает их на фрагменты, длина которых от 44 до 48 байтов. К ним могут добавляться заголовок (1 – 2 байта), идентифицирующий тип данного фрагмента, и окончание (до 2 байт), содержащее контрольную сумму. В результате получается 48-байтная последовательность, образующая информационное поле ячейки АТМ. Описанный выше алгоритм варьируется в зависимости от типа уровня адаптации. На приеме все процедуры выполняются в обратной последовательности. Уровень АТМ добавляет к полученным от подуровня SAR 48-байтным последовательностям 5-байтовые заголовки, формируя таким образом ячейки АТМ, передаваемые затем на физический уровень. К функциям уровня АТМ относятся также: управление входным потоком на интерфейсе пользователь-сеть; мультиплексирование ячеек, принадлежащим различным виртуальным каналам и трактам, в единый поток; преобразование идентификаторов виртуальных каналов в узлах коммутации. На приемной стороне уровень АТМ осуществляет демультиплексирование потока ячеек и удаление заголовков.

Физический уровень также состоит из двух подуровней: подуровень конвергенции передачи (TC) и подуровень, зависящий от физической среды (PMD). Подуровень ТС осуществляет согласование потока ячеек с используемой системой передачи (например, упаковывает ячейки АТМ в контейнеры SDH). Подуровень PMD ответственен за передачу и прием битов, передаваемых в конкретной физической среде (оптическое волокно, коаксиальный кабель).

Основная литература: 2

Дополнительная литература: 7

Контрольные вопросы:

1. Какую технику для передачи данных использует технология FR?

2. Какие два режима работы в сетях FR имеет протокол канального уровня LAP-F?

3. В чем заключается универсальность АТМ?

4.Какие уровни включает стек протоколов ATM?

5. Объясните структуру сети АТМ.

Последнее обновление: 31.10.2015

Протокол UDP не требует установки постоянного подключения, и, возможно, многим покажется легче работать с UDP, чем с TCP. Большинство принципов при работе с UDP те же, что и с TCP.

Вначале создается сокет:

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

Если сокет должен получать сообщения, то надо привязать его к локальному адресу и одному из портов с помощью метода Bind:

IPEndPoint localIP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5555); socket.Bind(localIP);

После этого можно отправлять и получать сообщения. Для получения сообщений используется метод ReceiveFrom() :

Byte data = new byte; // буфер для получаемых данных //адрес, с которого пришли данные EndPoint remoteIp = new IPEndPoint(IPAddress.Any, 0); int bytes = socket.ReceiveFrom(data, ref remoteIp);

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

Для отправки данных используется метод SendTo() :

String message = Console.ReadLine(); byte data = Encoding.Unicode.GetBytes(message); EndPoint remotePoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), remotePort); listeningSocket.SendTo(data, remotePoint);

В метод передается массив отправляемых данных, а также адрес, по которому эти данные надо отправить.

Создадим программу UDP-клиента:

Using System; using System.Text; using System.Threading.Tasks; using System.Net; using System.Net.Sockets; namespace SocketUdpClient { class Program { static int localPort; // порт приема сообщений static int remotePort; // порт для отправки сообщений static Socket listeningSocket; static void Main(string args) { Console.Write("Введите порт для приема сообщений: "); localPort = Int32.Parse(Console.ReadLine()); Console.Write("Введите порт для отправки сообщений: "); remotePort = Int32.Parse(Console.ReadLine()); Console.WriteLine("Для отправки сообщений введите сообщение и нажмите Enter"); Console.WriteLine(); try { listeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); Task listeningTask = new Task(Listen); listeningTask.Start(); // отправка сообщений на разные порты while (true) { string message = Console.ReadLine(); byte data = Encoding.Unicode.GetBytes(message); EndPoint remotePoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), remotePort); listeningSocket.SendTo(data, remotePoint); } } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { Close(); } } // поток для приема подключений private static void Listen() { try { //Прослушиваем по адресу IPEndPoint localIP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), localPort); listeningSocket.Bind(localIP); while (true) { // получаем сообщение StringBuilder builder = new StringBuilder(); int bytes = 0; // количество полученных байтов byte data = new byte; // буфер для получаемых данных //адрес, с которого пришли данные EndPoint remoteIp = new IPEndPoint(IPAddress.Any, 0); do { bytes = listeningSocket.ReceiveFrom(data, ref remoteIp); builder.Append(Encoding.Unicode.GetString(data, 0, bytes)); } while (listeningSocket.Available > 0); // получаем данные о подключении IPEndPoint remoteFullIp = remoteIp as IPEndPoint; // выводим сообщение Console.WriteLine("{0}:{1} - {2}", remoteFullIp.Address.ToString(), remoteFullIp.Port, builder.ToString()); } } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { Close(); } } // закрытие сокета private static void Close() { if (listeningSocket != null) { listeningSocket.Shutdown(SocketShutdown.Both); listeningSocket.Close(); listeningSocket = null; } } } }

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

После ввода портов запускается задача на прослушивание входящих сообщений. В отличие от tcp-сервера здесь не надо вызывать методы Listen и Accept. В бесконечном цикле мы напрямую можем получить получение данные с помощью метода ReceiveFrom() , который блокирует вызывающий поток, пока не придет очередная порция данных.

Этот метод возвращает через ref-параметр удаленную точку, с которой получены данные:

IPEndPoint remoteFullIp = remoteIp as IPEndPoint;

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

В главном потоке происходит отправка сообщений с помощью метода SendTo()

Таким образом, приложение сразу осуществляет функции и сервера, и клиента.

Теперь запустим две копии приложения и введем разные данные для портов. Первый клиент:

Введите порт для приема сообщений: 4004 Введите порт для отправки сообщений: 4005 Для отправки сообщений введите сообщение и нажмите Enter 127.0.0.1:4005 - привет порт 4004 добрый день, порт 4005 чудная погодка

Второй клиент:

Введите порт для приема сообщений: 4005 Введите порт для отправки сообщений: 4004 Для отправки сообщений введите сообщение и нажмите Enter привет порт 4004 127.0.0.1:4004 - добрый день, порт 4005 127.0.0.1:4004 - чудная погодка

Пора применить эрланг по его прямому назначению -- для реализации сетевого сервиса. Чаще всего такие сервисы делают на базе веб-сервера, поверх протокола HTTP . Но мы возьмем уровень ниже -- TCP и UDP сокеты.

Я полагаю, вы уже знаете, как устроена сеть, что такое Internet Protocol , User Datagram Protocol и Transmission Control Protocol . Эта тема большинству программистов известна. Но если вы почему-то ее упустили, то придется сперва наверстать упущенное, и потом вернуться к этому уроку.

UDP сокет

Вспомним в общих чертах, что такое UDP:

  • протокол передачи коротких сообщений (Datagram);
  • быстрая доставка;
  • без постоянного соединения между клиентом и сервером, без состояния;
  • доставка сообщения и очередность доставки не гарантируется.

Для работы с UDP используется модуль gen_udp .

Давайте запустим две ноды и наладим общение между ними.

На 1-й ноде откроем UDP на порту 2000:

1> {ok, Socket} = gen_udp:open(2000, ). {ok,#Port<0.587>}

Вызываем gen_udp:open/2 , передаем номер порта и список опций. Список всех возможных опций довольно большой, но нас интересуют две из них:

binary -- сокет открыт в бинарном режиме. Как вариант, сокет можно открыть в текстовом режиме, указав опцию list . Разница в том, как мы интерпретируем данные, полученные из сокета -- как поток байт, или как текст.

{active, true} -- сокет открыт в активном режиме, значит данные, приходящие в сокет, будут посылаться в виде сообщений в почтовый ящик потока, владельца сокета. Подробнее об этом ниже.

На 2-й ноде откроем UDP на порту 2001:

1> {ok, Socket} = gen_udp:open(2001, ). {ok,#Port<0.587>}

И пошлем сообщение с 1-й ноды на 2-ю:

2> gen_udp:send(Socket, {127,0,0,1}, 2001, <<"Hello from 2000">>). ok

Вызываем gen_udp:send/4 , передаем сокет, адрес и порт получателя, и само сообщение.

Адрес может быть доменным именем в виде строки или атома, или адресом IPv4 в виде кортежа из 4-х чисел, или адресом IPv6 в виде кортежа из 8 чисел.

На 2-й ноде убедимся, что сообщение пришло:

2> <0.587>,{127,0,0,1},2000,<<"Hello from 2000">>} ok

Сообщение приходит в виде кортежа {udp, Socket, SenderAddress, SenderPort, Packet} .

Пошлем сообщение с 2-й ноды на 1-ю:

3> gen_udp:send(Socket, {127,0,0,1}, 2000, <<"Hello from 2001">>). ok

На 1-й ноде убедимся, что сообщение пришло:

3> flush(). Shell got {udp,#Port<0.587>,{127,0,0,1},2001,<<"Hello from 2001">>} ok

Как видим, тут все просто.

Активный и пассивный режим сокета

И gen_udp , и gen_tcp , оба имеют одну важную настройку: режим работы с входящими данными. Это может быть либо активный режим {active, true} , либо пассивный режим {active, false} .

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

Для udp сокета это сообщения вида:

{udp, Socket, SenderAddress, SenderPort, Packet}

мы их уже видели:

{udp,#Port<0.587>,{127,0,0,1},2001,<<"Hello from 2001">>}

Для tcp сокета аналогичные сообщения:

{tcp, Socket, Packet}

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

В пассивном режиме данные нужно забрать самому вызовами gen_udp:recv/3 и gen_tcp:recv/3 :

Gen_udp:recv(Socket, Length, Timeout) -> {ok, {Address, Port, Packet}} | {error, Reason} gen_tcp:recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason}

Здесь мы указываем, сколько байт данных хотим прочитать из сокета. Если там есть эти данные, то мы получаем их сразу. Если нет, то вызов блокируется, пока не придет достаточное количество данных. Можно указать Timeout, чтобы не блокировать поток надолго.

Однако, gen_udp:recv игнорирует аргумент Length, и возвращает все данные, которые есть в сокете. Или блокируется и ждет каких-нибудь данных, если в сокете ничего нет. Непонятно, зачем вообще аргумент Length присутствует в АПИ.

Для gen_tcp:recv аргумент Length работает как надо. Если только не указана опция {packet, Size} , о которой речь пойдет ниже.

Еще есть вариант {active, once} . В этом случае сокет запускается в активном режиме, получает первый пакет данных как сообщение, и сразу переключается в пассивный режим.

TCP сокет

Вспомним в общих чертах, что такое TCP:

  • надежный протокол передачи данных, гарантирует доставку сообщения и очередность доставки;
  • постоянное соединение клиента и сервера, имеет состояние;
  • дополнительные накладные расходы на установку и закрытие соединения и на передачу данных.

Надо заметить, что долго держать постоянные соединения с многими тысячами клиентов накладно. Все соединения должны работать независимо друг от друга, а это значит -- в разных потоках. Для многих языков программирования (но не для эрланг) это серьезная проблема.

Именно поэтому так популярен протокол HTTP, который хоть и работает поверх TCP сокета, но подразумевает короткое время взаимодействия. Это позволяет относительно небольшим числом потоков (десятки-сотни) обслуживать значительно большее число клиентов (тысячи, десятки тысяч).

В некоторых случаях остается необходимость иметь долгоживущие постоянные соединения между клиентом и сервером. Например, для чатов или для многопользовательских игр. И здесь эрланг имеет мало конкурентов.

Для работы с TCP используется модуль gen_tcp .

Работать с TCP сокетом сложнее, чем с UDP. У нас появляются роли клиента и сервера, требующие разной реализации. Рассмотрим вариант реализации сервера.

Module(server). -export(). start() -> start(1234). start(Port) -> spawn(?MODULE, server, ), ok. server(Port) -> io:format("start server at port ~p~n", ), {ok, ListenSocket} = gen_tcp:listen(Port, ), ) || Id <- lists:seq(1, 5)], timer:sleep(infinity), ok. accept(Id, ListenSocket) -> io:format("Socket #~p wait for client~n", ), {ok, _Socket} = gen_tcp:accept(ListenSocket), io:format("Socket #~p, session started~n", ), handle_connection(Id, ListenSocket). handle_connection(Id, ListenSocket) -> receive {tcp, Socket, Msg} -> io:format("Socket #~p got message: ~p~n", ), gen_tcp:send(Socket, Msg), handle_connection(Id, ListenSocket); {tcp_closed, _Socket} ->

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

Listen Socket должен работать всегда, а для этого его поток-владелец не должен завершаться. Поэтому в server/1 мы добавили вызов timer:sleep(infinity) . Это заблокирует поток и не даст ему завершиться. Такая реализация, конечно, учебная. По хорошему нужно предусмотреть возможность корректно остановить сервер, а здесь этого нет.

Accept Socket и поток для него можно было бы создавать динамически, по мере появления клиентов. В начале можно создать один такой поток, вызвать в нем gen_tcp:accept/1 и ждать клиента. Этот вызов является блокирующим. Он завершается, когда появляется клиент. Дальше можно обслуживать текущего клиента в этом потоке, и создать новый поток, ожидающий нового клиента.

Но здесь у нас другая реализация. Мы заранее создаем пул из нескольких потоков, и все они ждут клиентов. После завершения работы с одним клиентом сокет не закрывается, а ждет нового. Таким образом, вместо того, чтобы постоянно открывать новые сокеты и закрывать старые, мы используем пул долгоживущих сокетов.

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

Потоки принадлежат эрланговской ноде, и мы можем создавать их сколько угодно. Но сокеты принадлежат операционной системе. Их количество лимитировано, хотя и довольно большое. (Речь идет о лимите на количество файловых дескрипторов, которое операционная система позволяет открыть пользовательскому процессу, обычно это 2 10 - 2 16).

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

Текущая сессия с клиентом обрабатывается в функции handle_connection/2 . Видно, что сокет работает в активном режиме, и поток получает сообщения вида {tcp, Socket, Msg} , где Msg -- это бинарные данные, пришедшие от клиента. Эти данные мы отравляет обратно клиенту, то есть, реализуем банальный эхо-сервис:)

Когда клиент закрывает соединение, поток получает сообщение {tcp_closed, _Socket} , возвращается обратно в accept/2 и ждет следующего клиента.

Вот как выглядит работа такого сервера с двумя telnet-клиентами:

$ telnet localhost 1234 Trying 127.0.0.1... Connected to localhost. Escape character is "^]". hello from client 1 hello from client 1 some message from client 1 some message from client 1 new message from client 1 new message from client 1 client 1 is going to close connection client 1 is going to close connection ^] telnet> quit Connection closed.

$ telnet localhost 1234 Trying 127.0.0.1... Connected to localhost. Escape character is "^]". hello from client 2 hello from client 2 message from client 2 message from client 2 client 2 is still active client 2 is still active but client 2 is still active but client 2 is still active and now client 2 is going to close connection and now client 2 is going to close connection ^] telnet> quit Connection closed.

2> server:start(). start server at port 1234 ok Socket #1 wait for client Socket #2 wait for client Socket #3 wait for client Socket #4 wait for client Socket #5 wait for client Socket #1, session started Socket #1 got message: <<"hello from client 1\r\n">> Socket #1 got message: <<"some message from client 1\r\n">> Socket #2, session started Socket #2 got message: <<"hello from client 2\r\n">> Socket #2 got message: <<"message from client 2\r\n">> Socket #1 got message: <<"new message from client 1\r\n">> Socket #2 got message: <<"client 2 is still active\r\n">> Socket #1 got message: <<"client 1 is going to close connection\r\n">> Socket #1, session closed Socket #1 wait for client Socket #2 got message: <<"but client 2 is still active\r\n">> Socket #2 got message: <<"and now client 2 is going to close connection\r\n">> Socket #2, session closed Socket #2 wait for client

Сервер в пассивном режиме

Это все хорошо, но хороший сервер должен работать в пассивном режиме. То есть, он должен получать данные от клиента не в виде сообщений в почтовый ящик, а вызовом gen_tcp:recv/2,3 .

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

Теперь нужно решить, сколько байт должен занимать этот служебный пакет. Если это будет 1 байт, то в него нельзя упаковать число больше 255. В 2 байта можно упаковать число 65535, в 4 байта 4294967295. 1 байт, очевидно, мало. Вполне вероятно, что клиенту будет нужно послать данных больше, чем 255 байт. Заголовок в 2 байта вполне подходит. Заголовок в 4 байта иногда бывает нужен.

Итак, клиент посылает служебный пакет размером в 2 байта, где указано, сколько данных последуют за ним, а затем сами эти данные:

Msg = <<"Hello">>, Size = byte_size(Msg), Header = <>, gen_tcp:send(Socket, <

>),

Полный код клиента:

Module(client2). -export(). start() -> start("localhost", 1234). start(Host, Port) -> spawn(?MODULE, client, ). send(Pid, Msg) -> Pid ! {send, Msg}, ok. stop(Pid) -> Pid ! stop, ok. client(Host, Port) -> io:format("Client ~p connects to ~p:~p~n", ), {ok, Socket} = gen_tcp:connect(Host, Port, ), loop(Socket). loop(Socket) -> receive {send, Msg} -> io:format("Client ~p send ~p~n", ), Size = byte_size(Msg), Header = <>, gen_tcp:send(Socket, <

>), loop(Socket); {tcp, Socket, Msg} -> io:format("Client ~p got message: ~p~n", ), loop(Socket); stop -> io:format("Client ~p closes connection and stops~n", ), gen_tcp:close(Socket) after 200 -> loop(Socket) end.

Сервер сперва читает 2 байта, определяет размер данных и затем читает все данные:

{ok, Header} = gen_tcp:recv(Socket, 2), <> = Header, {ok, Msg} = gen_tcp:recv(Socket, Size),

В коде сервера функции start/0 и start/1 не изменились, остальное немного поменялось:

Server(Port) -> io:format("start server at port ~p~n", ), {ok, ListenSocket} = gen_tcp:listen(Port, ), ) || Id <- lists:seq(1, 5)], timer:sleep(infinity), ok. accept(Id, ListenSocket) -> io:format("Socket #~p wait for client~n", ), {ok, Socket} = gen_tcp:accept(ListenSocket), io:format("Socket #~p, session started~n", ), handle_connection(Id, ListenSocket, Socket). handle_connection(Id, ListenSocket, Socket) -> case gen_tcp:recv(Socket, 2) of {ok, Header} -> <> = Header, {ok, Msg} = gen_tcp:recv(Socket, Size), io:format("Socket #~p got message: ~p~n", ), gen_tcp:send(Socket, Msg), handle_connection(Id, ListenSocket, Socket); {error, closed} -> io:format("Socket #~p, session closed ~n", ), accept(Id, ListenSocket) end.

Пример сессии со стороны клиента:

2> Pid = client2:start(). Client <0.40.0> connects to "localhost":1234 <0.40.0> 3> client2:send(Pid, <<"Hello">>). Client <0.40.0> send <<"Hello">> ok Client <0.40.0> got message: <<"Hello">> 4> client2:send(Pid, <<"Hello again">>). Client <0.40.0> send <<"Hello again">> ok Client <0.40.0> got message: <<"Hello again">> 5> client2:stop(Pid). Client <0.40.0> closes connection and stops ok

И со стороны сервера:

2> server2:start(). start server at port 1234 ok Socket #1 wait for client Socket #2 wait for client Socket #3 wait for client Socket #4 wait for client Socket #5 wait for client Socket #1, session started Socket #1 got message: <<"Hello">> Socket #1 got message: <<"Hello again">> Socket #1, session closed Socket #1 wait for client

Все это хорошо, но на самом деле нет необходимости вручную разбираться с заголовочным пакетом. Это уже реализовано в gen_tcp . Нужно указать размер служебного пакета в настройках при открытии сокета на стороне клиента:

{ok, Socket} = gen_tcp:connect(Host, Port, ),

и на стороне сервера:

{ok, ListenSocket} = gen_tcp:listen(Port, ),

и необходимость самому формировать и разбирать эти заголовки пропадает.

На стороне клиента упрощается отправка:

Gen_tcp:send(Socket, Msg),

и на стороне сервера упрощается получение:

Handle_connection(Id, ListenSocket, Socket) -> case gen_tcp:recv(Socket, 0) of {ok, Msg} -> io:format("Socket #~p got message: ~p~n", ), gen_tcp:send(Socket, Msg), handle_connection(Id, ListenSocket, Socket); {error, closed} -> io:format("Socket #~p, session closed ~n", ), accept(Id, ListenSocket) end.

Теперь при вызове gen_tcp:recv/2 мы указываем Length = 0. gen_tcp сам знает, сколько байт нужно прочитать из сокета.

Работа с текстовыми протоколами

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

Такой вариант характерен для текстовых протоколов (SMTP, POP3, FTP).

Писать свою реализацию чтения из сокета нет необходимости, все уже реализовано в gen_tcp . Нужно только указать в настройках сокета вместо {packet, 2} опцию {packet, line} .

{ok, ListenSocket} = gen_tcp:listen(Port, ),

В остальном код сервера остается без изменений. Но теперь мы можем опять вернуться к telnet-клиенту.

$ telnet localhost 1234 Trying 127.0.0.1... Connected to localhost. Escape character is "^]". hello hello hello again hello again ^] telnet> quit Connection closed.

TCP-сервер, текстовый протокол и telnet-клиент нам понадобятся в курсовой работе.

35 ответов

Резюме

Сокет TCP является экземпляром конечной точки , определяемым IP-адресом и портом в контексте определенного TCP-соединения или состояния прослушивания.

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

Сокет TCP не является соединением , это конечная точка определенного соединения.

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

Для данной комбинации адресов и портов может быть только один разъем-слушатель.

Экспозиция

Это был интересный вопрос, который заставил меня пересмотреть некоторые вещи, которые, как я думал, я знал наизнанку. Вы бы подумали, что такое имя, как "сокет", было бы само собой разумеющимся: очевидно, было выбрано, чтобы вызвать образы конечной точки, в которую вы подключаете сетевой кабель, там есть сильные функциональные параллели. Тем не менее, на сетевом языке слово "сокет" несет столько багажа, что необходимо тщательное повторное рассмотрение.

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

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

Комбинация IP-адреса и порта строго известна как конечная точка и иногда называется сокетом. Это использование связано с RFC793, оригинальной спецификацией TCP.

TCP-соединение определяется двумя конечными точками aka сокетами.

Конечная точка (сокет) определяется комбинацией сетевого адреса и идентификатора порта. Обратите внимание, что адрес/порт не полностью идентифицирует сокет (подробнее об этом позже).

Назначение портов - это различать несколько конечных точек на заданном сетевом адресе. Можно сказать, что порт является виртуализированной конечной точкой. Эта виртуализация делает возможным несколько параллельных подключений на одном сетевом интерфейсе.

Это пара сокетов (4-кортеж состоящий из IP-адреса клиента, номер порта клиента, IP-адрес сервера, и номер порта сервера), который указывает две конечные точки, которые однозначно идентифицирует каждое TCP-соединение в интернет. (TCP-IP Illustrated Volume 1, W. Richard Stevens)

В большинстве языков, основанных на C, TCP-соединения устанавливаются и обрабатываются с использованием методов в экземпляре класса Socket. Хотя распространено работать на более высоком уровне абстракции, обычно это экземпляр класса NetworkStream, он обычно предоставляет ссылку на объект сокета. К кодеру этот объект сокета, как представляется, представляет соединение, потому что соединение создается и управляется с помощью методов объекта сокета.

В С#, чтобы установить TCP-соединение (к существующему слушателю), вы сначала создаете TcpClient. Если вы не укажете конечную точку для конструктора TcpClient, она использует значения по умолчанию - так или иначе определяется локальная конечная точка. Затем вы вызываете Connect метод на созданном экземпляре. Этот метод требует параметра, описывающего другую конечную точку.

Все это немного запутывает и заставляет вас поверить, что сокет - это соединение, которое является блокировкой. Я работал под этим недоразумением, пока Ричард Дорман не задал вопрос.

Проделав много чтения и мышления, я теперь убежден, что было бы гораздо разумнее иметь класс TcpConnection с конструктором, который принимает два аргумента: LocalEndpoint и RemoteEndpoint. Вероятно, вы могли бы поддержать один аргумент RemoteEndpoint, когда допустимы значения по умолчанию для локальной конечной точки. Это неоднозначно для многоъядерных компьютеров, но неоднозначность может быть решена с использованием таблицы маршрутизации путем выбора интерфейса с кратчайшим маршрутом к удаленной конечной точке.

Ясность будет повышена и в других отношениях. Сокет не идентифицируется комбинацией IP-адреса и порта:

[...] TCP демультиплексирует входящие сегменты, используя все четыре значения, которые содержат локальные и внешние адреса: IP-адрес получателя, номер порта назначения, IP-адрес источника и номер порта источника. TCP не может определить, какой процесс получает входящий сегмент, только глядя на порт назначения. Кроме того, единственная из [различных] конечных точек в [данном номере порта], которая будет принимать входящие запросы на соединение, является одной в состоянии прослушивания. (p255, TCP-IP Illustrated Volume 1, W. Richard Stevens)

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

Хаграваль не верит мне (см. комментарии), так что здесь настоящий образец. Я подключил веб-браузер к http://dilbert.com , а затем запустил netstat -an -p tcp . Последние шесть строк вывода содержат два примера того, что адреса и порта недостаточно, чтобы однозначно идентифицировать сокет. Существует два разных соединения между 192.168.1.3 (моя рабочая станция) и 54.252.92.236:80

TCP 192.168.1.3:63240 54.252.94.236:80 SYN_SENT TCP 192.168.1.3:63241 54.252.94.236:80 SYN_SENT TCP 192.168.1.3:63242 207.38.110.62:80 SYN_SENT TCP 192.168.1.3:63243 207.38.110.62:80 SYN_SENT TCP 192.168.1.3:64161 65.54.225.168:443 ESTABLISHED

Поскольку сокет является конечной точкой соединения, есть два сокета с комбинацией адресов/портов 207.38.110.62:80 и еще два с комбинацией адресов/портов 54.252.94.236:80 .

Я думаю, что неправильное понимание Хаграваля возникает из моего очень осторожного использования слова "идентифицирует". Я имею в виду "полностью, однозначно и однозначно идентифицировать". В приведенном выше примере имеются две конечные точки с комбинацией адрес/порт 54.252.94.236:80 . Если у вас есть адрес и порт, у вас недостаточно информации, чтобы разделить эти разъемы. Недостаточно информации для идентификации сокета.

Добавление

В абзаце втором раздела 2.7 RFC793 говорится:

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

Это определение сокета не полезно с точки зрения программирования, поскольку оно не совпадает с объектом сокета, который является конечной точкой для определенного соединения. Для программиста, и большая часть этой аудитории является программистом, это жизненно важное функциональное различие.

Ссылки

    TCP-IP Иллюстрированный том 1 Протоколы, W. Richard Stevens, 1994 Addison Wesley

    Сокет представляет собой одно соединение между двумя сетевыми приложениями. Эти два приложения номинально работают на разных компьютерах, но сокеты также могут использоваться для межпроцессного взаимодействия на одном компьютере. Приложения могут создавать несколько сокетов для связи друг с другом. Сокеты являются двунаправленными, что означает, что обе стороны соединения способны отправлять и получать данные. Поэтому сокет может быть создан теоретически на любом уровне модели OSI с 2 вверх. Программисты часто используют сокеты в сетевом программировании, хотя и косвенно. Библиотеки программирования, такие как Winsock, скрывают многие низкоуровневые детали программирования сокетов. Розетки широко используются с начала 1980-х годов.

    Порт представляет собой конечную точку или "канал" для сетевой связи. Номера портов позволяют различным приложениям на одном компьютере использовать сетевые ресурсы, не мешая друг другу. Номера портов наиболее часто встречаются в сетевом программировании, особенно в программировании сокетов. Иногда, однако, номера портов становятся видимыми для случайного пользователя. Например, некоторые веб-сайты, которые человек посещает в Интернете, используют следующий URL-адрес:

    С некоторой аналогией

    Хотя для сокетов уже было дано много технических деталей ... Я хотел бы добавить свой ответ, на всякий случай, если кто-то все еще не может почувствовать разницу между ip, портом и сокетами

    Рассмотрим сервер S ,

    и скажем, человеку X, Y, Z нужна служба (скажем, служба чата) с этого сервера S

    IP-адрес говорит кто? тот сервер чата "S", с которым X, Y, Z хочет связаться

    хорошо, вы получили "кто сервер"

    но предположим, что сервер "S" также предоставляет некоторые другие услуги другим людям, скажем, "S" предоставляет услуги хранения для лиц A, B, C

    порт говорит ---> который? сервис, который вам (X, Y, Z) нужен, т.е. сервис чата, а не сервис хранения

    ладно.., вы заставляете сервер знать, что вам нужен чат-сервис, а не хранилище

    вам три года, и сервер может захотеть идентифицировать все три по-разному

    приходит розетка

    теперь сокет говорит какой? конкретное соединение

    то есть, скажем,

    розетка 1 для человека X

    розетка 2 для человека Y

    и розетка 3 для человека Z

    Я надеюсь, что это помогает кому-то, кто был все еще смущен:)

    Во-первых, я думаю, мы должны начать с небольшого понимания того, что составляет получение пакета от A до B.

    Общим определением для сети является использование модели OSI , которая разделяет сеть на несколько слоев в соответствии с назначением. Есть несколько важных, которые мы рассмотрим здесь:

    • Уровень канала передачи данных. Этот уровень отвечает за получение пакетов данных от одного сетевого устройства к другому и находится чуть выше уровня, фактически передающего. Он говорит о MAC-адресах и знает, как найти хосты на основе их MAC (аппаратного) адреса, но не более того.
    • Сетевой уровень - это уровень, который позволяет переносить данные по машинам и по физическим границам, таким как физические устройства. Сетевой уровень должен по существу поддерживать дополнительный механизм, основанный на адресе, который как-то связан с физическим адресом; введите IP-адрес (IPv4). IP-адрес может получить ваш пакет от A до B через Интернет, но ничего не знает о том, как проходить индивидуальные перелеты. Это обрабатывается уровнем выше в соответствии с информацией о маршрутизации.
    • Транспортный уровень. Этот уровень отвечает за определение способа получения информации от A до B и любых ограничений, проверок или ошибок в этом поведении. Например, TCP добавляет дополнительную информацию в пакет, так что можно вывести, если пакеты были потеряны.

    TCP содержит, помимо прочего, концепцию ports . Это фактически разные конечные точки данных на том же IP-адресе, к которому может привязываться Internet Socket (AF_INET).

    Короткий короткий ответ.

    A порт может быть описан как внутренний адрес внутри хоста, который идентифицирует программу или процесс.

    A socket можно описать как программный интерфейс , позволяющий программе общаться с другими программами или процессами, в Интернете или локально.

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

    Чтобы получить услугу, вам нужен сервисный номер. Этот сервисный номер называется портом. Просто как тот.

    Например, HTTP как служба работает на порту 80.

    Теперь многие люди могут запросить услугу, и соединение с клиент-сервером установлено. Будет много связей. Каждое соединение представляет клиента. Чтобы поддерживать каждое соединение, сервер создает сокет для каждого соединения для поддержки своего клиента.

    Кажется, что есть много ответов, сравнивающих сокет с соединением между двумя ПК. Я думаю, что это абсолютно неверно. Сокет всегда был конечной точкой на 1 ПК, который может быть или не быть подключен - конечно, мы все использовали приемник или UDP-сокеты * в какой-то момент. Важная часть заключается в том, что он адресный и активный. Отправка сообщения в файл 1.1.1.1:1234 вряд ли будет работать, поскольку для этой конечной точки нет сокета.

    Сокеты специфичны для протокола - поэтому реализация уникальности заключается в том, что TCP / и UDP / использует * (ipaddress: порт), отличается от, например, IPX (Сеть, Node и... гейм, сокет - но другой сокет, чем под общим термином "сокет". Номера сокетов IPX эквивалентны IP-портам). Но все они предлагают уникальную адресуемую конечную точку.

    Поскольку IP стал доминирующим протоколом, порт (в сетевых терминах) стал сингулярным с номером порта UDP или TCP, который является частью адреса сокета.

    • UDP не имеет отношения к подключению - это означает, что виртуальная схема между двумя конечными точками никогда не создается. Однако в качестве конечной точки мы по-прежнему относимся к UDP-сокетам . Функции API дают понять, что оба являются просто разными типами сокетов. SOCK_DGRAM - это UDP (просто отправка сообщения), а SOCK_STREAM - TCP (создание виртуальной схемы).

      Технически, заголовок IP содержит IP-адрес, а протокол поверх IP (UDP или TCP) содержит номер порта. Это позволяет использовать другие протоколы (например, ICMP , которые не имеют номеров портов, но имеют информацию о IP-адресе).

      Это термины из двух разных доменов: "порт" - это концепция из сетей TCP/IP, "сокет" - это API (программирование). "Сокет" создается (в коде), беря порт, имя хоста или сетевой адаптер и объединяя их в структуру данных, которую вы можете использовать для отправки или получения данных.

      Соединения TCP-IP - это двунаправленные пути, соединяющие один адрес: комбинация портов с другим адресом: комбинация портов. Поэтому, всякий раз, когда вы открываете соединение с локальной машины на порт на удаленном сервере (например, www.google.com:80), вы также связываете новый номер порта на вашем компьютере с подключением, чтобы сервер мог отправлять вещи назад к вам (например, 127.0.0.1:65234). Полезно использовать netstat для просмотра ваших соединений с компьютером:

      > netstat -nWp tcp (on OS X) Active Internet connections Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp4 0 0 192.168.0.6.49871 17.172.232.57.5223 ESTABLISHED ...

      Сокет - это особый тип дескриптора файла, который используется процессом для запроса сетевых сервисов из операционной системы. Адрес сокета - это тройной: {protocol, local-address, local-process}, где локальный процесс идентифицируется номером порта.

      В наборе TCP/IP, например:

      {tcp, 193.44.234.3, 12345}

      Разговор - это линия связи между двумя процессами, таким образом изображающая связь между двумя. Ассоциацией является 5-кортеж, который полностью определяет два процесса, которые содержат соединение: {protocol, local-address, local-process, foreign-address, foreign-process}

      В наборе TCP/IP, например:

      {tcp, 193.44.234.3, 1500, 193.44.234.5, 21}

      может быть допустимой ассоциацией.

      Полу-ассоциация: {протокол, локальный адрес, локальный процесс}

      {protocol, foreign-address, foreign-process}

      которые определяют каждую половину соединения.

      Полусвязь также называется сокет или транспортный адрес. То есть, сокет является конечной точкой для связи, которая может быть названа и адресована в сети. Интерфейс сокета является одним из нескольких интерфейсов прикладного программирования (API) для протоколов связи. Разработанный как универсальный коммуникационный интерфейс программирования, он впервые был внедрен системой UNIX 4.2BSD. Хотя он не был стандартизирован, он стал фактическим промышленным стандартом.

      Порт был самой легкой частью, это просто уникальный идентификатор сокета. Сокет - это то, что процессы могут использовать для установления соединений и общения друг с другом. У Tall Jeff была отличная телефонная аналогия, которая не была идеальной, поэтому я решил ее исправить:

      Приложение состоит из пары процессов, которые обмениваются данными по сети (пара клиент-сервер). Эти процессы отправляют и получают сообщения в сеть и из нее через программный интерфейс сокет . Учитывая аналогию, представленную в книге "Компьютерная сеть: подход сверху вниз". Существует дом, который хочет общаться с другим домом. Здесь дом аналогичен процессу и двери к розетке. Процесс отправки предполагает, что на другой стороне двери есть инфраструктура, которая будет передавать данные в пункт назначения. Как только сообщение поступит с другой стороны, оно проходит через дверь приемника (гнездо) в дом (процесс). Эта иллюстрация из той же книги может помочь вам:

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

      Сокет - это структура вашего программного обеспечения. Это более-менее файл; он имеет операции, такие как чтение и запись. Это не физическая вещь; это способ для вашего программного обеспечения ссылаться на физические вещи.

      Порт - вещь, подобная устройству. Каждый хост имеет одну или несколько сетей (физически); хост имеет адрес в каждой сети. Каждый адрес может иметь тысячи портов.

      Только один сокет может использовать порт по адресу. Розетка распределяет порт примерно так же, как выделение устройства для ввода/вывода файловой системы. Как только порт будет выделен, никакой другой сокет не сможет подключиться к этому порту. Порт будет освобожден, когда сокет будет закрыт.

      Сокет - это одна конечная точка двусторонней линии связи между двумя программами, запущенными в сети. Сокет привязан к номеру порта, так что уровень TCP может идентифицировать приложение, для которого данные предназначены для отправки.

      Относительная терминология TCP/IP, которую я предполагаю, подразумевает этот вопрос. В условиях неспециалиста:

      PORT - это номер телефона определенного дома в определенном почтовом индексе. Почтовый код города можно рассматривать как IP-адрес города и всех домов в этом городе.

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

      Порт и сокет можно сравнить с отделением банка.

      Номер здания "Банка" аналогичен IP-адресу. Банк имеет различные разделы, такие как:

      1. Отдел сберегательного счета
      2. Отдел персональных кредитов
      3. Отдел ипотечного кредитования
      4. Отдел рассмотрения жалоб

      Таким образом, 1 (отдел сберегательного счета), 2 (отдел персональных кредитов), 3 (отдел жилищных кредитов) и 4 (отдел рассмотрения жалоб) являются портами.

      Теперь позвольте нам сказать, что вы идете, чтобы открыть сберегательный счет, вы идете в банк (IP-адрес), затем вы идете в "отдел сберегательного счета" (порт № 1), затем вы встречаете одного из сотрудников, работающих в "отдел сберегательного счета" ". Давайте позвоним ему SAVINGACCOUNT_EMPLOYEE1 для открытия счета.

      SAVINGACCOUNT_EMPLOYEE1 - ваш дескриптор сокета, поэтому может быть от SAVINGACCOUNT_EMPLOYEE1 до SAVINGACCOUNT_EMPLOYEEN. Это все дескрипторы сокетов.

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

      Сокет является конечной точкой связи. Сокет не имеет прямого отношения к семейству протоколов TCP/IP, его можно использовать с любым протоколом, поддерживаемым вашей системой. API сокета C ожидает, что вы сначала получите пустой объект сокета из системы, который затем можно будет привязать к локальному адресу сокета (чтобы напрямую получать входящий трафик для протоколов без установления соединения или принимать входящие запросы на соединение для протоколов, ориентированных на установление соединения) или что вы можете подключиться к адресу удаленного сокета (для любого типа протокола). Вы можете даже сделать и то и другое, если хотите контролировать оба: локальный адрес сокета, к которому привязан сокет, и адрес удаленного сокета, к которому подключен сокет. Для протоколов без установления соединения подключение сокета даже необязательно, но если вы этого не сделаете, вам также придется передавать адрес назначения с каждым пакетом, который вы хотите отправить через сокет, как иначе, как бы сокет знал, куда отправить эти данные для? Преимущество заключается в том, что вы можете использовать один сокет для отправки пакетов на разные адреса сокетов. После того, как вы настроили сокет и, возможно, даже подключили его, считайте его двунаправленным каналом связи. Вы можете использовать его для передачи данных в какой-либо пункт назначения, а другой пункт назначения может использовать его для передачи данных вам. То, что вы пишете в сокет, отправляется, а то, что было получено, доступно для чтения.

      С другой стороны, порты - это то, что есть только у определенных протоколов стека протоколов TCP/IP. TCP и UDP пакеты имеют порты. Порт - это просто число. Комбинация порта источника и порта назначения определяет канал связи между двумя хостами. Например, у вас может быть сервер, который должен быть и простым HTTP-сервером, и простым FTP-сервером. Если сейчас приходит пакет для адреса этого сервера, как он узнает, является ли это пакет для HTTP или FTP-сервера? Что ж, он будет знать, так как HTTP-сервер будет работать на порту 80, а FTP-сервер - на порту 21, поэтому, если пакет поступает с портом назначения 80, он предназначен для HTTP-сервера, а не для FTP-сервера. Также пакет имеет порт источника, поскольку без такого порта источника сервер может иметь только одно подключение к одному IP-адресу за раз. Порт источника позволяет серверу различать идентичные соединения: все они имеют один и тот же порт назначения, например, порт 80, один и тот же IP-адрес назначения, всегда один и тот же адрес сервера и один и тот же IP-адрес источника, поскольку все они исходят от одного и того же клиент, но так как они имеют разные исходные порты, сервер может отличить их друг от друга. И когда сервер отправляет обратно ответы, он делает это с портом, с которого поступил запрос, таким образом, клиент также может различать разные ответы, которые он получает.

      Один порт может иметь один или несколько разъемов, подключенных к другому внешнему IP-адресу, например, к нескольким розеткам.

      TCP 192.168.100.2:9001 155.94.246.179:39255 ESTABLISHED 1312 TCP 192.168.100.2:9001 171.25.193.9:61832 ESTABLISHED 1312 TCP 192.168.100.2:9001 178.62.199.226:37912 ESTABLISHED 1312 TCP 192.168.100.2:9001 188.193.64.150:40900 ESTABLISHED 1312 TCP 192.168.100.2:9001 198.23.194.149:43970 ESTABLISHED 1312 TCP 192.168.100.2:9001 198.49.73.11:38842 ESTABLISHED 1312

      Socket - это абстракция, предоставляемая ядром пользовательским приложениям для ввода/вывода данных. Тип сокета определяется протоколом его обработки, IPC-связью и т.д. Поэтому, если кто-то создает сокет TCP, он может делать манипуляции, такие как чтение данных в сокет и запись данных на него с помощью простых методов и обработки протокола нижнего уровня, таких как преобразования TCP и пересылка пакетов на более низкие сетевые протоколы выполняется конкретной реализацией сокета в ядре. Преимущество заключается в том, что пользователю не нужно беспокоиться о том, как обращаться с конкретными конкретными протоколами, а также просто читать и записывать данные в сокет, как обычный буфер. То же самое верно в случае IPC, пользователь просто считывает и записывает данные в сокет, а ядро ​​обрабатывает все детали нижнего уровня в зависимости от типа созданного сокета.



Рекомендуем почитать

Наверх