Учебники

Unix Socket — Краткое руководство

Что такое сокет?

Сокеты обеспечивают связь между двумя разными процессами на одной и той же машине. Точнее, это способ общения с другими компьютерами с использованием стандартных файловых дескрипторов Unix. В Unix каждое действие ввода / вывода выполняется путем записи или чтения файлового дескриптора. Дескриптор файла — это просто целое число, связанное с открытым файлом, и это может быть сетевое соединение, текстовый файл, терминал или что-то еще.

Для программиста сокет выглядит и ведет себя как низкоуровневый дескриптор файла. Это связано с тем, что такие команды, как read () и write (), работают с сокетами так же, как с файлами и каналами.

Сокеты были впервые введены в 2.1BSD и впоследствии уточнены в их текущем виде с 4.2BSD. Функция сокетов теперь доступна в большинстве текущих версий UNIX.

Где используется гнездо?

Unix Socket используется в среде клиент-серверных приложений. Сервер — это процесс, который выполняет некоторые функции по запросу клиента. Большинство протоколов уровня приложений, таких как FTP, SMTP и POP3, используют сокеты для установления соединения между клиентом и сервером, а затем для обмена данными.

Типы сокетов

Пользователям доступны четыре типа сокетов. Первые два наиболее часто используются, а последние два используются редко.

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

  • Stream Sockets — доставка в сетевом окружении гарантирована. Если вы отправите через потоковый сокет три элемента «A, B, C», они будут поступать в том же порядке — «A, B, C». Эти сокеты используют TCP (протокол управления передачей) для передачи данных. Если доставка невозможна, отправитель получает индикатор ошибки. Записи данных не имеют границ.

  • Datagram Sockets — Доставка в сетевом окружении не гарантируется. Они не имеют соединения, потому что вам не нужно иметь открытое соединение, как в потоковых сокетах — вы создаете пакет с информацией о получателе и отправляете его. Они используют UDP (протокол пользовательских дейтаграмм).

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

  • Последовательные сокеты пакетов — они похожи на потоковые сокеты, за исключением того, что границы записей сохраняются. Этот интерфейс предоставляется только как часть абстракции сокета Network Systems (NS) и очень важен в большинстве серьезных приложений NS. Сокеты с последовательными пакетами позволяют пользователю манипулировать заголовками протокола пакетной последовательности (SPP) или протокола дейтаграмм Интернета (IDP) в пакете или группе пакетов, либо записывая заголовок прототипа вместе с любыми данными, которые должны быть отправлены, либо с помощью указание заголовка по умолчанию, который будет использоваться со всеми исходящими данными, и позволяет пользователю получать заголовки входящих пакетов.

Stream Sockets — доставка в сетевом окружении гарантирована. Если вы отправите через потоковый сокет три элемента «A, B, C», они будут поступать в том же порядке — «A, B, C». Эти сокеты используют TCP (протокол управления передачей) для передачи данных. Если доставка невозможна, отправитель получает индикатор ошибки. Записи данных не имеют границ.

Datagram Sockets — Доставка в сетевом окружении не гарантируется. Они не имеют соединения, потому что вам не нужно иметь открытое соединение, как в потоковых сокетах — вы создаете пакет с информацией о получателе и отправляете его. Они используют UDP (протокол пользовательских дейтаграмм).

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

Последовательные сокеты пакетов — они похожи на потоковые сокеты, за исключением того, что границы записей сохраняются. Этот интерфейс предоставляется только как часть абстракции сокета Network Systems (NS) и очень важен в большинстве серьезных приложений NS. Сокеты с последовательными пакетами позволяют пользователю манипулировать заголовками протокола пакетной последовательности (SPP) или протокола дейтаграмм Интернета (IDP) в пакете или группе пакетов, либо записывая заголовок прототипа вместе с любыми данными, которые должны быть отправлены, либо с помощью указание заголовка по умолчанию, который будет использоваться со всеми исходящими данными, и позволяет пользователю получать заголовки входящих пакетов.

Что дальше?

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

Unix Socket — Сетевые адреса

Прежде чем мы приступим к конкретным вещам, давайте немного поговорим о сетевых адресах — IP-адресах.

IP-адрес хоста, или чаще всего просто IP-адрес, используется для идентификации хостов, подключенных к Интернету. IP означает Интернет-протокол и относится к интернет-уровню общей сетевой архитектуры Интернета.

IP-адрес — это 32-разрядная величина, интерпретируемая как четыре 8-разрядных числа или октета. Каждый IP-адрес однозначно идентифицирует участвующую пользовательскую сеть, хост в сети и класс пользовательской сети.

IP-адрес обычно записывается в десятичном формате с точками в формате N1.N2.N3.N4, где каждый Ni — это десятичное число от 0 до 255 десятичных (от шестнадцатеричного числа от 00 до FF).

Адресные классы

IP-адреса управляются и создаются Управлением по присвоению номеров в Интернете (IANA). Существует пять разных классов адресов. Вы можете определить, к какому классу относится IP-адрес, изучив первые четыре бита IP-адреса.

  • Адреса класса A начинаются с 0xxx или от 1 до 126 десятичных.

  • Адреса класса В начинаются с 10хх или от 128 до 191 десятичного числа.

  • Адреса класса C начинаются с 110x или от 192 до 223 десятичных.

  • Адреса класса D начинаются с 1110 или от 224 до 239 десятичных.

  • Адреса класса E начинаются с 1111 или от 240 до 254 десятичных.

Адреса класса A начинаются с 0xxx или от 1 до 126 десятичных.

Адреса класса В начинаются с 10хх или от 128 до 191 десятичного числа.

Адреса класса C начинаются с 110x или от 192 до 223 десятичных.

Адреса класса D начинаются с 1110 или от 224 до 239 десятичных.

Адреса класса E начинаются с 1111 или от 240 до 254 десятичных.

Адреса, начинающиеся с 01111111 или 127 десятичных, зарезервированы для обратной связи и для внутреннего тестирования на локальной машине [Вы можете проверить это: вы всегда должны иметь возможность пинговать 127.0.0.1 , который указывает на вас]; Адреса класса D зарезервированы для многоадресной рассылки; Адреса класса E зарезервированы для будущего использования. Они не должны использоваться для адресов хоста.

пример

Учебный класс Левые биты Начальный адрес Конечный адрес
0ххх 0.0.0.0 127.255.255.255
В 10xx 128.0.0.0 191.255.255.255
С 110x 192.0.0.0 223.255.255.255
D 1110 224.0.0.0 239.255.255.255
Е 1111 240.0.0.0 255.255.255.255

Subnetting

Подсеть или подсеть в основном означает отделение от сети. Это может быть сделано по разным причинам, таким как сеть в организации, использование разных физических носителей (таких как Ethernet, FDDI, WAN и т. Д.), Сохранение адресного пространства и безопасность. Наиболее распространенной причиной является контроль сетевого трафика.

Основная идея подсетей — разделить часть идентификатора хоста IP-адреса на две части:

  • Адрес подсети внутри самого сетевого адреса; а также
  • Адрес хоста в подсети.

Например, общий формат адреса класса B — N1.N2.SH, где N1.N2 идентифицирует сеть класса B, 8-битное поле S идентифицирует подсеть, а 8-битное поле H идентифицирует хост в подсети.

Unix Socket — имена сетевых хостов

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

Процесс определения точечного IP-адреса на основе заданного алфавитно-цифрового имени хоста называется разрешением имени хоста .

Разрешение имени хоста выполняется специальным программным обеспечением, установленным в системах большой емкости. Эти системы называются системами доменных имен (DNS), которые хранят сопоставление IP-адресов и соответствующих обычных имен.

Файл / etc / hosts

Соответствие между именами хостов и IP-адресами сохраняется в файле с именем hosts . В большинстве систем этот файл находится в каталоге / etc.

Записи в этом файле выглядят следующим образом:

# This represents a comments in /etc/hosts file.
127.0.0.1       localhost
192.217.44.207  nalanda metro
153.110.31.18   netserve
153.110.31.19   mainserver centeral
153.110.31.20   samsonite
64.202.167.10   ns3.secureserver.net
64.202.167.97   ns4.secureserver.net
66.249.89.104   www.google.com
68.178.157.132  services.amrood.com

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

У вас не будет доступа для редактирования этого файла, поэтому, если вы хотите добавить имя хоста вместе с IP-адресом, вам потребуется разрешение root.

Unix Socket — модель клиент-сервер

Большинство сетевых приложений используют архитектуру клиент-сервер, которая относится к двум процессам или двум приложениям, которые взаимодействуют друг с другом для обмена некоторой информацией. Один из двух процессов действует как клиентский процесс, а другой — как сервер.

Процесс клиента

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

Например , Internet Browser работает как клиентское приложение, которое отправляет запрос на веб-сервер для получения одной веб-страницы HTML.

Процесс сервера

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

Пример — веб-сервер продолжает ожидать запросов от интернет-браузеров и, как только он получает любой запрос от браузера, он берет запрошенную HTML-страницу и отправляет ее обратно в этот браузер.

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

2-х и 3-х уровневая архитектура

Существует два типа клиент-серверных архитектур:

  • Двухуровневая архитектура. В этой архитектуре клиент напрямую взаимодействует с сервером. Этот тип архитектуры может иметь некоторые дыры в безопасности и проблемы с производительностью. Internet Explorer и веб-сервер работают на двухуровневой архитектуре. Здесь проблемы безопасности решаются с помощью Secure Socket Layer (SSL).

  • 3-уровневые архитектуры. В этой архитектуре между клиентом и сервером находится еще одно программное обеспечение. Это промежуточное программное обеспечение называется промежуточным программным обеспечением. Промежуточное программное обеспечение используется для выполнения всех проверок безопасности и балансировки нагрузки в случае большой нагрузки. Промежуточное программное обеспечение принимает все запросы от клиента и после выполнения требуемой аутентификации передает этот запрос на сервер. Затем сервер выполняет необходимую обработку и отправляет ответ обратно промежуточному программному обеспечению, и, наконец, промежуточное программное обеспечение передает этот ответ клиенту. Если вы хотите реализовать 3-уровневую архитектуру, вы можете хранить любое промежуточное программное обеспечение, такое как Web Logic или WebSphere, между вашим веб-сервером и веб-браузером.

Двухуровневая архитектура. В этой архитектуре клиент напрямую взаимодействует с сервером. Этот тип архитектуры может иметь некоторые дыры в безопасности и проблемы с производительностью. Internet Explorer и веб-сервер работают на двухуровневой архитектуре. Здесь проблемы безопасности решаются с помощью Secure Socket Layer (SSL).

3-уровневые архитектуры. В этой архитектуре между клиентом и сервером находится еще одно программное обеспечение. Это промежуточное программное обеспечение называется промежуточным программным обеспечением. Промежуточное программное обеспечение используется для выполнения всех проверок безопасности и балансировки нагрузки в случае большой нагрузки. Промежуточное программное обеспечение принимает все запросы от клиента и после выполнения требуемой аутентификации передает этот запрос на сервер. Затем сервер выполняет необходимую обработку и отправляет ответ обратно промежуточному программному обеспечению, и, наконец, промежуточное программное обеспечение передает этот ответ клиенту. Если вы хотите реализовать 3-уровневую архитектуру, вы можете хранить любое промежуточное программное обеспечение, такое как Web Logic или WebSphere, между вашим веб-сервером и веб-браузером.

Типы Сервера

Существует два типа серверов:

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

  • Параллельные серверы — этот тип сервера запускает несколько параллельных процессов для одновременного обслуживания множества запросов, поскольку один процесс может занять больше времени, а другой клиент не может ждать так долго. Самый простой способ написать параллельный сервер под Unix — это запустить дочерний процесс для обработки каждого клиента в отдельности.

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

Параллельные серверы — этот тип сервера запускает несколько параллельных процессов для одновременного обслуживания множества запросов, поскольку один процесс может занять больше времени, а другой клиент не может ждать так долго. Самый простой способ написать параллельный сервер под Unix — это запустить дочерний процесс для обработки каждого клиента в отдельности.

Как сделать клиент

Системные вызовы для установления соединения несколько различаются для клиента и сервера, но оба включают базовую конструкцию сокета. Оба процесса устанавливают свои собственные сокеты.

Шаги, связанные с установкой сокета на стороне клиента, следующие:

  • Создайте сокет с помощью системного вызова socket () .

  • Подключите сокет к адресу сервера с помощью системного вызова connect () .

  • Отправлять и получать данные. Есть несколько способов сделать это, но самый простой способ — использовать системные вызовы read () и write () .

Создайте сокет с помощью системного вызова socket () .

Подключите сокет к адресу сервера с помощью системного вызова connect () .

Отправлять и получать данные. Есть несколько способов сделать это, но самый простой способ — использовать системные вызовы read () и write () .

Как сделать сервер

Шаги, связанные с установкой сокета на стороне сервера, следующие:

  • Создайте сокет с помощью системного вызова socket () .

  • Свяжите сокет с адресом, используя системный вызов bind () . Для сокета сервера в Интернете адрес состоит из номера порта на хост-компьютере.

  • Прослушайте соединения с помощью системного вызова listen () .

  • Принять соединение с помощью системного вызова accept () . Этот вызов обычно блокирует соединение, пока клиент не соединится с сервером.

  • Отправка и получение данных с использованием системных вызовов read () и write () .

Создайте сокет с помощью системного вызова socket () .

Свяжите сокет с адресом, используя системный вызов bind () . Для сокета сервера в Интернете адрес состоит из номера порта на хост-компьютере.

Прослушайте соединения с помощью системного вызова listen () .

Принять соединение с помощью системного вызова accept () . Этот вызов обычно блокирует соединение, пока клиент не соединится с сервером.

Отправка и получение данных с использованием системных вызовов read () и write () .

Взаимодействие клиента и сервера

Ниже приведена диаграмма, показывающая полное взаимодействие клиента и сервера.

Socket Client Server

Unix Socket — Структуры

В Unix Socket Programming используются различные структуры для хранения информации об адресе и порте, а также другой информации. Большинство функций сокетов требуют указателя на структуру адреса сокета в качестве аргумента. Структуры, определенные в этой главе, относятся к семейству интернет-протоколов.

SOCKADDR

Первая структура — это sockaddr, который содержит информацию о сокете —

struct sockaddr {
   unsigned short   sa_family;
   char             sa_data[14];
};

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

атрибут Ценности Описание
sa_family

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Он представляет адресную семью. В большинстве интернет-приложений мы используем AF_INET.
sa_data Адрес протокола Содержимое 14 байтов специфичного для протокола адреса интерпретируется в соответствии с типом адреса. Для семейства Интернет мы будем использовать номер порта IP-адрес, который представлен структурой sockaddr_in, определенной ниже.

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Sockaddr в

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

struct sockaddr_in {
   short int            sin_family;
   unsigned short int   sin_port;
   struct in_addr       sin_addr;
   unsigned char        sin_zero[8];
};

Вот описание полей участника —

атрибут Ценности Описание
sa_family

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Он представляет адресную семью. В большинстве интернет-приложений мы используем AF_INET.
sin_port Сервисный порт 16-битный номер порта в сетевом порядке байтов.
sin_addr Айпи адрес 32-битный IP-адрес в сетевом порядке байтов.
sin_zero Не используется Вы просто устанавливаете это значение в NULL, поскольку оно не используется.

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

в адрес

Эта структура используется только в приведенной выше структуре в качестве поля структуры и содержит 32-битный netid / hostid.

struct in_addr {
   unsigned long s_addr;
};

Вот описание полей участника —

атрибут Ценности Описание
s_addr сервисный порт 32-битный IP-адрес в сетевом порядке байтов.

hostent

Эта структура используется для хранения информации, связанной с хостом.

struct hostent {
   char *h_name; 
   char **h_aliases; 
   int h_addrtype;  
   int h_length;    
   char **h_addr_list
	
#define h_addr  h_addr_list[0]
};

Вот описание полей участника —

атрибут Ценности Описание
h_name ti.com и т. д. Это официальное имя хоста. Например, tutorialspoint.com, google.com и т. Д.
h_aliases TI Содержит список псевдонимов имен хостов.
h_addrtype AF_INET Он содержит семейство адресов, а в случае интернет-приложения это всегда будет AF_INET.
h_length 4 Он содержит длину IP-адреса, которая равна 4 для Интернет-адреса.
h_addr_list in_addr Для интернет-адресов массив указателей h_addr_list [0], h_addr_list [1] и т. Д. Являются указателями на структуру in_addr.

ПРИМЕЧАНИЕ. — h_addr определяется как h_addr_list [0] для сохранения обратной совместимости.

servent

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

struct servent {
   char  *s_name; 
   char  **s_aliases; 
   int   s_port;  
   char  *s_proto;
};

Вот описание полей участника —

атрибут Ценности Описание
s_name HTTP Это официальное название сервиса. Например, SMTP, FTP POP3 и т. Д.
s_aliases ALIAS Содержит список псевдонимов служб. Большую часть времени это будет установлено в NULL.
s_port 80 Это будет иметь связанный номер порта. Например, для HTTP это будет 80.
s_proto

TCP

UDP

Он установлен на используемый протокол. Интернет-услуги предоставляются с использованием TCP или UDP.

TCP

UDP

Советы по сокетным конструкциям

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

Мы всегда передаем эти структуры по ссылке (т. Е. Передаем указатель на структуру, а не на саму структуру), и мы всегда передаем размер структуры в качестве другого аргумента.

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

Всегда устанавливайте для структурных переменных значение NULL (т. Е. ‘\ 0’), используя memset () для функций bzero (), иначе это может привести к непредвиденным нежелательным значениям в вашей структуре.

Unix Socket — Порты и услуги

Когда клиентский процесс хочет подключиться к серверу, клиент должен иметь способ идентифицировать сервер, к которому он хочет подключиться. Если клиент знает 32-битный интернет-адрес хоста, на котором находится сервер, он может связаться с этим хостом. Но как клиент идентифицирует конкретный процесс сервера, работающий на этом хосте?

Чтобы решить проблему идентификации конкретного серверного процесса, выполняющегося на хосте, как TCP, так и UDP определили группу известных портов.

Для нашей цели порт будет определяться как целое число от 1024 до 65535. Это связано с тем, что все номера портов, меньшие 1024, считаются общеизвестными — например, telnet использует порт 23, http использует 80, ftp использует 21, и так далее.

Назначения портов для сетевых служб можно найти в файле / etc / services. Если вы пишете свой собственный сервер, необходимо позаботиться о том, чтобы назначить порт вашему серверу. Вы должны убедиться, что этот порт не назначен никакому другому серверу.

Обычно рекомендуется назначать номера портов более 5000. Но есть много организаций, которые написали серверы с номерами портов более 5000. Например, Yahoo Messenger работает на 5050, SIP Server работает на 5060 и т. Д.

Примеры портов и сервисов

Вот небольшой список сервисов и связанных с ними портов. Самый последний список интернет-портов и связанных с ними услуг вы можете найти в IANA — Назначение портов TCP / IP .

обслуживание Номер порта Описание услуг
эхо 7 UDP / TCP отправляет обратно то, что получает.
отбрасывать 9 UDP / TCP отбрасывает ввод.
дневное время 13 UDP / TCP возвращает время ASCII.
CHARGEN 19 UDP / TCP возвращает символы.
FTP 21 Передача файлов по протоколу TCP
телнет 23 TCP удаленный вход.
SMTP 25 TCP электронная почта.
дневное время 37 UDP / TCP возвращает двоичное время.
TFTP 69 UDP тривиальная передача файлов.
Палец 79 TCP информация о пользователях.
HTTP 80 TCP World Wide Web.
авторизоваться 513 TCP удаленный вход.
кто 513 UDP разная информация о пользователях.
Xserver 6000 TCP X windows (NB> 1023).

Порт и сервисные функции

Unix предоставляет следующие функции для извлечения имени службы из файла / etc / services.

  • struct servent * getservbyname (char * name, char * proto) — этот вызов принимает имя службы и имя протокола и возвращает соответствующий номер порта для этой службы.

  • struct servent * getservbyport (int port, char * proto) — этот вызов принимает номер порта и имя протокола и возвращает соответствующее имя службы.

struct servent * getservbyname (char * name, char * proto) — этот вызов принимает имя службы и имя протокола и возвращает соответствующий номер порта для этой службы.

struct servent * getservbyport (int port, char * proto) — этот вызов принимает номер порта и имя протокола и возвращает соответствующее имя службы.

Возвращаемое значение для каждой функции — указатель на структуру со следующей формой:

struct servent {
   char  *s_name;
   char  **s_aliases;
   int   s_port;
   char  *s_proto;
};

Вот описание полей участника —

атрибут Ценности Описание
s_name HTTP Это официальное название сервиса. Например, SMTP, FTP POP3 и т. Д.
s_aliases ALIAS Содержит список псевдонимов служб. Большую часть времени он будет установлен в NULL.
s_port 80 Он будет иметь связанный номер порта. Например, для HTTP это будет 80.
s_proto

TCP

UDP

Он установлен на используемый протокол. Интернет-услуги предоставляются с использованием TCP или UDP.

TCP

UDP

Unix Socket — сетевые байтовые порядки

К сожалению, не все компьютеры хранят байты, содержащие многобайтовое значение, в одном и том же порядке. Рассмотрим 16-битный интернет, который состоит из 2 байтов. Есть два способа сохранить это значение.

  • Little Endian — в этой схеме байт младшего разряда сохраняется по начальному адресу (A), а байт старшего разряда сохраняется по следующему адресу (A + 1).

  • Big Endian — в этой схеме старший байт сохраняется по начальному адресу (A), а младший байт сохраняется по следующему адресу (A + 1).

Little Endian — в этой схеме байт младшего разряда сохраняется по начальному адресу (A), а байт старшего разряда сохраняется по следующему адресу (A + 1).

Big Endian — в этой схеме старший байт сохраняется по начальному адресу (A), а младший байт сохраняется по следующему адресу (A + 1).

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

При установлении соединения с интернет-сокетом вы должны убедиться, что данные в членах sin_port и sin_addr структуры sockaddr_in представлены в сетевом порядке байтов.

Функции упорядочения байтов

Процедуры для преобразования данных между внутренним представлением хоста и сетевым порядком байтов:

функция Описание
htons () Короткое замыкание в сети
Htonl () Хост к сети долго
ntohl () Сеть к хосту Лонг
ntohs () Короткая сеть к хосту

Ниже перечислены некоторые подробности об этих функциях —

  • unsigned short htons (unsigned short hostshort) — эта функция преобразует 16-битные (2-байтовые) величины из байтового порядка хоста в сетевой байтовый порядок.

  • unsigned long htonl (unsigned long hostlong) — эта функция преобразует 32-битные (4-байтовые) величины из байтового порядка хоста в сетевой байтовый порядок.

  • unsigned short ntohs (unsigned short netshort) — эта функция преобразует 16-битные (2-байтовые) величины из сетевого байтового порядка в хостовый.

  • unsigned long ntohl (unsigned long netlong) — эта функция преобразует 32-битные величины из сетевого байтового порядка в хостовый.

unsigned short htons (unsigned short hostshort) — эта функция преобразует 16-битные (2-байтовые) величины из байтового порядка хоста в сетевой байтовый порядок.

unsigned long htonl (unsigned long hostlong) — эта функция преобразует 32-битные (4-байтовые) величины из байтового порядка хоста в сетевой байтовый порядок.

unsigned short ntohs (unsigned short netshort) — эта функция преобразует 16-битные (2-байтовые) величины из сетевого байтового порядка в хостовый.

unsigned long ntohl (unsigned long netlong) — эта функция преобразует 32-битные величины из сетевого байтового порядка в хостовый.

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

Программа для определения порядка байтов хоста

Сохраните следующий код в файле byteorder.c, а затем скомпилируйте его и запустите на своем компьютере.

В этом примере мы сохраняем двухбайтовое значение 0x0102 в коротком целом числе и затем просматриваем два последовательных байта: c [0] (адрес A) и c [1] (адрес A + 1), чтобы определить байт порядок.

#include <stdio.h>

int main(int argc, char **argv) {

   union {
      short s;
      char c[sizeof(short)];
   }un;
	
   un.s = 0x0102;
   
   if (sizeof(short) == 2) {
      if (un.c[0] == 1 && un.c[1] == 2)
         printf("big-endian\n");
      
      else if (un.c[0] == 2 && un.c[1] == 1)
         printf("little-endian\n");
      
      else
         printf("unknown\n");
   }
   else {
      printf("sizeof(short) = %d\n", sizeof(short));
   }
	
   exit(0);
}

Вывод этой программы на компьютере Pentium выглядит следующим образом:

$> gcc byteorder.c
$> ./a.out
little-endian
$>

Unix Socket — Функции IP-адресов

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

Следующие три вызова функций используются для адресации IPv4 —

  • int inet_aton (const char * strptr, struct in_addr * addrptr)
  • in_addr_t inet_addr (const char * strptr)
  • char * inet_ntoa (struct in_addr inaddr)

int inet_aton (const char * strptr, struct in_addr * addrptr)

Этот вызов функции преобразует указанную строку в стандартной точечной нотации Интернета в сетевой адрес и сохраняет адрес в предоставленной структуре. Преобразованный адрес будет в сетевом порядке байтов (байты упорядочены слева направо). Возвращает 1, если строка была действительной, и 0 при ошибке.

Ниже приведен пример использования —

#include <arpa/inet.h>

(...)

   int retval;
   struct in_addr addrptr
   
   memset(&addrptr, '\0', sizeof(addrptr));
   retval = inet_aton("68.178.157.132", &addrptr);

(...)

in_addr_t inet_addr (const char * strptr)

Этот вызов функции преобразует указанную строку в стандартной точечной нотации Интернета в целочисленное значение, подходящее для использования в качестве адреса Интернета. Преобразованный адрес будет в сетевом порядке байтов (байты упорядочены слева направо). Он возвращает 32-битный двоичный сетевой упорядоченный IPv4-адрес и INADDR_NONE при ошибке.

Ниже приведен пример использования —

#include <arpa/inet.h>

(...)

   struct sockaddr_in dest;

   memset(&dest, '\0', sizeof(dest));
   dest.sin_addr.s_addr = inet_addr("68.178.157.132");
   
(...)

char * inet_ntoa (struct in_addr inaddr)

Этот вызов функции преобразует указанный адрес хоста в Интернет в строку в стандартной нотации Интернета.

Ниже приведен пример использования —

#include <arpa/inet.h>

(...)

   char *ip;
   
   ip = inet_ntoa(dest.sin_addr);
   
   printf("IP Address is: %s\n",ip);
   
(...)

Unix Socket — Основные функции

В этой главе описываются основные функции сокетов, необходимые для написания полноценного TCP-клиента и сервера.

Следующая диаграмма показывает полное взаимодействие клиента и сервера —

Socket Client Server

Функция розетки

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

#include <sys/types.h>
#include <sys/socket.h>

int socket (int family, int type, int protocol);

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

параметры

family — определяет семейство протоколов и является одной из констант, показанных ниже —

семья Описание
AF_INET Протоколы IPv4
AF_INET6 Протоколы IPv6
AF_LOCAL Доменные протоколы Unix
AF_ROUTE Маршрутные розетки
AF_KEY Кет розетка

Эта глава не охватывает другие протоколы, кроме IPv4.

type — определяет тип сокета, который вы хотите. Может принимать одно из следующих значений:

Тип Описание
SOCK_STREAM Потоковая розетка
SOCK_DGRAM Гнездо датаграммы
SOCK_SEQPACKET Последовательный пакетный сокет
SOCK_RAW Сырое гнездо

протокол — Аргумент должен быть установлен на конкретный тип протокола, указанный ниже, или 0, чтобы выбрать системное значение по умолчанию для данной комбинации семейства и типа —

протокол Описание
IPPROTO_TCP Транспортный протокол TCP
IPPROTO_UDP Транспортный протокол UDP
IPPROTO_SCTP Транспортный протокол SCTP

Функция подключения

Функция соединения используется TCP-клиентом для установления соединения с TCP-сервером.

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

Этот вызов возвращает 0, если он успешно подключается к серверу, в противном случае он возвращает -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • serv_addr — это указатель на структуру sockaddr, которая содержит IP-адрес и порт назначения.

  • addrlen — установить его в sizeof (struct sockaddr).

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

serv_addr — это указатель на структуру sockaddr, которая содержит IP-адрес и порт назначения.

addrlen — установить его в sizeof (struct sockaddr).

Функция связывания

Функция bind назначает сокету адрес локального протокола. В интернет-протоколах адрес протокола представляет собой комбинацию либо 32-битного IPv4-адреса, либо 128-битного IPv6-адреса вместе с 16-битным номером порта TCP или UDP. Эта функция вызывается только TCP-сервером.

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr,int addrlen);

Этот вызов возвращает 0, если он успешно связывается с адресом, в противном случае он возвращает -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • my_addr — это указатель на структуру sockaddr, которая содержит локальный IP-адрес и порт.

  • addrlen — установить его в sizeof (struct sockaddr).

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

my_addr — это указатель на структуру sockaddr, которая содержит локальный IP-адрес и порт.

addrlen — установить его в sizeof (struct sockaddr).

Вы можете поставить свой IP-адрес и порт автоматически

Значение 0 для номера порта означает, что система выберет произвольный порт, а значение INADDR_ANY для IP-адреса означает, что IP-адрес сервера будет назначен автоматически.

server.sin_port = 0;  		     
server.sin_addr.s_addr = INADDR_ANY;

ПРИМЕЧАНИЕ. — Все порты ниже 1024 зарезервированы. Вы можете установить порт выше 1024 и ниже 65535, если только они не используются другими программами.

Функция прослушивания

Функция прослушивания вызывается только TCP-сервером и выполняет два действия:

  • Функция listen преобразует неподключенный сокет в пассивный сокет, указывая, что ядро ​​должно принимать входящие запросы на подключение, направленные на этот сокет.

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

Функция listen преобразует неподключенный сокет в пассивный сокет, указывая, что ядро ​​должно принимать входящие запросы на подключение, направленные на этот сокет.

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

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);

Этот вызов возвращает 0 в случае успеха, в противном случае он возвращает -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • отставание — это количество разрешенных соединений.

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

отставание — это количество разрешенных соединений.

Функция принятия

Функция принятия вызывается сервером TCP для возврата следующего завершенного соединения с начала очереди завершенных соединений. Подпись звонка выглядит следующим образом —

#include <sys/types.h>
#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

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

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • cliaddr — это указатель на структуру sockaddr, которая содержит IP-адрес и порт клиента.

  • addrlen — установить его в sizeof (struct sockaddr).

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

cliaddr — это указатель на структуру sockaddr, которая содержит IP-адрес и порт клиента.

addrlen — установить его в sizeof (struct sockaddr).

Функция отправки

Функция отправки используется для отправки данных через потоковые сокеты или СОЕДИНЕННЫЕ сокеты датаграмм. Если вы хотите отправлять данные через несоединенные дейтаграммы, вы должны использовать функцию sendto ().

Вы можете использовать системный вызов write () для отправки данных. Его подпись выглядит следующим образом —

int send(int sockfd, const void *msg, int len, int flags);

Этот вызов возвращает количество отправленных байтов, в противном случае он вернет -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • msg — это указатель на данные, которые вы хотите отправить.

  • len — это длина данных, которые вы хотите отправить (в байтах).

  • флаги — он установлен в 0.

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

msg — это указатель на данные, которые вы хотите отправить.

len — это длина данных, которые вы хотите отправить (в байтах).

флаги — он установлен в 0.

Функция recv

Функция recv используется для получения данных через потоковые сокеты или СОЕДИНЕННЫЕ сокеты датаграмм. Если вы хотите получать данные через сокеты UNCONNECTED дейтаграммы, вы должны использовать recvfrom ().

Вы можете использовать системный вызов read () для чтения данных. Этот вызов объясняется в главе вспомогательных функций.

int recv(int sockfd, void *buf, int len, unsigned int flags);

Этот вызов возвращает количество байтов, считанных в буфер, в противном случае он вернет -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • buf — это буфер для чтения информации.

  • len — это максимальная длина буфера.

  • флаги — он установлен в 0.

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

buf — это буфер для чтения информации.

len — это максимальная длина буфера.

флаги — он установлен в 0.

Функция sendto

Функция sendto используется для отправки данных через несвязанные гнезда дейтаграмм. Его подпись выглядит следующим образом —

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

Этот вызов возвращает количество отправленных байтов, в противном случае он возвращает -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • msg — это указатель на данные, которые вы хотите отправить.

  • len — это длина данных, которые вы хотите отправить (в байтах).

  • флаги — он установлен в 0.

  • to — это указатель на структуру sockaddr для хоста, на который должны быть отправлены данные.

  • tolen — устанавливается в sizeof (struct sockaddr).

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

msg — это указатель на данные, которые вы хотите отправить.

len — это длина данных, которые вы хотите отправить (в байтах).

флаги — он установлен в 0.

to — это указатель на структуру sockaddr для хоста, на который должны быть отправлены данные.

tolen — устанавливается в sizeof (struct sockaddr).

Функция recvfrom

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

int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);

Этот вызов возвращает количество байтов, прочитанных в буфер, в противном случае он возвращает -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • buf — это буфер для чтения информации.

  • len — это максимальная длина буфера.

  • флаги — он установлен в 0.

  • from — это указатель на структуру sockaddr для хоста, на котором должны быть прочитаны данные.

  • fromlen — устанавливается в sizeof (struct sockaddr).

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

buf — это буфер для чтения информации.

len — это максимальная длина буфера.

флаги — он установлен в 0.

from — это указатель на структуру sockaddr для хоста, на котором должны быть прочитаны данные.

fromlen — устанавливается в sizeof (struct sockaddr).

Функция закрытия

Функция закрытия используется для закрытия связи между клиентом и сервером. Его синтаксис выглядит следующим образом —

int close( int sockfd );

Этот вызов возвращает 0 в случае успеха, в противном случае он возвращает -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

Функция выключения

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

int shutdown(int sockfd, int how);

Этот вызов возвращает 0 в случае успеха, в противном случае он возвращает -1 в случае ошибки.

параметры

  • sockfd — это дескриптор сокета, возвращаемый функцией сокета.

  • как — поставить одно из чисел —

    • 0 — указывает, что прием не разрешен,

    • 1 — указывает, что отправка запрещена, и

    • 2 — указывает, что отправка и получение не разрешены. Когда how имеет значение 2, это то же самое, что close ().

sockfd — это дескриптор сокета, возвращаемый функцией сокета.

как — поставить одно из чисел —

0 — указывает, что прием не разрешен,

1 — указывает, что отправка запрещена, и

2 — указывает, что отправка и получение не разрешены. Когда how имеет значение 2, это то же самое, что close ().

Функция выбора

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

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

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

Ниже приведен синтаксис выбора

 int select(int  nfds, fd_set  *readfds, fd_set  *writefds, fd_set *errorfds, struct timeval *timeout);

Этот вызов возвращает 0 в случае успеха, в противном случае он возвращает -1 в случае ошибки.

параметры

  • nfds — указывает диапазон файловых дескрипторов для тестирования. Функция select () проверяет файловые дескрипторы в диапазоне от 0 до nfds-1

  • readfds — указывает на объект типа fd_set, который при вводе указывает файловые дескрипторы, которые необходимо проверить на готовность к чтению, а при выводе указывает, какие файловые дескрипторы готовы к чтению. Это может быть NULL, чтобы указать пустой набор.

  • writefds — указывает на объект типа fd_set, который при вводе указывает файловые дескрипторы, которые необходимо проверить на готовность к записи, а при выводе указывает, какие файловые дескрипторы готовы к записи. Это может быть NULL, чтобы указать пустой набор.

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

  • timeout — указывает на структуру timeval, которая указывает, как долго вызов select должен опрашивать дескрипторы для доступной операции ввода-вывода. Если значение тайм-аута равно 0, то выбор вернется немедленно. Если аргумент тайм-аута равен NULL, select будет блокироваться, пока хотя бы один дескриптор файла / сокета не будет готов к доступной операции ввода-вывода. В противном случае команда select возвращается после истечения времени ожидания ИЛИ, когда хотя бы один дескриптор файла / сокета готов к операции ввода-вывода.

nfds — указывает диапазон файловых дескрипторов для тестирования. Функция select () проверяет файловые дескрипторы в диапазоне от 0 до nfds-1

readfds — указывает на объект типа fd_set, который при вводе указывает файловые дескрипторы, которые необходимо проверить на готовность к чтению, а при выводе указывает, какие файловые дескрипторы готовы к чтению. Это может быть NULL, чтобы указать пустой набор.

writefds — указывает на объект типа fd_set, который при вводе указывает файловые дескрипторы, которые необходимо проверить на готовность к записи, а при выводе указывает, какие файловые дескрипторы готовы к записи. Это может быть NULL, чтобы указать пустой набор.

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

timeout — указывает на структуру timeval, которая указывает, как долго вызов select должен опрашивать дескрипторы для доступной операции ввода-вывода. Если значение тайм-аута равно 0, то выбор вернется немедленно. Если аргумент тайм-аута равен NULL, select будет блокироваться, пока хотя бы один дескриптор файла / сокета не будет готов к доступной операции ввода-вывода. В противном случае команда select возвращается после истечения времени ожидания ИЛИ, когда хотя бы один дескриптор файла / сокета готов к операции ввода-вывода.

Возвращаемое значение из select — это количество дескрипторов, указанных в наборах дескрипторов файлов, которые готовы к вводу / выводу. Если предел времени, указанный в поле времени ожидания, достигнут, выберите return 0. Существуют следующие макросы для управления набором дескрипторов файлов:

  • FD_CLR (fd, & fdset) — очищает бит для файлового дескриптора fd в наборе файловых дескрипторов fdset.

  • FD_ISSET (fd, & fdset) — возвращает ненулевое значение, если бит для дескриптора файла fd установлен в наборе дескрипторов файла, на который указывает fdset , и 0 в противном случае.

  • FD_SET (fd, & fdset) — устанавливает бит для дескриптора файла fd в наборе дескрипторов файла fdset.

  • FD_ZERO (& fdset) — Инициализирует набор файловых дескрипторов fdset, чтобы иметь нулевые биты для всех файловых дескрипторов.

FD_CLR (fd, & fdset) — очищает бит для файлового дескриптора fd в наборе файловых дескрипторов fdset.

FD_ISSET (fd, & fdset) — возвращает ненулевое значение, если бит для дескриптора файла fd установлен в наборе дескрипторов файла, на который указывает fdset , и 0 в противном случае.

FD_SET (fd, & fdset) — устанавливает бит для дескриптора файла fd в наборе дескрипторов файла fdset.

FD_ZERO (& fdset) — Инициализирует набор файловых дескрипторов fdset, чтобы иметь нулевые биты для всех файловых дескрипторов.

Поведение этих макросов не определено, если аргумент fd меньше 0 или больше или равен FD_SETSIZE.

пример

fd_set fds;

struct timeval tv;

/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;

/* tv now represents 1.5 seconds */
FD_ZERO(&fds);

/* adds sock to the file descriptor set */
FD_SET(sock, &fds); 

/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);

if (FD_ISSET(sock, &fds)) {
   recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
   /* do something */
}
else {
   /* do something else */
}

Unix Socket — вспомогательные функции

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

Функция записи

Функция записи пытается записать n-байтовые байты из буфера, на который указывает buf, в файл, связанный с дескриптором открытого файла, fildes .

Вы также можете использовать функцию send () для отправки данных другому процессу.

#include <unistd.h>

int write(int fildes, const void *buf, int nbyte);

После успешного завершения write () возвращает количество байтов, фактически записанных в файл, связанный с fildes. Это число никогда не превышает nbyte. В противном случае возвращается -1.

параметры

  • fildes — это дескриптор сокета, возвращаемый функцией сокета.

  • buf — это указатель на данные, которые вы хотите отправить.

  • nbyte — количество записываемых байтов. Если nbyte равно 0, write () вернет 0 и не будет иметь других результатов, если файл является обычным файлом; в противном случае результаты не уточняются.

fildes — это дескриптор сокета, возвращаемый функцией сокета.

buf — это указатель на данные, которые вы хотите отправить.

nbyte — количество записываемых байтов. Если nbyte равно 0, write () вернет 0 и не будет иметь других результатов, если файл является обычным файлом; в противном случае результаты не уточняются.

Функция чтения

Функция чтения пытается прочитать n-байтовые байты из файла, связанного с буфером, fildes, в буфер, на который указывает buf.

Вы также можете использовать функцию recv () для чтения данных в другой процесс.

#include <unistd.h>

int read(int fildes, const void *buf, int nbyte);

После успешного завершения write () возвращает количество байтов, фактически записанных в файл, связанный с fildes. Это число никогда не превышает nbyte. В противном случае возвращается -1.

параметры

  • fildes — это дескриптор сокета, возвращаемый функцией сокета.

  • buf — это буфер для чтения информации.

  • nbyte — это количество байтов для чтения.

fildes — это дескриптор сокета, возвращаемый функцией сокета.

buf — это буфер для чтения информации.

nbyte — это количество байтов для чтения.

Функция вилки

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

#include <sys/types.h>
#include <unistd.h>

int fork(void);

После успешного завершения fork () возвращает 0 дочернему процессу, а идентификатор процесса дочернего процесса — родительскому процессу. В противном случае -1 возвращается родительскому процессу, дочерний процесс не создается, и errno устанавливается для указания ошибки.

параметры

  • void — означает, что параметр не требуется.

void — означает, что параметр не требуется.

Функция bzero

Функция bzero помещает n-байтовые нулевые байты в строку s . Эта функция используется для установки всех структур сокетов с нулевыми значениями.

void bzero(void *s, int nbyte);

Эта функция ничего не возвращает.

параметры

  • s — определяет строку, которая должна быть заполнена нулевыми байтами. Это будет точка структурной переменной сокета.

  • nbyte — указывает количество байтов, которые должны быть заполнены нулевыми значениями. Это будет размер структуры сокета.

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

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

Функция bcmp

Функция bcmp сравнивает байтовую строку s1 с байтовой строкой s2. Предполагается, что длина обеих строк составляет n байтов.

int bcmp(const void *s1, const void *s2, int nbyte);

Эта функция возвращает 0, если обе строки идентичны, 1 в противном случае. Функция bcmp () всегда возвращает 0, если nbyte равно 0.

параметры

  • s1 — указывает первую строку для сравнения.

  • s2 — определяет вторую строку для сравнения.

  • nbyte — определяет количество байтов для сравнения.

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

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

nbyte — определяет количество байтов для сравнения.

Функция bcopy

Функция bcopy копирует байты n байтов из строки s1 в строку s2. Перекрывающиеся строки обрабатываются правильно.

void bcopy(const void *s1, void *s2, int nbyte);

Эта функция ничего не возвращает.

параметры

  • s1 — указывает исходную строку.

  • s2v — указывает строку назначения.

  • nbyte — указывает количество копируемых байтов.

s1 — указывает исходную строку.

s2v — указывает строку назначения.

nbyte — указывает количество копируемых байтов.

Функция memset

Функция memset также используется для установки структурных переменных так же, как bzero . Посмотрите на его синтаксис, приведенный ниже.

void *memset(void *s, int c, int nbyte);

Эта функция возвращает указатель на void; на самом деле, указатель на установленную память и вам нужно соответственно ее кастовать.

параметры

  • s — указывает источник, который будет установлен.

  • c — Указывает символ для установки на nbyte мест.

  • nbyte — определяет количество байтов, которые будут установлены.

s — указывает источник, который будет установлен.

c — Указывает символ для установки на nbyte мест.

nbyte — определяет количество байтов, которые будут установлены.

Unix Socket — Примеры серверов

Чтобы сделать процесс сервером TCP, вам нужно выполнить шаги, приведенные ниже —

  • Создайте сокет с помощью системного вызова socket () .

  • Свяжите сокет с адресом, используя системный вызов bind () . Для сокета сервера в Интернете адрес состоит из номера порта на хост-компьютере.

  • Прослушайте соединения с помощью системного вызова listen () .

  • Принять соединение с помощью системного вызова accept () . Этот вызов обычно блокируется, пока клиент не соединится с сервером.

  • Отправка и получение данных с использованием системных вызовов read () и write () .

Создайте сокет с помощью системного вызова socket () .

Свяжите сокет с адресом, используя системный вызов bind () . Для сокета сервера в Интернете адрес состоит из номера порта на хост-компьютере.

Прослушайте соединения с помощью системного вызова listen () .

Принять соединение с помощью системного вызова accept () . Этот вызов обычно блокируется, пока клиент не соединится с сервером.

Отправка и получение данных с использованием системных вызовов read () и write () .

Теперь давайте поместим эти шаги в виде исходного кода. Поместите этот код в файл server.c и скомпилируйте его с помощью компилятора gcc .

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int  n;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
      
   /* Now start listening for the clients, here process will
      * go in sleep mode and will wait for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   /* Accept actual connection from the client */
   newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
	
   if (newsockfd < 0) {
      perror("ERROR on accept");
      exit(1);
   }
   
   /* If connection is established then start communicating */
   bzero(buffer,256);
   n = read( newsockfd,buffer,255 );
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   
   /* Write a response to the client */
   n = write(newsockfd,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
      
   return 0;
}

Обработка нескольких соединений

Чтобы позволить серверу обрабатывать несколько одновременных соединений, мы вносим следующие изменения в приведенный выше код:

  • Поместите оператор accept и следующий код в бесконечный цикл.

  • После того, как соединение установлено, вызовите fork (), чтобы создать новый процесс.

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

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

Поместите оператор accept и следующий код в бесконечный цикл.

После того, как соединение установлено, вызовите fork (), чтобы создать новый процесс.

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

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

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

void doprocessing (int sock);

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int n, pid;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
   
   /* Now start listening for the clients, here
      * process will go in sleep mode and will wait
      * for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   while (1) {
      newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
		
      if (newsockfd < 0) {
         perror("ERROR on accept");
         exit(1);
      }
      
      /* Create child process */
      pid = fork();
		
      if (pid < 0) {
         perror("ERROR on fork");
         exit(1);
      }
      
      if (pid == 0) {
         /* This is the client process */
         close(sockfd);
         doprocessing(newsockfd);
         exit(0);
      }
      else {
         close(newsockfd);
      }
		
   } /* end of while */
}

Следующий фрагмент кода показывает простую реализацию функции обработки данных.

