Целые числа могут иметь разное представление на серверном и клиентском компьютерах, поэтому мы переводим их в строки с символом новой строки в конце, а для их чтения применяем функцию readLine() (см. листинг 55.1).
И сервер, и клиент задействуют заголовочный файл, представленный в листинге 55.5, который, в свою очередь, подключает другие файлы и определяет номер TCP-порта для нашего приложения.
Серверная программа, представленная в листинге 55.6, выполняет следующие шаги.
• Начинает последовательность либо с 1, либо с числа, переданного в виде аргумента командной строки
• Игнорирует сигнал SIGPIPE
• Вызывает функцию getaddrinfo()
• Входит в цикл, перебирающий структуры с адресами сокетов, полученными в предыдущем шаге
• Устанавливает параметр SO_REUSEADDR для сокета, созданного в предыдущем шаге
• Делает сокет слушающим
• Входит в бесконечный цикл for
• Принимает новое соединение
• Считывает клиентское сообщение
• Возвращает клиенту текущий элемент последовательности (seqNum) в виде строки с нулевым символом в конце
• Обновляет текущий элемент последовательности, добавляя к seqNum значение reqLen
Листинг 55.5. Заголовочный файл, используемый программами is_seqnum_sv.c и is_seqnum_cl.c
sockets/is_seqnum.h
#include
#include
#include
#include "read_line.h" /* Объявление readLine() */
#include "tlpi_hdr.h"
#define PORT_NUM "50000" /* Номер порта для сервера */
#define INT_LEN 30 /* Размер строки, достаточный для хранения наибольшего
целого числа (включая завершающий символ '\n') */
sockets/is_seqnum.h
Листинг 55.6. Итерационный сервер, который взаимодействует с клиентами с помощью потокового сокета
sockets/is_seqnum_sv.c
#define _BSD_SOURCE /* Получаем определения NI_MAXHOST и NI_MAXSERV
из файла
#include
#include "is_seqnum.h"
#define BACKLOG 50
int
main(int argc, char *argv[])
{
uint32_t seqNum;
char reqLenStr[INT_LEN]; /* Длина запрашиваемой последовательности */
char seqNumStr[INT_LEN]; /* Начало выделенной последовательности */
struct sockaddr_storage claddr;
int lfd, cfd, optval, reqLen;
socklen_t addrlen;
struct addrinfo hints;
struct addrinfo *result, *rp;
#define ADDRSTRLEN (NI_MAXHOST + NI_MAXSERV + 10)
char addrStr[ADDRSTRLEN];
char host[NI_MAXHOST];
char service[NI_MAXSERV];
if (argc > 1 && strcmp(argv[1], "-help") == 0)
usageErr("%s [init-seq-num]\n", argv[0]);
errExit("signal");
/* Вызываем getaddrinfo(), чтобы получить список адресов,
к которым можем попытаться привязать наш сокет */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC; /* Поддержка IPv4 или IPv6 */
/* Универсальный IP-адрес; имя службы имеет числовой формат */
errExit("getaddrinfo");
/* Перебираем полученный список, пока не находим структуру с адресом,
подходящую для создания и привязывания сокета */
optval = 1;
lfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (lfd == -1)