Пример: аргументы типа «значение-результат»

Пример: аргументы типа «значение-результат»

В листинге 4.2 представлен измененный код из листинга 1.5 (вывод IP-адреса и номера порта клиента), обрабатывающий аргумент типа «значение-результат» функции accept.

Листинг 4.2. Сервер определения времени и даты, сообщающий IP-адрес и номер порта клиента

//intro/daytimetcpsrv1.c

 1 #include "unp.h"

 2 #include <time.h>

 3 int

 4 main(int argc, char **argv)

 5 {

 6  int listenfd, connfd;

 7  socklen_t len;

 8  struct sockaddr_in servaddr, cliaddr;

 9  char buff[MAXLINE];

10  time_t ticks;

11  listenfd = Socket(AF_INET, SOCK_STREAM, 0);

12  bzero(&servaddr, sizeof(servaddr));

13  servaddr.sin_family = AF_INET;

14  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

15  servaddr.sin_port = htons(13); /* сервер времени и даты */

16  Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));

17  Listen(listenfd, LISTENQ);

18  for (;;) {

19   len = sizeof(cliaddr);

20   connfd = Accept(listenfd, (SA*)&cliaddr, &len);

21   printf("connection from %s, port %d ",

22    Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff));

23   ntohs(cliaddr.sin_port));

24   ticks = time(NULL);

25   snprintf(buff, sizeof(buff), "% 24s ", ctime(&ticks));

26   Write(connfd, buff, strlen(buff));

27   Close(connfd);

28  }

29 }

Новые объявления

7-8 Мы определяем две новых переменных: len, которая будет переменной типа «значение-результат», и cliaddr, которая будет содержать адрес протокола клиента.

Принятие соединения и вывод адреса клиента

19-23 Мы инициализируем переменную len, присвоив ей значение, равное размеру структуры адреса сокета, и передаем указатель на структуру cliaddr и указатель на len в качестве второго и третьего аргументов функции accept. Мы вызываем функцию inet_ntop (см. раздел 3.7) для преобразования 32-битового IP-адреса в структуре адреса сокета в строку ASCII (точечно-десятичную запись), а затем вызываем функцию ntohs (см. раздел 3.4) для преобразования сетевого порядка байтов в 16-битовом номере порта в порядок байтов узла.

ПРИМЕЧАНИЕ

При вызове функции sock_ntop вместо inet_ntop наш сервер станет меньше зависеть от протокола, однако он все равно зависит от IPv4. Мы покажем версию этого сервера, не зависящего от протокола, в листинге 11.7.

Если мы запустим наш новый сервер, а затем запустим клиент на том же узле, то дважды соединившись с сервером, мы получим от клиента следующий вывод:

solaris % daytimetcpcli 127.0.0.1

Thu Sep 11 12:44:00 2003

solaris % daytimetcpcli 192.168.1.20

Thu Sep 11 12:44:09 2003

Сначала мы задаем IP-адрес сервера как адрес закольцовки на себя (loopback address) (127.0.0.1), а затем как его собственный IP-адрес (192.168.1.20). Вот соответствующий вывод сервера:

solaris # daytimetcpsrv1

connection from 127.0.0.1, port 43388

connection from 192.168.1.20, port 43389

Обратите внимание на то, что происходит с IP-адресом клиента. Поскольку наш клиент времени и даты (см. листинг 1.1) не вызывает функцию bind, как сказано в разделе 4.4, ядро выбирает IP-адрес отправителя, основанный на используемом исходящем интерфейсе. В первом случае ядро задает IP-адрес равным адресу закольцовки, во втором случае — равным IP-адресу интерфейса Ethernet. Кроме того, мы видим, что динамически назначаемый порт, выбранный ядром Solaris, — это 33 188, а затем 33 189 (см. рис. 2.10).

Наконец, заметьте, что приглашение интерпретатора команд изменилось на знак # — это приглашение к вводу команды для привилегированного пользователя. Наш сервер должен обладать правами привилегированного пользователя, чтобы с помощью функции bind связать зарезервированный порт 13. Если у нас нет прав привилегированного пользователя, вызов функции bind оказывается неудачным:

solaris % daytimetcpsrv1

bind error: Permission denied

Данный текст является ознакомительным фрагментом.