void doprocessing (int sock) {
   int n;
   char buffer[256];
   bzero(buffer,256);
   n = read(sock,buffer,255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   n = write(sock,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
	
}

Unix Socket — Примеры клиентов

Чтобы сделать процесс TCP-клиентом, вам нужно выполнить шаги, указанные ниже & минус;

  • Создайте сокет с помощью системного вызова socket () .

  • Подключите сокет к адресу сервера с помощью системного вызова connect () .

  • Отправлять и получать данные. Есть несколько способов сделать это, но самый простой способ — использовать системные вызовы read () и write () .

Создайте сокет с помощью системного вызова socket () .

Подключите сокет к адресу сервера с помощью системного вызова connect () .

Отправлять и получать данные. Есть несколько способов сделать это, но самый простой способ — использовать системные вызовы read () и write () .

Теперь давайте поместим эти шаги в виде исходного кода. Поместите этот код в файл client.c и скомпилируйте его с помощью компилятора gcc .

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

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main(int argc, char *argv[]) {
   int sockfd, portno, n;
   struct sockaddr_in serv_addr;
   struct hostent *server;
   
   char buffer[256];
   
   if (argc < 3) {
      fprintf(stderr,"usage %s hostname port\n", argv[0]);
      exit(0);
   }
	
   portno = atoi(argv[2]);
   
   /* Create a socket point */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
	
   server = gethostbyname(argv[1]);
   
   if (server == NULL) {
      fprintf(stderr,"ERROR, no such host\n");
      exit(0);
   }
   
   bzero((char *) &serv_addr, sizeof(serv_addr));
   serv_addr.sin_family = AF_INET;
   bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
   serv_addr.sin_port = htons(portno);
   
   /* Now connect to the server */
   if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR connecting");
      exit(1);
   }
   
   /* Now ask for a message from the user, this message
      * will be read by server
   */
	
   printf("Please enter the message: ");
   bzero(buffer,256);
   fgets(buffer,255,stdin);
   
   /* Send message to the server */
   n = write(sockfd, buffer, strlen(buffer));
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
   
   /* Now read server response */
   bzero(buffer,256);
   n = read(sockfd, buffer, 255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
	
   printf("%s\n",buffer);
   return 0;
}

Unix Socket — Резюме

Вот список всех функций, связанных с программированием сокетов.

Порт и сервисные функции

Unix предоставляет следующие функции для извлечения имени службы из файла / etc / services.

struct servent * getservbyname (char * name, char * proto) — этот вызов принимает имя службы и имя протокола и возвращает соответствующий номер порта для этой службы.

struct servent * getservbyport (int port, char * proto) — этот вызов принимает номер порта и имя протокола и возвращает соответствующее имя службы.

unsigned short htons (unsigned short hostshort) — эта функция преобразует 16-битные (2-байтовые) величины из байтового порядка хоста в сетевой байтовый порядок.

unsigned long htonl (unsigned long hostlong) — эта функция преобразует 32-битные (4-байтовые) величины из байтового порядка хоста в сетевой байтовый порядок.

unsigned short ntohs (unsigned short netshort) — эта функция преобразует 16-битные (2-байтовые) величины из сетевого байтового порядка в хостовый.

unsigned long ntohl (unsigned long netlong) — эта функция преобразует 32-битные величины из сетевого байтового порядка в хостовый.

int inet_aton (const char * strptr, struct in_addr * addrptr) — этот вызов функции преобразует указанную строку в стандартной точечной нотации Интернета в сетевой адрес и сохраняет адрес в предоставленной структуре. Преобразованный адрес будет в сетевом порядке байтов (байты упорядочены слева направо). Возвращает 1, если строка верна, и 0 при ошибке.

in_addr_t inet_addr (const char * strptr) — этот вызов функции преобразует указанную строку в стандартной точечной нотации Интернета в целочисленное значение, подходящее для использования в качестве адреса Интернета. Преобразованный адрес будет в сетевом порядке байтов (байты упорядочены слева направо). Он возвращает 32-битный двоичный сетевой упорядоченный IPv4-адрес и INADDR_NONE при ошибке.

char * inet_ntoa (struct in_addr inaddr) — этот вызов функции преобразует указанный адрес хоста в Интернете в строку в стандартной нотации Интернета.

int socket (семейство int, тип int, протокол int) — Этот вызов возвращает дескриптор сокета, который можно использовать в последующих системных вызовах, или он дает -1 при ошибке.

int connect (int sockfd, struct sockaddr * serv_addr, int addrlen) — функция соединения используется TCP-клиентом для установления соединения с TCP-сервером. Этот вызов возвращает 0, если он успешно подключается к серверу, в противном случае он возвращает -1.

int bind (int sockfd, struct sockaddr * my_addr, int addrlen) — функция bind назначает сокету адрес локального протокола. Этот вызов возвращает 0, если он успешно связывается с адресом, в противном случае он возвращает -1.

int listen (int sockfd, int backlog) — функция прослушивания вызывается только TCP-сервером для прослушивания запроса клиента. Этот вызов возвращает 0 в случае успеха, в противном случае он возвращает -1.

int accept (int sockfd, struct sockaddr * cliaddr, socklen_t * addrlen) — TCP-сервер вызывает функцию accept для приема клиентских запросов и установления фактического соединения. Этот вызов возвращает неотрицательный дескриптор в случае успеха, в противном случае он возвращает -1.

int send (int sockfd, const void * msg, int len, int flags) — Функция отправки используется для отправки данных через потоковые сокеты или СОЕДИНЕННЫЕ сокеты датаграмм. Этот вызов возвращает количество отправленных байтов, в противном случае он возвращает -1.

int recv (int sockfd, void * buf, int len, unsigned int flags) — Функция recv используется для получения данных через потоковые сокеты или сокеты датаграмм CONNECTED. Этот вызов возвращает количество байтов, прочитанных в буфер, в противном случае он возвращает -1 в случае ошибки.

int sendto (int sockfd, const void * msg, int len, флаги unsigned int, const struct sockaddr * to, int tolen) — Функция sendto используется для отправки данных через сокеты UNCONNECTED дейтаграммы. Этот вызов возвращает количество отправленных байтов, в противном случае он возвращает -1 в случае ошибки.

int recvfrom (int sockfd, void * buf, int len, флаги unsigned int struct sockaddr * from, int * fromlen) — Функция recvfrom используется для получения данных из сокетов датаграмм UNCONNECTED. Этот вызов возвращает количество байтов, прочитанных в буфер, в противном случае он возвращает -1 в случае ошибки.

int close (int sockfd) — функция close используется для закрытия связи между клиентом и сервером. Этот вызов возвращает 0 в случае успеха, в противном случае он возвращает -1.

int shutdown (int sockfd, int how) — функция shutdown используется для постепенного закрытия связи между клиентом и сервером. Эта функция дает больше контроля по сравнению с функцией закрытия. Возвращает 0 в случае успеха, -1 в противном случае.

int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout) — эта функция используется для чтения или записи нескольких сокетов.

int write (int fildes, const void * buf, int nbyte) — Функция записи пытается записать n-байтовые байты из буфера, на который указывает buf, в файл, связанный с дескриптором открытого файла, fildes. После успешного завершения write () возвращает количество байтов, фактически записанных в файл, связанный с fildes. Это число никогда не превышает nbyte. В противном случае возвращается -1.

int read (int fildes, const void * buf, int nbyte) — Функция чтения пытается прочитать n-байтовые байты из файла, связанного с дескриптором открытого файла, fildes, в буфер, на который указывает buf. После успешного завершения write () возвращает количество байтов, фактически записанных в файл, связанный с fildes. Это число никогда не превышает nbyte. В противном случае возвращается -1.

int fork (void) — функция fork создает новый процесс. Новый процесс, называемый дочерним процессом, будет точной копией вызывающего процесса (родительского процесса).

void bzero (void * s, int nbyte) — функция bzero помещает нулевые байты nbyte в строку s. Эта функция будет использоваться для установки всех структур сокетов с нулевыми значениями.

int bcmp (const void * s1, const void * s2, int nbyte) — Функция bcmp сравнивает байтовую строку s1 с байтовой строкой s2. Предполагается, что длина обеих строк составляет n байтов.

void bcopy (const void * s1, void * s2, int nbyte) — функция bcopy копирует n-байтовые байты из строки s1 в строку s2. Перекрывающиеся строки обрабатываются правильно.

void * memset (void * s, int c, int nbyte) — функция memset также используется для установки структурных переменных так же, как bzero.