Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » UNIX: разработка сетевых приложений - Уильям Стивенс

UNIX: разработка сетевых приложений - Уильям Стивенс

Читать онлайн UNIX: разработка сетевых приложений - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 250 251 252 253 254 255 256 257 258 ... 263
Перейти на страницу:

В системе с обратным порядком байтов это значение интерпретируется как 0xeaffffff, или -352 521 537. Аналогично, представление числа -77 в прямом порядке байтов выглядит как 0xffffffb3, но в системах с обратным порядком оно представляется как 0xb3ffffff, или -1 275 068 417. Сложение, выполняемое сервером, приводит к результату 0x9efffffe, или -1 627 389 954. Полученное значение в обратном порядке байтов посылается через сокет клиенту, где в прямом порядке байтов оно интерпретируется как 0xfeffff9e, или -16 777 314 — это то значение, которое выводится в нашем примере.

5.9. Метод правильный (преобразование двоичных значений в сетевой порядок байтов), но нельзя использовать функции htonl и ntohl. Хотя символ l в названиях данных функций обозначает «long», эти функции работают с 32-разрядными целыми (раздел 3.4). В 64-разрядных системах long занимает 64 бита, и эти две функции работают некорректно. Для решения этой проблемы следует определить две новые функции hton64 и ntoh64, но они не будут работать в системах, представляющих значения типа long 32 битами.

5.10. В первом сценарии сервер будет навсегда блокирован при вызове функции readn в листинге 5.14, поскольку клиент посылает два 32-разрядных значения, а сервер ждет два 64-разрядных значения. В случае, если клиент и сервер поменяются узлами, клиент будет посылать два 64-разрядных значения, а сервер считает только первые 64 бита, интерпретируя их как два 32-разрядных значения. Второе 64-разрядное значение останется в приемном буфере сокета сервера. Сервер отправит обратно 32-разрядное значение, и клиент навсегда заблокируется в вызове функции readn в листинге 5.13, поскольку будет ждать для считывания 64-разрядное значение.

5.11. Функция IP-маршрутизации просматривает IP-адрес получателя (IP-адрес сервера) и пытается по таблице маршрутизации определить исходящий интерфейс и следующий маршрутизатор (см. главу 9 [111]). В качестве адреса отправителя используется первичный IP-адрес исходящего интерфейса, если сокет еще не связан с локальным IP-адресом.

Глава 6

6.1. Массив целых чисел содержится внутри структуры, а язык С позволяет использовать со структурами оператор присваивания.

6.2. Если функция select сообщает, что сокет готов к записи, причем буфер отправки сокета вмещает 8192 байта, а мы вызываем для этого блокируемого сокета функцию write с буфером размером 8193 байта, то функция write может заблокироваться, ожидая места для последнего байта. Операции считывания на блокируемом сокете будут возвращать сообщение о неполном считывании, если доступны какие-либо данные, но операции записи на блокируемом сокете заблокированы до принятия всех данных ядром. Поэтому, чтобы избежать блокирования при использовании функции select для проверки на возможность записи, следует переводить сокет в неблокируемый режим.

6.3. Если оба дескриптора готовы для чтения, выполняется только первый тест — тест сокета. Но это не нарушает работоспособность клиента, а только лишь уменьшает его эффективность. Поэтому если при завершении функции select оба дескриптора готовы для чтения, первое условие if оказывается истинным, в результате чего сначала вызывается функция readline для считывания из сокета, а затем функция fputs для записи в стандартный поток вывода. Следующее условие if пропускается (поскольку мы добавили else), но функция select вызывается снова, сразу находит стандартное устройство ввода, готовое к чтению, и завершается. Суть в том, что условие готовности стандартного потока ввода для чтения сбрасывается считыванием из сокета, а не возвратом функции select.

6.4. Воспользуйтесь функцией getrlimit для получения значений константы RLIMIT_NOFILE, а затем вызовите функцию setrlimit для установки текущего гибкого предела (rlim_cur) равным жесткому пределу (rlim_max). Например, в Solaris 2.5 гибкий предел равен 64, но любой процесс может увеличить это значение до используемого по умолчанию значения жесткого предела (1024).

6.5. Серверное приложение непрерывно посылает данные клиенту, клиент TCP подтверждает их прием и сбрасывает.

