27.3.2. Создание и связывание сокета
27.3.2. Создание и связывание сокета
Основная задача управляющих функций — организовать взаимодействие двух компьютеров, точнее процессов, а также завершить сеанс связи этих процессов. К управляющим функциям относятся функции:
? socket() — создание сокета;
? bind() — связывание сокета;
? close() и shutdown() — завершение сеанса связи.
Начнем по порядку, а именно, с функции socket(). Ее прототип следующий:
#include <sys/types.h>
#include <sys/socket.h>
extern int socket(int __domain, int __type,
int __protocol) __THROW;
Первый аргумент определяет набор протоколов. Особо вдаваться в подробности не будем — просто всегда в качестве параметра domain передавайте значение AF_INET, что означает использование стека протоколов TCP/IP.
Аргумент type позволяет установить режим работы: с установлением соединения и без такового — значения SOCK_STREAM и SOCK_DGRAM соответственно. Для непосредственного доступа к протоколам IPv4 используется параметр SOCK_RAW. Для его использования нужно подключить заголовочный файл:
#include <netinet/in.h>
Третий параметр лучше всего установить равным 0. В этом случае будет выбран протокол по умолчанию в зависимости от режима работы:
TCP, если мы выбрали режим SOCK_STREAM;
UDP, если мы выбрали SOCK_DGRAM.
Если вы установили значение SOCK_RAW, вы можете указывать в качестве последнего параметра непосредственно значения из файла /etc/protocols. Фрагмент этого файла приведен ниже.
Листинг 27.2. Фрагмент файла /etc/protocols
ip 0 IP # Протокол Интернета
icmp 1 ICMP # Протокол ICMP
igmp 2 IGMP # Протокол IGMP
# (Internet Group Management Protocol)
ggp 3 GGP # Протокол GGP (gateway-gateway )
tcp 6 TCP # Протокол TCP
udp 17 UDP # Протокол UDP
Если сокет создан успешно, функция возвращает дескриптор сокета — целое положительное число. В случае ошибки функция возвращает значение -1 (отрицательное число). Вот небольшой пример:
int sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock==-1) {
printf("Ошибка при создании сокета ");
exit(1);
}
Чтобы связать созданный нами сокет с локальным портом, например, 1234, нужно использовать системный вызов bind():
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
extern int bind(int fd, struct sockaddr *addr,
socklen_t len) __THROW;
Первый аргумент функции задает дескриптор нашего сокета. Второй — это указатель на структуру типа sockaddr. Все структуры данного типа определены в файле socket.h:
# define __SОСKADDR_ALLTYPES
__SOCKADDR_ONETYPE (sockaddr)
__SOCKADDR_ONETYPE (sockaddr_at)
__SОСKADDR_ONETYPE (sockaddr_ax25)
__SOCKADDR_ONETYPE (sockaddr_dl)
__SOCKADDR_ONETYPE (sockaddr_eon)
__SОСKADDR_ONETYPE (sockaddr_in)
__SOCKADDR_ONETYPE (sockaddr_in6)
__SОСKADDR_ONETYPE (sockaddr_inarp)
__SOCKADDR_ONETYРЕ (sockaddr_ipx)
__SОСKADDR_ONETYPE (sockaddr_iso)
__SОСKADDR_ONETYPE (sockaddr_ns)
__SOCKADDR_ONETYPE (sockaddr_un)
__SOCKADDR_ONETYPE (sockaddr_x25)
Мы программируем для сети TCP/IP, поэтому будем использовать структуру sockaddr_in (для IPv4) или sockaddr_in6 (для IPv6).
Последний аргумент — это длина выбранной нами структуры (sockaddr_in) в байтах.
Структура sockaddr_in определена в файле in.h так:
struct sockaddr_in {
__SОСKADDR_COMMON(sin_);
in_port_t sin_port; /* Номер порта */
struct in_addr sin_addr; /* IP-адрес */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SОСKADDR_COMMON_SIZE -
sizeof(in_port_t) - sizeof (struct in_addr)];
};
/* для IPv6. */
struct sockaddr_in6 {
__SОСKADDR_COMMON(sin6_);
in_port_t sin6_port; /* Порт транспортного уровня */
uint32_t sin6_flowinfo; /* Информация потока IPv6 */
struct in6_addr sin6_addr; /* адрес IPv6 */
uint32_t sin6_scope_id; /* IPv6-идентификатор */
};
Поля структуры sockaddr_in означают следующее:
? sin_ — набор используемых протоколов. Так как мы используем TCP/IP, данное поле должно содержать значение AF_INET;
? sin_port — номер порта;
? sin_addr — структура, определяющая адрес узла;
? sin_zero — обычно не используется.
Структура struct in_addr, определяющая адрес узла, также описана в файле in.h:
struct in_addr {
in_addr_t s_addr;
};
Обычно поле s_addr должно принимать значение INADDR_ANY — сейчас поясню почему. Структура sockaddr_in должна быть заполнена ДО вызова функции bind(). Если поле sin_addr.s_addr принимает значение INADDR_ANY, то функция bind() автоматически привяжет к сокету адрес локального компьютера и нам не нужно будет указывать его явно — так наша программа будет универсальной.
Функция bind() возвращает 0 в случае успеха, и -1, если произошла ошибка. Вот небольшой пример использования этой функции:
struct sockaddr_in client;
...
client.sin_family = AF_INET;
client.sin_addr.s_addr = INADDR_ANY;
client.sin_port = 1235;
bind(sock, (struct sockaddr *)&client, sizeof(client));
Данный текст является ознакомительным фрагментом.