Листинг 55.7. Клиент, использующий потоковые сокеты
sockets/is_seqnum_cl.c
#include
#include "is_seqnum.h"
int
main(int argc, char *argv[])
{
char *reqLenStr; /* Длина запрашиваемой последовательности */
char seqNumStr[INT_LEN]; /* Начало выделенной последовательности */
int cfd;
ssize_t numRead;
struct addrinfo hints;
struct addrinfo *result, *rp;
if (argc < 2 || strcmp(argv[1], "-help") == 0)
usageErr("%s server-host [sequence-len]\n", argv[0]);
/* Вызываем getaddrinfo(), чтобы получить список адресов,
к которым можем попытаться привязать наш сокет */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
hints.ai_family = AF_UNSPEC; /* Поддержка IPv4 или IPv6 */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV;
errExit("getaddrinfo");
/* Перебираем полученный список, пока не находим структуру с адресом,
подходящую для успешного соединения с сокетом */
if (cfd == -1)
continue; /* В случае ошибки пробуем следующий адрес */
break; /* Успех */
/* Сбой соединения: закрываем этот сокет и пробуем следующий адрес */
close(cfd);
}
if (rp == NULL)
fatal("Could not connect socket to any address");
freeaddrinfo(result);
/* Отправляем длину запрашиваемой последовательности с символом новой строки в конце */
if (write(cfd, reqLenStr, strlen(reqLenStr))!= strlen(reqLenStr))
fatal("Partial/failed write (reqLenStr)");
if (write(cfd, "\n", 1)!= 1)
fatal("Partial/failed write (newline)");
/* Считываем и выводим число, полученное от сервера */
if (numRead == -1)
errExit("readLine");
if (numRead == 0)
fatal("Unexpected EOF from server");
exit(EXIT_SUCCESS); /* Закрывает 'cfd' */
}
sockets/is_seqnum_cl.c
Здесь мы воспользуемся функциями, представленными в разделе 55.10, чтобы реализовать библиотеку для выполнения задач, которые обычно необходимо решать при работе с сокетами интернет-домена (данная библиотека инкапсулирует многие из тех шагов, которые мы показали на примере программ в разделе 55.11). Так как эти функции используют вызовы getaddrinfo() и getnameinfo(), не зависящие от протокола, их можно применять как для IPv4, так и для IPv6.
Заголовочный файл с объявлением данных функций представлен в листинге 55.8. Многие из них имеют похожие аргументы.
• Аргумент host содержит строку с именем сетевого узла или его числовым адресом (для IPv4 это десятичные числа, разделенные точкой, а для IPv6 — шестнадцатеричная строка). Можно также передать аргументу host нулевой указатель, чтобы использовать IP-адрес, замкнутый на себя.
• Аргументу service можно передать либо имя службы, либо порт, заданный в виде десятичной строки.
• Аргумент type обозначает тип сокета: SOCK_STREAM или SOCK_DGRAM.
Листинг 55.8. Заголовочный файл для программы inet_sockets.c
sockets/inet_sockets.h
#ifndef INET_SOCKETS_H
#define INET_SOCKETS_H /* Не даем подключить заголовок больше одного раза */
#include
#include
int inetConnect(const char *host, const char *service, int type);
int inetListen(const char *service, int backlog, socklen_t *addrlen);
int inetBind(const char *service, int type, socklen_t *addrlen);
char *inetAddressStr(const struct sockaddr *addr, socklen_t addrlen,
char *addrStr, int addrStrLen);
#define IS_ADDR_STR_LEN 4096
/* Рекомендуемая длина строкового буфера, который нужно передать
в inetAddressStr(). Должна быть больше чем (NI_MAXHOST + NI_MAXSERV + 4) */
#endif
sockets/inet_sockets.h
Функция inetConnect() создает сокет типа type и подключает его к адресу, заданному с помощью аргументов host и service. Эта функция предназначена для TCP- и UDP-клиентов, которым нужно подключиться к серверному сокету.
#include "inet_sockets.h"
int inetConnect(const char *
Возвращает файловый дескриптор или -1 при ошибке
Файловый дескриптор нового сокета возвращается в качестве результата выполнения функции.
Функция inetListen() создает слушающий сокет (SOCK_STREAM), привязанный к универсальному IP-адресу и TCP-порту, заданному с помощью аргумента service. Она предназначена для использования в TCP-серверах.
#include "inet_sockets.h"
int inetListen(const char *
Возвращает файловый дескриптор или -1 при ошибке
Файловый дескриптор нового сокета возвращается в качестве результата выполнения функции.