6.6. Функция shutdown с аргументами SHUT_WR и SHUT_RDWR всегда посылает сегмент FIN, в то время как функция close посылает сегмент FIN только если в момент вызова функции close счетчик ссылок дескриптора равен 1.

6.7. Функция readline возвращает ошибку, и наша функция-обертка Readline завершает работу сервера. Но серверы должны справляться с такими ситуациями. Обратите внимание на то, как мы обрабатываем эти условия в листинге 6.6, хотя даже этот код не является удовлетворительным. Рассмотрим, что произойдет, если соединение между клиентом и сервером прервется и время ожидания одного из ответов сервера будет превышено. Возвращаемой ошибкой может быть ошибка ETIMEDOUT.

Обычно сервер не должен прекращать свою работу из-за подобных ошибок. Он должен записать ее в файл журнала, закрыть сокет и продолжать обслуживание других клиентов. Следует понимать, что обработка таких ошибок путем прекращения работы сервера недопустима для серверов, у которых один процесс выполняет обработку всех клиентов. Но если сервер был дочерним процессом, обрабатывающим только один клиент, то прекращение работы одного дочернего процесса не отразится ни на родительском процессе (который, по нашему предположению, обрабатывает все новые соединения и порождает новые дочерние процессы), ни на одном из других дочерних процессов, обрабатывающих другие клиенты.

Глава 7

7.2. Решение упражнения приведено в листинге Д.2. Вывод строки данных, возвращаемых сервером, был удален, поскольку это значение нам не нужно.

Листинг Д.2. Вывод размера приемного буфера сокета и MSS до и после установления соединения

//sockopt/rcvbuf.c

 1 #include "urp.h"

 2 #include <netinet/tcp.h> /* для TCP_MAXSEG */

 3 int

 4 main(int argc, char **argv)

 5 {

 6  int sockfd, rcvbuf, mss;

 7  socklen_t len;

 8  struct sockaddr_in servaddr;

 9  if (argc != 2)

10   err_quit("usage: rcvbuf <Ipaddress>");

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

12  len = sizeof(rcvbuf);

13  Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len);

14  len = sizeof(mss);

15  Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len);

16  printf("defaults: SO_RCVBUF = %d. MSS = %dn", rcvbuf, mss);

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

18  servaddr.sin_family = AF_INET;

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

20  Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

21  Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));

22  len = sizeof(rcvbuf);

23  Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len);

24  len = sizeof(mss);

25  Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len);

26  printf("after connect: SO_RCVBUF = %d, MSS = %dn", rcvbuf, mss);

27  exit(0);

28 }

He существует какого-то одного «правильного» вывода для данной программы. Результаты зависят от системы. Некоторые системы (в особенности Solaris 2.5.1 и более ранние версии) всегда возвращают нулевой размер буфера сокета, не давая нам возможности увидеть, что происходит с этим значением в процессе соединения.

До вызова функции connect выводится значение MSS по умолчанию (часто 536 или 512), а значение, выводимое после вызова функции connect, зависит от возможных параметров MSS, полученных от собеседника. Например, в локальной сети Ethernet после выполнения функции connect MSS может иметь значение 1460. Однако после соединения (connect) с сервером в удаленной сети значение MSS может быть равно значению по умолчанию, если только ваша система не поддерживает обнаружение транспортной MTU. Если это возможно, запустите во время работы вашей программы программу tcpdump или подобную ей (см. раздел В.5), чтобы увидеть фактическое значение параметра MSS в сегменте SYN, полученном от собеседника.

Многие реализации после установления соединения округляют размер приемного буфера сокета в большую сторону, чтобы он было кратным MSS. Чтобы узнать размер приемного буфера сокета после установления соединения, можно исследовать пакеты с помощью программы типа tcpdump и посмотреть, каков размер объявленного окна TCP.

7.3. Разместите в памяти структуру linger по имени ling и проинициализируйте ее следующим образом:

str_cli(stdin, sockfd);

ling.l_onoff = 1;

ling.l_linger = 0;

Setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));

exit(0);

Это заставит TCP на стороне клиента прекратить работу путем отправки сегмента RST вместо нормального обмена четырьмя сегментами. Дочерний процесс сервера вызывает функцию readline, возвращает ошибку ECONNRESET и выводит следующее сообщение:

1 ... 250 251 252 253 254 255 256 257 258 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии