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

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

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

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

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

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

Типы розеток

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

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

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

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

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

  • Sequenced Packet Sockets- Они похожи на сокет потока, за исключением сохранения границ записи. Этот интерфейс предоставляется только как часть абстракции сокетов сетевых систем (NS) и очень важен для большинства серьезных приложений NS. Сокеты с последовательными пакетами позволяют пользователю манипулировать заголовками Sequence Packet Protocol (SPP) или Internet Datagram Protocol (IDP) в пакете или группе пакетов, либо записывая заголовок прототипа вместе с любыми данными, которые должны быть отправлены, либо с помощью указание заголовка по умолчанию, который будет использоваться для всех исходящих данных, и позволяет пользователю получать заголовки входящих пакетов.

Что следующее?

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

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

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

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

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

Классы адресов

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

  • Class A адреса начинаются с 0xxx, или же 1 to 126 десятичный.

  • Class B адреса начинаются с 10xx, или же 128 to 191 десятичный.

  • Class C адреса начинаются с 110x, или же 192 to 223 десятичный.

  • Class D адреса начинаются с 1110, или же 224 to 239 десятичный.

  • Class E адреса начинаются с 1111, или же 240 to 254 десятичный.

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

пример

Class Leftmost bits Start address Finish address
А 0xxx 0.0.0.0 127.255.255.255
B 10xx 128.0.0.0 191.255.255.255
C 110x 192.0.0.0 223.255.255.255
D 1110 224.0.0.0 239.255.255.255
E 1111 240.0.0.0 255.255.255.255

Подсети

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

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

  • Адрес подсети в самом сетевом адресе; и
  • Адрес хоста в подсети.

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

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

Процесс определения IP-адреса с точками на основе данного буквенно-цифрового имени хоста известен как hostname resolution.

Разрешение имени хоста выполняется специальным программным обеспечением, находящимся в высокопроизводительных системах. Эти системы называются системами доменных имен (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.

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

Клиентский процесс

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

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

Серверный процесс

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

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

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

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

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

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

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

Типы серверов

У вас может быть два типа серверов:

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

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

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

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

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

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

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

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

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

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

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

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

  • Слушайте связи с listen() системный вызов.

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

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

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

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

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

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 байтов адреса конкретного протокола интерпретируется в соответствии с типом адреса. Для семейства Internet мы будем использовать IP-адрес с номером порта, который представлен структурой sockaddr_in, определенной ниже.

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, поскольку оно не используется.

в адресе

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

struct in_addr {
   unsigned long s_addr;
};

Вот описание полей членов -

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

хостинг

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

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.

NOTE - h_addr определяется как h_addr_list [0] для сохранения обратной совместимости.

слуга

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

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

Вот описание полей членов -

Атрибут Значения Описание
s_name http Это официальное название сервиса. Например, SMTP, FTP POP3 и т. Д.
s_aliases НИКНЕЙМЫ Он содержит список псевдонимов служб. В большинстве случаев это значение будет равно NULL.
s_port 80 Он будет иметь связанный номер порта. Например, для HTTP это будет 80.
s_proto

TCP

UDP

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

Советы по структурам сокетов

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

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

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

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

Когда клиентский процесс хочет подключиться к серверу, клиент должен иметь способ идентифицировать сервер, к которому он хочет подключиться. Если клиенту известен 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 Port Assignments .

Service Port Number Service Description
эхо 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 (NB> 1023).

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

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

  • 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 НИКНЕЙМЫ Он содержит список псевдонимов служб. В большинстве случаев он будет установлен в NULL.
s_port 80 Он будет иметь связанный номер порта. Например, для HTTP это будет 80.
s_proto

TCP

UDP

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

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

  • 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-битные величины из сетевого порядка байтов в порядок байтов хоста.

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

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

Сохраните следующий код в файле 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 предоставляет различные вызовы функций, которые помогут вам управлять 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);
   
(...)

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

На следующей диаграмме показано полное взаимодействие клиента и сервера -

Функция сокета

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

#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 Сырая розетка

protocol - Аргумент должен быть установлен на конкретный тип протокола, указанный ниже, или 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 - Это указатель на struct sockaddr, который содержит IP-адрес и порт назначения.

  • addrlen - Установите значение sizeof (struct sockaddr).

Связывают функции

Функция привязки назначает сокету адрес локального протокола. В случае Интернет-протоколов адрес протокола представляет собой комбинацию 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 - Это указатель на struct sockaddr, который содержит локальный IP-адрес и порт.

  • addrlen - Установите значение sizeof (struct sockaddr).

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

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

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

NOTE- Все порты ниже 1024 зарезервированы. Вы можете установить порт выше 1024 и ниже 65535, если только они не используются другими программами.

Слушать функции

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

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

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

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

int listen(int sockfd,int backlog);

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

Параметры

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

  • backlog - Это количество разрешенных подключений.

Принимает функцию

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

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

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

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

Параметры

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

  • cliaddr - Это указатель на struct sockaddr, который содержит IP-адрес и порт клиента.

  • addrlen - Установите значение sizeof (struct sockaddr).

Посыла Функция

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

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

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

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

Параметры

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

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

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

  • flags - Установлен на 0.

RECV Функция

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

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

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

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

Параметры

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

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

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

  • flags - Установлен на 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 - Это длина данных, которые вы хотите отправить (в байтах).

  • flags - Установлен на 0.

  • to - Это указатель на struct 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 - Это максимальная длина буфера.

  • flags - Установлен на 0.

  • from - Это указатель на struct sockaddr для хоста, с которого должны быть прочитаны данные.

  • fromlen - Он установлен на sizeof (struct sockaddr).

Близко Функция

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

int close( int sockfd );

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

Параметры

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

Выключение функции

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

int shutdown(int sockfd, int how);

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

Параметры

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

  • how - Поставьте одну из цифр -

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

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

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

Выберите Функции

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

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

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

Ниже приведен синтаксис 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, чтобы указать пустой набор.

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

  • timeout- Он указывает на структуру timeval, которая определяет, как долго вызов select должен опрашивать дескрипторы для доступной операции ввода-вывода. Если значение тайм-аута равно 0, select вернется немедленно. Если аргумент тайм-аута равен ПУСТО (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 меньше 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 */
}

В этой главе описаны все вспомогательные функции, которые используются при программировании сокетов. Другие вспомогательные функции описаны в главах -Ports and Services, и Сеть Byte Orders.

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

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

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

#include <unistd.h>

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

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

Параметры

  • 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. Это число никогда не превышает n байтов. В противном случае возвращается -1.

Параметры

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

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

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

Вилка Функция

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

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

int fork(void);

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

Параметры

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

Bzero Функция

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

void bzero(void *s, int 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 - Указывает количество байтов для сравнения.

Bcopy Функция

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

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

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

Параметры

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

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

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

MemSet Функция

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

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

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

Параметры

  • s - Указывает источник, который нужно установить.

  • c - Указывает символ, который нужно установить в n байтах.

  • nbyte - Он определяет количество устанавливаемых байтов.

Чтобы сделать процесс TCP-сервером, вам необходимо выполнить следующие шаги:

  • Создайте сокет с помощью системного вызова 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 , передав новый дескриптор файла сокета в качестве аргумента. Когда два процесса завершили свой диалог, на что указывает возврат 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 */
}

В следующем фрагменте кода показана простая реализация функции doprocessing .

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);
   }
	
}

Чтобы сделать процесс TCP-клиентом, вам необходимо выполнить следующие действия & minus;

  • Создайте сокет с помощью системного вызова 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 предоставляет следующие функции для получения имени службы из файла / 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-битные величины из сетевого порядка байтов в порядок байтов хоста.

Функции IP-адреса

  • 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 family, int type, int protocol) - Этот вызов возвращает дескриптор сокета, который вы можете использовать в последующих системных вызовах, или выдает -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)- Функция привязки назначает сокету адрес локального протокола. Этот вызов возвращает 0, если он успешно привязывается к адресу, в противном случае он возвращает -1.

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

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

  • int send(int sockfd, const void *msg, int len, int flags)- Функция отправки используется для отправки данных через сокеты потоков или сокеты датаграмм CONNECTED. Этот вызов возвращает количество отправленных байтов, в противном случае он возвращает -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 flags, const struct sockaddr *to, int tolen)- Функция sendto используется для отправки данных через НЕ ПОДКЛЮЧЕННЫЕ сокеты дейтаграммы. Этот вызов возвращает количество отправленных байтов, в противном случае он возвращает -1 в случае ошибки.

  • int recvfrom (int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen)- Функция recvfrom используется для получения данных из НЕ ПОДКЛЮЧЕННЫХ сокетов датаграмм. Этот вызов возвращает количество байтов, прочитанных в буфер, в противном случае он возвращает -1 в случае ошибки.

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

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

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

Функции Socket Helper

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

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

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

  • void bzero (void *s, int nbyte)- Функция bzero помещает n байт нулевых байтов в строку 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.