Создание и конфигурирование сокета.
Создание сокета.
#include <sys/types.h>
#include <sys/socket.h>
int socket (int domain, int type, int protocol)
Функция создания сокета так и называется – socket(). У нее имеется три аргумента. Первый аргумент – domain– обозначает коммуникационный домен, к которому должен принадлежать создаваемый сокет. Для двух рассмотренных нами доменов соответствующие константы будут равны, как мы уже говорили, AF_UNIX и AF_INET. Второй аргумент – type – определяет тип соединения, которым будет пользоваться сокет (и, соответственно, тип сокета). Для двух основных рассматриваемых нами типов сокетов это будут константы SOCK_STREAM для соединения с установлением виртуального канала и SOCK_DGRAM для датаграмм[5]. Третий аргумент – protocol – задает конкретный протокол, который будет использоваться в рамках данного коммуникационного домена для создания соединения. Если установить значение данного аргумента в 0, система автоматически выберет подходящий протокол. В наших примерах мы так и будем поступать. Однако здесь для справки приведем константы для протоколов, используемых в домене AF_INET:
IPPROTO_TCP – обозначает протокол TCP (корректно при создании сокета типа SOCK_STREAM)
IPPROTO_UDP – обозначает протокол UDP (корректно при создании сокета типа SOCK_DGRAM)
Функция socket() возвращает в случае успеха положительное целое число – дескриптор сокета, которое может быть использовано в дальнейших вызовах при работе с данным сокетом. Заметим, что дескриптор сокета фактически представляет собой файловый дескриптор, а именно, он является индексом в таблице файловых дескрипторов процесса, и может использоваться в дальнейшем для операций чтения и записи в сокет, которые осуществляются подобно операциям чтения и записи в файл (подробно эти операции будут рассмотрены ниже).
В случае если создание сокета с указанными параметрами невозможно (например, при некорректном сочетании коммуникационного домена, типа сокета и протокола), функция возвращает –1.
Связывание.
Для того чтобы к созданному сокету мог обратиться какой-либо процесс извне, необходимо присвоить ему адрес. Как мы уже говорили, формат адреса зависит от коммуникационного домена, в рамках которого действует сокет, и может представлять собой либо путь к файлу, либо сочетание IP-адреса и номера порта. Но в любом случае связывание сокета с конкретным адресом осуществляется одной и той же функцией bind:
#include <sys/types.h>
#include <sys/socket.h>
int bind (int sockfd, struct sockaddr *myaddr, int addrlen)
Первый аргумент функции – дескриптор сокета, возвращенный функцией socket(); второй аргумент – указатель на структуру, содержащую адрес сокета. Для домена AF_UNIX формат структуры описан в <sys/un.h> и выглядит следующим образом:
#include <sys/un.h>
struct sockaddr_un {
short sun_family; /* == AF_UNIX */
char sun_path[108];
};
Для доменаAF_INETформат структуры описан в<netinet/in.h>и выглядит следующим образом:
#include <netinet/in.h>
struct sockaddr_in {
short sin_family; /* == AF_INET */
u_short sin_port; /* port number */
struct in_addr sin_addr; /* host IP address */
char sin_zero[8]; /* not used */
};
Последний аргумент функции задает реальный размер структуры, на которую указывает myaddr.
Важно отметить, что если мы имеем дело с доменом AF_UNIX и адрес сокета представляет собой имя файла, то при выполнении функции bind()система в качестве побочного эффекта создает файл с таким именем. Поэтому для успешного выполнения bind() необходимо, чтобы такого файла не существовало к данному моменту. Это следует учитывать, если мы «зашиваем» в программу определенное имя и намерены запускать нашу программу несколько раз на одной и той же машине – в этом случае для успешной работы bind() необходимо удалять файл с этим именем перед связыванием. Кроме того, в процессе создания файла, естественно, проверяются права доступа пользователя, от имени которого производится вызов, ко всем директориям, фигурирующим в полном путевом имени файла, что тоже необходимо учитывать при задании имени. Если права доступа к одной из директорий недостаточны, вызов bind() завершится неуспешно.
В случае успешного связывания bind() возвращает 0, в случае ошибки – -1.
Предварительное установление соединения.