UNIX: разработка сетевых приложений
Значение
интерпретируется как 16 777 216. Аналогично, целое число 2, отправленное клиентом, интерпретируется сервером как0x01000000, или 33 554 432. Сумма этих двух целых чисел равна 50 331 648, или0x02000000. Когда это значение, записанное в обратном порядке байтов, отправляется клиенту, оно интерпретируется клиентом как целое число 3.0x03000000Но 32-разрядное целое число -22 представляется в системе с прямым порядком байтов так, как показано на рис. Д.3 (мы предполагаем, что используется поразрядное дополнение до двух для отрицательных чисел).
Рис. Д.3. Представление 32-разрядного целого числа -22 в формате прямого порядка байтов
В системе с обратным порядком байтов это значение интерпретируется как
, или -352 521 537. Аналогично, представление числа -77 в прямом порядке байтов выглядит как0xeaffffff, но в системах с обратным порядком оно представляется как0xffffffb3, или -1 275 068 417. Сложение, выполняемое сервером, приводит к результату0xb3ffffff, или -1 627 389 954. Полученное значение в обратном порядке байтов посылается через сокет клиенту, где в прямом порядке байтов оно интерпретируется как0x9efffffe, или -16 777 314 — это то значение, которое выводится в нашем примере.0xfeffff9e5.9. Метод правильный (преобразование двоичных значений в сетевой порядок байтов), но нельзя использовать функции
иhtonl. Хотя символntohlв названиях данных функций обозначает «long», эти функции работают с 32-разрядными целыми (раздел 3.4). В 64-разрядных системахlзанимает 64 бита, и эти две функции работают некорректно. Для решения этой проблемы следует определить две новые функцииlongиhton64, но они не будут работать в системах, представляющих значения типаntoh6432 битами.long5.10. В первом сценарии сервер будет навсегда блокирован при вызове функции
в листинге 5.14, поскольку клиент посылает два 32-разрядных значения, а сервер ждет два 64-разрядных значения. В случае, если клиент и сервер поменяются узлами, клиент будет посылать два 64-разрядных значения, а сервер считает только первые 64 бита, интерпретируя их как два 32-разрядных значения. Второе 64-разрядное значение останется в приемном буфере сокета сервера. Сервер отправит обратно 32-разрядное значение, и клиент навсегда заблокируется в вызове функцииreadnв листинге 5.13, поскольку будет ждать для считывания 64-разрядное значение.readn5.11. Функция IP-маршрутизации просматривает IP-адрес получателя (IP-адрес сервера) и пытается по таблице маршрутизации определить исходящий интерфейс и следующий маршрутизатор (см. главу 9 [111]). В качестве адреса отправителя используется первичный IP-адрес исходящего интерфейса, если сокет еще не связан с локальным IP-адресом.
Глава 6
6.1. Массив целых чисел содержится внутри структуры, а язык С позволяет использовать со структурами оператор присваивания.
6.2. Если функция
сообщает, что сокет готов к записи, причем буфер отправки сокета вмещает 8192 байта, а мы вызываем для этого блокируемого сокета функциюselectс буфером размером 8193 байта, то функцияwriteможет заблокироваться, ожидая места для последнего байта. Операции считывания на блокируемом сокете будут возвращать сообщение о неполном считывании, если доступны какие-либо данные, но операции записи на блокируемом сокете заблокированы до принятия всех данных ядром. Поэтому, чтобы избежать блокирования при использовании функцииwriteдля проверки на возможность записи, следует переводить сокет в неблокируемый режим.select6.3. Если оба дескриптора готовы для чтения, выполняется только первый тест — тест сокета. Но это не нарушает работоспособность клиента, а только лишь уменьшает его эффективность. Поэтому если при завершении функции
оба дескриптора готовы для чтения, первое условиеselectоказывается истинным, в результате чего сначала вызывается функцияifдля считывания из сокета, а затем функцияreadlineдля записи в стандартный поток вывода. Следующее условиеfputsпропускается (поскольку мы добавилиif), но функцияelseвызывается снова, сразу находит стандартное устройство ввода, готовое к чтению, и завершается. Суть в том, что условие готовности стандартного потока ввода для чтения сбрасывается считыванием из сокета, а не возвратом функцииselect.select6.4. Воспользуйтесь функцией
для получения значений константыgetrlimit, а затем вызовите функциюRLIMIT_NOFILEдля установки текущего гибкого предела (setrlimit) равным жесткому пределу (rlim_cur). Например, в Solaris 2.5 гибкий предел равен 64, но любой процесс может увеличить это значение до используемого по умолчанию значения жесткого предела (1024).rlim_max6.5. Серверное приложение непрерывно посылает данные клиенту, клиент TCP подтверждает их прием и сбрасывает.
6.6. Функция
с аргументамиshutdownиSHUT_WRвсегда посылает сегмент FIN, в то время как функцияSHUT_RDWRпосылает сегмент FIN только если в момент вызова функцииcloseсчетчик ссылок дескриптора равен 1.close6.7. Функция
возвращает ошибку, и наша функция-оберткаreadlineзавершает работу сервера. Но серверы должны справляться с такими ситуациями. Обратите внимание на то, как мы обрабатываем эти условия в листинге 6.6, хотя даже этот код не является удовлетворительным. Рассмотрим, что произойдет, если соединение между клиентом и сервером прервется и время ожидания одного из ответов сервера будет превышено. Возвращаемой ошибкой может быть ошибкаReadline.ETIMEDOUTОбычно сервер не должен прекращать свою работу из-за подобных ошибок. Он должен записать ее в файл журнала, закрыть сокет и продолжать обслуживание других клиентов. Следует понимать, что обработка таких ошибок путем прекращения работы сервера недопустима для серверов, у которых один процесс выполняет обработку всех клиентов. Но если сервер был дочерним процессом, обрабатывающим только один клиент, то прекращение работы одного дочернего процесса не отразится ни на родительском процессе (который, по нашему предположению, обрабатывает все новые соединения и порождает новые дочерние процессы), ни на одном из других дочерних процессов, обрабатывающих другие клиенты.
Глава 7
7.2. Решение упражнения приведено в листинге Д.2. Вывод строки данных, возвращаемых сервером, был удален, поскольку это значение нам не нужно.