СЕТЕВОЕ ПРОГРАММИРОВАНИЕ В LINUX
Со́кеты Со́кеты (англ. socket углубление, гнездо, разъём) название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет абстрактный объект, представляющий конечную точку соединения. Со́кеты (англ. socket углубление, гнездо, разъём) название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет абстрактный объект, представляющий конечную точку соединения. Существуют два вида сокетов синхронные (блокируемые) и асинхронные (неблокируемые). Синхронные сокеты задерживают управление на время выполнения операции, а асинхронные возвращают его немедленно, продолжая выполнение в фоновом режиме, и, закончив работу, уведомляют об этом вызывающий код. Существуют два вида сокетов синхронные (блокируемые) и асинхронные (неблокируемые). Синхронные сокеты задерживают управление на время выполнения операции, а асинхронные возвращают его немедленно, продолжая выполнение в фоновом режиме, и, закончив работу, уведомляют об этом вызывающий код.
Со́кеты Сокеты, независимо от вида, делятся на два типа: Сокеты, независимо от вида, делятся на два типа: Потоковые; Потоковые; Дейтаграммные. Дейтаграммные. Потоковые сокеты работают с установкой соединения, обеспечивая надежную идентификацию обоих сторон и гарантируют целостность и успешность доставки данных. Потоковые сокеты работают с установкой соединения, обеспечивая надежную идентификацию обоих сторон и гарантируют целостность и успешность доставки данных. Дейтаграмные сокеты работают без установки соединения и не обеспечивают ни идентификации отправителя, ни контроля успешности доставки данных, зато они заметно быстрее потоковых. Дейтаграмные сокеты работают без установки соединения и не обеспечивают ни идентификации отправителя, ни контроля успешности доставки данных, зато они заметно быстрее потоковых. Выбор типа сокетов определяется транспортным протоколом на котором работает сервер, - клиент не может по своему желанию установить с дейтаграммным сервером потоковое соединение. Выбор типа сокетов определяется транспортным протоколом на котором работает сервер, - клиент не может по своему желанию установить с дейтаграммным сервером потоковое соединение.
Со́кеты Замечание: дейтаграммные сокеты опираются на протокол UDP, а потоковые на TCP. Замечание: дейтаграммные сокеты опираются на протокол UDP, а потоковые на TCP. Кроме потоковых и дейтаграмных сокетов существуют сырые сокеты. Серый сокет – это сокет, который принимает пакеты, обходит уровни TCP и UDP в стеке TCP/IP и отправляет их непосредственно приложению. При использовании таких сокетов пакет не проходит через фильтр TCP/IP, то есть никак не обрабатывается, и предстает в своей серой форме. В таком случае обязанность обработать все данные и выполнить такие действия, как удаление заголовков и разбор полей, ложиться на получающее приложение – все равно что включить в приложение небольшой стек TCP/IP. Кроме потоковых и дейтаграмных сокетов существуют сырые сокеты. Серый сокет – это сокет, который принимает пакеты, обходит уровни TCP и UDP в стеке TCP/IP и отправляет их непосредственно приложению. При использовании таких сокетов пакет не проходит через фильтр TCP/IP, то есть никак не обрабатывается, и предстает в своей серой форме. В таком случае обязанность обработать все данные и выполнить такие действия, как удаление заголовков и разбор полей, ложиться на получающее приложение – все равно что включить в приложение небольшой стек TCP/IP.
Со́кеты Серые сокеты главным образом используются при разработке специализированных низкоуровневых протокольных приложений, таких как traceroute и ping. Работа с серыми сокетами требует солидного знания базовых протоколов TCP/UDP/IP и лежит за рамками данного курса. Серые сокеты главным образом используются при разработке специализированных низкоуровневых протокольных приложений, таких как traceroute и ping. Работа с серыми сокетами требует солидного знания базовых протоколов TCP/UDP/IP и лежит за рамками данного курса. В Unix дескрипторы сокетов реализованы так же, как дескрипторы фалов. В действительности большинство функций, работающих с дескрипторами файлов, таких как read или write, будут работать и с дескрипторами сокетов. В Unix дескрипторы сокетов реализованы так же, как дескрипторы фалов. В действительности большинство функций, работающих с дескрипторами файлов, таких как read или write, будут работать и с дескрипторами сокетов.
Со́кеты Перед использованием сокетов в программе необходимо подключить библиотеки. В Unix нет необходимости проводить инициализацию и деинициализацию библиотеки для работы с сокетами, как в Windows. Библиотеку достаточно просто подключить. Для написания сетевого клиент- серверного приложения для операционных систем семейства Unix необходимы библиотеки : Перед использованием сокетов в программе необходимо подключить библиотеки. В Unix нет необходимости проводить инициализацию и деинициализацию библиотеки для работы с сокетами, как в Windows. Библиотеку достаточно просто подключить. Для написания сетевого клиент- серверного приложения для операционных систем семейства Unix необходимы библиотеки : sys/types.h - содержит определения системных типов данных, которые используются в следующих двух заголовочных файлах; sys/types.h - содержит определения системных типов данных, которые используются в следующих двух заголовочных файлах; sys/socket.h - содержит определения структур, необходимых для работы с сокетами; sys/socket.h - содержит определения структур, необходимых для работы с сокетами; netinet/in.h - содержит константы и структуры, необходимые для пространства адресов интернета. netinet/in.h - содержит константы и структуры, необходимые для пространства адресов интернета.
Со́кеты В программе сокет идентифицируется дескриптором - это просто переменная типа int. Программа получает дескриптор от операционной системы при создании сокета, а затем передаёт его сервисам socket API для указания сокета, над которым необходимо выполнить то или иное действие. В программе сокет идентифицируется дескриптором - это просто переменная типа int. Программа получает дескриптор от операционной системы при создании сокета, а затем передаёт его сервисам socket API для указания сокета, над которым необходимо выполнить то или иное действие. int socket(int af, int type, int protocol); int socket(int af, int type, int protocol); Функция создания сокета принципиально ничем не отличается от ее аналога в Windows (см. методические указания к л.р. по сокетам для windows). Стоит упомянуть, что первый слева параметр в Unix может принимать значение AF_UNIX, соответствующее сокетам в файловом пространстве имен. Функция создания сокета принципиально ничем не отличается от ее аналога в Windows (см. методические указания к л.р. по сокетам для windows). Стоит упомянуть, что первый слева параметр в Unix может принимать значение AF_UNIX, соответствующее сокетам в файловом пространстве имен.
Адреса Прежде чем передавать данные через сокет, его необходимо связать с адресом в выбранном домене. Вид адреса зависит от выбранного вами домена. В Unix-домене это текстовая строка - имя файла, через который происходит обмен данными. В Internet- домене адрес задаётся комбинацией IP-адреса и 16- битного номера порта. IP-адрес определяет хост в сети, а порт - конкретный сокет на этом хосте. Протоколы TCP и UDP используют различные пространства портов. Прежде чем передавать данные через сокет, его необходимо связать с адресом в выбранном домене. Вид адреса зависит от выбранного вами домена. В Unix-домене это текстовая строка - имя файла, через который происходит обмен данными. В Internet- домене адрес задаётся комбинацией IP-адреса и 16- битного номера порта. IP-адрес определяет хост в сети, а порт - конкретный сокет на этом хосте. Протоколы TCP и UDP используют различные пространства портов. Для явного связывания сокета с некоторым адресом используется функция bind. Её прототип имеет вид: Для явного связывания сокета с некоторым адресом используется функция bind. Её прототип имеет вид: #include #include int bind(int sockfd, struct sockaddr *addr, int addrlen); int bind(int sockfd, struct sockaddr *addr, int addrlen);
Адреса В качестве первого параметра передаётся дескриптор сокета, который мы хотим привязать к заданному адресу. Второй параметр, addr, содержит указатель на структуру с адресом, а третий - длину этой структуры. В качестве первого параметра передаётся дескриптор сокета, который мы хотим привязать к заданному адресу. Второй параметр, addr, содержит указатель на структуру с адресом, а третий - длину этой структуры. struct sockaddr { struct sockaddr { unsigned short sa_family;//Семейство адресов, AF_xx unsigned short sa_family;//Семейство адресов, AF_xx char sa_data[14]; // 14 байтов для хранения адреса char sa_data[14]; // 14 байтов для хранения адреса }; }; Поле sa_family содержит идентификатор домена, тот же, что и первый параметр функции socket. В зависимости от значения этого поля по-разному интерпретируется содержимое массива sa_data. Поле sa_family содержит идентификатор домена, тот же, что и первый параметр функции socket. В зависимости от значения этого поля по-разному интерпретируется содержимое массива sa_data.
Адреса Можно использовать вместо sockaddr одну из альтернативных структур вида sockaddr_XX (XX - суффикс, обозначающий домен: "un" - Unix, "in" - Internet и т. д.). При передаче в функцию bind указатель на эту структуру приводится к указателю на sockaddr. Можно использовать вместо sockaddr одну из альтернативных структур вида sockaddr_XX (XX - суффикс, обозначающий домен: "un" - Unix, "in" - Internet и т. д.). При передаче в функцию bind указатель на эту структуру приводится к указателю на sockaddr. Существует два порядка хранения байтов в слове и двойном слове. Один из них называется порядком хоста (host byte order), другой - сетевым порядком (network byte order) хранения байтов. При указании IP-адреса и номера порта необходимо преобразовать число из порядка хоста в сетевой. Для этого используются функции htons (Host TO Network Short) и htonl (Host TO Network Long). Обратное преобразование выполняют функции ntohs и ntohl. Существует два порядка хранения байтов в слове и двойном слове. Один из них называется порядком хоста (host byte order), другой - сетевым порядком (network byte order) хранения байтов. При указании IP-адреса и номера порта необходимо преобразовать число из порядка хоста в сетевой. Для этого используются функции htons (Host TO Network Short) и htonl (Host TO Network Long). Обратное преобразование выполняют функции ntohs и ntohl.
Установка соединения (сервер) Установка соединения на стороне сервера состоит из четырёх этапов, ни один из которых не может быть опущен. Сначала сокет создаётся и привязывается к локальному адресу. Если компьютер имеет несколько сетевых интерфейсов с различными IP- адресами, вы можете принимать соединения только с одного из них, передав его адрес функции bind. Если же вы готовы соединяться с клиентами через любой интерфейс, задайте в качестве адреса константу INADDR_ANY. serv_addr.sin_addr.s_addr = INADDR_ANY; Что касается номера порта, вы можете задать конкретный номер или 0 (в этом случае система сама выберет произвольный неиспользуемый в данный момент номер порта).
Установка соединения (сервер) Далее создаётся очередь запросов на соединение. Сокет переводится в режим ожидания запросов со стороны клиентов. Всё это выполняет функция listen: // ожидание подключений, размер очереди - 5 listen(sockfd,5); Первый параметр - дескриптор сокета, а второй задаёт размер очереди запросов. Каждый раз, когда очередной клиент пытается соединиться с сервером, его запрос ставится в очередь, так как сервер может быть занят обработкой других запросов. Если очередь заполнена, все последующие запросы будут игнорироваться. Когда сервер готов обслужить очередной запрос, он использует функцию accept. newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
Установка соединения (сервер) Функция accept создаёт для общения с клиентом новый сокет и возвращает его дескриптор в переменную newsockfd типа int. Параметр sockfd задаёт слушающий сокет. После вызова он остаётся в слушающем состоянии и может принимать другие соединения. В структуру, на которую ссылается cli_addr, записывается адрес сокета клиента, который установил соединение с сервером. В переменную, адресуемую указателем clilen, изначально записывается размер структуры; функция accept записывает туда длину, которая реально была использована. Если вас не интересует адрес клиента, вы можете просто передать NULL в качестве второго и третьего параметров.
Установка соединения (клиент) На стороне клиента для установления соединения используется функция connect. На стороне клиента для установления соединения используется функция connect. connect(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr)) connect(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr)) Здесь sockfd - сокет, который будет использоваться для обмена данными с сервером, serv_addr содержит указатель на структуру с адресом сервера, а addrlen - длину этой структуры. Здесь sockfd - сокет, который будет использоваться для обмена данными с сервером, serv_addr содержит указатель на структуру с адресом сервера, а addrlen - длину этой структуры.
Обмен данными Для обмена данными используются функции send и recv. В Unix для работы с сокетами можно использовать также файловые функции read и write, но они обладают меньшими возможностями. Функция send используется для отправки данных и имеет следующий прототип. Для обмена данными используются функции send и recv. В Unix для работы с сокетами можно использовать также файловые функции read и write, но они обладают меньшими возможностями. Функция send используется для отправки данных и имеет следующий прототип. int send(int sockfd, const void *msg, int len, int flags); int send(int sockfd, const void *msg, int len, int flags); Здесь sockfd - это дескриптор сокета, через который мы отправляем данные, msg - указатель на буфер с данными, len - длина буфера в байтах, а flags - набор битовых флагов, управляющих работой функции (если флаги не используются, передайте функции 0). Здесь sockfd - это дескриптор сокета, через который мы отправляем данные, msg - указатель на буфер с данными, len - длина буфера в байтах, а flags - набор битовых флагов, управляющих работой функции (если флаги не используются, передайте функции 0).
Обмен данными Флаг MSG_OOB используется для приёма срочных данных, а MSG_DONTROUTE запрещает маршрутизацию пакетов. Нижележащие транспортные слои могут проигнорировать флаг MSG_DONTROUTE. Флаг MSG_OOB используется для приёма срочных данных, а MSG_DONTROUTE запрещает маршрутизацию пакетов. Нижележащие транспортные слои могут проигнорировать флаг MSG_DONTROUTE. Функция send возвращает число байтов, которое на самом деле было отправлено (или -1 в случае ошибки). Функция send возвращает число байтов, которое на самом деле было отправлено (или -1 в случае ошибки). Для чтения данных из сокета используется функция recv. Для чтения данных из сокета используется функция recv. int recv(int sockfd, void *buf, int len, int flags); int recv(int sockfd, void *buf, int len, int flags);
Обмен данными В целом её использование аналогично send. Она точно так же принимает дескриптор сокета, указатель на буфер и набор флагов. Флаг MSG_OOB используется для приёма срочных данных, а MSG_PEEK заставляет функцию recv просматривать данные вместо их чтения. В целом её использование аналогично send. Она точно так же принимает дескриптор сокета, указатель на буфер и набор флагов. Флаг MSG_OOB используется для приёма срочных данных, а MSG_PEEK заставляет функцию recv просматривать данные вместо их чтения.
Закрытие сокета Закончив обмен данными, закройте сокет с помощью функции close. Это приведёт к разрыву соединения. #include int close(int fd); Можно также запретить передачу данных в каком-то одном направлении, используя shutdown. int shutdown(int sockfd, int how); Параметр how может принимать одно из следующих значений: 0 - запретить чтение из сокета 1 - запретить запись в сокет 2 - запретить и то и другое
Закрытие сокета Хотя после вызова shutdown с параметром how, равным 2, больше нельзя использовать сокет для обмена данными, но всё равно потребуется вызвать close, чтобы освободить связанные с ним системные ресурсы. Если что-то пошло не так, все рассмотренные функции возвращают -1, записывая в глобальную переменную errno код ошибки. Можно проанализировать значение этой переменной и предпринять действия по восстановлению нормальной работы программы, не прерывая её выполнения. А можно просто выдать диагностическое сообщение и завершить программу: perror(msg); //msg – сообщение об ошибке exit(1);
Примеры Примеры клиентских и серверных приложений, использующих как протокол TCP, так и UDP, доступны на сайте дисциплины: