UNIX: разработка сетевых приложений
//names/daytimetcpcli.c1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int sockfd, n;6 char recvline[MAXLINE + 1];7 socklen_t len;8 struct sockaddr_storage *ss;9 if (argc != 3)10 err_quit11 ("usage, daytimetcpcli <hostname/IPaddress> <service/port#>");12 sockfd = Tcp_connect(argv[1], argv[2]);13 len = sizeof(ss);14 Getpeername(sockfd, (SA*)&ss, &len);15 printf("connected to %s\n", Sock_ntop_host((SA*)&ss, len));16 while ((n = Read(sockfd, recvline, MAXLINE)) > 0) {17 recvline[n] = 0; /* завершающий нуль */18 Fputs(recvline, stdout);19 }20 exit(0);21 }Аргументы командной строкиТеперь нам требуется второй аргумент командной строки для задания либо имени службы, либо номера порта, что позволит нашей программе соединяться с другими портами.9-11Соединение с серверомТеперь весь код сокета для этого клиента выполняется функцией12.tcp_connectВывод ответа сервераМы вызываем функцию13-15, чтобы получить адрес протокола сервера и вывести его. Мы делаем это для проверки протокола, используемого в примерах, которые скоро покажем.getpeernameОбратите внимание, что функция
не возвращает размера структуры адреса сокета, который использовался для функцииtcp_connect. Мы могли добавить еще один аргумент-указатель, чтобы получить это значение, но при создании этой функции мы стремились добиться меньшего числа аргументов, чем у функцииconnect. Поэтому мы определяем константуgetaddrinfoв нашем заголовкеMAXSOCKADDRтак, чтобы ее размер равнялся размеру наибольшей структуры адреса сокета. Обычно это размер структуры адреса доменного сокета Unix (см. раздел 14.2), немного более 100 байт. Мы выделяем в памяти пространство для структуры указанного размера и заполняем ее с помощью функцииunp.h.getpeernameЭта версия нашего клиента работает и с IPv4, и с IPv6, тогда как версия, представленная в листинге 1.1, работала только с IPv4, а версия из листинга 1.2 — только с IPv6. Сравните нашу новую версию с представленной в листинге Д.6, которую мы написали, чтобы использовать функции
иgethostbynameдля поддержки и IPv4, и IPv6.getservbynameСначала мы задаем имя узла, поддерживающего только IPv4:
freebsd % <b>daytimetcpcli linux daytime</b>connected to 206 168.112.96Sun Jul 27 23:06:24 2003Затем мы задаем имя узла, поддерживающего и IPv4, и IPv6:
freebsd % <b>daytimetcpcli aix daytime</b>connected to 3ffe:b80:1f8d:2:204:acff:fe17:bf38Sun Jul 27 23:17:13 2003Используется адрес IPv6, поскольку у узла имеется и запись типа AAAA, и запись типа А. Кроме того, функция
устанавливает семейство адресовtcp_connect, поэтому, как было отмечено в табл. 11.3, сначала идет поиск записей типа AAAA, и только если этот поиск неудачен, выполняется поиск записей типа А.AF_UNSPECВ следующем примере мы указываем на необходимость использования именно адреса IPv4, задавая имя узла с суффиксом
, что, как мы отмечали в разделе 11.2, в соответствии с принятым нами соглашением означает имя узла, который поддерживает только записи типа А:-4freebsd % <b>daytimetcpcli aix-4 daytime</b>connected to 192.168.42.2Sun Jul 27 23:17:48 200311.13. Функция tcp_listen
Наша следующая функция,
, выполняет обычные шаги сервера TCP: создание сокета TCP, связывание его с заранее известным портом с помощью функции bind и разрешение приема входящих запросов через соединение. В листинге 11.6 представлен исходный код.tcp_listen#include "unp.h"int tcp_listen(const char *<i>hostname</i>, const char *<i>service</i>, socklen_t *<i>lenptr</i>);<i>В случае успешного выполнения возвращает дескриптор присоединенного сокета, в случае ошибки не возвращает ничего</i>Листинг 11.6. Функция tcp_listen: выполнение обычных шагов сервера TCP
//lib/tcp_listen.c1 #include "unp.h"2 int3 tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)4 {5 int listenfd, n;6 const int on = 1;7 struct addrinfo hints, *res, *ressave;8 bzero(&hints, sizeof(struct addrinfo));9 hints.ai_flags = AI_PASSIVE;10 hints.ai_family = AF_UNSPEC;11 hints.ai_socktype = SOCK_STREAM;12 if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)13 err_quit("tcp_listen error for %s, %s: %s",14 host, serv, gai_strerror(n));