Слайд 1Архитектура вычислительных систем.
Лекция 10.
Ловецкий К.П.
Москва,
ноябрь 2014
Взаимодействие по сети.
Сокеты
Слайд 2Понятие протокола. Модель ISO OSI
Под протоколом обмена (или для краткости
просто «протоколом») обычно понимается набор соглашений, которым должны следовать участники
обмена информацией, чтобы понять друг друга.
Это не обязательно должны быть компьютеры. Скажем, азбука Морзе также является своего рода протоколом; более сложный пример некомпьютерного протокола - правила радиообмена между пилотами самолетов и наземными диспетчерами.
Слайд 3При любом осмысленном взаимодействии по компьютерной сети задействуется сразу несколько
протоколов, относящихся к разным уровням. Так, модем, с помощью которого
мы вошли в сеть, следует протоколу, фиксирующему правила перевода цифровых данных в аналоговый сигнал, передающийся по телефонной линии, и обратно. В это же время запущенный нами браузер связывается с сайтом в сети Internet, используя транспортный протокол TCP/IP (Transmission Control Protocol / Internet Protocol). Сервер и браузер обмениваются информацией, используя протокол HTTP (hypertext transfer protocol).
Понятие протокола. Модель ISO OSI
Слайд 4Существует стандартная модель (ISO/OSI), предполагающая разделение всех сетевых протоколов на
семь уровней. ISO расшифровывается как International Standard Organization (организация, утвердившая
соответствующий стандарт), OSI означает Open Systems Interconnection (буквально переводится как «взаимосоединение открытых систем», но обычно при переводе используется слово «взаимодействие»).
Модель включает семь уровней:
7 Прикладной напр. HTTP, SMTP, SNMP, FTP, Telnet, scp, NFS, RTSP
6 Представительный напр. XML, XDR, ASN.1, SMB, AFP
5 Сеансовый напр. TLS, SSL, ISO 8327 / CCITT X.225, RPC, NetBIOS, ASP
4 Транспортный напр. TCP, UDP, RTP, SCTP, SPX, ATP, DCCP, BGP, GRE
3 Сетевой напр. IP, ICMP, IGMP, CLNP, ARP, RARP, OSPF, RIP, IPX, DDP
2 Канальный напр. Ethernet, Token ring, PPP, HDLC, X.25, Frame relay, ISDN, ATM, MPLS, Wi-Fi
1 Физический напр. электричество, радио, лазер
Модель ISO OSI
Слайд 5Модель ISO OSI
• Физический. Соглашения об использовании физического соединения между
машинами, включая количество проводов в кабеле, частоту и другие характеристики
сигнала, и т.п.
• Канальный. Соглашения о том, как будет использоваться физическая среда для передачи данных; это включает, например, коррекцию ошибок.
• Сетевой. Компьютерная сеть обычно состоит из множества каналов, соединяющих компьютеры. Некоторые компьютеры, называемые шлюзами, подключаются к нескольким каналам одновременно и передают пакеты из одного канала в другой. Сетевой уровень протоколов определяет соглашения о том, как данные будут передаваться по сети, так, чтобы из любой точки сети можно было передать данные в любую другую точку сети, вне зависимости от того, по скольким каналам придется передавать информацию, и сколько шлюзов при этом будет задействовано. Сетевой уровень отвечает за адресацию, маршрутизацию пакетов и т.п.
Слайд 6Модель ISO OSI
• Транспортный. Пакеты, передаваемые по сети с помощью
протоколов сетевого уровня, обычно ограничены в размерах и, кроме того,
могут доставляться не в том порядке, в котором были отправлены, теряться, а в некоторых случаях искажаться. Обычно прикладным программам требуется более высокий уровень сервиса, обеспечивающий надежность доставки данных и простоту работы. За это отвечают протоколы транспортного уровня; реализующие их программы сами следят за доставкой пакетов, отправляя и анализируя соответствующие подтверждения, нумеруют пакеты и расставляют их в нужном порядке после получения, и т.п.
• Сеансовый. Определяет порядок проведения сеанса связи, очередность запросов и т.п.
• Представительный. На этом уровне определяются правила представления данных, в частности, кодировка, правила представления двоичных данных текстом, и т.п.
• Прикладной. Протоколы этого уровня определяют, как конечные приложения будут использовать соединения для решения конкретных задач, для которых они предназначены.
Слайд 7Модель ISO OSI
Для упрощения запоминания названий уровней модели ISO/OSI существует
мнемоническая фраза «All People Seem To Need Data Processing» («всем
людям, похоже, нужна обработка данных»). Первые буквы слов этой фразы соответствуют первым буквам английских названий уровней модели:
Application – Прикладной;
Presentation – Представительный;
Session – Сеансовый;
Transport – Транспортный;
Network – Сетевой;
Datalink – Канальный;
и Physical - Физический.
Слайд 8Сокеты. Семейства адресации и типы взаимодействия
Дать строгое определение сокета
достаточно сложно. Ограничимся замечанием, что сокет (socket - углубление, гнездо,
разъём) - это объект ядра операционной системы, через который происходит сетевое взаимодействие.
Это нельзя считать определением сокета хотя бы по той причине, что взаимодействие по сети бывает основано не только на сокетах, а основанное на сокетах взаимодействие не обязательно происходит по сети.
Сокет - это название программного интерфейса для обеспечения информационного обмена между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет — абстрактный объект, представляющий конечную точку соединения.
Слайд 9Сокеты. Семейства адресации и типы взаимодействия
Следует различать клиентские и
серверные сокеты. Клиентские сокеты грубо можно сравнить с оконечными аппаратами
телефонной сети, а серверные — с коммутаторами. Клиентское приложение (например, браузер) использует только клиентские сокеты, а серверное (например, веб-сервер, которому браузер посылает запросы) — как клиентские, так и серверные сокеты.
Интерфейс сокетов впервые появился в BSD Unix. Программный интерфейс сокетов описан в стандарте POSIX.1 и в той или иной мере поддерживается всеми современными операционными системами.
Сокет на сленге системных администраторов означает комбинацию IP-адреса и номера порта, например 10.10.10.10:80.
Слайд 10Сокеты. Семейства адресации и типы взаимодействия
Для идентификации сокетов (или,
точнее, абонентов связи) в сетях используются адреса. В зависимости от
используемых протоколов адреса могут выглядеть совершенно по-разному. Так, в протоколе Internet TCP/IP адрес сокета состоит из двух частей: ip-адреса (4 байта, записывается в виде четырех десятичных чисел через точку, например 192.168.10.12) и порта (двухбайтовое целое число).
В перспективе возможен переход Internet на протокол IPv6 (ныне используемый называется IPv4), в котором ip-адрес будет занимать 16 байт и записываться в виде восьми групп по четыре шестнадцатеричные цифры, например 12ff:2001:0055:2eab:0767:1212:f1b1:a00a.
Ясно, что для представления таких адресов необходимы совершенно иные структуры данных.
Слайд 11Сокеты. Семейства адресации и типы взаимодействия
В сетях, построенных по
технологии компании Novell, используется стек протоколов IPX/SPX. В рамках этих
протоколов адрес сокета состоит из трех частей: 4х-байтного номера сети, 6-байтного номера машины (хоста) и 2-байтного номера сокета.
Существуют и другие семейства протоколов. Кроме того, отдельный специальный вид сокетов предназначен для связи процессов в рамках одной машины; в качестве адресов такие сокеты используют имена специальных файлов в файловой системе.
Несмотря на такие различия, между различными видами взаимодействия много общего. В любом случае, было бы категорически неприемлемо модифицировать интерфейс ядра операционной системы (и, соответственно, переделывать все прикладное программное обеспечение) при добавлении поддержки очередного семейства протоколов.
Слайд 12Сокеты. Семейства адресации
Именно поэтому введена подсистема сокетов, представляющая собой своего
рода общий знаменатель между всеми видами сетевого взаимодействия процессов. При
создании сокета указывается, к какому семейству адресации (address family) данный сокет будет принадлежать. Набор поддерживаемых семейств может быть расширен добавлением соответствующих модулей в ядро и написанием прикладных программ, работающих с новыми адресами; при этом системные вызовы останутся прежними, а значит, не придется переделывать системные библиотеки и программы, не использующие новые протоколы.
Слайд 13Сокеты. Типы взаимодействия
Кроме используемого семейства адресации при создании сокета
необходимо задать тип взаимодействия. Рассмотрим два из них: дейтаграммный и
потоковый.
При дейтаграммном соединении на сокете доступны две основные операции: передача пакета и прием пакета данных, причем размер пакета, вообще говоря, ограничен (но a priori размер этого ограничения неизвестен). Переданный пакет может быть потерян или, наоборот, случайно сдублирован (то есть получено будет два или более одинаковых пакета). Два переданных пакета могут прийти получателю в обратном порядке. Соответственно, при таком режиме работы обеспечение надежности ложится на пользовательскую программу (приложение).
Слайд 14Сокеты. Типы взаимодействия
Потоковый тип взаимодействия предоставляет прикладному программисту иллюзию
надежного двунаправленного канала передачи данных. Данные могут быть записаны в
канал порциями любого размера; гарантируется, что на другом конце данные либо будут получены без потерь и в том же порядке, либо не будут получены вообще (соединение в этом случае будет разорвано с фиксацией ошибки). В этом случае заботу о передаче подтверждений, о расстановке пакетов в исходном порядке, о повторной передаче потерянных пакетов и т.п. берет на себя операционная система.
Слайд 15Сокеты. Типы взаимодействия
Сокет в ОС Unix создается с помощью
вызова
int socket(int address_family, int type, int protocol);
Параметр address_family задает
используемое семейство адресации. Два из них:
AF_INET для взаимодействия по сети посредством протоколов TCP/IP (адрес сокета в этом случае представляет собой пару ip-адрес/порт);
AF_UNIX для взаимодействия в рамках одной машины (в этом случае адрес сокета представляет собой имя файла).
Параметр type задает тип взаимодействия. Константа SOCK_STREAM используется для потокового взаимодействия и
SOCK_DGRAM для дейтаграммного.
Слайд 16Сокеты. Типы взаимодействия
Наконец, последний параметр задает конкретный используемый протокол.
Для рассматриваемых двух семейств адресации и двух типов взаимодействия протокол
однозначно определяется значениями первых двух параметров, так что в качестве этого параметра всегда можно указать число 0.
Вызов возвращает -1 в случае ошибки;
в случае успеха возвращается номер файлового дескриптора, связанного с созданным сокетом.
Слайд 17Работа с адресами сокетов. Вызов bind()
Связывание сокета с конкретным
адресом производится вызовом bind():
int bind(int sockfd, struct sockaddr *addr, int
addrlen);
где sockfd - дескриптор сокета, полученный в результате выполнения вызова socket(); addr - указатель на структуру, содержащую адрес; наконец, addrlen - размер структуры адреса в байтах.
Реально в качестве параметра addr используется не структура типа sockaddr, а структура другого типа, который зависит от используемого семейства адресации.
Слайд 18Работа с адресами сокетов. Вызов bind()
В семействе AF_INET используется
структура struct sockaddr_in, умеющая хранить пару «IP-адрес + порт». Эта
структура имеет следующие поля:
• sin_family - обозначает семейство адресации (в данном случае это AF_INET).
• sin_port - задает номер.
• sin_addr - задает IP-адрес. Поле sin_addr само является структурой, имеющей лишь одно поле с именем s_addr, которое хранит ip-адрес в виде беззнакового четырехбайтного целого.
Слайд 19Работа с адресами сокетов. Вызов bind()
В семействе AF_UNIX используется
структура struct sockaddr_un, в которой можно хранить имя файла. Эта
структура состоит из двух полей:
• sun_family - обозначает семейство адресации (в данном случае это AF_UNIX).
• sun_path - массив на 108 символов, в который непосредственно записывается строка имени файла.
Вызов bind() возвращает 0 в случае успеха, -1 в случае ошибки. Существует множество ситуаций, в которых вызов bind() может не пройти; например, в случае попытки использования привилегированного номера порта (от 1 до 1023) или порта, который на данной машине уже кем-то занят (возможно, другой вашей программой). Поэтому обработка ошибок при вызове bind() особенно важна.
Слайд 20Прием и передача дейтаграмм
Сокет дейтаграммного типа создается вызовом socket()
с указанием константы SOCK_DGRAM в качестве второго параметра. Связать сокет
с конкретным адресом можно с помощью bind(), в противном случае с сокета можно будет отправлять данные (система выберет один из своих адресов и портов в качестве адреса отправителя), но нельзя будет получить ответ.
После того, как сокет создан и связан с адресом, для передачи и приема данных можно использовать системные вызовы sendto() и recvfrom():
int sendto(int s, const void *buf, int len, int flags, const struct sockaddr *to, socklen_t tolen);
int recvfrom(int s, void *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen);
Слайд 21Потоковые сокеты. Клиент-серверная модель
При взаимодействии с помощью потоковых сокетов
необходимо перед началом взаимодействия установить соединение. Ясно, что если речь
идет о взаимодействии неродственных процессов, и тем более о взаимодействии процессов, находящихся на разных машинах, один из участников взаимодействия должен быть инициатором соединения, а второй - принять соединение (согласиться на его установление).
Слайд 22Потоковые сокеты. Клиент-серверная модель
Здесь мы сталкиваемся с понятиями клиента
и сервера.
Под сервером понимается программа, ожидающая
запросов и производящая какие-либо действия исключительно в ответ на запросы, а при отсутствии запросов не делающая вообще ничего.
Под клиентом понимается программа, обращающаяся с запросом к серверу.
При установлении соединения между потоковыми сокетами один процесс ожидает запрос на установление соединения, а другой инициирует такой запрос.
Эти процессы и называются с точки зрения установления соединения сервером и клиентом.
В частности, при работе по сети Интернет для организации взаимодействия потоковых сокетов используется протокол TCP, а соответствующие программы называются TCP-сервером и TCP-клиентом.
Слайд 23Организация сервера
Чтобы начать ожидание запросов на соединение, сервер создает
сокет соответствующего типа, связывает его с адресом и переводит в
специальное состояние, называемое слушающим (Listening). На сокете, находящемся в слушающем состоянии, может быть осуществлена только одна операция - принятие соединения. При установлении соединения ядро операционной системы, которая обслуживает программу-сервер, создает еще один сокет, который и будет использоваться для передачи данных по только что установленному соединению.
Итак, на стороне сервера сокет необходимо создать вызовом socket() с соответствующими параметрами и связать его с конкретным адресом, на котором будут приниматься соединения, с помощью вызова bind(). Затем сокет следует перевести в слушающий режим с помощью вызова int listen(int sd, int qlen);
Слайд 24Организация сервера
Сокет переведен в слушающий режим:
int listen(int sd,
int qlen);
Параметр sd - связанный с сокетом файловый дескриптор. Параметр
qlen задает размер очереди непринятых запросов на соединение.
Принятие соединения производится вызовом
int accept(int sd, struct sockaddr *addr, socklen_t *addrlen);
Параметр sd задает дескриптор слушающего сокета. Параметр addr указывает на структуру, в которую следует записать адрес сокета, с которым установлено соединение (иначе говоря, адрес другого конца соединения). Параметр addrlen представляет собой указатель на переменную типа socklen_t, причем перед вызовом accept() в эту переменную следует занести размер адресной структуры, на которую указывает предыдущий параметр; после возврата из accept() переменная будет содержать количество байт, которые вызов в итоге в эту структуру записал.
Слайд 25Организация сервера
Вызов accept() возвращает файловый дескриптор нового сокета, созданного
специально для обслуживания вновь установленного соединения (либо -1 в случае
ошибки). Если на момент выполнения accept() запросов на соединение еще не поступило, вызов блокирует вызвавший процесс и ожидает поступления запроса на соединение, возвращая управление только после того, как такой запрос поступит, и соединение будет установлено.
Слайд 26Организация клиента
Клиентская программа должна, как и сервер, создать сокет
вызовом socket(). Связывать сокет с конкретным адресом не обязательно; если
этого не сделать, система выберет адрес автоматически.
Запрос на соединение формируется вызовом
int connect(int sd, struct sockaddr *addr, int addrlen);
Параметр sd - связанный с сокетом файловый дескриптор. Параметр addr указывает на структуру, содержащую адрес сервера (т.е. адрес слушающего сокета, с которым мы хотим установить соединение). Естественно, используется при этом структура типа, соответствующего избранному семейству адресации (sockaddr_in для AF_INET и sockaddr_un для AF_UNIX). Параметр addrlen должен быть равен размеру этой структуры.
Вызов возвращает 0 в случае успеха, -1 в случае ошибки.
Слайд 27Обмен данными
После успешного установления соединения для передачи по нему
данных можно использовать уже известные нам вызовы read() и write(),
считая дескрипторы соединенных сокетов обычными файловыми дескрипторами (на стороне сервера это дескриптор, возвращенный вызовом accept(), на стороне клиента - дескриптор сокета, к которому применялся вызов connect()).
Для более гибкого управления обменом данными существуют также вызовы recv() и send(), отличающиеся от read() и write() только наличием дополнительного параметра flags.
Слайд 28Обмен данными
Завершить работу с сокетом можно с помощью вызова
int shutdown(int sd, int how);
Параметр sd задает дескриптор сокета, how
- что именно следует прекратить.
При how == 0 сокет закрывается на чтение, при how == 1 - на запись, при how == 2 - полностью (в оба направления).
Можно также просто закрыть дескриптор сокета с помощью вызова close(). Сделать это следует в любом случае, т.к. shutdown() только прекращает обмен данными на сокете, но сам сокет (и его файловый дескриптор) при этом не исчезают, а количество одновременно открытых файловых дескрипторов в системе ограничено.
В случае, если дальний конец соединения закрыт, очередной вызов read() или recv() вернет 0, сигнализируя о «конце файла».
Слайд 29Использование сокетов для связи родственных процессов
В отличие от неименованных
каналов, сокеты представляют собой двунаправленный канал связи, что делает их
в некоторых случаях более удобным средством даже при взаимодействии родственных процессов. В ОС Unix предусмотрен вызов
int socketpair(int af, int type, int protocol, int sv[2]);
Параметры af, type и protocol задают, соответственно, семейство адресации, тип и протокол для создаваемых сокетов. Часто допускается лишь одна комбинацию этих параметров: AF_UNIX, SOCK_STREAM и 0. Параметр sv должен указывать на массив из двух элементов типа int, в которые вызов занесет файловые дескрипторы двух созданных сокетов. Сокеты создаются уже связанными друг с другом, причем оба конца открыты как на чтение, так и на запись.
Вызов возвращает 0 в случае успеха, -1 в случае ошибки.