Linux Make Netservice Rubook PDF
Linux Make Netservice Rubook PDF
приложений в среде
Linux
Руководство разработчика
Шон Уолтон
Москва • Санкт
Петербург • Киев
2001
www.books-shop.com
ББК 32.973.26
018.2.75
УДК 681.3.07
Уолтон, Шон.
Создание сетевых приложений в среде Linux. : Пер. с англ.— М. : Издательский дом
"Вильяме", 2001. — 464 с.: ил. — Парал: тит. англ.
ISBN 5
8459
0193
6 (рус.)
Данная книга в основном посвящена программированию сокетов на языке С в среде
Linux. В ней шаг за шагом рассказывается о том, как писать профессиональные сетевые
клиентские, серверные и одноранговые приложения. Читатель узнает, как работать с су
ществующими клиент
серверными протоколами (в частности, HTTP), взаимодействовать
с другими компьютерами по протоколу UDP и создавать свои собственные протоколы. В
книге описываются все типы пакетов, поддерживаемых в семействе протоколов TCP/IP,
их достоинства и недостатки.
Помимо базового материала представлены сведения о различных методиках многоза
ББК 32.973.26
018.2.75
Все названия программных продуктов являются зарегистрированными торговыми марками соответст
вующих фирм.
Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой бы то ни
было форме и какими бы то ни было средствами, будь то электронные или механические, включая фотоко
пирование и запись на магнитный носитель, если на это нет письменного разрешения издательства Sams
Publishing.
Authorized translation from the English language edition published by Sams Publishing Copyright © 2001
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, elec
tronic or mechanical, including photocopying recording or by any information storage retrieval system, without
permission from the Publisher.
Russian language edition published by Williams Publishing House according to the Agreement with R&I En
ISBN 5
8459
0193
6 (рус.) О Издательский дом "Вильяме", 2001
ISBN 0
672
31935
7 (англ.) © Sams Publishing, 2001
www.books-shop.com
Оглавление
Часть I. Создание сетевых клиентских приложений 23
Глава 1. Простейший сетевой клиент 24
Глава 2. Основы TCP/IP 41
Глава 3. Различные типы Internetпакетов 58
Глава 4. Передача сообщений между одноранговыми компьютерами 79
Глава 5. Многоуровневая сетевая модель 99
Часть II. Создание серверных приложений 115
Глава 6. Пример сервера 116
Глава 7. Распределение нагрузки: многозадачность 131
Глава 8. Механизмы вводавывода 167
Глава 9. Повышение производительности 188
Глава 10. Создание устойчивых сокетов 213
Часть III. Объектноориентированные сокеты 231
Глава 11. Экономия времени за счет объектов 232
Глава 12. Сетевое программирование в Java 248
Глава 13. Программирование сокетов в C++ 263
Глава 14. Ограничения объектноориентированного программирования 279
www.books-shop.com
Введение 19
Часть I. Создание сетевых клиентских приложений 23
Глава 1. Простейший сетевой клиент 24
Связь с окружающим миром посредством сокетов 27
Правила общения: основы адресации в TCP/IP 28
Прослушивание сервера: простейший алгоритм клиентской программы 29
Системный вызов socket() 30
Подключение к серверу 32
Получение ответа от сервера 35
Разрыв соединения 39
Резюме: что происходит за кулисами . 40
Глава 2. Основы TCP/IP 41
IPадресация 42
Идентификация компьютера 42
Структура адресов Internet , 43
Маски подсети 45
Маршрутизаторы и преобразование адресов 46
Специальные и потерянные адреса 46
Номера IPпортов 48
Порядок следования байтов 50
Функции преобразования данных 50
Расширение клиентского приложения 52
Различные виды пакетов 56
Именованные каналы в UNIX 56
Резюме: IPадреса и средства преобразования данных 57
Глава 3. Различные типы Internetпакетов 58
Базовый сетевой пакет 59
Поле version 61
Поле header_len 61
Поле serve_type 62
Поле ID 62
Поля dont_frag, more_frag и frag_offset 62
Поле time_to_live (TTL) 63
Поле protocol 63
Поле options 64
Поле data 64
Анализ различных типов пакетов 64
Характеристики пакетов 65
Типы пакетов 67
Взаимосвязь между протоколами 75
Анализ сетевого трафика с помощью утилиты tcpdump 76
Создание сетевого анализатора 77
Резюме: выбор правильного пакета 78
Глава 4. Передача сообщений между одноранговыми компьютерами . 79
Сокеты, требующие установления соединения 80
Открытые каналы между программами 80
Надежные соединения 80
Соединения по протоколам более низкого уровня 82
Содержание
www.books-shop.com
Пример: подключение к демону HTTP , 84
Упрощенный протокол HTTP 84
Получение HTTPстраницы 84
Сокеты, не требующие установления соединения 85
Задание адреса, сокета 86
Упрощение процедуры квитирования 87
88
ПротоколТ/ТСР
Прямая доставка сообщений 89
Привязка порта к сокету 90
Обмен сообщениями 92
Прием сообщения 93
Подтверждение доставки UDPсообщения 94
Усиление надежности UDP 94
Упорядочение пакетов 95
Избыточность пакетов . 96
Проверка целостности данных 96
Задержки при передаче данных 97
Переключение задач: введение в многозадачность 97
Резюме: модели взаимодействия с установлением и без установления соединения 98
Глава 5. Многоуровневая сетевая модель 99
Решение сетевой задачи 100
Аппаратная среда 100
Передача данных в сети 102
Взаимодействие сети и операционной системы 103
. Взаимодействие сети и приложений 104
Сетевая модель OSI 105
Уровень 1: физический 105
Уровень 2: канальный ' 106
Уровень 3: сетевой 107
Уровень 4: транспортный 107
Уровень 5: сеансовый ' 107
Уровень 6: представительский 108
Уровень 7: прикладной 108
Набор протоколов Internet 108
Уровень 1: доступ к сети 108
Уровень 2: межсетевой (IP) 109
Уровень 2: управляющие расширения (ICMP) 110
Уровень 3: межузловой (UDP) 110
Уровень 3: потоки данных (TCP) 111
Уровень 4: прикладной 112
Фундаментальные различия между моделями OSI и IP 112
Что чему служит 113
Резюме: от теории к практике 113
Часть II. Создание серверных приложений 115
Глава 6. Пример сервера 116
Схема работы сокета: общий алгоритм сервера ,' 117
Простой эхосервер 118
Привязка порта к сокету 119
Создание очереди ожидания 121
Прием запросов от клиентов 122
Взаимодействие с клиентом 123
Общие правила определения протоколов 124
Содержание
www.books-shop.com
Какая программа должна начинать передачу первой? 125
Какая программа должна управлять диалогом? 125
Какой уровень сертификации требуется? 125
Какой тип данных используется? 125
Как следует обрабатывать двоичные данные? 126
Как обнаружить взаимоблокировку? 126
Необходима ли синхронизация по таймеру? 126
Как и когда переустанавливать соединение? 126
Когда завершать работу? 127
Более сложный пример: сервер HTTP 127
Резюме: базовые компоненты сервера 130
Глава 7. Распределение нагрузки: многозадачность 131
Понятие о многозадачности: процессы и потоки 132
Когда следует применять многозадачный режим 134
Характеристики многозадачного режима 135
Сравнение процессов и потоков 136
Создание процесса 136
Создание потока 139
Системный вызов _clone() 142
Взаимодействие заданий 145
Обгоняя время: исключающие семафоры и гонки 155
Гонки за ресурсами 155
Исключающий семафор 156
Проблемы с исключающими семафорами в библиотеке Pthreads 158
Предотвращение взаимоблокировки . 158
Управление дочерними заданиями и заданиязомби 159
Приоритеты и планирование дочерних заданий 159
Уничтожение зомби: очистка после завершения 160
Расширение существующих версий клиента и сервера 162
Вызов внешних программ с помощью функций семейства ехес() 163
Резюме 166
Глава 8. Механизмы вводавывода 167
Блокирование вводавывода: зачем оно необходимо? 168
Когда следует переходить в режим блокирования? 170
Альтернативы блокированию 170
Сравнение различных методик вводавывода 171
Опрос каналов вводавывода • 172
Чтение данных по методике опроса 173
Запись данных по методике опроса 175
Установление соединений по методике опроса . 176
Асинхронный вводвывод 177
Чтение данных по запросу ' 179
Асинхронная запись данных 180
Подключение по запросу 181
Устранение нежелательного блокирования с помощью функций poll() и select() 182
Реализация таймаутов 185
Резюме: выбор методик вводавывода 186
Глава 9. Повышение производительности 188
Подготовка к приему запросов на подключение 189
• Ограничение числа клиентских соединений 189
Предварительное ветвление сервера 191
Адаптация к различным уровням загруженности 193
Содержание
www.books-shop.com
Расширение возможностей сервера с помощью функции select() , , 195
Лавиноподобная загрузка планировщика ,. • 195
Чрезмерное использование функции select() 196
Разумное использование функции select() 197
Проблемы реализации 198
Перераспределение нагрузки 199
Анализ возможностей сокета 200
Общие параметры 201
Параметры протокола IP 203
Параметры стандарта IPv6 205
Параметры протокола TCP 206
Восстановление дескриптора сокета 206
Досрочная отправка: перекрытие сообщений . 207
Проблемы файлового вводавывода 208
Вводвывод по запросу: рациональное использование ресурсов процессора 208
Ускорение работы функции send() 208
Разгрузка функции recv() 209
Отправка приоритетных сообщений 209
Резюме 212
Глава 10. Создание устойчивых сокетов 213
Методы преобразования данных 214
Проверка возвращаемых значений 215
Обработка сигналов 217
SIGPIPE 218
SIGURG 219
SIGCHLD 219
SIGHUP 220
SIGIO 220
SIGALRM 220
Управление ресурсами 221
Управление файлами 221
Динамическая память ("куча") 221
Статическая память 223
Ресурсы процессора, совместно используемая память и процессы 223
Критические серверы 223
Что называется критическим сервером 224
Коммуникационные события и прерывания 224
Особенности возобновления сеанса 225
Способы возобновления сеанса 226
Согласованная работа клиента и сервера 227
Сетевые взаимоблокировки 228
Сетевое зависание 228
Отказ от обслуживания 229
Резюме: несокрушимые серверы 230
Часть III. Объектноориентированные сокеты 231
Глава 11. Экономия времени за счет объектов 232
Эволюция технологий программирования 233
Функциональное программирование: пошаговый подход 233
Модульное программирование: структурный подход 234
Абстрактное программирование: отсутствие ненужной детализации 235
Объектноориентированное программирование: естественный способ общения с миром 235
Рациональные методы программирования 236
Содержание
www.books-shop.com
Повторное использование кода 236
Создание подключаемых компонентов 237
Основы объектноориентированного программирования 238
Инкапсуляция кода 238
Наследование поведения 239
Абстракция данных • 240
Полиморфизм методов 241
Характеристики объектов 241
Классы и объекты 241
Атрибуты 241
Свойства 242
Методы 242
Права доступа 242
Отношения 242
Расширение объектов 243
Шаблоны ' . 243
Постоянство 243
Потоковая передача данных 243
Перегрузка 244
. Интерфейсы 244
События и исключения 244
Особые случаи 245
Записи и структуры 245
Наборы функций 245
Языковая поддержка 246
Классификация объектноориентированных языков 246
Работа с объектами в процедурных языках 246
Резюме: объектноориентированное мышление 247
Глава 12. Сетевое программирование в Java 248
Работа с сокетами 249
Программирование клиентов и серверов 250
Передача UDPсообщений 253
Групповая передача дейтаграмм 254
Вводвывод в Java 256
Классификация классов вводавывода 256
Преобразование потоков 257
Конфигурирование сокетов . . . 258
Общие методы конфигурирования 258
Конфигурирование групповых сокетов . 259
Многозадачные программы 259
Создание потокового класса 259
Добавление потоков к классу 260
Синхронизация методов 261
Существующие ограничения 262
Резюме ' , 262
Глава 13. Программирование сокетов в C++ 263
Зачем программировать сокеты в C++? 264
Упрощение работы с сокетами 264
Отсутствие ненужной детализации 264
Создание многократно используемых компонентов ' • 265
Моделирование по необходимости 265
Создание библиотеки сокетов 265
Определение общих характеристик 266
10 Содержание
www.books-shop.com
Группировка основных компонентов 267
Построение иерархии классов 269
Определение задач каждого класса 271
Тестирование библиотеки сокетов 274
.! Эхоклиент и эхосервер , 275
Многозадачная одноранговая передача сообщений 277
Существующие ограничения 278
Передача сообщений неизвестного/неопределенного типа • 278
Поддержка многозадачности 278
Резюме: библиотека сокетов упрощает программирование 278
Глава 14. Офаничения объектноориентированного программирования 279
Правильное использование объектов 280
Начальный анализ 280
Именование объектов 281
Разграничение этапов анализа и проектирования 281
Правильный уровень детализации 282
Избыточное наследование 282
Неправильное повторное использование 282
Правильное применение спецификатора friend 283
Перегруженные операторы 283
Объекты не решают всех проблем 284
И снова об избыточном наследовании 284
Недоступный код 284
Проблема чрезмерной сложности 285
Игнорирование устоявшихся интерфейсов 285
Множественное наследование 286
Разрастание кода 286
Проблема управления проектами 287
Нужные люди в нужное время 287
Между двух огней 288
Тестирование системы 288
Резюме: зыбучие пески ООП . 289
Часть IV. Сложные сетевые методики 291
Глава 15. Удаленные вызовы процедур (RPC) 292
Возвращаясь к модели OSI 293
Сравнение методик сетевого и процедурного программирования 294
Границы языков 294
Сохранение сеанса в активном состоянии 295
Связующие программные средства 297
Сетевые заглушки 298
Реализация сервисных функций 298
Реализация представительского уровня 299
Создание RPCкомпонентов с помощью утилиты rpcgen 300
Язык утилиты rpcgen 300
Создание простейшего интерфейса 300
Использование более сложных Хфайлов 302
Добавление записей и указателей 304
Учет состояния сеанса в открытых соединениях 305
Выявление проблем с состоянием сеанса 305
Хранение идентификаторов 305
Следование заданному маршруту 306
Восстановление после ошибок 306
Содержание 11
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Резюме: создание набора RPCкомпонентов 307
Глава 16. Безопасность сетевых приложений 308
Потребность в защите данных 309
Уровни идентификации 309
Формы взаимообмена 310
Проблема безопасности в Internet 310
Все является видимым 311
Виды атак 311
Незащищенность TCP/IP 312
Защита сетевого компьютера 313
Ограничение доступа 313
Брандмауэры 313
Демилитаризованные зоны 314
Защита канала 316
Шифрование сообщений 318
Виды шифрования 319
Опубликованные алгоритмы шифрования 319
Проблемы с шифрованием 319
Протокол SSL 320
Библиотека OpenSSL 320
Создание SSLклиента 321
Создание SSLсервера 323
Резюме: безопасный сервер 324
Глава 17. Широковещательная, групповая и магистральная передача сообщений 326
Широковещание в пределах домена 327
Пересмотр структуры IPадреса 327
Работа в широковещательном режиме 328
Ограничения широковещательного режима 329
Передача сообщения группе адресатов 329
Подключение к группе адресатов 330
Отправка многоадресного сообщения 332
Как реализуется многоадресный режим в сети 332
Глобальная многоадресная передача сообщений 333
Ограничения многоадресного режима ' 334
Резюме: совместное чтение сообщений 335
Глава 18. Неструктурированные сокеты 336
Когда необходимы неструктурированные сокеты ' 337
Протокол ICMP 337
Заполнение IPзаголовка 337
Ускоренная передача пакетов 338
Ограничения неструктурированных сокетов 338
Работа с неструктурированными сокетами 339
Выбор правильного протокола 339
Создание ICMPпакета 339
Вычисление контрольной суммы 340
Управление IPзаголовком • 341
Сторонний трафик 341
Как работает команда ping 341
Принимающий модуль программы MyPing 342
Отправляющий модуль программы MyPing 342
Как работают программы трассировки ' 343
Резюме: выбор неструктурированных сокетов 344
12 Содержание
www.books-shop.com
Глава 19. IPv6: следующее поколение протокола IP 345
Существующие проблемы адресации 346
Решение проблемы вырождающегося адресного пространства 346
Особенности стандарта IPv6 . 346
Как могут совместно работать IPv4 и IPv6? 348
Работало протоколу IPv6 348
Конфигурирование ядра 348
Конфигурирование программных средств 349
Преобразование вызовов IPv4 в вызовы IPv6 349
Преобразование неструктурированных сокетов в сокеты IPv6 351
Протокол ICMPv6 352
Новый протокол группового вещания 352
Достоинства и недостатки IPv6 354
Ожидаемая поддержка со стороны Linux 354
Резюме: подготовка программ к будущим изменениям 355
Часть V. Приложения 357
Приложение А. Информационные таблицы 358
Домены: первый параметр функции socket() 359
Типы: второй параметр функции socket() 363
Определения протоколов 364
Стандартные назначения Internetпортов (первые 100 портов) 365
Коды состояния HTTP 1.1 366
Параметры сокетов (функции get/setsockopt()) 368
Определения сигналов 372
Коды ICMP ' 374
Диапазоны групповых адресов IPv4 375
Предложенное распределение адресов IPv6 375
Koды lCMPv6 376
Поле области видимости в групповом адресе IPv6 377
Поле флагов в групповом адресе IPv6 378
Приложение Б. Сетевые функции . 379
Подключение к сети 380
socket() 380
bind() 381
listen() 382
accept() 383
connect() 384
socketpair() 385
Взаимодействие по каналу 386
send() 387
sendto() 388
sendmsg() 389
sendfile() 390
recv() 391
recvfrom() 393
recvmsg() 394
Разрыв соединения 395
shutdown() 395
Преобразование данных в сети 396
htons(), htonl() 396
ntohs(), ntohl() 397
Содержание 13
www.books-shop.com
inet_addr() 397
inet_aton() 398
inet_ntoa() 399
inet_pton() 399
inet_ntop() 400
Работа с сетевыми адресами 401
getpeername() 401
gethostname() 402
gethostbyname() 403
getprotobyname() 404
Управление сокетами 405
setsockopt() 405
getsockopt() 406
Приложение В. APIфункции ядра 407
Задания 408
fork() 408
_clone() 409
exec() 410
sched_yield() 412
wait(), waitpid() 413
Потоки 415
pthread_create() 415
pthread_ join() 416
pthread exit() 416
pthread_detach() 417
Блокировка 418
pthread_mutex_init(), pthread_mutex_destroy() 418
pthread_mutex_lock(), pthread_mutex_trylock() 419
pthread_mutex_unlock() ~ 420
Сигналы 421
signal() 421
sigaction() 422
sigprocmask() 423
Работа с файлами 424
bzero(), memset() 424
fcntl() 425
pipe() 427
poll() 427
read() 429
select() 430
write() 432
close() 433
Приложение Г. Вспомогательные классы 434
Исключения C++ 435
Exception (надкласс) 435
NetException (класс) 435
Служебные классы C++ 436
SimpleString (класс) 436
HostAddress (класс) 436
Классы сообщений C++ 437
Message (абстрактный класс) 437
TextMessage (класс) 437
Классы сокетов C++ 438
14 Содержание
www.books-shop.com
Socket(надкласс) 438
SocketStream (класс) 440
SocketServer (класс) 440
SocketClient (класс) 440
Datagram (класс) 441
Broadcast (класс) 441
MessageGroup (класс) 442
Исключения Java 442
java.io.lOException (класс) 442
java.net.SocketException (класс) 443
Служебные классы Java 443
java.net.DatagramPacket (класс) 443
java.net.lnetAddress (класс) 444
Классы вводавывода Java 444
java.io.lnputStream (абстрактный класс) 445
java.io.ByteArraylnputStream (класс) 445
java.io.ObjectlnputStream (класс) 445
java.io.OutputStream (абстрактный класс) 446
java.io.ByteArrayOutputStream (класс) 447
java.io.ObjectOutputStream (класс) 447
java.io.BufferedReader (класс) 448
java.io.PrintWriter (класс) 449
Классы сокетов Java 450
java.netSocket (класс) 450
java.net.ServerSocket (класс) 451
java.net.DatagramSocket (класс) 452
java.net.MulticastSocket (класс) 453
Предметный указатель 454
Содержание 15
www.books-shop.com
www.books-shop.com
Об авторе
Шон Уолтон получил степень магистра компьютерных наук в 1990 г. в универ
ситете Бригема Янга, штат Юта, специализируясь в области теории языков про
ванием BSD
сокетов. Впоследствии, будучи сотрудником компании Hewlett
Packard,
он разработал средство автоматического распознавания языка (PostScript и PCL),
применяемое в принтерах LaserJet 4 и более поздних моделей. Шон также создал
микрооперационную систему реального времени для микроконтроллера 8052, ко
рованием сокетов в Java и позднее включил этот предмет в свой курс лекций по
Java. В настоящее время он сотрудничает с компанией Nationwide Enterprise и парал
www.books-shop.com
Благодарности
Эта книга не появилась бы на свет без помощи нескольких людей. В первую
очередь, это моя любимая жена Сьюзан, которая служила мне музой и источни
ком вдохновения. Во
вторых, это мой отец Уэндел, который пытался учитгь меня
организованности и правильному изложению мыслей. В
третьих, это само сооб
ную систему, давшую мне материал для работы. Наконец, я сам не понимал, на
сколько эта работа важна для меня в профессиональном плане, пока мои друзья
Чарльз Кнутсон и Майк Хольстейн не дали ей свою высокую оценку.
www.books-shop.com
Введение
вание, несомненно, обогатит ваш опыт, так как вам придется координировать ра
шинстве других операционных систем) является сокет. Так же, как функции фай
лового ввода
вывода определяют интерфейс взаимодействия с файловой систе
мой, сокет соединяет программу с сетью. С его помощью она посылает и прини
мает сообщения.
Создавать сетевые приложения труднее, чем даже заниматься многозадачным
программированием, так как круг возникающих проблем здесь гораздо шире. Не
блем. Она содержит как ответы на конкретные вопросы, так и описание общих
стратегий построения профессиональных сетевых программ.
Структура книги
Книга состоит из пяти частей, каждая из которых связана с предыдущим ма
териалом.
• Часть I, "Создание сетевых клиентских приложений"
Представлены вводные сведения о сокетах, определены основные тер
Введение 19
www.books-shop.com
• Часть V,"Приложения"
В приложения вынесен справочный материал, касающийся сокетов. В
приложение А включены таблицы, которые слишком велики для основ
Предпочтения профессионалов
Данная книга предназначена для профессиональных программистов. Обычно
профессионалам нужно одно из трех: либо изучить новую технологию, либо ре
шить конкретную задачу, либо найти нужные примеры или определения. Главы и
приложения книги построены с учетом этих потребностей.
• Последовательное чтение от начала до конца. В любом месте книги рас
крывается какая
то одна методика. Каждый раздел и глава основаны на
предыдущем материале. Вся книга организована по принципу нарастания
сложности. Программирование сокетов может показаться поначалу до
му. Читатель может пропускать разделы до тех пор, пока не найдет то,
что ему нужно.
• Справочный материал. Экспертам, как правило, требуется быстро найти
то, что им нужно (таблицы, фрагменты программ, описания API
дакторе);
• как скомпилировать полученный файл;
• как запустить скомпилированную программу.
Некоторые примеры требуют модификации ядра (для режимов широковеща
20 Введение
www.books-shop.com
уметь создавать и компилировать исходные тексты на языках С, C++ и
Java;
иметь в наличии все необходимые компиляторы;
уметь конфигурировать ядро;
уметь компилировать и инсталлировать новое ядро.
Введение 21
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
www.books-shop.com
Создание сетевых
клиентских прило
жений
В этой части...
Глава 1. Простейший сетевой клиент
Глава 2. Основы TCP/IP
Глава 3. Различные типы Internetпакетов
Глава 4. Передача сообщений между одноранговы
ми компьютерами
Глава 5. Многоуровневая сетевая модель
www.books-shop.com
Глава
Простейший сетевой
клиент
1
В этой главе...
Связь с окружающим миром посредством сокетов 27
Правила общения: основы адресации в TCP/IP 28
Прослушивание сервера: простейший алгоритм
клиентской программы 29
Резюме: что происходит за кулисами 40
www.books-shop.com
В темной комнате с плотно затянутыми шторами слабо мерцает экран
компьютера. На полу валяются пустые банки из!под пива. На экран устав!
шими и потускневшими после нескольких бессонных ночей глазами смотрит
программист.
— Черт бы побрал этот CMOS! Опять батарея полетела! Который час?
Так, звоним в справочную.
— Восемь часов двадцать три минуты и сорок секунд.
— Утра или вечера? Мне что, встать и выглянуть в окно?!
кие
нибудь ресурсы или само предоставляет их. В некоторых соединениях для
получения информации не требуется двустороннее взаимодействие. Подобно
описанному выше звонку в справочную, сетевой клиент в простейшем случае
просто подключается к серверу и принимает от него данные.
Какие ресурсы, или сервисы, могут предоставляться сервером? Их множество,
но все они делятся на четыре категории:
• общие — дисковые ресурсы;
• ограниченные — принтеры, модемы, дисковые массивы;
• совместно используемые — базы данных, программные проекты, доку
ментация;
• делегируемые — удаленные программы, распределенные запросы.
В этой главе последовательно рассказывается о том, как написать простейшее
клиентское приложение, подключающееся к некоторому серверу. Читатель узна
ет, что собой представляет процесс написания сетевых программ. Наш клиент
первоначально будет обращаться к серверу для того, чтобы определить правиль
www.books-shop.com
Выполнение примеров, представленных в книге
Большинство программ, приводимых в книге, можно выполнять, не имея подключения к сети,
при условий, что сетевые функции:ядра сконфигурованы и демон inetd запущен. В этих про
граммах используется локальный сетевой адрес 127.0.0.1 (тaк называемый адрес обратной
связи). Даже если сетевые драйверы отсутствуют, дистрибутивы Linux содержат все необходи
мые средства для организации, сетевого взаимодействия с использованием адреса Обратной
связи.
Клиентская программа должна предпринять несколько действий для установ
чему нельзя все упростить?" Дело в том, что на каждом из этапов программа мо
изводительности").
www.books-shop.com
3. Привязка к определенному адресу/порту (необязательно). Задание кон
кретного IP
адреса, а также выбор порта. Если пропустить этот этап,
операционная система разрешит связь с любым IP
адресом и назначит
произвольный номер порта (подробно об этом — в главе 2, "Основы
TCP/IP").
4. Подключение к одноранговому компьютеру/серверу (необязательно).
Организация двунаправленного канала связи между клиентской и дру
гой сетевой программой. Если пропустить этот этап, будет создан канал
адресной передачи сообщений без установления соединения.
5. Частичный разрыв соединения (необязательно). Выбор одного из двух
режимов работы: прием или передача. Этот этап можно выполнить, ес
посредством сокетов
Несколько лет назад под сетью подразумевался последовательный канал связи
между двумя компьютерами. Все компьютеры общались между собой по разным
каналам, а для передачи файлов в UNIX применялась система UUCP (UNIX
to
нал. При этом могут возникать различного рода проблемы, например взаимобло
www.books-shop.com
ложений большинство таких проблем вполне можно избежать. О работе в много
в TCP/IP
В сетях применяется множество различных протоколов. Программисты при
нить, сколько раз нам говорили о том, что функции oреn() (возвращает деск
www.books-shop.com
IPX, Rose) осуществляется с помощью единственной функции socket(). Она
скрывает в себе все детали реализации.
Любой передаваемый пакет содержит в себе данные, адреса отправителя и по
рода или страны, номер порта добавляется к адресу компьютера. Портов бывает
множество, и они не являются физическими сущностями — это абстракции, су
www.books-shop.com
Когда соединение будет установлено, программа получит приветственное сооб
www.books-shop.com
Окончание табл. 1.1
SOCK_RDM Протокол пакетной передачи данных с подтверждением доставки (еще
не реализован в большинстве операционных систем)
SOCK_RDM Протокол пакетной передачи данных с подтверждением доставки (еще
не реализован в большинстве операционных систем)
SOCK_DGRAM Протокол пакетной передачи данных без подтверждения доставки
(UDP User Datagram Protocol)
SOCK_RAW Протокол пакетной передачи низкоуровневых данных без подтвержде
ния доставки
protocol Представляет собой 32разрядное целое число с сетевым порядком •
следования байтов (подробно об этом — в главе 2, "Основы TCP/IP"). В
большинстве типов соединений допускается единственное значение
данного параметра: 0 (нуль), а в соединениях типа SOCK_RAW параметр
должен принимать значения от 0 до 255
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
#include <sys/socket.h> /* содержит прототипы функций */
#include <sys/types.h> /* содержит объявления стандартных
системных типов данных */
#include <resolv.h> /* содержит объявления дополнительных
типов данных */
В файле sys/socket.h находятся объявления функций библиотеки Socket API
(включая функцию socket(), естественно). В файле sys/types.h определены
многие типы данные, используемые при работе с сокетами.
Подключение к серверу
После создания сокета необходимо подключиться к серверу. Эту задачу вы
фонного номера, у IP
адреса есть определенный формат.
• Соединение, телефонное или сетевое, представляет собой канал переда
www.books-shop.com
необходимо назначить ей канал (или порт) и сообщить о нем своим
клиентам.
Синтаксис функции connect () таков:
#include <sys/socket.h>
iinclude <resolv.h>
int connect(int sd, struct sockaddr *server, int addr_len);
Первый параметр (sd) представляет собой дескриптор сокета, который был создан
функцией socket(). Последний, третий, параметр задает длину структуры
sockaddr, передаваемой во втором параметре, так как она может иметь разный
тип и размер. Это самый важный момент, делающий функцию socket() принци
www.books-shop.com
ряет первое поле. Следует также отметить, что это единственное поле с серверным
порядком следования байтов (подробно об этом — в главе 2, "Основы TCP/IP").
Поля
заполнители (sa_data и _pad) используются во многих структурах. По су
ный массив заполняется нулями, его размер не имеет значения (в случае структу
тии, что эти поля будут поддерживаться в другой системе. В любом случае доста
санные поля. В листинге 1.2 показано, как это сделать (полный текст примера
имеется на Web
узле). Вообще говоря, в Linux не требуется приводить структуру
sockaddr_in к типу sockaddr. Если же предполагается использовать программу в
разных системах, можно легко добавить операцию приведения типа.
Приведение к типу sockaddr
В UNIXсистемах любую структуру данного семейства можно привести к типу sockaddr. Это по
зволит избежать получения предупреждений компилятора. В приводимых примерах данная опе
рация не используется только потому, что это делает, примеры немного понятнее (да и Linux это
го не требует).
Листинг 1.2. Использование функции connect ()
www.books-shop.com
dest.sin_port = htons ( PORT_TIME ) ; /* выбираем порт */
inet_aton(host, &dest.sin_addr) ; /* задаем адрес */
if ( connect (sd, &dest, sizeof (dest)) != 0 ) /* подключаемся! */
{
perror ( " socket connection " ) ;
abort();
ром ввода
вывода, доступным обеим программам. Большинство серверов ориен
ции read ( ) :
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
Эта функция должна быть вам знакома. Вы много раз применяли ее при рабо
www.books-shop.com
bytes_read = read(sd, buffer, MAXBUF); /* чтение данных .*/
if ( bytes_read < 0 )
/* сообщить об ошибках; завершить работу */
www.books-shop.com
Возвращаясь к функции read(), отметим, что чаще всего в результате ее вы
вторно.
• EBADF. Указан неверный дескриптор файла, либо файл не был открыт для
чтения. Эта ошибка может возникнуть, если вызов функции socket ()
завершился неуспешно или же программа закрыла входной поток (канал
доступен только для записи).
• EINVAL. Указанный дескриптор связан с объектом, чтение из которого
невозможно.
Функция read() не имеет информации о том, как работает сокет. В Linux есть
другая функция, recv(), которая наряду с чтением данных позволяет контролиро
жения (флаг1 | флаг2 | ...). Обычно последний параметр задается равным нулю.
Читатели могут поинтересоваться, для чего в таком случае вообще вызывать
функцию recv()? He проще ли вызвать функцию read()? Мое мнение таково:
лучше применять функцию recv() — это может помочь вам, если впоследствии
работа программы усложнится. Да и, вообще говоря, всегда следует придержи
ваться какого
то одного стиля.
Ниже перечислены полезные флаги, с помощью которых можно управлять
работой сокета. Полный их список представлен в приложении Б, "Сетевые
функции".
• MSG_OOB, Обработка внеполосных данных. Применяется для сообщений с
повышенным приоритетом. Некоторые протоколы позволяют выбирать,
с каким приоритетом следует послать сообщение: обычным или высо
www.books-shop.com
также задать в свойствах самого сокета. Обычно, если данные в очереди
отсутствуют, диспетчер очереди ждет до тех пор, пока они не поступят.
А когда этот флаг установлен, функция, запрашивающая данные, немед
вывода.)
Получение фрагментированныхпакетов
Программа может работать гораздо быстрее чем сеть. Иногда пакет приходят по частям, по
тому что маршрутизаторы фрагментируют их для ускорения передачи по медленным сетям. Если
в подобной ситуации вызвать функцию recv(), будет прочитано неполное сообщение. Вот по
чему даже при наличии флага MSG_PEEK функция recv() при последовательных вызовах может
возвращать разные данные: например, сначала 500 байтов, а затем 750. Для решения подобных
проблем предназначен флаг MSG_WAITALL.
Функция recv() является более гибкой, чем read(). Ниже показано, как про
влекается из нее.
Во всех трех фрагментах есть одно преднамеренное упущение. Что если сервер
пошлет больше информации, чем может вместить буфер? В действительности ни
www.books-shop.com
Вообще говоря, функция read() тоже может вернуть эти коды, поскольку на
самом деле она проверяет, какой дескриптор ей передан, и если это дескриптор
сокета, она просто вызывает функцию recv().
Разрыв соединения
Информация от сервера получена, сеанс прошел нормально — настало время
прекращать связь. Опять
таки, есть два способа сделать это. В большинстве про
ется значение 0.
www.books-shop.com
Резюме: что происходит за кулисами
Когда программа создает сокет и подключается к TCP
серверу, происходит
целый ряд действий. Сам по себе сокет организует лишь очередь сообщений. Ос
модействие.
Для организации соединения требуется знать язык и правила сетевого обще
www.books-shop.com
Основы TCP/IP Глава
2
В этой главе...
IPадресация 43
Номера IPпортов 49
Порядок следования байтов 51
Различные виды пакетов 57
Именованные каналы в UNIX 57
Резюме: IPадреса и средства преобразования
данных 58
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Работа в Internet требует знания принципов адресации. Адрес — неотъемлемая
часть сообщения. Подобно телефону, компьютер должен иметь идентификацион
ный номер, или адрес, чтобы другие компьютеры могли направлять ему данные.
В предыдущей главе рассказывалось о том, что такое сокет. В этой главе мы
продолжим знакомство с библиотекой Socket API и узнаем о том, что такое сис
тема IP
адресации, как происходит маршрутизация, какие бывают форматы пере
IPадресация
В TCP/IP адрес имеет фиксированную длину. Данное ограничение потребова
ционирования. В свое время протокол позволял решать целый ряд задач, таких
как идентификация компьютера, маршрутизация и преобразование адресов. Се
Идентификация компьютера
В сети происходит совместное использование одного ресурса (сетевого кабеля)
несколькими компьютерами. Все компьютеры должны быть способны принимать
данные, передаваемые по сети. Но если бы каждый компьютер идентифициро
вался именем Bob или Sasha, невозможно было бы определить, кому из них сле
Конфликты адресов
Рабочая станция не может правильно функционировать в сети, если она делит с кемто свой ад
рес (имеет место конфликт адресов). Любой; кто пытался обнаружить данную коллизию, скажет
вам, что это не так просто. Ситуация становится еще хуже, если на одном из компьютеров при
меняется протокол динамического назначения адреса (например, DHCP). Наиболее очевидный (и
самый трудоемкий) способ решения проблемы — просмотреть свойства каждого компьютера в
данном сетевом сегменте.
Сети — это динамичные структуры, имеющие тенденцию усложняться со вре
нет ненужной.
Компьютер, подключенный к сети, уже имеет уникальный идентификатор, на
зываемый МАС
адресом (Media Access Control — протокол управления доступом
к среде). В качестве примера можно привести идентификатор платы Ethernet.
www.books-shop.com
Данный идентификатор применяется в процессе сетевой загрузки бездисковых
станций, все постоянные ресурсы которых расположены на сервере. Идентифика
адресом. Основное из них заключается в том, что адрес может меняться (он не
зафиксирован жестко). Благодаря этому появляется возможность вводить класте
Протокол ARP
В ARP используется простая таблица преобразования IPадреса в МАСадрес. Все сетевые со
общения должны нести в себе МАСадрес, чтобы сетевой адаптер или драйвер могли легко об
наруживать нужные сообщения. Но отправитель может не знать МАСадрес получателя, далеко
спрятанный за несколькими маршрутизаторами. Поэтому ближайший маршрутизатор просто по
сылает сообщение маршрутизаторам, управляющим работой подсети, которая указана в IP
адресе. Когда сообщение доходит до маршрутизатора, использующего протокол ARP, проверя
ется адресная таблица. Если, найдено совпадение, МАСадрес пакета определяет пункт назначе
ния. В противном случае маршрутизатор посылает широковещательное сообщение компьютерам
подсети, чтобы определить, кому принадлежит IРадрес.
www.books-shop.com
IP
адрес представляет собой нечто вроде дорожной карты для маршрутизатора.
Каждый элемент адреса уточняет пункт назначения. Первым элементом, как уже
было сказано, является класс сети, последним — номер компьютера в сети. По
ределения класса сети проверяются первые биты адреса. Например, в сетях клас
са А первый бит адреса равен 0. В сетях класса В первый бит равен 1, а следую
щий за ним — 0.
• Класс А: 0 (00000000)
126 (01111110)
• Класс В: 128 (10000000) — 191 (10111111)
• Класс С: 192 (11000000)
223 (11011111)
• Класс D: 224 (11100000) — 239 (11101111)
• Класс Е: 240 (11110000)
255 (11111111)
С момента возникновения Internet маршрутизаторы руководствовались такой
схемой для быстрой (оценивалось всего 4 бита) проверки того, нужно ли пропус
www.books-shop.com
тивно благодаря протоколу CIDR (Classless Internet Domain Routing — бесклассо
ро определить, что нужно делать: блокировать пакет или передать его интерфейс
ряются первые три цифры, указывающие регион или штат, а затем — следующие
три цифры, определяющие телефонный аппарат.
Маски подсети
В некоторых классах сетей требуется дополнительная фильтрация адресов.
Сеть, состоящая из 16 миллионов узлов, будет чересчур громоздкой, если все уз
гается, что она может увеличиться не более чем на пять машин, задайте для мар
ший байт попадает в диапазон ll0lxxxx, где ХХХХ— это сегмент адресов актив!
ной подсети. Теперь можно назначать адреса: 187.35.209.208 (маршрутизатор),
187.35.209.209 (компьютер № 1) и т.д. до 187.35.209.222. Старшие биты послед
него байта маски не имеют значения, так как начало диапазона адресов опреде
www.books-shop.com
В большинстве случаев нет необходимости заниматься настройкой маршрути
www.books-shop.com
Первая группа адресов обозначает "текущую" сеть. Например, если адрес компь
Инфляция адресов
В одной маленькой компании, где я работал, было зарезервировано 128 адресов. Из них в то
время использовалось только 30. Лично для меня выделили 10 адресов, но мне так никогда и не
понадобилось больше двух из них. Я полагаю, что эти адреса до сих пор зарезервированы за
мной, хотя я покинул компанию более четырех лет назад.
В итоге специальные и неиспользуемые адреса поглотили почти все адресное
пространство. Недавние исследования показали, что в сетях класса В и С почти
не осталось свободных адресов. Провайдеры Internet задействовали почти все ад
рованные адреса.
Напомню, что компьютер, подключенный к сети, в процессе загрузки запус
ние, чтобы найти сервер DHCP. Будучи найденным, сервер выделяет адрес из
своего пула доступных адресов и посылает его (вместе с соответствующей маской
подсети) запрашивающему модулю.
Модуль принимает данные и конфигурирует сетевые протоколы локального
компьютера. Иногда из
за большой загруженности сети это может занять не
www.books-shop.com
IPамнезия
Амнезия адреса происходит, когда маска подсети и адрес компьютера не заданы должным обра
зом. Это может оказаться серьезной проблемой. Даже команда ping 127.0.0.1 не будет рабо
тать. Если компьютер проявляет признаки амнезии, исправьте системные файлы. В Red Hat Linux
(и родственных системах) требуемые параметры устанавливаются в файлах /etc/sysconfig/
network и /etc/sysconfig/networkscripts/ifcfg*.
Другое решение заключается в увеличении длины самого IP
адреса. Здесь
то
на арену и выходит стандарт IPv6 [RFC2460]. В нем применяются адреса четы
38
чении адресного пространства. Наличие 3х10 адресов надолго устраняет сущест
Номера IPпортов
Все сообщения посылаются по тому или иному заданному адресу. Программа,
принимающая сообщения, может в действительности получать сообщения, адре
www.books-shop.com
Со всеми стандартными сервисами связаны номера портов. Полный их список
содержится в файле /etc/services (обратитесь к приложению А, "Информацион
Привилегированные порты
Система безопасности ядра сконфигурирована таким образом, что для доступа к портам с номера
ми, меньшими 1024, необходимо иметь права пользователя root. Например, если требуется запус
тить сервер времени (порт 13), это должен сделать пользователь root или принадлежащая ему
программа, для которой установлен бит SUID. Данная аббревиатура расшифровывается как "set
user ID" — смена идентификатора пользователя. Программа с установленным .битом SUID всегда
будет выполняться так, будто ее запустил владелец, даже если на самом деле это был другой поль
зователь. Например, общедоступная утилита /usr/bin/at (пользовательский планировщик зада
ний) должна обращаться к системным cronтаблицам, принадлежащим пользователю root. При
этом она автоматически переключается в режим суперпользователя. Подробнее о данной возмож
ности рм. в документации к системе UNIX. Будьте осторожны: бит SUID потенциально несет в себе
угрозу безопасности системы. Если вы являетесь суперпользователем, устанавливайте данный бит
только дпя тех программ, о которых известно, что они совершенно безопасны.
www.books-shop.com
Как отмечалось ранее, если с сокетом не был связан порт, операционная сис
Безопасная отладка
Экспериментируя с различными портами, следуйте простым правилам:
* создавайте программу, запускайте ее на выполнение и отлаживайте как обычный пользователь (не
пользователь root);
* используйте безопасные номера портов и адреса::
* ведите журнал всех поступающих сообщений.
Обратите внимание на то, что старший байт (ОС) указан первым. Процессор с
прямым порядком байтов хранит число по
другому:
Адрес: 00 01 02 03 04
Данные: ВЗ 57 С5 DC ...
полняющих поля структур семейства sockaddr. Тут есть одна тонкость: не все по
www.books-shop.com
ставлены в серверном порядке. Рассмотрим фрагмент программы из главы 1,
"Простейший сетевой клиент":
/**************************************************************/
/*** Пример преобразования порядка следования байтов: ***/
/*** заполнение структуры sockaddr_in. ***/
dest.sin_family = AF_INET;
dest.sin_port = htons(13); /* порт 13 (сервер времени) */
if (inet_aton(dest_addr, &dest.sin_addr) == 1) {
Здесь заполняются три поля: sin_family, sin_port и sin_addr. Первое имеет сер
Функция Описание
inet_aton() Преобразует адрес из точечной записи (###.###.###.###) в двоичную форму с сетевым
порядком следования байтов; возвращает нуль в случае неудачи и ненулевое значение,
если адрес допустимый
inet_addr() Устарела (аналог inet_aton()), так как неправильно обрабатывает ошибки; при возник
новении ошибки возвращает 1 (хотя 255.255.255.255 — обычный широковещательный
адрес)
inet_ntoa() Преобразует IPадрес, представленный в двоичном виде с сетевым порядком следования
байтов, в точечную запись формата ASCII
gethost Просит сервер имен преобразовать имя (такое как www.linux.org) в один или несколько
byname () IPадресов
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
В библиотеках могут содержаться дополнительные функции преобразования. В
данной книге рассматриваются только те из них, которые часто используются.
Дополнительная информация об этих функциях содержится в приложении Б,
"Сетевые функции".
www.books-shop.com
int sd, bytes_written = 0, retval;
sd = socket(PF_INET, SOCK_STREAM, 0);
нять цикл while для передачи всех данных. Хотя мы и имеем дело с сокетом, нет
гарантии, что программа сможет послать данные за один прием. Во втором случае
подобного ограничения нет, поскольку с дескриптором типа FILE* связана от
ту сокета.
• MSG_OOB. Режим внеполосной передачи. Позволяет послать серверу или
другому сетевому компьютеру один байт, обозначающий срочное сооб
www.books-shop.com
прямую. Если адресат недостижим, будет получен код ошибки
ENETUNREACH (сеть недоступна). Подобная опция применяется только в
программах диагностики и маршрутизации.
• MSG_DONTWAIT. He ждать завершения функции send(). Это позволяет про
чает сигнал SIGPIPE. Если программа не готова его обработать, она будет
завершена.
Флаги можно объединять с помощью операции побитового сложения. Вот ти
ные позднее.
• EPIPE. Противоположный конец сокета был закрыт. Программа также
получит сигнал SIGPIPE, если только не установлен флаг MSG_NOSIGNAL.
Функция send() обычно применяется для передачи серверу служебной инфор
крывает порт 79, посылает имя пользователя и ждет ответа. При подобном алго
www.books-shop.com
фрагменте программы демонстрируется, как разрешить такого рода проблему
(полный текст программы имеется на Web
узле):
/***************************************************************/
/*** Расширение клиентской программы: добавлена возможность ****/
/*** доступа к любому порту и отправки сообщения. ****/
нии сокетов. Иногда можно быть уверенным в том, что сервер закроет соедине
www.books-shop.com
Различные виды пакетов
значений и соглашений, но все они используют библиотеку Socket API, что сущест
темный журнальный файл. Все необходимые для этого функции имеются в биб
жать строку длиной до 104 байтов (включая завершающий символ NULL). Далее к
www.books-shop.com
сокету можно применять стандартные библиотечные функции. То же самое спра
странства.
В TCP/IP существует также понятие порта. Большинство клиентских прило
www.books-shop.com
Глава
Различные типы
3 Internet
пакетов
В этой главе:
Базовый сетевой пакет 60
Анализ различных типов пакетов 65
Взаимосвязь между протоколами 77
Анализ сетевого трафика с помощью утилиты
tcpdump 77
Создание сетевого анализатора 79
Резюме: выбор правильного пакета 79
www.books-shop.com
В физической сети поддерживается несколько типов логических сетевых архи
тектур, таких как сети Novell (IPX), Microsoft (NetBEUI), AppleTalk и, конечно
же, TCP/IP. Но во всех архитектурах данные передаются в виде пакета, который
в общем случае состоит из идентификаторов отправителя и получателя, а также
собственно данных. У каждой архитектуры есть свой набор функций и стек про
ную структуру, но одна черта у них общая: они передают данные, посланные
программой. Некоторые протоколы включают в сообщение адрес отправителя,
другие требуют указания адреса получателя. Последнее кажется совершенно оче
www.books-shop.com
Замещение пакетов
Автономность пакетов имеет также отрицательную стророну. Поскольку пакет может быть послан
откуда угодно куда угодно, злобные хакеры способны обмануть сетевое обеспечение. В сети не
требуется проверять достоверность адреса отправителя. Выполнить замещение аппаратного ад
реса (заменить истинного отправителя другим) трудно, нo возможно. Необходимо отметить, что
в последних версиях ядра Linux замещение адресов запрещено.
Как рассказывалось в главе 2, "Основы TCP/IP", пакеты имеют сетевой
(обратный) порядок следования байтов. Памятуя об этом, давайте рассмотрим
программную (листинг 3.1) и физическую (рис. 3.2) структуры пакета.
12
16
www.books-shop.com
uint time_to_live:8; /* число переходов через маршрутизатор */
uint protocol:8; /* протокол: ICMP, UDP, TCP */
uint hdr_chksum:16; /* контрольная сумма заголовка */
uint IPv4_source:32; /* IP
адрес отправителя */
uint IPv4_dest:32; /* IP
адрес получателя */
uchar options[]; /* до 40 байтов служебных данных */
uchar data[]; /* до 64 Кбайт данных */
}
Заметьте, что пакет содержит не только те базовые поля, о которых говори
общает о том, что сообщение нельзя разбивать на блоки: оно должно быть пере
Поле version
В первом поле передается номер версии используемого протокола IP. Боль
рованный сокет плюс указываете на то, что будете заполнять заголовок самостоя
тельно (для этого служит параметр сокета IP_HDRINCL). Но даже в этом случае в
поле можно указать значение 0. Нулевой флаг информирует ядро о том, что в
данное поле следует подставить нужное значение.
Поле header_len
В данном поле указывается длина заголовка в виде количества двухбайтовых
слов. Наибольшее допустимое значение — 15 (60 байтов). Опять
таки, единст
венная ситуация, при которой необходимо заполнять это поле, — когда создается
неструктурированный сокет и установлена опция IP_HDRINCL. Поскольку все IP
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
заголовки содержат не менее 20 байтов, минимальное значение данного поля
равно 5 (20/4).
Поле serve__type
Данное поле указывает на то, как следует обрабатывать пакет. В нем выделя
Поле ID
IP
подсистема присваивает каждому пакету уникальный идентификатор. По
скольку данное поле занимает всего 16 разрядов, несложно понять, что иденти
Пользовательские идентификаторы
При самостоятельном создании идентификаторов обязательно помните: ваша программа не
единственная, кто может посылать сообщения. IPподсистема отслеживает идентификаторы всех
проходящих пакетов. Будьте предельно внимательны при выборе идентификаторов, чтобы све
сти к минимуму риск совпадений.
www.books-shop.com
Флаг dont_frag сообщает о том, что маршрутизатор или сервер не должны раз
бивать пакет. Если этот флаг установлен, а пакет слишком велик, чтобы пройти
через сетевой сегмент, маршрутизатор удалит пакет и вернет ICMP
пакет с сооб
щением об ошибке.
Флаг more_frags сообщает получателю о том, что следует ожидать дополни
ние поля умножается на 8 при вычислении реального смещения. Это также озна
чает, что размер каждого фрагмента (кроме последнего) должен быть кратен 8.
Но не беспокойтесь по поводу различных технических подробностей: IP
кет должен существовать при передаче по сети. Впоследствии в нем стали указы
вать число переходов. Переходом называется этап ретрансляции пакета при пере
даче через компьютер или маршрутизатор (узел сети), когда пакет удаляется из
одной сети и попадает в другую.
Поле time_to_live занимает 8 разрядов, что позволяет ретранслировать пакет
255 раз, прежде чем он будет удален. Когда маршрутизатор или ретранслирующий
сервер получает пакет, он уменьшает значение данного поля на единицу. Если
поле станет равным нулю до того, как пакет достигнет адресата, узел сети удалит
пакет и пошлет отправителю сообщение об ошибке. Подобная схема предотвра
Поле protocol
Каждый пакет в сети Internet предается по тому или иному протоколу: ICMP
(IPPROTO_ICMP, или 1), UDP (IPPROTO_UDP, или 17) или TCP (IPPROTO_TCP, или 6).
Данное поле сообщает системе о том, как следует интерпретировать пакет. Этот
параметр можно задать вручную, если при вызове функции socket() указать кон
www.books-shop.com
Поле options
IP
подсистема может задавать в пакете различные параметры. Это может быть
информация для маршрутизатора, метка времени, атрибуты безопасности, преду
Поле data
В этом поле передается собственно сообщение, занимающее до 65535 байтов
(минус 60 байтов — максимальный размер заголовка). В разделе данных могут
передаваться заголовки протоколов более высокого уровня. Например, для ICMP
требуется 4 байта, для UDP — 8, а для TCP — 20
60.
Описанную структуру имеют все пакеты IPv4. Протоколы верхних уровней до
мать буквально. Если надежность отмечена как низкая, то это не означает, что
данный протокол не гарантирует доставку сообщений. На основании приведен
www.books-shop.com
Характеристики пакетов
Передача данных по каждому из протоколов связана с определенными особен
нять, почему тот или иной протокол одни возможности реализует, а другие — нет.
Размер сообщения
Чтобы вычислить пропускную способность сети, необходимо знать размер па
Надежность
Одной из проблем, возникающих в сети, является потеря данных. Сообщение
может повредиться или потеряться при переходе от одного компьютера или мар
Тип сообщения
Некоторые сообщения являются самодостаточными и не зависят от других со
кументы, двоичные файлы и т.д. Тип сообщения определяет, в каком виде лучше
всего отправлять данные по тому или иному протоколу.
www.books-shop.com
Протокол HTTP
В протоколе HTTP 1.0 данные вполне могли бы передаваться в виде UDP, а не TCPпакетов.
Клиент просто посылает серверу запрос на Конкретный документ, а сервер в ответ высылает
файл. По сути, никакого взаимодействия между клиентом и сервером не происходит.
Пропускная способность
Наиболее важным аспектом передачи данных является пропускная способ
ность сети. Чем она выше, тем спокойнее работается пользователям. Ее необхо
кунду еще ничего не означает, это только часть уравнения: данный показатель
указывает лишь на то, как могла бы работать сеть в идеальных условиях.
Пропускная способность определяет, сколько данных реально может быть пе
собность и наоборот.
Целостность данных
Современные сетевые технологии предусматривают множество способов обес
дем примеры.
• Ошибки недопустимы — жизненно важные данные. Все, что может быть
связано со здоровьем/жизнью: сигналы от медицинского оборудования,
команды ракетных установок и т.п.
• Критические данные — особо важные, высоконадежные данные, кото
www.books-shop.com
• Данные с потерями — данные, которые могут быть частично потеряны
без заметного снижения полезности. К таковым относятся аудио
и ви
Фрагментация
Большие сообщения, передаваемые в медленных сетях, могут тормозить рабо
кола IP, этот процесс может быть прозрачным для протоколов более высокого
уровня. Но в некоторых ситуациях пакет должен поступать целиком. Это особен
Типы пакетов
В следующих разделах рассмотрен каждый из типов пакетов с описанием
структуры заголовка, если таковая имеется.
Неструктурированные данные
Неструктурированные данные напрямую записываются в IP
пакет. Это может
быть полезно при работе со специальными или пользовательскими протоколами.
Атрибуты данного пакета перечислены в табл. 3.3.
www.books-shop.com
Создать IP
пакет можно самостоятельно, предварительно указав при вызове
функции socket () константу SOCK_RAW. По соображениям безопасности пользова
мировать IP
пакет. Можно сконфигурировать сокет двумя способами: для пере
дачи только данных или данных плюс заголовок. В первом случае передача будет
организована по типу протокола UDP, но только не будут поддерживаться порты.
Во втором случае необходимо напрямую заполнять поля заголовка пакета.
У пакетов данного типа есть как преимущества, так и недостатки. К недостат
кам можно отнести то, что доставка сообщений не гарантируется. Но зато сооб
Протокол ICMP
ICMP (Internet Control Message Protocol — протокол управляющих сообщений
в сети Internet) является надстройкой над протоколом IP. Он используется всеми
компьютерами, подключенными к Internet (клиентами, серверами, маршрутиза
www.books-shop.com
Листинг 3.2. Определение структуры ICMРпакета
*/
/*** Определение структуры ICMPпакета. ***/
/*** Формальное определение находится в ***/
/*** файле netinet/ip_icmp.h. ***/
struct ICMP_header {
ui8 type; /* тип ошибки */
ui8 code; /* код ошибки */
ui16 checksum; /* контрольная сумма сообщения */
uchar msg[]; /* дополнительное описание ошибки */
никновение ошибки.
Протокол UDP
UDP (User Datagram Protocol — протокол передачи дейтаграмм пользователя)
применяется для передачи данных без установления соединения (независимые
сообщения). Он позволяет повторно использовать один и тот же сокет для от
правки данных по другому адресу. Атрибуты пакета UDP перечислены в табл. 3.5.
www.books-shop.com
ки ошибок и передачи сообщений ядром системы. Кроме того, он обрабатывает
фрагментированные сообщения.
Сообщение, отправляемое по протоколу UDP, напоминает электронное пись
www.books-shop.com
Листинг 3.3. Определение структуры UDPпакета
struct UDP_header {
ui16 src_port; /* номер порта отправителя */
ui16 dst_port; /* номер порта получателя */
ui16 length; /* длина сообщения */
ui16 checksum; /* контрольная сумма сообщения */
uchar data[]; /* данные */
Протокол TCP
TCP (Transmission Control Protocol — протокол управления передачей) являет$
ся основным протоколом сокетов, используемым в Internet. Он позволяет исполь$
зовать функции read() и write() и требует повторного открытия сокета при каж$
дом новом подключении. Атрибуты пакета TCP перечислены в табл. 3.6.
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Дальнейшее повышение надежности обеспечивается путем проверки того, что
адресат действительно получил данные, причем именно в том виде, в каком они
были посланы. Протокол UDP работает достаточно быстро, но он не имеет той
надежности, которая требуется многим программам. Проблема надежности реша
www.books-shop.com
пешно доставлено, TCP
подсистема автоматически инициирует повторную дос
мер ограничен.
Протокол UDP пытается передавать настолько большие данные, насколько
это возможно, вследствие чего могут возникать проблемы, связанные с ограни
тям.
В свою очередь, протокол TCP ограничивает каждый пакет небольшим бло
жет быть увеличен до 1500 байтов. Вручную задать это значение можно с помо
изводительности").
Следует также отметить, что при получении пакетов сообщения в неправиль
Заголовок TCP$пакета
Протокол TCP предлагает много различных возможностей, поэтому в заголо
вок TCP
пакета добавлен целый ряд дополнительных полей. Размер заголовка в
TCP примерно в три раза превышает размер UDP
заголовка. Структура заголовка
приведена в листинге 3.4.
struct TCP_header {
ui16 src_port; /* номер порта отправителя */
ui16 dst_port; /* номер порта получателя */
ui32 seq_num; /* порядковый номер */
ui32 ack_num; /* номер подтверждения */
www.books-shop.com
uint data_off:4; /* смещение данных */
uint res:6; /* (зарезервировано) */ .
uint urg_flag:1; /* срочное, внеполосное сообщение */
uint ack_flag:1; /* поле подтверждения корректно */
uint psh_flag:1; /* немедленно выдать сообщение процессу */
uint rst_flag:1; /* разорвать соединение вследствие ошибок */
uint syn_flag:1; /* открыть виртуальное соединение (канал) */
uint fin_flag:1; /* закрыть соединение */
ui16 window; /* число байтов, получаемых адресатом */
ui16 checksum; /* контрольная сумма сообщения */
ui16 urg_pos; /* последний байт срочного сообщения */
ui8 options[]; /* опции TCP */
ui8 padding[]; /* (необходимо для выравнивания
массива data[]) */
uchar data[]; /* данные */
2464
90 eg
битовых слов.
Некоторые поля используются только для открытия соединения, управления
передачей и закрытия соединения. Часть полей в процессе передачи данных не
заполняется.
В TCP
заголовках используются те же номера портов, что и в UDP. Поля
seq_num и ack_num позволяют выполнять трассировку потока. Когда посылается
сообщение, IP
подсистема присваивает ему порядковый номер (seq_num). Получа
www.books-shop.com
с номером (ack_num), на единицу большим, чем порядковый номер принятого со
раничения.
www.books-shop.com
ду ними вообще нет никакой связи. Они могут использовать ряд функций друг
друга, но все же столь тесного взаимодействия, чтобы считать их неразделимыми,
не наблюдается.
Рассмотренные протоколы — ICMP, UDP, TCP и собственно IP — играют в
сети каждый свою роль. Об этом следует помнить, проектируя сетевое приложе
пакете. Свои заголовки и данные они размещают в разделе данных этого пакета.
утилиты tcpdump
вателю root.
По умолчанию утилита работает в беспорядочном режиме, перехватывая все се
Сетевая этика
Знание является грозным оружием и налагает на его обладателя большую ответственность. Имея
привилегии пользователя root, можно выполнять много полезной работы, а можно и нанести ог
ромный вред. Будучи инсталлированной на вашем компьютере, система Linux предполагает, что вы
используете ее с добрыми намерениями, Наилучший способ дискредитировать сообщество разра
ботчиков открытого программного обеспечения— злоупотребить доверием и властью, которые из
самых лучших побуждений были предоставлены вам хорошими людьми.
Обычно сетевой адаптер принимает только те сообщения, которые несут в се
бе его Ethernet
адрес. Вспомните главу 2, "Основы TCP/IP", в которой говори
www.books-shop.com
Беспорядочный режим работы утилиты разрешается отключать. Утилита
tcpdump располагает множеством опций, с помощью которых можно фильтровать
нежелательные сообщения, выполнять переадресацию и многое другое. Перечис
Опция Директива
а Пытаться связывать имена с сетевыми и широковещательными адресами (требуется
доступ к серверу имен)
с <счетчик> Остановиться после получения заданного числа сообщений
п Не преобразовывать адреса узлов в символические адреса (полезна, когда нет дос
тупа к серверу имен)
р Не переводить плату в беспорядочный режим; при работе в небольшой сети про
смотр всех пакетов может показаться интересным, но в крупной сети компьютер
быстро "захлебнется" потоком сообщений
v Выводить более детальный отчет (отображается значение поля TTL пакета)
vv Выводить исчерпывающий отчет
w <файл> Записывать пакеты в файл
мые командой ping 127.0.0.1, так как сетевая подсистема не передает эти сооб
www.books-shop.com
применять несколько фильтров. Фильтр сообщает IP
подсистеме о том, какого
рода пакеты следует перехватывать. Перечислим некоторые из фильтров:
ЕТН_Р_802_3 Кадры стандарта 802.3
ЕТН_Р_АХ25 Кадры стандарта АХ.25
ETH_P_ALL Все кадры (будьте осторожны!)
ЕТН_Р_802_2 Кадры стандарта 802.2
нать, как в сети создаются кадры. При анализе кадров необходимо помнить, что
порядок следования битов может быть разным и зависит от аппаратуры. В струк
туре IP
адреса предполагается, что бит со смещением нуль является первым би
компилятора.
чаются в IP
пакет, общий размер заголовка может составлять от 20 до 120 байтов.
Соотношение между размером заголовка и объемом передаваемых данных опре
www.books-shop.com
Передача сообщений Глава
между одноранговыми
компьютерами 4
В этой главе...
Сокеты, требующие установления соединения 82
Пример: подключение к демону HTTP 86
Сокеты, не требующие установления соединения 87
Прямая доставка сообщений 92
Подтверждение доставки UDPсообщения 97
Переключение задач: введение в многозадачность 100
Резюме: модели взаимодействия с установлением и
без установления соединения 101
www.books-shop.com
Сообщения могут передаваться двумя различными способами: непрерывным
потоком (TCP) или в виде дискретных пакетов (UDP). Первый способ напомина
соединения
В Linux (и во всех других операционных системах семейства UNIX) програм
ние соединения.
щений.
TCP
подсистема запоминает, с кем общается программа. Каждое сообщение
на более низком уровне (уровне протокола IP) несет в себе адрес получателя. Это
подобно набору номера всякий раз, когда вы хотите поговорить по телефону со
своим другом.
При подключении к другой программе (с помощью системного вызова
connect()) сокет запоминает адрес получателя сообщений и его порт. В процессе
дальнейшего обмена данными можно использовать высокоуровневые функции
библиотеки потокового ввода
вывода, такие как fprintf() и fgets(). Это сущест
Надежные соединения
В TCP предполагается, что путь, по которому передаются данные, чист и сво
боден от каких
либо помех. Протокол гарантирует надежность соединения: або
www.books-shop.com
нент получает все, что посылает ему ваша программа. Сетевая подсистема, объе
кетов не имеет значения, хотя на самом деле это не так. Ниже приводится список
вопросов, ответы на которые позволят определить, необходима ли потоковая дос
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
С другой стороны, одиночный пакет данных напоминает бандероль. Та
тельность соединения.
уровня
Функцию connect() можно вызвать и для UDP
сокета. Это удобно, если не
нужно использовать высокоуровневые функции ввода
вывода: протокол UDP
обеспечивает существенное повышение производительности благодаря автоном
много по
другому, чем в случае TCP.
В главе 3, "Различные типы Internet
пакетов", описывался процесс трехфазо
www.books-shop.com
тавка не гарантируется. Представленный в листинге 4.1 фрагмент программы на
int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_DGRAM, 0); /* дейтаграммный сокет */
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(DEST_PORT);
inet_aton(DEST_IPADDR, &addr.sin_addr);
if ( connected, &addr, sizeof (addr)) != 0 ) /* подключаемся! */
perror("connect");
/*— это не потоковое соединение! —*/
send(sd, buffer, msg_len); /* передача данных как в TCP */
Обычно при работе по протоколу UDP используются функции sendto() и
recvfrom() (описаны ниже). Функция send() предполагает, что программа зареги
имуществ UDP. В TCP требуется сначала закрыть сокет, а затем открыть его по
вторно.
Еще раз повторю, что, выбирая протокол UDP, вы сознательно идете на сни
Протокол RDP
Протокол RDP (Reliable Data Protocol — протокол надежной доставки данных) [RFC908, RFC1151]
не только обеспечивает гарантированную доставку, как в TCP, но и позволяет достичь скорости
UDP. Сообщения могут приходить в неправильном порядке, но, по крайней мере, это хороший
компромисс между UDP и TCP. К сожалению, хоть этот протокол уже давно описан, Linux и дру
гие UNIXсистемы еще не поддерживают его.
www.books-shop.com
Пример: подключение к демону HTTP
Одним из наиболее часто используемых протоколов высокого уровня является
HTTP. Он реализует очень простой интерфейс выдачи запроса. Сервер анализи*
рует запрос и отправляет клиенту сообщение. В сообщении может содержаться
любой документ.
Несмотря на простоту протокола, на его примере можно проследить различ
Получение HTTPстраницы
Составление запроса — это самая простая часть соединения. Нужно лишь
обеспечить, чтобы сервер понял сообщение. В листинге 4.2 продемонстрирован
один из способов получения Web
страницы. Эта программа открывает соедине
int sd;
struct servent *serv;
if ( (serv = getservbyname("http", "tcp")) == NULL )
PANIC("HTTP servent");
if ( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
www.books-shop.com
PANIC("Socket");
ные читаются до тех пор, пока сервер не разорвет соединение. В последних вер
соединения
Не во всех соединениях требуется открытый двунаправленный канал связи
между компьютерами. Если телефонный разговор рассматривать как пример по
писывает в него адрес получателя и отправляет в сеть, не заботясь о том, как оно
достигнет адресата. (Повторюсь: ненадежность дейтаграмм означает только то,
что факт доставки не проверяется. Это не означает, что сообщение не дойдет.)
www.books-shop.com
Задание адреса сокета
В полную инсталляционную версию Linux обычно входят средства, позволяю
щие посылать короткие сообщения от одной рабочей станции к другой. При этом
необходимо указать лишь адрес компьютера и, возможно, имя пользователя. По
ставляет собой указатель на целое число. Дело в том, что в функции sendto() по
www.books-shop.com
Передача длины адреса
В функцию recvf rom() передается указатель на длину адресной структуры. Это пережиток, дос
тавшийся в наследство от другого семейства протоколов — PF_LOCAL, При вызове функции ей не
обходимо сообщить (в шестом параметре), каков максимальный размер буфера адреса. По завер
шении функции в этом же параметре будет содержаться реальная длина полученного адреса. Та
ким образом, параметр должен передаваться по ссылке, чтобы система могла записывать в него
возвращаемое значение.
В связи с тем, что функция recvfrom() может изменять значение параметра
addr_len, его необходимо задавать при каждом следующем вызове функции. В
противном случае оно будет постоянно уменьшаться. Рассмотрим пример:
/**************************************************************/
/*** Пример функции recvfrom() ***/
int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_DGRAM, 0);
/*— привязка к конкретному порту —*/
while ()
{ int bytes, addr_len=sizeof(addr);
bytes = recvfrom(sd, buffer, sizeof(buffer), 0, fcaddr,
&addr_len);
fprintf(log, "Got message from %s:%d (%d bytes)\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
bytes);
/**** обработка запроса ****/
sendto(sd, reply, len, 0, &addr, addr_len);
}
В этом примере в цикле while программа ожидает входящих сообщений, регист
ными, т.е. они не могут содержать значение NULL (0). Поскольку в UDP соединение
не устанавливается, необходимо знать, кто послал запрос. Лучше всего хранить зна
ленным.
Протокол, в котором соединение не устанавливается, имеет упрощенную про
www.books-shop.com
Благодаря отсутствию квитирования в UDP существенно повышается быстро
Протокол Т/ТСР
В TCP требуется до десяти инициализирующих пакетов. Это очень накладно,
особенно если запрашивающая сторона собирается выполнить одну
рон. Это необходимо, чтобы гарантировать получение как клиентом, так и серве
www.books-shop.com
Протокол Т/ТСР обеспечивает очень высокое быстродействие. Существует
только одна проблема: вся информация должна быть передана в пределах одного
сегмента, максимальный размер которого составляет всего 540 байтов. Правда,
существует возможность поднять этот предел до 64 Кбайт. Кроме того, программа
может посылать не одно сообщение, а несколько.
Для взаимодействия по протоколу Т/ТСР от серверной программы ничего не
требуется. Алгоритм, реализующий TCP
соединение, прекрасно подходит для
этой цели, так как все требуемые функции запрограммированы в сетевой подсис
щать буферы данных слишком быстро. Для этого в показанном фрагменте про
Т/TСР и Linux
К сожалению, протокол Т/ТСР на сегодняшний день еще не реализован в Linux. Это произойдет
в будущем, а пока можно воспользоваться преимуществами протокола в других UNIXсистемах.
Соответствующие программы находятся на Webузле.
Как уже упоминалось, в TCP требуется, чтобы для каждого нового соединения
сокет создавался повторно, поскольку закрыть соединение можно, только закрыв
сам сокет. В Т/ТСР этого не требуется, так как соединение устанавливается и
разрывается неявно.
Т/ТСР позволяет обмениваться короткими пакетами с сервером при мини
www.books-shop.com
квитировании. Когда сообщение передается напрямую (не через канал), в про
вах, если не выбрать порт, операционная система назначит его сокету произволь
www.books-shop.com
#include <sys/socket.h>
#include <resolv.h>
int bind(int sd, struct sockaddr *addr, int addr_size);
В этом разделе даются лишь общие сведения о данной функции. Подробнее
она рассматривается в главе 6, "Пример сервера". Схема ее вызова примерно та
кая же, как и в случае функции connect(). В первую очередь необходимо инициа
ся порт MY_PORT, а во
вторых, адрес указан как INADDR_ANY. В реальной программе
номер порта будет другим, так как он зависит от компьютера, к которому произ
водится подключение.
Константа INADDR_ANY является специальным флагом (она раскрывается как
0.0.0.0), который указывает на то, что любой компьютер может подключаться по
любому интерфейсу. В некоторых компьютерах имеется более одного интерфейс
ного устройства (например, две сетевые платы, модем с сетевой платой или аль
тернативные IP
адреса). С каждым аппаратным или логическим интерфейсом
связан свой IP
адрес. С помощью функции bind() можно выбрать нужный ин
терфейс или сразу все интерфейсы. Вторая возможность поддерживается для про
токолов TCP и UDP и может использоваться при передаче данных через бранд
мауэры.
Если нужно выбрать конкретное интерфейсное устройство, задайте адрес сле
дующим образом:
if ( inet_aton("128.48.5.161", &addr.sin_addr) == 0 )
perrorf("address error");
полнить по
другому:
addr.sin_addr.s_addr = htonl(0x803005Al); /* 128.48.5.161 */
Оба варианта приведут к одному и тому же результату. Обратите внимание на
то, что при использовании константы INADDR ANY вызывать функцию htonl() не
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
требуется. В данном случае адрес состоит из одних нудей, поэтому порядок сле
Обмен сообщениями
Передача и прием UDP
сообшений напоминает игру в футбол при сильном
тумане. Игроки по очереди бьют или принимают мяч, хотя почти не видят друг
друга. Первый "удар по мячу" наносит отправитель сообщения. Получатель дол
жен знать адрес и номер порта, заданные с помощью функции bind(). В этот
порт будут поступать данные. Отправителю не нужно указывать свой порт, по
www.books-shop.com
Предполагая, что порт получателя — 9999, программа посылает ему SQL
запрос.
Отправитель не обязан запрашивать конкретный порт, поскольку получатель
принимает данные из адресной структуры в функции recvfrom(). (Еще раз на
помню, что для всех сокетов требуется порт. Если он не выбран, система назна
Прием сообщения
В отличие от отправителя, получатель должен задать порт с помощью функ
ции bind(). После создания сокета номер порта должен быть опубликован, чтобы
компьютер на противоположной стороне мог по нему подключиться. В листин
else
www.books-shop.com
perror( "Awaiting message with RecvFrom");
while ( !quit );
вращает результат.
Отправитель посылает только одно сообщение. Получатель функционирует
подобно серверу, принимая, обрабатывая и отвечая на каждое сообщение. При
этом возникает фундаментальная проблема несоответствия между двумя алго
общений.
Если программа работает с сообщениями переменной длины, выделите буфер
большего размера. Максимальный размер UDP
сообщения составляет 64 Кбайт.
Это значение можно принять как верхнюю границу буфера.
Большие UDPсообщения
Если размер сообщения больше, чем величина буфера, диспетчер очереди отбрасывает лишние
байты. Это происходит Bо многих протоколах, работающих с дейтаграммами.
Подтверждение доставки
UDPсообщения
Отбрасывание части сообщения не является хорошим решением. Это один из
тех моментов, о которых следует помнить при работе по протоколу UDP. Необ
вать, что адресат получил сообщение. Может показаться, что надежность являет
www.books-shop.com
• Как отправитель узнает, что получатель принял пакет?
Решая первую проблему, необходимо помочь получателю отслеживать все па
чатель должен запросить нужный пакет. Кроме того, отправитель должен полу
ключается в том, что отправитель должен хранить список всех посланных паке
дика, по сути, как раз и применяется в TCP. Однако одновременно с этим воз
пример, если размер пакета 1024 байта и посылается сообщение длиной 1 Мбайт,
получатель должен принять 1000 пакетов. Имея предел в 1024 байта, получатель
может ответить сразу на 10 пакетов. Тогда максимальный размер журнала пакетов
будет 10x1024 байта. Чтобы правильно определить предельные размеры, необхо
но, что отвечать нужно на каждый десятый пакет, а отправитель посылает всего
пять пакетов, произойдет следующее:
• получатель никогда не пошлет подтверждения;
• отправитель решит, что пакет не дошел до получателя, и осуществит по
вторную передачу.
Чтобы решить эту проблему, необходимо сообщить получателю общее число
пакетов. Когда приходит последний пакет, получатель посылает укороченное
подтверждающее сообщение.
Упорядочение пакетов
Каждый пакет UDP
сообщения может достигать адресата в неправильной по
www.books-shop.com
регрузки в сети могут заставить маршрутизатор задержать сообщение или послать
его по другому маршруту.
Чтобы обеспечить правильную доставку пакетов, необходимо присвоить им
уникальные номера. Номера должны идти по возрастанию и быть уникальными
в пределах сообщения. Если пакет приходит вне очереди, программа должна
задержать его, дожидаясь доставки предыдущих пакетов. Например, когда по
Избыточность пакетов
В сети может возникать дублирование пакетов, когда один и тот же пакет
дважды достигает адресата. Чаще всего это происходит, если получатель запросил
повторную передачу, а в это время появляется запоздавший пакет. Присвоение
пакетам порядковых номеров позволяет избежать избыточности. Получатель про
ные коды (CRC — cyclical redundancy check) или коды коррекции ошибок
(ЕСС — error correction code). Все тот же пакет 3 мог на самом деле прийти, но
проверка контрольных сумм IP и UDP выявила наличие ошибок.
При передаче пакетов и сообщений можно самостоятельно применять различ
тичны к ошибкам;
• порядок данных — контрольные суммы хорошо подходят для быстрой
проверки итоговых значений, но в них не учитывается порядок следова
www.books-shop.com
ния байтов. Для получения CRC
кода требуются более трудоемкие вычис
многозадачность
Зачем нужно создавать две разные программы: отправителя и получателя? По
www.books-shop.com
временно, причем каждую в своем адресном пространстве, и обеспечить тем са
ния поддерживаются как в протоколе TCP, так и в UDP, но лишь в TCP гаран
пьютерам.
Протокол TCP обеспечивает надежность, но снижает производительность.
Можно повысить надежность UDP, внедрив в программу средства проверки дос
www.books-shop.com
Глава
Многоуровневая
сетевая модель
5
В этой главе...
Решение сетевой задачи 103
Сетевая модель OSI 108
Набор протоколов Internet 111
Фундаментальные отличия между моделями OSI и IP 115
Что чему служит 116
Резюме: от теории к практике 116
www.books-shop.com
Одним из любимых десертных блюд в Европе является торт. Он обычно со
стоит из нескольких (от пяти до восьми) прослоек. Каждая из них имеет свой
вкус и цвет. Сверху торт может быть залит глазурью.
Если проанализировать архитектуру сетевой подсистемы (или стека протоко!
лов), то обнаружится очевидная аналогия с тортом. На поверхности находится
общедоступный программный интерфейс, под которым располагаются слои про
токолов. Каждый сетевой слой основан на другом слое, без которого он не смо
жет функционировать. Эта зависимость очень важна: если в одном из слоев воз
никнут проблемы, вся сеть "рухнет" или, по крайней мере, начнет функциониро
вать неоптимально.
Сетевая подсистема представляет собой сложное объединение, в которое вхо
нии сетевой задачи, и описываются две сетевые модели, в которых это решение
найдено.
Аппаратная среда
Существует много технологий построения сетей. В основе сети может нахо
www.books-shop.com
Окончание табл. 5.1
Среда Проводник Электричес Направленность Расстояние Максимальная
кий канал пропускная
способность
Беспроводная Нет Да Широковещание > 1000 км < 10 Кбит/с
связь: HF
Беспроводная Нет Да Широковещание <30км < 40 Кбит/с
связь: в пределах пря
VHF/UHF мой видимости
Беспроводная Нет Да Да, в пределах <30км < 1 Мбит/с
связь: микро прямой видимо
волны сти
Спутник Нет Да Да (Не ограниче < 10 Мбит/с
но)
Инфракрасное Нет Нет Да, широковеща <10м < 1 Мбит/с
излучение ние
Лазер Нет Нет Да Очень большое < 100 Гбит/с
К счастью, подобные детали (скрываются на уровне ядра системы. Представь
вторно.
Помехи в передающем канале приводят к потере сигнала и разрушению паке
ратных устройств. Если сетевое соединение установлено всего между двумя ком
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
на кадры, или фреймы, — наименьшие единицы передаваемой информации. В
сетях Ethernet каждый кадр содержит МАС
адрес источника и приемника. Впо
тификатор.
слеживается время жизни пакета. Пакет устаревает, когда число переходов через
маршрутизаторы превышает заданное число. Каждый пакет может осуществлять
до 255 переходов. По умолчанию задан лимит 64 перехода. Обычно этого доста
ние. На основании его таблицы адресов выясняется, что наилучший путь проле
дается туда
сюда до тех пор, пока счетчик TTL не достигнет предельного значе
www.books-shop.com
порядковый номер. В сети может происходить не только ненужное дублирование
информации, но также ее потеря при прохождении через ненадежные маршрути
токолах очень низкого уровня. Если у вас нет желания заниматься ими и вас уст
тельность.
Интерфейс между ядром и сетью сложен, особенно если ядро не является ре
нако в ядре Linux проблема реентерабельности уже решена, поэтому общая зада
ча упрощается.
Когда приходит сообщение, сетевое устройство посылает центральному про
ра сигналом о том, что плата готова принять следующую порцию данных. В РРР
требуется более интенсивное взаимодействие с процессором, но проблем с нало
www.books-shop.com
могут обратиться к нему одновременно. Поэтому операционная система должна
обрабатывать запросы по одному за раз.
Кроме того, ядро должно подготавливать сообщение к передаче и разупаковы
вия сети и приложения. Здесь скрыто много проблем, с каждой из которых необ
ся друг на друге, образуя слои. Если, как в луковице, снять слой за слоем, то в
середине обнаружится среда физической передачи данных (электричество, радио
волны, свет). В этой главе будут рассмотрены две основные модели: OSI и IP.
www.books-shop.com
Сетевая модель OSI
Наиболее известная сете
фессиональные программи
ли организации сети.
Уровень 1: физический
Физический уровень охватывает все аппаратные интерфейсы, описывая среду
передачи данных и способы распространения сигнала. Среда — это носитель
(например, витая пара, коаксиальный кабель, оптоволокно), по которому переда
ется сигнал.
На физическом уровне работает сетевой адаптер. Он функционирует в качест
www.books-shop.com
Микроконтроллер сетевого адаптера проверяет состояние сети перед отправ
Уровень 2: канальный
Основное предназначение канального уровня заключается в управлении пере
www.books-shop.com
Уровень 3: сетевой
Сетевой уровень отвечает за преобразование адресов и маршрутизацию. Его
функция заключается в поиске узла
адресата с помощью шлюзов и маршрутиза
Уровень 4: транспортный
Транспортный уровень отвечает за доставку сообщения в неповрежденном ви
действующими друг с другом в среде Internet. В нем имеются все средства, необ
Уровень 5: сеансовый
Основное предназначение сеансового уровня заключается в контроле над со
рации в системе.
Другой функцией сеансового уровня является управление потоком данных.
Одна из проблем клиент
серверного взаимодействия состоит в определении по
рядка ведения диалога. В сеансовом уровне эта проблема решается путем переда
www.books-shop.com
операции. Использование маркеров позволяет уменьшить вероятность возникно
Уровень 6: представительский
Представительский уровень определяет способы обработки данных: шифрова
Уровень 7: прикладной
Последний, прикладной, уровень предоставляет различные сетевые сервисы,
такие как пересылка файлов, эмуляция терминала, электронная почта и сетевое
управление. Данный уровень используется практически всеми приложениями,
поскольку в нем вводятся библиотеки API
функций, помогающих программе
взаимодействовать с сетью. На прикладном уровне реализована сетевая файловая
система (NFS), являющаяся частью архитектуры операционной системы и по
прямую. Но эта модель является отправным пунктом для понимания стека про
Примечание
Протокол IP появился в 1972 г. в сети ARPAnet, основанной организацией DARPA (Defense
Advanced Research Projects Agency— Управление перспективных исследовательских профамм
министерства обороны США). Корпорация BBN реализовала первые варианты протокола. Позд
нее UNIX, бесплатная операционная система, разработанная компанией Bell Labs, также приняла
модель IP. Университеты, входившие в сеть ARPAnet,, использовали UNIX для проверки того, как
могут компьютеры взаимодействовать и между собой в пределах США. Библиотека Socket API поя
вилась в ВSD 4.2 в 1983 г.
Набор протоколов Internet состоит из четырех уровней, которые тесно связаны
с моделью OSI. Самый верхний уровень называется прикладным. Он охватывает
уровни OSI с пятого по седьмой (рис. 5.2).
www.books-shop.com
Модель OSI Стек протоколов Internet
ет драйвер.
Linux усложняет данный уровень собственным расширением: сетевыми адап
ра из памяти.
www.books-shop.com
Межсетевой уровень соответствует сетевому уровню модели OSI. В нем вы
ются "network not reachable" (сеть недоступна) и "host not found" (узел не най
ден).
Этот протокол работает совместно с другими подсистемами, выполняя различ
ранговыми компьютерами", они позволяют программе вести себя так, как если
бы она монопольно владела сетью. Все остальные свойства UDP соответствуют
параметрам межсетевого уровня стека протоколов Internet. Правильнее всего
UDP позиционируется между сетевым и транспортным уровнями модели OSI,
больше попадая в сетевой уровень.
www.books-shop.com
Модель OSI Стек протоколов Internet
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Уровень 4: прикладной
На прикладном уровне модель TCP/IP заканчивается. Он охватывает сеансо
ботают Web
броузеры, шлюзы, Telnet и FTP (File Transfer Protocol — протокол
передачи файлов). Технология RPC соответствует представительскому уровню
OSI. Сетевая файловая система (NFS) основана на RPC и находится на приклад
моделями OSI и IP
В моделях OSI и IP весь стек протоколов распределен по уровням. Обе они
ориентированы на то, чтобы не писать машинно
зависимый код, а создавать аб
рые отличия.
В OSI по уровням распределены не только протоколы, но и данные. На каж
дом уровне к пакету добавляется свой заголовок. По мере того как данные про
ловков.
На принимающей стороне происходит обратный процесс: данные последова
www.books-shop.com
на потоковую передачу данных. Сам протокол IP предназначен для разработки
новых протоколов.
граммирования.
В обеих сетевых моделях, OSI и IP, круг решаемых задач разбит на уровни.
Все уровни, или слои, основаны друг на друге, подобно прослойкам торта. Пер
рость взаимодействия.
www.books-shop.com
www.books-shop.com
Часть
Создание сервер
ных приложений
В этой части...
Глава 6. Пример сервера
Глава 7. Распределение нагрузки: многозадачность
Глава 8. Механизмы вводавывода
Глава 9. Повышение производительности
Глава 10. Создание устойчивых сокетов
www.books-shop.com
Глава Пример сервера
6
В этой главе...
Схема работы сокета: общий алгоритм сервера 121
Простой эхосервер 122
Общие правила определения протоколов 129
Более сложный пример: сервер HTTP 131
Резюме: базовые компоненты сервера 134
www.books-shop.com
В сетевом соединении всегда есть отправитель и получатель. В общем случае
отправителем является клиент, который запрашивает сервис, предоставляемый
сетевым компьютером. В части I, "Создание сетевых клиентских приложений",
рассматривались основы клиентского программирования: как подключить клиен
вать HTML
сообщения.
зовов здесь будет такой: socket(), bind(), listen(), accept() и close(). В то время
как клиент создает активное соединение, серверное соединение пассивно. Функ
www.books-shop.com
Рис. 6.1. Алгоритмы построения клиента и
сервера сходны, но схема подключения к
сети в них разная
Простой эхосервер
Прежде чем перейти к рассмотрению системных функций, следует рассказать
о том, какого рода сервер мы будем создавать. В качестве образца был выбран
стандартный эхо
сервер. Это основа основ серверного программирования, подоб
www.books-shop.com
В общем случае в серверной программе требуется в определенной последова
дается константа SOCK_STREAM. Но теперь требуется задать также номер порта, что
лов, адрес сервера и номер порта (см. главу 1, "Простейший сетевой клиент").
Последний параметр содержит размер структуры sockaddr. Его необходимо зада
вать, потому что такова концепция библиотеки Socket API: один интерфейс, но
много архитектур. Операционная система поддерживает множество протоколов, у
каждого из которых своя адресная структура.
Перед вызовом функции bind() необходимо заполнить поля структуры
sockaddr (листинг 6.1).
www.books-shop.com
Листинг 6.1. Вызов функции bind() в TCP!сервере
#include <linux/un.h>
struct sockaddr_ux addr; /* создаем локальный именованный сокет */
bzero(Saddr, sizeof(addr)); /* обнуляем структуру */
addr.sun_family = AF_LOCAL; /* выбираем именованные сокеты */
strcpy(addr.sun_path, "/tmp/mysocket"); /* выбираем имя */
if ( bind(sd, saddr, sizeof(addr)) != 0 ) /* привязка к файлу */
perror("Bind AF_LOCAL");
Если запустить на выполнение эту программу, то после ее завершения в ката
щения.
В результате выполнения функции bind() могут возникнуть перечисленные
ниже ошибки.
• EBADF. Указан неверный дескриптор сокета. Эта ошибка возникает, если
вызов функции socket () завершился неуспешно, а программа не прове
ма. Эта ошибка может также возникнуть, если сервер завис и вы тут же
запускаете его повторно. Для операционной системы требуется время,
чтобы освободить занятый порт (до пяти минут!).
www.books-shop.com
Функция bind() пытается зарезервировать для серверного сокета указанное
имя файла или порт (список доступных или стандартных портов содержится в
файле /etc/services). Клиенты подключаются к данному порту, посылая и при
редь ожидания.
Очередь сокета активизируется при вызове функции listen(). Когда сервер
вызывает эту функцию, он указывает число позиций в очереди. Кроме того, сокет
переводится в режим "только прослушивание". Это очень важно, так как позво
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Прием запросов от клиентов
На данный момент программа создала сокет, назначила ему номер порта и
организовала очередь ожидания. Теперь она может принимать запросы на под
рес отправителя.
#include <sys/socket.h>
#include <resolv.h>
int accept(int sd, sockaddr *addr, int *addr_size);
Как всегда, параметр sd является дескриптором сокета. Во втором параметре
возвращается адрес клиента и номер порта, а в третьем — размер структуры
sockaddr. В отличие от функции recvfrom(), последние два параметра являются
необязательными. Если в программе не требуется знать адрес клиента, задайте
эти параметры равными нулю.
Необходимо убедиться, что размер буфера адреса достаточен для размещения в
нем полученной адресной структуры. Беспокоиться о повреждении данных из
за
переполнения буфера не стоит: функция задействует ровно столько байтов,
сколько указано в третьем параметре. Параметр addr_size передается по ссылке,
поэтому программа может легко узнать реальный размер полученной структуры
(листинг 6.4).
www.books-shop.com
clientsd = accept(sd, &addr, &size); /* ожидание подключения */
if { clientsd > 0 ) /* ошибок нет */
{
/*** Взаимодействие с клиентом ***/
close(clientsd); /* очистка и отключение */
}
else /* произошла ошибка */
perror("Accept");
Взаимодействие с клиентом
Обратите внимание на то, что в приведенном выше фрагменте программы за
кета. Это очень важный момент, поскольку для каждого соединения создается
отдельный дескриптор. Если забыть их закрыть, лимит дескрипторов может со
временем исчерпаться.
ния байтов. Извлечь адрес и номер порта из переменной addr можно с помощью
функций преобразования (листинг 6.5).
www.books-shop.com
• EBADF. Указан неверный дескриптор сокета.
• EOPNOTSUPP. При вызове функции accept() сокет должен иметь тип
SOCK_STREAM.
• EAGAIN. Сокет находится в режиме неблокируемого ввода
вывода, а оче
ное сообщение до тех пор, пока не поступит команда bye (листинг 6.6).
do
{
nbytes recv(client, buffer, sizeof(buffer), 0);
if ( nbytes > 0 ) /* если получены данные, возвращаем их */
send(client, buffer, nbytes, 0);
}
while ( nbytes > 0 && strncmp("bye\r", buffer, 4) 1=0);
close(client);
}
Заметьте, что признаком окончания сеанса является строка "bye\r", а не
"bye\n". В общем случае это зависит от того, как выполняется обработка входного
потока. Из соображений надежности следует проверять оба случая. Попробуйте
протестировать данную программу, использовав в качестве клиента утилиту
Telnet.
протоколов
www.books-shop.com
Какая программа должна начинать передачу
первой?
БОЛЬШИНСТВО серверов первыми начинают сеанс. Но в некоторых системах с
высоким уровнем безопасности предполагается, что клиент должен отправить
первое сообщение. Сервер может заставить клиента идентифицировать себя
(указать не только адрес узла и порт).
Следует избегать ненужного взаимодействия. Если сервер начинает первым,
он, как правило, выдает одну и ту же информацию при каждом подключении.
Нужно ли это клиенту? Не замедлит ли это работу?
дает ответ.
Но иногда необходимо меняться ролями. Например, клиент запрашивает ин
формацию из базы данных сервера. После того как данные были переданы, дру
гой клиент обновляет часть полей, с которыми работает первый клиент. Если
первый клиент принимает на основании имеющейся информации какие
то ре
шения, они могут быть неправильными. В такой ситуации сервер должен само
пасности.
С другой стороны, сертификация не всегда нужна, а вместо этого необходимо
регистрироваться в системе. Как часто пользователи посещают сервер? Требуется
ли настраивать работу сервера в соответствии с предпочтениями отдельных поль
www.books-shop.com
Как следует обрабатывать двоичные данные?
Передавать двоичные данные — особенно в сжатом виде — намного эффек
навливать данные.
Случается ли так, что программа начинает передачу данных в текстовом виде,
а затем переключается в двоичный режим? В этом случае необходимо, чтобы
клиент или сервер посылал соответствующее уведомление.
ния ресурсов. Зависание обычно происходит, когда клиент или сервер выполняет
какие
то другие действия помимо передачи данных. Как и при взаимоблокиров
лов по сети).
Одно из решений проблемы синхронизации заключается в том, чтобы прово
чания.
www.books-shop.com
В TCP/IP существует понятие приоритетного сообщения, с помощью которого
можно просигнализировать об отмене. Подробная информация о приоритетных
сообщениях и передаче внеполосных данных приводится в главе 9, "Повышение
производительности". Но отправка приоритетного сообщения — это только пол
дела: как сервер, так и клиент должны вернуться к некой начальной точке, что
представляет собой серьезную проблему в структурном программировании.
Повторное открытие соединения позволяет начать сеанс сначала. Посредством
приоритетного сообщения клиент или сервер уведомляется о том, что необходи
кеты. Пришло время прощаться. Определить конец сеанса может быть не так
просто, как кажется. Например, при взаимодействии с HTTP
сервером сеанс за
мощью функции read() или recv(), а буфер имеет недостаточный размер. В этом
случае сервер превысит время ожидания и объявит о разрыве соединения.
Кроме того, бывает трудно определить, какая программа должна прервать со
единение первой. Клиент получит сообщение о разрыве канала (EPIPE), если сер
ется на Web
узле (файл html
ls
server.c).
Сервер генерирует HTML
код динамически, а не загружает его из файла. Это
упрощает программу (листинг 6.7).
while(1)
{ int client;
int size = sizeof(addr);
www.books-shop.com
bzero(buffer, sizeof(buffer) ); /* очистка буфера */
recv(client, buffer, sizeof(buffer), 0); /* получение
сообщения */
send(client, reply, strlen(reply), 0); /* ответ клиенту */
/* — отображение клиентского сообщения — */
fprintf(stderr, "%s", buffer);
close (client);
}
else
perror( "Accept");
формируют сервер о том, какого рода данные готов принимать клиент. В первую
строку может входить конфигурационная информация, позволяющая серверу оп
вают гораздо более сложными). Протокол HTTP 1.0 допускает наличие пробелов
в путевом имени, поэтому запрос включает в себя все, что находится от началь
(пустая строка)
(пустая строка)
<html>
<head>
www.books-shop.com
Листинг 6.8. Расширенный алгоритм HTTPсервера
/**************************************************************/
/ * * * Пример сервера HTTP 1.0: устанавливаем соединение, ***/
/*** принимаем запрос, открываем каталог и создаем ***/
/*** для него список файлов в формате HTML ***/
/**************************************************************/
знавая тип каждого файла, добавляя коды ошибок HTTP 1.1 и т.д.
www.books-shop.com
Резюме: базовые компоненты
сервера
В этой главе рассматривались основы создания серверных приложений. Серве
ствие клиента и сервера и как должна вести себя каждая из сторон. Пошаговый
анализ используемого протокола позволит определить, как наилучшим образом
обрабатывать каждый запрос.
www.books-shop.com
Глава
Распределениенагрузки
многозадачность
7
В этой главе...
Понятие о многозадачности: процессы и потоки 136
Обгоняя время: исключающие семафоры и гонки 160
Управление дочерними заданиями и заданиязомби 164
Расширение существующих версий клиента и
сервера 167
Вызов внешних программ с помощью функций
семейства ехес() 168
Резюме 171
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Представим себе, что мы одновременно выполняем множество различных за
единение, вынужден был ждать, пока сервер обслужит текущего клиента. Как
было бы здорово принимать несколько запросов на подключение одновременно!
А как насчет того, чтобы подключаться сразу к нескольким серверам?
Многозадачность — это очень мощная методика, позволяющая существенно
упростить программирование, если только вы способны разделить общий алго
личие между ними, каковы их сильные и слабые стороны. Кроме того, приводит
Понятие о многозадачности:
процессы и потоки
Многозадачность — это одна из важнейших особенностей систем Linux и
UNIX. Она позволяет выделять каждой программе свою долю процессорного
времени (квантование времени) и других системных ресурсов. Программы могут
работать намного эффективнее, если они написаны с учетом многозадачности.
Задания представляют собой отдельные исполняемые модули в системе. Каж
сти выделяются два основных понятия: процессы и потоки (или облегченные про!
цессы). Они определяют два различных способа совместного использования дан
www.books-shop.com
виде таблицы, преобразующей программные адреса в физические. Когда опера
пользуйте поток.
На рис. 7.1 представлено, какие компоненты задания допускают совместное
использование. В любом случае совместный доступ к стеку и контексту задания
запрещен. В текущей реализации библиотеки потоковых функций потоку разре
чер заданий должен выгружать меньшее число записей. В версиях ядра Linux 2.0
и 2.2 скорость переключения заданий почти такая же. Сходство возникает из
за
четко отлаженного алгоритма переключения.
В Linux также поддерживается симметричная мультипроцессорная обработка.
Если программа написана с учетом многозадачности, то в мультипроцессорной
системе она получает дополнительное ускорение. (На момент написания книги в
Linux могло одновременно поддерживаться максимум 16 процессоров.)
www.books-shop.com
Рис. 7.1. Задания в Linux имеют несколько областей памяти
www.books-shop.com
Смешение потоков и процессов в рамках одной программы может показаться
непривычным. Однако так часто происходит в больших интерактивных приложе
ды top или ps aux) имеются общие атрибуты. Благодаря им можно лучше понять
сущность многозадачности.
Во
первых, у каждого задания имеется предок. (Необходимо добавить слово
"почти". В списке процессов можно заметить программу init. Она является пра
гда задание
потомок завершается, его предок должен выполнить финальную очи
занный с клавиатурой;
• stdout — стандартный выходной поток (только для записи), обычно свя
занный с экраном;
• stderr — стандартный поток ошибок (только для записи), обычно свя
www.books-shop.com
Планирование заданий в Linux
В многозадачных операционных системах применяются различные методики планирования зада
ний. В Linux используется схема приоритетного кругового обслуживания. В этой схеме каждое
задание по очереди получает свою долю процессорного времени. Задания с высоким приорите
том перемещаются по списку быстрее, чем те, у которых низкий приоритет.
Процессы Потоки
После успешного вызова функции fork() сущест Родительская программа указывает имя функции,
вуют два процесса, выполняющихся параллельно которая будет выполняться в качестве дочернего
потока
Дочерний процесс должен быть явно завершен с Дочерний поток можно завершить явно либо неявно
помощью системного вызова exit () с помощью функции pthread_exit(void* arg)
или инструкции return
Общих данных нет; единственная информация, пе Потомок имеет доступ к данным предка, принимая
редаваемая потомку, — это снимок данных роди от него параметры и возвращая значения
тельского процесса
Дочерний процесс всегда связан с родительским; Дочерний поток может выполняться независимо от
когда процесспотомок завершается, его предок родительского и завершиться без его вмешательст
должен произвести очистку ва (если поток не является независимым, родитель
ская программа также должна производить очистку
после него)
Поскольку данные процесса недоступны другим Все совместно используемые данные должны быть
процессам, не происходит конфликтов при доступе идентифицированы и заблокированы, чтобы не про
к ресурсам изошло их повреждение
Независимая работа с файловой системой Потомок реагирует на все изменения текущего ка
талога (команда chdir), корневого каталога
(команда enroot) и стандартного режима доступа к
файлам (команда umask)
Таблицы дескрипторов открытых файлов не являют Совместное использование таблиц дескрипторов;
ся общими; операционная система копирует табли если дочерний поток закрывает файл, родительский
цы, поэтому если в двух процессах открыт один и поток теряет к нему доступ
тот же файл, то закрытие его в одном процессе не
приведет к изменению работы другого процесса
Сигналы обрабатываются независимо Один поток может блокировать сигнал с помощью
функции sigprocmask(), не влияя на работу дру
гих потоков
Создание процесса
Многозадачность чаще всего реализуется с помощью процессов. Процесс
представляет собой новый экземпляр программы, наследующий от нее копии де
www.books-shop.com
другими данными. Для порождения нового процесса предназначен системный
вызов fork():
#include <unistd.h>
pid_t fork(void);
Функция fork() проста и "немногословна": вы просто вызываете ее, и внезап
ну ошибки.
В большинстве программ функция fork() помещается в условную конструк
цию (например, if). Результат проверки позволяет определить, кем стала про
грамма — предком или потомком. В листингах 7.1 и 7.2 приведены два типичных
примера использования функции.
if ( (pchild = fork()) == 0 )
{ /* это процесспотомок */
/*— выполняем соответствующие действия —*/
exit(status); /* Это важно! */
}
else if ( pchild > 0 )
{ /* это процесспредок */
int retval;
/*— выполняем соответствующие действия —*/
wait(&retval); /* дожидаемся завершения потомка */
}
else
{ /* произошла какаято ошибка */
perror("Tried to fork() a process");
www.books-shop.com
Листинг 7.2. Пример делегирования полномочий
/**********************************************************/
/*** Предок (сервер) и потомок (обрабатывает задание) ***/
/**********************************************************/
int pchild;
for (;;) /* бесконечный цикл */
{
/*— ожидаем получения запроса —*/
if ( (pchild = fork()) == 0 )
{ /* это процесспотомок */
/*— обрабатываем запрос —*/
exit(status);
}
else if ( pchild > 0 )
{ /* предок выполняет очистку */
/* функция wait() НЕ НУЖНА */
/* используйте сигналы (см. далее) */
}
else
{ /* произошла какаято ошибка */
perror("Can't process job request");
}
В программе, представленной в листинге 7.1, процесс
предок выполняет ка
кую
то работу, а затем дожидается завершения процесса
потомка. В листинге 7.2
происходит распределение полномочий. Когда какая
то внешняя программа по
ской точки зрения, она уничтожает суть многозадачности, так как в ней не про
исходит дифференцирования.
www.books-shop.com
Устойчивость к ошибкам за счет слияния
Дублирование процессов может применяться при реализации отказоустойчивых систем. В отка
зоустойчивой системе вычисления дублируются с целью повышения достоверности результатов.
Запускается несколько одинаковых заданий, каждое из которых закрепляется за отдельным про
цессором {эта возможность еще не реализована в Linux). Через определенный промежуток вре
мени все задания посылают свои результаты модулю проверки. Если в какомто процессоре
произошел сбой, полученные данные будут отличаться. Соответствующее задание выгружается.
Если при выполнении функции fork() произошла ошибка, то, очевидно, воз
возможно выделить блок памяти требуемого размера или нельзя создать новое за
Создание потока
Благодаря потокам можно организовать совместный доступ к ресурсам со сто
читься на решении основной задачи. Например, один поток может читать графи
www.books-shop.com
Таблица 7.1. Параметры функции pthread create ()
Параметр Описание
child Дескриптор нового потока; с помощью этого дескриптора можно управлять потоком
после завершения функции
attr Набор атрибутов, описывающих поведение нового потока и его взаимодействие с ро
дительской программой (может быть равен NULL)
fn Указатель на функцию, содержащую код потока; в отличие от процессов, каждый поток
выполняется в отдельной подпрограмме родительской программы, и когда эта подпро
грамма завершается, система автоматически останавливает поток
arg Параметр, передаваемый функции потока и позволяющий конфигурировать его на
чальные установки; необходимо, чтобы блок данных, на который осуществляется ссыл
ка, был доступен потоку, т.е. нельзя ссылаться на стековую переменную (этот пара
метр тоже может быть равен NULL)
Как уже было сказано, после завершения функции существуют два потока: ро
www.books-shop.com
else if ( pchild > 0 )
{ /* это процесспредок */
/* закрываем ненужные ресурсы вводавывода */
/* дожидаемся завершения потомка */
wait();
}
return 0;
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Системный вызов clone()
В Linux имеется низкоуровневая функция _clone(), позволяющая гораздо силь
грамме страниц виртуальной памяти (см. рис. 7.1), то окажется, что функция
_clone() дает возможность указывать любую комбинацию общих областей памяти.
Параметр Описание
fn Как и в функции pthread_create(), это указатель на функцию потока; когда она за
вершается (с помощью инструкции return или системного вызова exit()), поток ос
танавливается
stacktop Указатель на вершину стека дочернего задания; в большинстве процессоров (за исклю
чением HP/PA RISC) стек заполняется в направлении сверху вниз, поэтому необходимо
задать указатель на первый байт стека (чтобы добиться совместимости, воспользуйтесь
директивами условной компиляции)
flags Набор флагов, определяющих, какие области памяти используются совместно
(табл. 7.3) и какой сигнал посылать при завершении дочернего задания (по умолча
нию SIGCHLD)
arg Аналогично функции pthread_create(), передается в качестве параметра функции fn
Работа со стеками
Поскольку совместное использование аппаратного стека недопустимо, родительский процесс
должен зарезервировать в программе дополнительную память для стека дочернего задания. Эта
область памяти будет общедоступной.
Флаги, представленные в табл. 7.3, позволяют выбрать, какие из общих облас
www.books-shop.com
Таблица 7.3. Флаги функции clone ()
Флаг Описание
CLONE_VM Совместное использование области данных между заданиями; если флаг указан, будут
доступны все статические и предварительно инициализированные переменные, а так
же блоки, выделенные в куче, в противном случае в дочернем задании будет создана
копия области данных
CLONE_FS Совместное использование информации о файловой системе: текущем каталоге, кор
невом каталоге и стандартном режиме доступа к файлам (значение umask); если флаг
не указан, задания будут вести себя независимо друг от друга
CLONE_FILES Совместное использование открытых файлов; когда в одном задании перемещается
указатель текущей позиции файла, в другом задании отразится это изменение, и если
закрыть файл в одном задании, то и в другом он станет недоступным (если флаг не
указан, в дочернем задании создаются новые ссылки на открытые индексные дескрип
торы)
CLONE_SIGHAND Совместное использование таблиц сигналов; каждое задание может запретить обра
ботку того или иного сигнала с помощью функции sigprocraaskf), и это не отразится
на других заданиях (если флаг не указан, в дочернем задании создается копия табли
цы сигналов)
CLONE_PID Совместное использование идентификатора процесса; применять данный флаг следует
осторожно, так как он не всегда поддерживается (как это имеет место в случае биб
лиотеки Pthreads); если флаг не указан, в дочернем задании создается новый иденти
фикатор процесса
if ( (pchild = fork()) == 0 )
Child();
else if ( pchild > 0 )
www.books-shop.com
wait();
else
perror( "Can't fork process");
/***********************************************/
/*** Библиотечная функция pthread_create () ***/
/***********************************************/
void* Child (void *arg)
{
/* код потомка */
return &Result;
}
int main (void)
{ pthread_t tchild;
if ( pthread_create(Stchild, 0, &Child, 0) != 0
perror ( "Can ' t create thread" ) ;
pthread_join(tchild, 0);
}
Листинг 7.7. Эквивалентный пример функции clone ()
/*****************************************/
/*** Эквивалент системному вызову fork() */
/*****************************************/
void Child(void *arg)
{
/* код потомка */
exit(O);
}
idefine STACK 1024
int main(void)
{ int cchild;
char *stack = mallocfSTACK);
if ((cchild = _clone(&Child, stack+STACK1, SIGCHLD, 0)) == 0)
{/** секция дочернего задания — недоступна **/}
else if ( cchild > 0 )
wait();
else
perror("Can't clone task");
}
/********************************************/
/*** Эквивалент функции pthread_create() ***/
/********************** "~
void* Child(void *arg)
{
/* код потомка */
exit(O);
}
www.books-shop.com
tdefine STACK 1024
int main(void)
{ int cchild;
char *stack = malloc(STACK);
if ((cchild = _clone(&Child, stack+STACK
1, CLONE_VM |
CLONE FS | CLONE_FILES | CLONE_SIGHAND |
SIGCHLD, 0 ) ) < 0)
perror("Can't clone");
wait();
}
Функцию _clone() можно использовать практически везде, где стоит вызов
одной из двух эквивалентных ей функций. Следует, однако, отметить, что она не
полностью совместима с библиотекой Pthreads. Например, клонированное зада
ние может возвращать только целочисленное значение (как и процесс), тогда как
в библиотеке Pthreads допускается возвращение произвольного значения из пото
ка. Кроме того, отличается процедура завершения задания. Если создается про
Взаимодействие заданий
При создании заданий необходимо позаботиться об их дифференцировании,
чтобы они не выполняли дублирующуюся работу. Это можно сделать как путем
полного разделения процессов, так и путем организации взаимодействия потоков.
К примеру, рассмотрим игру в покер. Игра делится на несколько фаз, в каждой
из которых игроки должны подчиняться определенным правилам. Поскольку у
игроков на руках разные карты, а каждый из участников имеет свой стиль игры и
неодинаковую сумму денег, игра становится интересной. Точно так же и в про
Инициализация потомка
В покере каждый игрок покупает набор фишек, а сдающий раздает карты. У
каждого из игроков свои принципы торговли, а на сдающего возложена дополни
стоянии. После запуска как процессы, так и потоки следуют свои путем, обычно
не пересекаясь с родительской программой.
Родительская программа передает дочернему заданию данные, формируя сре
ги!") характер.
Процедура запуска процессов и потоков в целом похожа. В первую очередь
необходимо определить совместно используемые области памяти, а затем создать
новое задание. Потоки дополнительно могут принимать параметр типа void*. Тип
данных void* определен в языке С для работы с абстрактными структурами. Бла
годаря ему можно передавать в потоковую функцию любое значение, а уже при
www.books-shop.com
нимающая сторона будет анализировать это значение. Получение данного пара
Общедоступная память
В покере имеются два ресурса: кон и колода. От них зависит, как играть и
когда говорить "пас". Эти ресурсы ограничены и используются игроками совме
щую кому
то другому. Совместное использование памяти заложено в концепцию
потоков: родительская программа и дочерний поток (а также все параллельные
потоки) по умолчанию имеют доступ к одним и тем же областям памяти. Единст
темного вызова shmget(). Через этот блок памяти процессы могут обмениваться
данными, однако их работа замедлится, поскольку управление доступом к памяти
берет на себя операционная система.
Взаимодействие процессов
Во время игры в покер игроки обмениваются сообщениями друг с другом и со
сдающим. Некоторые сообщения являются инструкциями, а некоторые носят
эмоциональный характер. Один игрок может передать сообщение другому игроку
или всем присутствующим.
Проектируемое задание может взаимодействовать с другими процессами.
Обычно подобное взаимодействие организуется в форме каналов: сообщение на
www.books-shop.com
Ниже представлен пример использования функции pipe():
/****************************************************************/
/*** Пример функции pipe() ***/
/****************************************************************/
int f d [ 2 ] ; /* создание массива, содержащего два дескриптора */
if ( pipe(fd) != О ) /* создание канала */
perror("Pipe creation error");
Индексация дескрипторов
Каждому каналу вводавывода назначается номер, который является индексом в таблице деск
рипторов файлов. По умолчанию у каждого процесса, есть три канала вводавывода: stdin(0),
stdout (1) и stderr (2). Когда создается новый канал, ему назначаются две ближайшие пози
ции в таблице (одна — для входного конца, другая — для выходного). Например, если у задания
нет открытых файлов, то в результате вызова функции pipe() будет создан канал чтения (3) и
канал записи (4).
Создавать канал в многозадачной среде довольно сложно. Но достаточно сде
www.books-shop.com
int FDs[2); /* создание массива, содержащего два дескриптора */
pipe(FDs); /* создание канала: FDs[0] чтение, FDs[l] запись */
char buffer[1024];
/*— Создание потока с помощью функции pthread create() —*/
write(FDs[l], buffer, buffer_len); /* передача сообщения
дочернему заданию */
/*— закрытие канала предоставляется потомку —*/
pthread_join(pchild, arg);
Дочерние процессы и потоки решают другие задачи и программируются по
разному. Сравните листинги 7.10 и 7.11.
Листинг 7.10. Создание канала в дочернем процессе
www.books-shop.com
read(FDs[0], buffer, sizeof(buffer)); /* чтение сообщения от
родительского процесса */
pthread_exit(arg);
При создании канала между двумя заданиями пользуйтесь схемой, представ
ность действий.
1. Родительская программа объявляет массив дескрипторов файлов. Этот
массив будет заполняться функцией pipe().
2. Родительская программа создает канал. При этом ядро создает очередь
ввода
вывода и помещает дескриптор канала чтения в элемент fd[0], a
дескриптор канала записи — в элемент fd[l].
стно используют таблицы дескрипторов файлов. Это означает, что если закрыть
файл в одном потоке, то он автоматически закроется во всех остальных потоках
программы. В то же время процессы создают копии таблиц дескрипторов, так что
закрытие одного из каналов не влияет на работу процессов программы.
www.books-shop.com
Рис. 7.2. При создании канала между заданиями необходимо придержи!
ваться определенной последовательности действий по перенаправле!
нию и закрытию каналов
www.books-shop.com
Рис. 7.3. Если между предком и потомком необходимо организовать
двунаправленное соединение, создайте дополнительный канал
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Сигнализация о завершении
В покере есть несколько прямых управляющих команд. В любой момент игрок
может сказать "пас" (выйти из текущей игры). Если бы игра происходила на Ди
работчику до тех пор, пока не будет явно остановлен). Если сигнал посылается с
помощью системного вызова signal(), он будет однократным. Но на это можно
не обращать внимания: следует ожидать, что сигнал будет перехватываться мно
гократно.
Сброс сигнала
Для сброса сигнала некоторые программисты помещают вызов системного обработчика сигна
лов непосредственно в тело собственного обработчика. К сожалению, это может привести к
возникновению такого состояния, как "гонка", когда сигнал приходит раньше, чем вызывается
сбрасывающая его функция.
Вместо функции signal() лучше пользоваться системным вызовом sigaction(),
который позволяет лучше контролировать поведение сигнальной подсистемы.
Прототип этой функции таков:
iinclude <signal.h>
int sigaction(int sig_num, const struct sigaction *action,
const struct sigaction *old);
Первый параметр определяет номер перехватываемого сигнала. Второй пара
метр задает способ обработки сигнала. Если последний параметр не равен NULL,
будет запомнено последнее выполненное действие. Ниже приведено определение
структуры sigaction:
www.books-shop.com
struct sigaction
{
/* Указатель на функцию обработки */
void (*sa_handler)(int signum);
/* Специальная функция обратного вызова */
void (*sa_sigaction)(int, siginfo t *, void *);
/* Массив битовых флагов, указывающих, какие сигналы
следует игнорировать, находясь в теле обработчика */
sigset_t sajnask;
/* Выполняемое действие */
int sa_flags;
/* (больше не используется
должно быть равно 0) */
void (* sa_restorer)(void);
};
Для активизации третьего параметра необходимо, чтобы первое поле
(sa_handler) отличалось от указателя на функцию во втором параметре. Если по
пользуйтесь третьим полем структуры, sa mask. Каждый бит (всего их 1024) обо
значает разрешение (1) или запрет (0) обработки сигнала. По умолчанию обра
чен.
• SA_RESETHAND. To же, ЧТО И SA_ONESHOT.
• SA_RESTART. Повторный запуск некоторых системных функций, если сиг
www.books-shop.com
Листинг 7.12. Обработчик сигналов SIGFPE и SIGINT
#include <signal.h>
int main(void)
{ struct sigaction act;
bzero(&act, sizeof(act));
act.sa_handler = sig_catcher;
sigaction(SIGFPE, act, 0); /* перехватываем ошибку в
операции с плавающей запятой */
www.books-shop.com
#include <sys/types.h>
#include <signal.h>
int kill(pid_t PID, int sig_num);
Детальное описание параметров этой функции можно найти в интерактивном
справочном руководстве. По сути, программа вызывает функцию kill(), указывая
идентификатор задания и сигнал, который следует ему послать.
вых переменных. Лучше передавать данные через кучу или глобальную перемен
ную.
семафоры и гонки
Сила, заключенная в потоках, очень привлекательна. Если правильно управ
сам.
Гонки за ресурсами
Возможно, вам знакомо состояние гонки, в котором оказываются два потока,
пытающиеся сохранить свои данные. Ранее в этой главе рассматривался пример,
когда гонка возникала при сбросе сигнала. Критической секцией называется раз
www.books-shop.com
int queue[lO];
int in, out, empty;
/************** поток 1 **************/
/* Чтение данных из очереди */
if ( !empty ) /* избегаем чтения пустой очереди */
{ int val = queue[out];
out++;
if ( out >= sizeof(queue) )
out = 0; /* начинаем заново */
empty = (out == in);
}
int queue[10] ;
int in, out, empty;
/************** поток 2 **************/
/* Запись данных в очередь */
if ( Jempty && out != in ) /* избегаем переполнения очереди */
{ queue [in] = 50;
дит очень редко. Что если первый поток отстает от второго на несколько строк?
Поток 2 мог выполнить проверку переменной empty как раз перед тем, как по
ток 1 сбросил значение переменной out. Возникнет проблема, так как перемен
Исключающий семафор
Работать с критическими секциями можно, блокируя другие процессы при об
www.books-shop.com
вый захватил ресурс, тот блокирует остальных) или сериализацией (разрешение
одновременного доступа к ресурсу только для одного задания). Эта методика по
скую секцию. Если флаг установлен (семафор поднят), поток блокируется до тех
пор, пока доступ не будет разрешен.
Существуют две методики блокировки: грубая и точная. В первом случае, ко
хватывает его в свое распоряжение. Если оказывается, что ресурс уже зарезерви
pthread_mutex_unlock(&mutex);
/* Конец критической секции */
www.books-shop.com
• С проверкой ошибок — PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP. Прове
мафор, что и поток, который заблокировал его. Если это другой поток,
возвращается ошибка и блокировка не снимается.
В библиотеке имеется дополнительная функция pthread_mutex_trylock(), кото
библиотеке Pthreads
При работе с библиотекой Pthreads следует помнить о некоторых ограничени
ях. Во
первых, исключающий семафор не содержит ссылку на блокируемую об
ласть памяти. Он представляет собой всего лишь флаг. Поэтому существует воз
можность, что два разных потока используют один и тот же семафор для блоки
Предотвращение взаимоблокировки
Представьте двух детей, играющих одними и теми же игрушками. Каждый ре
бенок видит игрушку другого и хочет ее, но не желает отдавать свою. В програм
www.books-shop.com
кированы, прежде чем работа продолжится. Если проявить невнимательность,
возникнет взаимоблокировка.
Рассмотрим следующий пример.
Поток 1 Поток 2
1. Блокирует семафор Funds_Mutex_1. 1. Блокирует семафор Funds_Mutex_2.
2. Блокирует семафор Funds_Mutex_2. 2. Блокирует семафор Funds_Mutex_1.
3. Используя семафор Funds_Mutex_2, изменяет 3. Используя семафор Funds_Mutex_1, изменяет
семафор Funds_Mutex_1. семафор Funds_Mutex_2.
4. Разблокирует семафор Funds_Mutex_2. 4. Разблокирует семафор Funds Mutex_2.
5. Разблокирует семафор Funds_Mutex_1. 5. Разблокирует семафор Funds Mutex_1.
за того, что оба потока ожидают ресурсов друг друга. Ниже перечислены правила,
которые позволяют снизить вероятность возникновения взаимоблокировок:
• наименование ресурсов по группам — идентифицируйте взаимосвязан
заданиязомби
Итак, мы создали несколько потоков и процессов. Всеми дочерними задания
ми можно управлять. Вопрос в том, как это делать. Выше уже говорилось о том,
что взаимодействовать с дочерними заданиями можно посредством сигналов, пе
www.books-shop.com
же отказываться от владения ими. В Linux применяется алгоритм приоритетного
кругового обслуживания. В библиотеке Pthreads поддерживаются три алгоритма:
• обычный — аналогичен алгоритму планирования в Linux (принят по
умолчанию);
• круговой — планировщик игнорирует значение приоритета, и каждый
поток получает свою долю времени, пока не завершится (этот алгоритм
применяется в системах реального времени);
• FIFO — планировщик помешает каждый поток в очередь и выполняет
его до тех пор, пока он не завершится (этот алгоритм также применяет
вать задание, имеющее статус Z (зомби). Обычно его можно уничтожить, уничто
Появление зомби
Предок должен заботиться обо всех своих потомках. Но он не всегда это делает. Если родитель
ский процесс завершился, оставив после себя дочерние процессы, ждущие подтверждения о за
вершении, управление над ними принимает программа init. В ее обязанности входит планиро
вание, выполнение и завершение процессов, однако она не всегда справляется с последней ча
стью задачи. В этом случае в таблице процессов появляются:зомби. Их нельзя удалить даже с
помощью команды kill. (Можно попробовать выполнить команду init s или init 1 для очи
стки таблицы процессов, но нет гарантии, что она будет работать.)
В отличие от процессов, создаваемых с помощью системного вызова fork()
или _clone(), библиотека Pthreads позволяет отказаться от владения потоком
(отсоединить его). Отсоединив поток, можно продолжить выполнение програм
www.books-shop.com
мы, не дожидаясь его завершения. Объявление соответствующей библиотечной
функции выглядит так:
#include <pthread.h>
int pthread_detach(pthread_t tchild);
Параметр tchild является ссылкой, получаемой от функции pthread_create().
В результате выполнения функции могут возникать следующие ошибки.
• ESRCH. Для заданного параметра tchild не найден поток.
• EINVAL. Поток уже был отсоединен.
Общая схема использования функции pthread_detach() такова:
/****************************************************************/
/*** Пример отсоединения потока от родительского задания. ***/
/*** это позволяет заданию продолжить свою работу, ***/
/*** не проверяя завершение дочернего потока. ***/
/**************************************************************/
/* Создание переменной, содержащей ссылку на поток */
pthread_t tchild;
/* Создание потока */
if ( pthread_create(&tchild, 0, Child_Fn, Child_Arg) != О )
perror( "Could not create thread");
else
/* Отсоединяем поток */
pthread_detach(tchild);
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Чтобы иметь возможность получать подобные уведомления, необходимо свя
клиента и сервера
Концепции, рассмотренные в этой главе, можно применить к серверам и кли
сколько клиентов telnet. Это именно то, чего ждут пользователи, — работа с
многозадачным сервером в псевдомонопольном режиме.
www.books-shop.com
Вызов внешних программ с помощью
формацию о заданиях с помощью команды ps, чем делать это программным спо
собом. Кроме того, писать анализатор строк намного удобнее на Perl, чем на С.
Каким образом можно воспользоваться преимуществами готовых программ?
Набор системных вызовов семейства ехес() расширяет возможности функции
fork(), позволяя вызывать внешние программы и взаимодействовать с ними. Это
напоминает использование команд CGI в Web
среде.
• На одном из сетевых узлов содержится CGI
сервер, принимающий ко
вается.
www.books-shop.com
/* Путь к команде ls ищется в переменной PATH */
if ( execlp("ls", "ls", "
aF", "/etc", NULL) != 0 )
perror("execlp failed");
exit(
l);
• execle(). Аналогична функции execl(), но дополнительный параметр
представляет собой массив строк, содержащих установки переменных
среды (не забывайте оканчивать его значением 0 или NULL).
/* Например: */
char *env[] = {"PATH=/bin:/usr/bin", "USER=gonzo",
"SHELL=/bin/ash", NULL};
if ( execle("/bin/ls", "/bin/ls", "
aF", "/etc", NULL, env) != 0 )
perror("execle failed");
exit(
l);
• execv(). Принимает два параметра. Первый представляет собой полное
путевое имя программы. Второй является массивом аргументов команд
ней программы, поэтому не нужна даже проверка if. Если после нее все же вы
полняются какие
либо инструкции, то это означает, что функция ехес() потерпе
ла неудачу.
Чтобы применить эту функцию в рассматриваемом нами сервере, замените
цикл ожидания клиентских подключений следующим кодом:
while(l)
{ int client, addr size = sizeof(addr);
www.books-shop.com
client = acceptfsd, &addr, &addr_size);
printf("Connected: %s:%d\n", inet_ntoa(addr.sin_addr),
ntohs(addr.sin_port));
if ( £ork() )
close(client);
else
{
close(sd); /* клиенту не нужен доступ к сокету */
dup2(client, 0); /* замещаем поток stdin */
dup2(client, 1); /* замещаем поток stdout */
dup2(client, 2); /* замещаем поток stderr */
execl("/bin/ls", "/bin/ls", "al", "/etc", 0);
perror("Exec failed!"); /* чтото случилось */
}
}
www.books-shop.com
Резюме
Многозадачность является средством ускорения работы программ и повыше
www.books-shop.com
Глава
Механизмы ввода
вывода
8
В этой главе...
необходимо
Блокирование вводавывода: зачем оно
173
Когда следует переходить в режим блокирования? 175
Альтернативы блокированию. 175
Сравнение различных методик вводавывода 176
Опрос каналов вводавывода
l77
Асинхронный вводвывод 182
Устранение нежелательного блокирования с
помощью фунщий pоll() и select() 187
Реализация таймаутов 190
Резюме: выбор методик вводавывода 191
www.books-shop.com
Учитывая современные требования к производительности, не помешает изу
чить методы экономии времени при работе в сети. Важно помнить, что самый
критический, самый лимитированный ресурс компьютера — это центральный
процессор. Любому заданию, выполняющемуся в системе, требуется получить
доступ к процессору, причем доступ должен быть ограничен небольшими проме
ет ввода
вывода, оно приостанавливается (блокируется), вследствие чего
активизируется другое задание.
Не всегда требуется дожидаться завершения операций ввода
вывода, чтобы
продолжить выполнение задания. Представьте, как утомительно было бы рабо
тать с броузером, который позволяет закрыть себя только после того, как страни
руемого ввода
вывода и задать предельное время ожидания (тайм
аут). Для опре
вывода, либо приказать ядру послать профамме сигнал при наличии данных. Это
позволит профамме сосредоточиться на взаимодействии с пользователем или на
выполнении основного вычислительного алгоритма.
Блокирование ввода
вывода может применяться в сочетании с многозадачно
стью или заменять ее. Это еще одно мощное средство в арсенале профаммиста.
Определить, когда следует применять режим блокирования, а когда — нет, не
всегда легко. Этой проблеме и посвящена данная глава.
Блокирование ввода$вывода:
зачем оно необходимо?
В многозадачной среде можно выполнять множество действий одновременно.
Правильно спроектированная профамма должна на 100% подходить для симмет
сти системы.
www.books-shop.com
Одно из правил гласит, что любое задание периодически должно давать воз
гда чем
то занято
Ответ будет "и да, и нет". Одной из причин простоя является ожидание за
вершения какой
либо операции ввода
вывода. Чтобы понять это, сравните ско
полняется на процессоре Pentium III с частотой 500 МГц, для выполнения каж
дой инструкции в общем случае требуется один такт процессора. В среднем это
2—4 нс. За то время, пока происходит позиционирование головки диска, про
ный показатель для сетей TCP/IP). В сети с пропускной способностью 100 Мбит
этот показатель увеличивается лишь в 10 раз (3 Мбайт/с). Даже если не учиты
должает ВЫПОЛНЯТЬСЯ:
Блокирование заданий, осуществляющих ввод
вывод, — это, скорее, правило,
чем исключение. Чтобы узнать, сколько заданий в действительности выполняют
www.books-shop.com
Когда следует переходить в режим
блокирования?
Программа может блокироваться (перестать выполняться и перейти в режим
ожидания), если для завершения какой
либо операции ввода
вывода требуется
время. Каждый раз, когда программа делает системный вызов, операционная сис
кой
нибудь из буферов не освободится.
• Подключение — блокирование данного типа происходит, когда функции
accept() и connect() не обнаруживают поступивших запросов на под
Альтернативы блокированию
Какие имеются альтернативы блокированию? Можно заставить программу вы
грамма может:
• проверить целостность данных;
• инициировать другие запросы или отслеживать их появление;
• обслуживать несколько других соединений;
• выполнять вычисления, требующие интенсивного использования про
цессора.
Устранение возможного блокирования может показаться очень привлека
www.books-shop.com
Асинхронный и сигнальный вводвывод
Алгоритмы асинхронного вводавывода, представленные в этой главе, в действительности рабо
тают на основе сигналов, которые посылаются, когда буферы готовы для чтения или записи. При
истинно асинхронном вводевыводе, который определен в стандарте POSIX.1, никогда не возни
кает блокирование. Например, вызов функции read() немедленно завершается. Буферы счита
ются незаполненными до тех пор, пока не завершится операция чтения и программа не получит
сигнал. Linux (как и многие другие операционные системы) не соответствует стандарту POSIX.1,
касающемуся асинхронного вводавывода для сокетов. Чтобы не грешить против истины, лучше
употреблять термин "сигнальный вводвывод".
системой ввода
вывода. Программа может получать данные из внешних источни
ков. Четкий контроль над тем, когда и как следует переходить в режим блокиро
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Работая в режиме блокирования, программа ожидает поступления данных. В
режиме опроса программа вызывает функцию recv() до тех пор, пока данные не
появятся. В режиме тайм
аута программа может вообще не получить данные, ес
ливается, ожидая, пока ядро полностью отправит сообщение. (Это не совсем вер
но, так как в действительности ядро ожидает момента, когда можно будет завер
кируемых режимов.
www.books-shop.com
интересует, как и когда сообщение покинет компьютер. С ее точки зрения под
система ввода
вывода отправляет сообщение целиком и за один прием. Но часто
это не так. Большинство сообщений разбивается на фрагменты из
за ограничен
ной пропускной способности сети. Точно так же при приеме сообщения оно мо
вую очередь сокет должен быть переведен в неблокируемый режим. Затем необ
лам ввода
вывода, ее эффективность возрастает. Но если не задать секцию вы
метре либо дескриптор файла, либо дескриптор сокета. Параметр command должен
www.books-shop.com
быть равен F_SETFL, а параметр option — O_NONBLOCK. (У этих параметров очень
много возможных значений, которые перечислены в приложении В, "API
функции ядра")
Формат использования функции таков:
if ( f c n t l ( s d , F_SETFL, 0_NONBLOCK) != О )
perror("Fcntl — could not set nonblocking");
Для примера предположим, что создается модуль музыкального проигрывате
ля. Вместо того чтобы ждать, пока весь аудиоклип будет загружен из сети
Internet, можно воспроизводить поступающие данные. Естественно, следует так
же учитывать, что данные могут приходить в сжатом виде. Общий алгоритм про
www.books-shop.com
Запись данных по методике опроса
Программный код выполняется в несколько раз быстрее, чем любая функция
ввода
вывода. (По сути, чтобы производительность компьютера повышалась, не
точно долго.
Алгоритм записи данных по методике опроса напоминает рассмотренный вы
int pos[MAXCLIENTS];
bzero(pos, sizeof(pos));
for ( i = 0; i < ClientCount; i++ )
if ( fcntl(client[i], F_SETFL, 0_NONBLOCK) != 0 )
perrorf'Fcntl — could not set nonblocking");
•••
done = 0;
/*— повторяем до тех пор, пока все клиенты —*/
/*— не получат сообщение целиком —*/
while( !done )
{ int bytes;
done = 0;
/*— для всех клиентов —*/
for { i = 0; i < ClientCount; i++ )
/*— если имеются неотправленные данные... —*/
if ( pos[i] < size )
{
/*— отправляем сообщение, отслеживая, —*/
/*— сколько байтов послано —*/
bytes = send(client[i], buffer+pos[i], sizepos[i], 0);
if ( bytes > 0 )
{
pos[i] += bytes;
/* если сообщение благополучно отправлено —*/
/*— всем клиентам, завершаем работу —*/
www.books-shop.com
if ( pos[i] < size )
done = 0;
www.books-shop.com
Представленный в программе алгоритм может показаться очень привлекатель
Асинхронный вводвывод
Проблему файлового ввода
вывода можно решить, если заставить операцион
грамма получает сигнал SIGIO, когда данные накоплены в буфере чтения или бу
фер записи готов для приема следующей порции данных. Как и в случае со всеми
остальными сигналами, программа не получает никакой дополнительной инфор
стояние канала.
Программа переходит в режим сигнального ввода
вывода, уведомляя ядро о
том, что она готова обрабатывать сигнал SIGIO. Когда поступает сигнал, его обра
www.books-shop.com
Листинг 8.3. Алгоритм асинхронного вводавывода
/***************************************************************/
/*** Общий алгоритм асинхронного, или сигнального, ***/
/*** вводавывода ***/
/***************************************************************/
int ready=0;
void sig_io(int sig)
{
/*** функция recv(): получаем все данные из буфера ***/
/*** функция send(): отправляем все обработанные данные ***/
ready =1; /* сообщаем программе о завершении транзакции */
}
for (;;)
{
if ( ready > 0 )
{
/*** Временно блокируем сигнал SIGIO ***/
ready = 0;
/*** функция recv(): копируем данные в буферы
для обработки ***/
/*** функция send(): заполняем выходной буфер из очереди
обработанных данных ***/
/*** Разблокируем сигнал SIGIO ***/
}
/*** Обработка поступающих данных ***/
/*** ИЛИ ***/
/*** Подготовка новых данных для отправки ***/
}
Блокирование сигнала SIGIO может показаться несколько необычным. Оно не
обходимо из
за того, что обработчик сигнала и основная программа имеют доступ к
одной и той же переменной, поэтому фактически в данном месте программы при
www.books-shop.com
/*** Запуск обработчика сигналов SIGIO ***/
/*****************************************************/
ступления (по запросу). Когда приходит новая порция данных, ядро посылает
программе сигнал. В ответ на это программа извлекает данные из буфера и обра
www.books-shop.com
/*— сигналы SIGIO и SIGURG —*/
if ( fcntl(sd, F_SETOWN, getpid()) < 0 )
PANIC("Can't own SIGIO");
while ( Idone )
if ( ready > 0 )
/*** Временно блокируем сигнал SIGIO ***/
ready = 0;
FillQueue(Queue, buffer, bytes);
/*** Разблокируем сигнал SIGIO ***/
ность, что все сообщение целиком поместится в буферах ядра. Но возможно так
же, что этого не произойдет и сохранена будет только часть данных. Поэтому не
www.books-shop.com
int ready=0, bytes, size=0, pos=0;
Подключение по запросу
Теоретически можно создать сервер, устанавливающий соединения с клиента
int Connections[MAXCONNECTIONS];
int sd, NumConnections=0;
void sig_io(int sig)
{ int client;
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
/* — прием запросов на подключение; если запросов — */
/* — слишком много, выдаем сообщение об ошибке — */
/* — и разрываем связь — */
if ( (client = acceptf sd, 0, 0) > 0 )
if ( NumConnections < MAXCONNECTIONS )
Connections [NumConnections++] = client;
else
{
sendfclient, "Too many connections !\n, 22, 0");
close(client);
}
else
perror(" Accept");
}
Хотя такой сервер может оказаться полезным, преимущество многозадачности
заключается в том, что она позволяет, во
первых, сохранять значения локальных
переменных, а во
вторых, обрабатывать каждый запрос на подключение по от
дельности, по
своему.
Устанавливать соединения по запросу трудно с точки зрения программирова
Устранение нежелательного
роll() и select()
В Linux есть две функции, которые помогают работать с несколькими откры
менению.
Функция select () довольно сложна. С ней связан ряд дополнительных макро
www.books-shop.com
FD_CLR(int fd, fd_set *set); /*. удаляет дескриптор из списка */
FD_ISSET(int f d , fd_set *set); /* проверяет наличие
дескриптора в списке */
FD_SET(int fd, fd_set *set); /* добавляет дескриптор в список */
FD_ZERO(fd_set *set); /* инициализирует список дескрипторов */
Описание параметров функции select() приведено в табл. 8.1.
www.books-shop.com
timeout.tv_usec = 250000;
/*— Ожидаем завершения функции select() —*/
if ( (count = select(6+l, &set, 0, 0, &itimeout)) > 0 )
/*** Находим канал, состояние которого изменилось ***/
else if ( count == 0 )
fprintf(stderr, "Timed out!");
else
perror("Select");
ло приоритетное сообщение.
• POLLOUT. Канал готов. Функция завершается, если вызов функции write ()
не будет блокирован.
Объявление функции poll() выглядит так:
linclude <sys/poll.h>
int poll(struct pollfd *list, unsigned int cnt, int timeout);
Программа заполняет массив структур pollfd, прежде чем вызвать функцию.
Параметр cnt определяет число дескрипторов в массиве. (Учтите, что массив де
www.books-shop.com
может работать с пустыми структурами.) Параметр timeout аналогичен одноимен
Реализация таймаутов
Вместо опроса каналов можно сообщить операционной системе о том, что ждать
требуемого события нужно не дольше указанного времени. В Linux это нельзя сде
мени.
ка EINTR.
Любое задание может послать сигнал самому себе, но оно должно знать, как
его обрабатывать. В некоторых системах тайм
аут задается с помощью функции
alarm(), но предварительно следует включить обработку сигнала SIGALRM. Рассмот
рим пример:
www.books-shop.com
void reader()
{ struct sigaction act;
лось о том, что при обработке сигналов данный флаг следует задавать. Но как
видно из примера, это не относится к режиму тайм
аутов.
И последнее замечание: избегайте использовать функцию alarm() вместе с
системным вызовом sleep(), так как это может привести к возникновению про
www.books-shop.com
В этой главе рассматривались вопросы, связанные с блокированием ввода
вывода (что это такое, когда оно необходимо и для чего оно нужно), а также
описывались методики, позволяющие его избежать. Мы познакомились с функ
кальным вычислениям.
Еще одним важным инструментом являются тайм
ауты. Их можно реализовать
посредством функции select() или poll() либо с помощью сигнала таймера, ко
www.books-shop.com
Глава
Повышение
9 производительности
В этой главе...
Подготовка к приему запросов на подключение 194
Расширение возможностей сервера с помощью
функции select() 200
Анализ возможностей сокета 206
Восстановление дескриптора сокета 212
Досрочная отправка: перекрытие сообщений 213
Проблемы файлового вводавывода 213
Вводвывод по запросу: рациональное
использование ресурсов процессора 214
Отправка приоритетных сообщений 215
Резюме 217
www.books-shop.com
Как добиться максимальной производительности сервера или клиента? В биб
лиотеке Socket API имеется ряд средств, позволяющих решить эту задачу доста
точно просто. Тем не менее следует рассмотреть проблему под разными углами,
поскольку все ее аспекты тесно связаны друг с другом.
В сетевой программе можно выделить три основных компонента, требующих
отдельного анализа: задание (главный процесс), соединение (сокет) и сообщение.
Первый компонент отвечает за управление всеми связанными процессами и по
токами. Второй компонент — это сам сокет и его параметры. Третий компо
подключение
К настоящему моменту мы узнали, как создать сервер, как сделать его много
www.books-shop.com
addr.sin_addr = INADDR_ANY;
if (bind(sd, &addr, sizeof(addr)) != 0 )
PANIC("bind() failed");
if (listen(sd, 20) != 0)
PANIC ("listen() failed");
for (;;}
{ int client, len=sizeof(addr);
client = accept(sd, &addr, &len);
if (client > 0)
{ int pid;
if ( (pid = fork()) == 0 )
{
close(sd);
Child(client); /* Обслуживаем нового клиента */
}
else if( pid > 0 )
close(client) ;
else
perror("fork() failed");
}
}
Этот алгоритм приводился в главе 7, "Распределение нагрузки: многозадач
www.books-shop.com
for (;;)
{ int client, len=sizeof (addr);
while ( ChildCount >= MAXCLIENTS )
sleep(l);
client = accept (sd, &addr, &len);
if ( client > 0 )
{ int pid;
if ( (pid = fork()) == 0 )
{/* — Потомок — */
close(sd);
Child(client); /* — Обслуживаем нового клиента — */
}
else if ( pid > 0 )
{/* — Предок — */
ChildCount++;
close(client) ;
}
else
perror("fork() failed");
}
}
В этом примере программа отслеживает число дочерних процессов. Когда их
становится слишком много, программа просто переходит в "спяший" режим, по
нения одной
единственной транзакции, накладные расходы, связанные с созда
вера к нулю. Идея заключается в том, чтобы иметь набор уже выполняющихся
процессов, ожидающих поступления запросов на подключение.
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Как можно заметить, дочерний процесс закрывает дескриптор сокета (sd). Но
ведь клиентский дескриптор идентифицирует само соединение! Зачем процесс
его закрывает?
Дело в том, что, когда создается дочерний процесс, он принимает дескрипто
main()
else
sleep(l);
}
}
Здесь применяется обратный порядок вызова функций accept() и fork(): вме
сто того чтобы создавать новый процесс после подключения, программа сначала
запускает дочерний процесс, который затем ожидает поступления запросов.
В этом фрагменте может быть создано, к примеру, 10 процессов. Все они вхо
www.books-shop.com
цессы блокируются (переходят в "спящий" режим). Когда появляется запрос, все
процессы "пробуждаются", но только один из них устанавливает соединение, а
остальные девять снова "отходят ко сну". Подобный цикл продолжается беско
нечно долго.
Что произойдет, если один из процессов завершит работу? Именно по причи
ности системы. Сервер должен решать две разные задачи: он должен определять,
когда допускается создавать новые сервлеты и когда требуется уничтожать лишние
процессы. Вторая задача проще: процесс
сервлет сам себя уничтожает, если нахо
www.books-shop.com
Один из возможных методов напоминает ловлю рыбы на приманку. Рыбак пе
ется этот процесс, позволяет определить, сколько рыбы водится в пруду или дан
запно может оказаться, что функция fork() просто вызывается для каждого но
му, чтобы избежать этого. Кроме того, требуется свести к минимуму число вы
полняющихся сервлетов.
Именно здесь на помощь приходит статистика. В адаптивном алгоритме воз
тов равно числу завершившихся. Например, если сервер порождает один сервлет
каждые 60 секунд (с 30
секундным предельным временем простоя), схема этого
процесса будет соответствовать верхней части рис. 9.1.
www.books-shop.com
int delay=MAXDELAY; /* например, 5 секунд */
time_t lasttime;
void sig_child(int signum)
{
wait(O); /* подтверждаем завершение */
timej&lasttime); /* определяем текущее время */
delay = MAXDELAY; /* сбрасываем значение задержки */
}
void Chuiraner( void (*servlet)(void))
{
time(&lasttime) ; /* запоминаем дату начала процесса */
for (;;)
{
if ( !fork() ) /* создаем новый сервер */
servlet(); /* вызываем потомка (должен завершиться/
с помощью функции exit()) */
sleep(delay); /* берем таймаут */
/* если ни один из сервлетов не завершился,
удваиваем частоту */
if ( times[0] timesll] >= delay 1 )
if ( delay > MINDELAY ) /* не опускаемся ниже
минимального порога */
delay /= 2; /* удваиваем частоту */
}
}
В этом фрагменте программы демонстрируется, как управлять группой сервле
дится иметь дело с большой таблицей процессов. Вторая заложена в самой кон
www.books-shop.com
Таблица процессов легко может включать несколько сотен процессов. В зависи
ние задач происходит каждые 0,01 с. При условии, что в этом промежутке пере
дается 1 Кбайт данных, общая скорость передачи данных составит 200 Кбайт/с в
сети с пропускной способностью 10 Мбит/с (примерная норма в сетях TCP/IP).
Но в каждом конкретном соединении скорость передачи будет лишь 512 байт/с.
Это основное следствие неконтролируемого роста числа процессов.
Вторая проблема связана с сутью механизма предварительного ветвления: не
тать запрос.
Однако соблазн отказаться от многозадачности и перейти к методике опроса
каналов может привести к потере производительности. Когда за обработку ин
да
вывода. При этом теряется возможность выполнять другие действия. К приме
щью опции
j утилиты make. (При наличии достаточного количества оперативной
памяти распределение обязанностей между двумя или тремя заданиями на каж
www.books-shop.com
Разумное использование функции select()
При создании мощного многопользовательского сервера распределять нагрузку
можно различными способами. Два из них — это многозадачность и опрос кана
лов ввода
вывода. Но если использовать только многозадачный режим, можно
потерять контроль над системным планировщиком и драгоценное время на бес
/* — в дочернем процессе — */
maxfd ~ sd;
FD SET(sd, &set);
f or ( ; ; )
{ struct timeval timeout={2,0}; /* 2 секунды */
/* — Ожидаем команды — */
if ( select (maxfd+1, &set, 0, 0, stimeout) > 0 )
{
/* — Если новое соединение, принимаем его — */
/* — и добавляем дескриптор в список — */
if ( FD_ISSET(sd, &set) )
{ int client = accept(sd, 0, 0);
if ( maxfd < client )
maxfd = client;
FD SET(client, &set);
}
/* — Если запрос от существующего клиента, — */
/*— обрабатываем его— */
else
/*** обработка запроса ***/
/* — ЕСЛИ клиент завершил работу, — */
/* — удаляем дескриптор из списка — */
}
www.books-shop.com
В этой программе создается небольшое число процессов (намного меньше,
чем в чисто многозадачном сервере), например 5—10 сервлетов, С каждым из
сервлетов, в свою очередь, может быть связано 5
10 соединений.
Коллизии выбора
Возможно, читателям доводилось слышать о проблеме в ВSD
системах, которая называется
коллизия выбора. В рассмотренной программе предполагается, что функция select()
"пробуждается" только в том случае, когда меняется состояние одного из дескрипторов. Однако
в BSD4.4 пробуждаются одновременно все процессы, заблокированные функцией select().
Похоже, что ОС Linux лишена подобного ограничения.
Подобный алгоритм позволяет обрабатывать сотни соединений без каких бы
то ни было конфликтов. Благодаря балансу между числом заданий и обслуживае
Проблемы реализации
Несложно заметить, что и в описанном алгоритме есть ряд недостатков. Пер
www.books-shop.com
ние каждого соединения, необходимо проверять, какое именно сообщение при
Перераспределение нагрузки
Проблему распределения нагрузки можно решить, если создавать потоки, а не
процессы. Как описывалось выше, сервер не может просто так назначить зада
ный алгоритм работает только в том случае, если родительский поток способен
принять все запросы.
Способ вызова функции poll() может быть разным. Как правило, период
тайм
аута устанавливается достаточно коротким, чтобы потоки быстро реагирова
www.books-shop.com
fd_count++;
/* — ЕСЛИ свободные позиции имеются? — */
/* — устанавливаем соединение — */
if (i < fd_count)
{
fds[i].fd = accept (sd, 0,0);
fds[i].events = POLLIN | POLLHUP;
}
else /* в противном случае ненадолго переходим */
sleep(1); /* в "спящий" режим *[
}
/* — Дочерний поток — */
void *Servlet(void *init)
{ int start = *(int*)init; /* начало диапазона дескрипторов */
for (;;)
{ int result;
/* ожидаем 0,5 секунды */
if ( (result = poll(fds+start, RANGE, 500)) > 0 )
{ int i;
for (i = 0; i < RANGE; i++)
{
if ( fds[i).revents & POLLIN )
/*** Обрабатываем сообщение .***/,
else if ( fds[i].revents & POLLHUP )
/*** Разрываем соединение ***/
}
}
else if ( result < 0 )
perror( "poll() error");
мать данные и обрабатывать их. Клиент может все это делать, поскольку большая
часть времени в сетевых соединениях тратится на ожидание ответа сервера.
www.books-shop.com
Управление параметрами сокетов осуществляется с помощью функций
getsockopt() и setsockopt(). Они позволяют конфигурировать как общие парамет
ры, так и те, что зависят от протокола (полный список всех возможных парамет
Общие параметры
Общие параметры применимы ко всем сокетам. Они относятся к уровню
SOL_SOCKET.
• SO_BROADCAST. Позволяет сокету посылать и принимать широковещатель
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
SO_DONTROUTE. Включает/отключает маршрутизацию. В редких случаях
пакеты не должны подвергаться маршрутизации. Например, это могут
быть пакеты конфигурирования самого маршрутизатора. (Булево значе
ляет собой структуру типа linger, в которой есть два поля: l_onoff
(включить/отключить режим задержки) и l_linger (максимальная задерж
www.books-shop.com
SO_RCVTIMEO. Задает предельную длительность тайм
аута при чтении.
Когда функция чтения (read(), readv(), recv(), recvfrom() или recvmsg())
превышает указанное время ожидания, генерируется сообщение об
ошибке. (Структура типа timeval, по умолчанию 1 с, только для чтения.)
SO_REUSEADDR. С помощью этого параметра можно создать два сокета, ко
Параметры протокола IP
Перечисленные ниже параметры применимы к дейтаграммным и неструктури
нию False.)
www.books-shop.com
IP_MTU_DISCOVER. Позволяет начать процесс определения максимального
размера передаваемого блока (MTU
Maximum Transmission Unit). При
этом отправитель и получатель договариваются о размере пакета. Дан
ла;
• IP_PMTUDISC DO (2) — всегда посылать нефрагментированные паке
даются в заголовке IP
пакета и сообщают принимающей стороне раз
рено.)
IP_TTL. Задает предельное время жизни всех пакетов. Равен максималь
www.books-shop.com
Параметры стандарта IPv6
Параметры данной группы применимы к сокетам, работающим по стандарту
IPv6. Они относятся к уровню SOL_IPV6.
• IPV6_ADD_MEMBERSHIP. Как и в IPv4, с помощью этого параметра можно
добавить адресата к группе многоадресной доставки сообщений.
(Структура типа ipv6_mreq, по умолчанию не инициализирована, только
для записи.)
• IPV6_ADDRFORM. С помощью этого параметра можно задать преобразование
сокета из стандарта IPv4 в стандарт IPv6. (Булево значение, по умолча
нию False.)
• IPV6_CHECKSUM. При работе с неструктурированными сокетами стандарта
IPv6 с помощью этого параметра можно задать смещение поля кон
чанию False.)
• IPV6_PKTINFO. Если этот флаг установлен, программе будет передан номер
интерфейса и целевой адрес IPv6. (Булево значение, по умолчанию
False.)
• IPV6_PKTOPTIONS. С помощью этого параметра можно задать опции пакета
в виде массива байтов. Данный массив передается с помощью функции
sendmsg(). (Массив байтов, по умолчанию пуст.)
www.books-shop.com
IPV6_UNICAST_HOPS. Как и в IPv4 (параметр IPJTTL), задает максимальное
число переходов для одноадресных сообщений. (Целочисленное значе
ню SOL_TCP.
• TCP_KEEPALIVE. Сокет, для которого установлен флаг SO_KEEPALIVE, ожида
тов.)
• TCP_NODELAY. В TCP применяется алгоритм Нейгла, который запрещает
отправку сообщений, размер которых меньше максимального, до тех
пор, пока принимающая сторона не подтвердит получение ранее по
www.books-shop.com
предосторожности. Задержка необходима, чтобы пакеты, которые еще находятся
в пути, были удалены, прежде чем будет установлено новое соединение.
Проблемы можно избежать, если установить флаг SO_REUSEADDR, Считается, что
он должен быть установлен на всех серверах. Как уже говорилось, это позволяет
быстро создавать повторное подключение, даже если ядро все еще не освободило
порт. Ниже показано, как задать данный флаг.
сообщений
Для сервера важно быстро восстанавливать свою работу в случае сбоев. (Имеет
также значение, насколько быстро клиент способен послать запрос серверу.
Можно установить флаг TCP_NODELAY, чтобы максимально ускорить отправку кли
ентских запросов.
Как уже упоминалось, в TCP применяется алгоритм Нейгла, позволяющий ог
ные сообщения. Конечно, если данных в буфере больше, чем максимальный рад
мер сегмента, они будут отправлены без промедления, Это означает, что, когда
есть несколько маленьких сообщений, они будут передаваться дольше, чем одно
большое, потому что функция write() отправляет их не сразу, а дожидается полу
чения подтверждений.
В данной ситуации потери данных не происходит, а лишь снижается пропуск
ная способность сети, но это можно контролировать. Помните: если размер заго
www.books-shop.com
Проблемы файлового ввода$вывода
Когда смотришь на обилие параметров, связанных с передачей сообщений,
хочется вернуться к стандартным системным или высокоуровневым библиотеч
нее преобразовывать.
Однако функции файлового ввода
вывода нежелательно применять, если про
скольку раз копировать данные из файловых буферов в буферы сокета. Это нано
му ввода
вывода.
цесс создания пакета. После его окончания ядро возвращает в программу код за
www.books-shop.com
Однако большинство ошибок происходит в процессе передачи, а не в самой
функции send(). Проблемы, связанные, например, с неправильным использова
нение буфера. Оно нечасто происходит в системе, где много свободной памяти.
Если же такая ситуация возникает, необходимо применять асинхронный ввод
общение придет, ядро пошлет программе сигнал SIGIO. Обработчик сигналов его
примет и вызовет функцию recv(). По окончании чтения обработчик установит
флаг, свидетельствующий о том, что данные подготовлены.
Не забывайте о том, что сигнал служит лишь признаком поступления данных;
он не говорит о том, сколько именно данных прибыло. Кроме того, если выпол
нять ввод
вывод непосредственно в обработчике, можно не успеть обслужить
другие сигналы, которые поступают в это же время. Решить данную проблему
можно, если установить в обработчике флаг, информирующий программу о том,
что она должна вызвать функцию recv() со сброшенной опцией MSG WAITALL.
www.books-shop.com
Такие сообщения называются внеполосными (ООВ — out
of
band). Несмотря на
заманчивое название, действительность несколько разочаровывает: согласно спе
цификации, срочное сообщение может занимать всего один байт (такие сообще
рает первое. Это связано с тем, как сетевая подсистема хранит срочные сообще
вером и клиентом:
/****************************************************************/
/*** Обмен срочными сообщениями между клиентом и сервером ***/
/*** (сервер отвечает на сигналы). ***/
/*** (Взято из файла heartbeat
server. с на Web
узле.) ***/
int clientfd;
void sig_handler(int signum)
{
if ( signum == SIGURG )
{ char c;
recv(clientfd, &c, sizeof(c));
if (с == '?') /* Ты жив? */
send (clientfd, "Y", 1, MSG ООВ); /* ДА! */
}
}
int main()
( int sockfd;
struct sigaction act;
www.books-shop.com
bzero(&act, sizeof(act));
act.sa handler = sig_handler;
sigaction(SIGURG, &act, 0); /* регистрируем сигнал SIGURG */
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Можно реализовать полностью двустороннюю связь, осуществив несложную
проверку на сервере. Если сообщение от клиента не поступило в течение задан
ного промежутка времени, сервер будет знать о том, что на клиентском конце со
ния.
Резюме
До сих пор в каждой главе рассматривался один из кусочков мозаики, каковой
является код высокопроизводительного сервера или клиента. Любая сетевая про
лиотеки Socket API, так как они выполняются быстрее, чем стандартные низко
www.books-shop.com
Создание устойчивых Глава
сокетов
10
В этой главе...
Методы преобразования данных 220
Проверка возвращаемых значений 221
Обработка сигналов 223
Управление ресурсами 227
Критические серверы 230
Согласованная работа клиента и сервера 234
Отказ от обслуживания 235
Резюме: несокрушимые серверы 236
www.books-shop.com
Итак, наша задача — создание клиентских и серверных приложений коммер
ческого уровня. Это достойная цель, даже если программа будет распространяться
бесплатно вместе с исходными текстами на условиях открытой лицензии. Ведь
никому не хочется, чтобы его критиковали за ошибки программирования. Так
как же сделать хорошую программу безупречной? Хороший вопрос!
В первую очередь следует подумать о том, чего вы стремитесь достичь. Если
программа создается для конкретной категорий пользователей, анализируете ли
вы программу с точки зрения такого пользователя? Можете ли вы с кем
нибудь
из них встретиться и узнать, чего на самом деле они ждут от вашей программы?
Насколько надежной должна быть клиентская или серверная программа?
Создание устойчивой сетевой программы не является чем
то особенным, про
ром Alpha или 68040, где по умолчанию используется данная кодировка. В таких
системах функции заменяются "заглушками", которые не выполняют никаких
действий. Но если вы читаете эту книгу, то, скорее всего, ваша программа будет
распространяться в среде Linux. В этом случае функции преобразования обеспе
www.books-shop.com
• если функция завершается без ошибок, она должна возвращать значение 0;
• если в процессе выполнения функции произошла ошибка, она должна
возвращать отрицательное значение и записывать код ошибки в библио
дартных ситуаций.
Это лишь некоторые из правил. Наилучшим решением будет просмотреть
текст стандартной функции и взять его за основу.
В целом необходимо отметить, что программу, написанную стандартным и
понятным способом, легче использовать, модифицировать и улучшать. Подумай
те: сам Линус Торвальдс объявил о том, что не собирается владеть правами на яд
www.books-shop.com
либо вызвать функцию sigaction() с флагом SA_RESTART, либо проигно
www.books-shop.com
ние. При любых обстоятельствах дополнительная проверка того, успешно ли за
кой программы.
Обработка сигналов
В сетевом приложении задействовано много технологий, и некоторые из них
связаны с сигналами. Программа должна уметь правильно их обрабатывать. Из
тех сигналов, которые приводят к аварийному завершению программы, чаще
всего забывают о сигнале SIGPIPE.
С обработкой сигналов связаны свои проблемы, о ряде из которых упомина
зана с тем, что любой процесс одновременно принимает только один сигнал кон
www.books-shop.com
Во
вторых, можно разрешить прерывать выполнение обработчика. Применять
данный подход следует осторожно, так как обработчик сигналов может помещать
свои данные при каждом следующем вызове в специальный аппаратный стек.
Глубина этого стека по умолчанию невелика, поэтому легко возникает перепол
нение стека. ,
В
третьих, можно заставить обработчик помещать сообщения о сигналах в
очередь главной программы, которая будет сама их обрабатывать. Это не столь
эффективное решение, как кажется на первый взгляд. Сигнал говорит лишь о
том, что что
то произошло. Программа знает только тип сигнала (в Linux их 32) и
больше ничего. Программе придется самостоятельно определять, относится ли
группа однотипных сообщений к одному или нескольким сигналам.
Порядок обработки каждого сигнала зависит от типа сигнала. Из всех 32
х
сигналов (информацию о них можно получить в приложении А,
"Информационные таблицы", и разделе 7 интерактивной документации) чаще
всего обрабатываются такие: SIGPIPE, SIGURG, SIGCHLD, SIGHUP, SIGIO и SIGALRM.
SIGPIPE
В руководстве по UNIX сказано, что лишь простейшие программы игнориру
ные, которые требуется передать либо получить, или действия, которые нужно
выполнить. Когда клиент и сервер общаются по известному им протоколу, дос
рочное закрытие соединения мало вероятно. Скорее всего, либо случилась сис
темная ошибка, либо произошел разрыв на линии. В любом случае, если необхо
лать это немедленно либо выдержать небольшую паузу, чтобы дать возможность
удаленной системе загрузиться повторно. Если после нескольких попыток не уда
www.books-shop.com
SIGURG
При передаче данных между клиентом и сервером необходимо учитывать все
возможные способы обмена информацией. Программы могут посылать друг другу
запросы на прерывание потока данных или инициализирующие сигналы (см. гла
щений.
SIGCHLD
Сигнал SIGCHLD возникает в многозадачной среде, когда дочернее задание (в
частности, процесс) завершается. Ядро сохраняет контекст задания, чтобы роди
задачность").
Обычно при получении сигнала SIGCHLD программа вызывает функцию wait().
Однако следующий сигнал может поступить быстрее, чем завершится данная
функция. Это неприятная проблема, но ее легко решить, обрабатывая все сигна
лы в цикле.
Поскольку функция wait() блокирует работу программы (а в обработчике сиг
www.books-shop.com
вызывается, когда завершается один из пррцессов
потомков. Если бы на месте
указанной функции стояла функция wait() и во время ее выполнения пришел
новый сигнал, он был бы просто потерян. А вот функция waitpid() на следующей
итерации цикла благополучно обнаружит появившийся контекст потомка. Таким
образом, одна функция обрабатывает все отложенные команды завершения, а не
только одну.
SIGHUP
Что произойдет с дочерним процессом, если завершится родительская про
цесса, и программа init создает новый процесс. Если же у нее не получается это
сделать, можно вызвать функцию ехес(), чтобы принудительно запустить сервер.
Предварительно необходимо вручную уничтожить все дочерние процессы, отно
SIGIO
Ускорить работу сетевой программы можно, поручив обработку событий вво
да
вывода ядру. Правильно написанная программа получает сигнал SIGIO всякий
раз, когда буфер ввода
вывода становится доступен для очередной операции
(обращение к нему не вызовет блокирования программы). В случае записи дан
ных это происходит, когда буфер готов принять хотя бы один байт (пороговое
значение устанавливается с помощью параметра SO_SNDLOWAT сокета). В случае
чтения данных сигнал поступает, если в буфере есть хотя бы один байт
(пороговое значение устанавливается с помощью параметра SO_RCVLOWAT сокета). О
реализации обработчика этого сигнала рассказывалось в главе 8, "Механизмы
ввода
вывода", а о способах повышения производительности подсистемы ввода
SIGALRM
Подобно сигналу SIGIO, программа получает сигнал SIGALRM, только если явно
его запрашивает. Обычно он генерируется функцией alarm(), которая "будит"
программу после небольшой паузы. Этот сигнал часто используется демонами,
которые проверяют, работает ли та или иная программа.
www.books-shop.com
Управление ресурсами
Сигналы — это лишь малая часть ресурсов программы. Они позволяют сни
но используемой памяти. По
настоящему надежный сервер (да и клиент тоже)
должен тщательно заботиться о своих ресурсах.
Управление файлами
При запуске программы автоматически создаются три стандартных файла
(потока); stdin, stdout и stderr. Это широко известный факт, как и то, что любой
из перечисленных потоков можно переадресовать. Проблем с такой переадреса
боты с памятью.
Первая и наиболее часто встречающаяся ошибка заключается в том, что забы
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Возьмите за правило присваивать освобождаемым указателям значение NULL.
Это позволит быстро находить недействительные ссылки (указатели, ссылающиеся
на области памяти после того, как они были освобождены).
При выделении памяти можно запросить ровно столько, сколько нужно или
сколько предположительно понадобится. Это два разных подхода к одной про
вать в одной и той же программе, следует только помнить о том, где и какого
размера блоки были выделены.
www.books-shop.com
Как правило, указатель "принадлежит" тому модулю, в котором он был соз
дан. Это означает, что когда указатель передается в другой модуль, нужно каким
то образом создать копию адресуемого блока памяти. Такой подход называют де!
тальным копированием, поскольку все ссылки внутри блока также должны быть
раскрыты и скопированы.
Статическая память
Среди всех типов ресурсов меньше всего проблем возникает со статической
памятью. Сюда входят инициализированные и неинициализированные перемен
память и процессы
Что касается последних трех типов ресурсов, то здесь достаточно сделать лишь
несколько замечаний.
• Совместно используемая память. Работа с ней напоминает работу с фай
Критические серверы
Получение информации о внешних и внутренних событиях (сигналах, напри
мер) важно для понимания того, как работает система. Создавая клиентские и
серверные приложения, необходимо заранее определить, что может произойти и
когда. Это позволит жестко регламентировать работу программы в любых ситуа
www.books-shop.com
иметь самую разную конфигурацию, даже если на них установлена одна и та же
операционная система.
Но многообразие конфигураций — это еще не самое страшное. В сетевом
программировании вообще трудно заранее делать какие
либо допущения. Тем не
менее определенная степень контроля все же имеется. Если вы четко понимаете,
что должна делать программа и с кем она будет взаимодействовать, можно соста
димо получать доступ к определенным файлам, можно оговорить, что эти файлы
обязаны находиться в конкретном каталоге и иметь заданный формат. Систем
ный администратор будет знать, что в случае ошибки необходимо проверить эти
файлы.
Серверы требуют особенного подхода к проектированию. Пользователи пред
полагают, что сервер будет доступен на момент обращения к нему. Они также
надеются, что время его реакции будет "разумным". Необходимо выяснить, что
значит "разумным". Кроме того, нужно определить, за сколько времени, по мне
нию пользователей, сервер должен возобновить работу в случае отказа. Все это
зависит от того, насколько критическим является сервер.
ся. Что могло стать причиной этого? Ведь протокол TCP считается достаточно
надежным. Что же вызвало появление ошибки? Объяснений может быть масса.
Разрывы соединений могут приводить к утрате данных, денег и даже жизни.
Как бы ни были соединены между собой два компьютера, всегда существует риск
потери связи. Анализируя, что могло бы произойти, необходимо выяснить, какие
типы информационных каналов существуют и как протокол TCP взаимодействует
с каждым из них.
www.books-shop.com
Физические прерывания
В сети может существовать столько видов физических соединений и способов
потери несущей (пропадание электрического или оптического сигнала, передаю
тельства извне. Но если новый сетевой путь не может быть проложен, программа
должна сама позаботиться о возобновлении сеанса.
Сбои маршрутизаторов
Физические разрывы вызывают сбои в работе маршрутизаторов, проявляю
правляет их.
зает в случае фатального сбоя. При этом все данные о транзакциях теряются. Ес
ру. Это связано с целым рядом проблем. Обычно протокол TCP решает их авто
матически, но очень редко это проходит незаметно для клиента или сервера.
Первая проблема связана с нарушением транзакции. Если клиент не отслежи
www.books-shop.com
Предположим, например, что клиент запрашивает перевод 100$ с депозит
Обратное цитирование
Заставить клиента обнаружить разрыв соединения не такто просто, поскольку сетевые ошибки
возникают не сразу; а через какоето время. Можно применять обратное квитирование — уста
навливать соединение в обоих направлениях. Обычно клиент подключается к серверу. Но в алго
ритме обратного квитирования сервер в ответ на запрос клиента сам подключается к «ему. Че
рез обратный канал можно посылать служебные сообщения (например, о необходимости по
вторного подключения). Реализовать такой канал можно не по протоколу ТСР, а с помощью
надежного варианта протокола UDP.
Процесс установления соединения может включать принудительную повтор
www.books-shop.com
ся без участия пользователя. Процесс сертификации должен быть проведен зано
нению данных. Следить за выборкой данных, как правило, не нужно (если толь
"Тонкие" клиенты
Если риск потери синхронизации с сервером слишком велик, создайте "тонкий" клиент, который
не хранит информацию локально. Такой клиент попрежнему отслеживает состояние транзакций,
но все изменения регистрируются в виде запросов к серверу.
Когда сеанс внезапно прекращается, клиент и сервер должны его восстано
и сервера
При работе с одновременно выполняющимися сетевыми программами можно
столкнуться с теми же проблемами взаимоблокировок и зависаний, что и в случае
потоков и процессов. Однако в данном случае их труднее обнаружить.
Проблемы конкуренции в сетевом программировании имеют немного другой
характер. Причина этого проста: клиенты и серверы не имеют совместного досту
www.books-shop.com
Сетевые взаимоблокировки
В большинстве сетевых соединений определяется, какая программа должна
начинать диалог первой. Например, одна программа посылает запросы, а другая
отвечает на них. В качестве иллюстрации рассмотрим серверы HTTP и Telnet.
HTTP
сервер принимает запросы от клиента, предоставляя клиенту право вести
диалог. С другой стороны, сервер Telnet выдает пользователю приглашение на
ввод имени и пароля. Будучи зарегистрированным в системе, клиент знает о том,
что сервер готов отвечать, когда видит строку приглашения. Сервер Telnet может
принимать и асинхронные команды, посылаемые посредством прерываний кла
виатуры (<Ctrl+C>).
Сетевая взаимоблокировка возникает очень просто: клиент и сервер забывают
о том, чья очередь говорить, поэтому переходят в режим бесконечного ожидания.
Эту форму тупика трудно обнаружить, так как его симптомы совпадают с при
Сетевое зависание
Другая проблема, с которой часто сталкиваются в сетевом программирова
кое
то соединение никогда не будет обслужено.
Подобная проблема возникает при подключении к очень загруженным
Internet
серверам. Ее симптом примерно такой же, как и в случае взаимоблоки
реди ожидания помните, что ничего плохого в отложенных соединениях нет, если
они будут обслужены в пределах отведенного для этого времени. Необходимо из
www.books-shop.com
Можно использовать другой подход — осуществить динамическое планирование
соединений или назначить им приоритеты. Если, к примеру, выполняются три
процесса, только один из них может получить доступ к процессору в конкретный
момент времени. Поэтому планировщик повышает эффективный приоритет про
Отказ от обслуживания
Сетевые злоумышленники вызывают особые формы взаимоблокировок и зави
саний. О них следует знать, хотя это и достаточно старый способ атаки на сервер.
Как описывалось выше, сетевая подсистема принимает клиентские запросы на
подключение по определенному порту. У данного порта есть очередь ожидания.
Нарушитель подключается к этому же порту. Процесс трехфазового квитиро
лено, пока не вызвана функция accept()). Затем сервер создает для него сервлет.
Итак, вот суть проблемы. Если нарушитель ничего не делал и не посылал ни
единений, все ресурсы сервера (в том числе очередь ожидания) будут заняты за
служивании.
Предотвратить подобную ситуацию можно в три этапа.
1. Всегда задавайте тайм
ауты для всех функций ввода
вывода. Их легко
реализовать, и они помогут не потерять контроль над сервером.
2. Всегда оставляйте один процесс (обычно родительский) свободным,
чтобы он мог следить за дочерними процессами. С его помощью можно
определить, сколько процессов находится в зависшем состоянии.
3. Ограничьте число соединений от конкретного узла или подсети
(идентификатор узла включается в запрос на подключение). Это слабая
мера защиты, так как злоумышленники очень изобретательны, и можно
наказать совершенно невинных пользователей.
www.books-shop.com
Резюме: несокрушимые серверы
В данной главе рассказывалось о том, как создавать надежные и стабильно ра
ности.
Важно также понимать, как работает сервер и как он взаимодействует с кли
www.books-shop.com
Часть
Объектно
ориентированные
сокеты III
В этой части...
Глава 11. Экономия времени за счет объектов
Глава 12. Сетевое программирование в Java
Глава 13. Программирование сокетов в C++
Глава 14. Ограничения объектноориентированного
программирования
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Глава
Экономия времени за
11 счет объектов
В этой главе...
Эволюция технологий программирования 239
Рациональные методы программирования 242
Основы объектноориентированного
программирования 244
Характеристики объектов 247
Расширение объектов 249
Особые случай 251
Языковая поддержка 252
Резюме: объектноориентированное мышление 253
www.books-shop.com
Батарейка в часах дает им энергию, которая приводит в действие часовой ме
ектно
ориентированной технологии. Сначала мы совершим небольшой историче
щихся объектно
ориентированными.
Примечание
Эта глава предшествует главам, в которых рассказывается о конкретных способах создания
объектноориентированных сокетов. Чтобы понять особенности реализации сокетов в таких язы
ках, как Java и C++, необходимо получить базовые представления об объектах. В книгах, посвя
щенных объектноориентированному программированию, не обойтись без вводного теоретиче
ского раздела. Большинство программистов не понимает до конца объектную технологию, по
этому создаваемые ими приложения не всегда соответствуют ее исходным положениям.
Эволюция технологий
программирования
Программирование не имеет столь древней истории, как физика, математика
или другие точные науки. В то же время, будучи компьютерной наукой, оно в зна
вый подход
Первая методология разработки программного обеспечения развилась из кон
цепции блок
схем. Идея заключалась в том, что в программе образовывались
блоки вызова функций, принятия решений, обработки данных и ввода
вывода.
Блок
схема демонстрировала пошаговый алгоритм преобразования осмысленных
входных данных в требуемые выходные результаты.
В функциональном моделировании выделялись этапы описания исходных
данных, анализа задачи и системного проектирования. Конечным результатом
было получение конкретной программной реализации.
www.books-shop.com
На этапе описания исходных данных выяснялись системные требования. Здесь
устанавливались границы между данными, вычисляемыми или определяемыми в
самой программе, и данными, задаваемыми извне. На этом этапе разработчик
должен был описать программное окружение системы и категорию пользовате
жить пользователю.
На этапе анализа задачи строились диаграммы потоков данных в системе, раз
подход
Одной из проблем функционального моделирования является частое нарушение
правил видимости программных компонентов. Под областью видимости перемен
Глобальные переменные
Не воспринимайте вышесказанное так, будто никогда нельзя использовать глобальные перемен
ные. В некоторых ситуациях требуется очень экономное расходование памяти. Программы могут
для временных расчетов создавать общедоступные переменные, с которыми одновременно ра
ботает несколько процессов или потоков. Но только помните, что это усложняет процесс про
граммирования.
www.books-shop.com
Многие программисты избегают глобальных переменных из
за возможности
побочных эффектов. Когда с такой переменной связано несколько разделов
программы и в одном из них значение переменной изменяется, это отразится на
всех остальных разделах (естественно, сказанное не относится к переменным
константам).
Модульное программирование определяет потребность в задании правил ви
фейсы редко меняются. Кроме того, программисты не обязаны были знать детали
их реализации, а это упрощало модификацию модулей.
Объектноориентированное программирова
ние: естественный способ общения с миром
В настоящее время программирование развивается в объектно
www.books-shop.com
Объекты приближают программу к реальному миру. Все в природе наделено
свойствами (атрибутами) и поведением (функциями или методами), а также внут
ренними особенностями. Дети наследуют черты своих родителей. Все это находит
отражение в объектах.
Рациональные методы
программирования
Конечная цель и мечта любого программиста — избежать многократного на
ских правах.
Принцип повторного использования заключается в том, чтобы не изобретать
колесо, а брать за основу готовые решения. Представьте, сколько строился бы
дом, если бы строителям пришлось, кроме всего прочего, отливать металл для
гвоздей. И это сегодня, когда в заводских условиях изготавливаются целые дома,
доставляемые по частям прямо на участок в течение трех дней! Почему нельзя
делать то же самое в компьютерной индустрии? Можно, но для этого требуется
дисциплина.
www.books-shop.com
Принцип повторного использования можно понимать двояко: как поиск гото
выми версиями.
Важно уметь мыслить обобщенно. Представьте проблему в виде головоломки,
состоящей из набора элементов. Чем в более обобщенном виде будут представле
ны все элементы, тем выше вероятность того, что другие пользователи смогут по
вторно их использовать.
www.books-shop.com
качестве примера можно привести модель OSI. На каждом ее уровне свои законы
и интерфейсы.
Основы объектноориентированного
программирования
Как уже упоминалось, объектно
ориентированное программирование (ООП)
на сегодняшний день является ведущей технологией программирования. В ней
реализован целый ряд новых концепций, позволяющих решать большинство су
Инкапсуляция кода
Первой ключевой концепцией ООП является инкапсуляция. Она критически
важна для реализации принципа повторного использования, так как предписыва
Глобальные объекты
В хорошей программе не должно быть глобальных данных. Все данные должны модифициро
ваться только их непосредственными владельцами. Возникает вопрос: как следует интерпрети
ровать глобальные объекты? Правило гласит, что они не считаются глобальными данными. Их
вполне можно создавать, и на практике это используется очень часто.
В объекте инкапсулируются данные, о которых никто не должен знать или
доступ к которым должен быть ограничен. Есть два различных типа инкапсули
граммам не нужно знать, как именно в объекте хранятся числа. Если какая
www.books-shop.com
Наследование поведения
Допустим, нужно написать модуль, который делает то же самое, что и другой
модуль, но несколько функций в нем отличаются. Добиться этого можно с по
бавив ряд своих. На рис. 11.1 изображена иерархия объекта Device (устройство), у
которого есть ряд потомков.
www.books-shop.com
Абстракция данных
Третья базовая концепция, абстракция, схожа с описанной ранее моделью аб
дифицировать код:
BlockDevice *dev = new Disk();
dev
>Address();
Нужный интерфейс выбирается при объявлении переменной. Конечно, можно
просто привести переменную к требуемому типу, но не во всех языках програм
www.books-shop.com
Полиморфизм методов
Четвертая, и последняя, из базовых концепций ООП — полиморфизм. Его суть
заключается в том, что родственные методы, реализованные на разных уровнях
иерархии, могут иметь одинаковые имена. Это упрощает их запоминание.
Принадлежность метода к тому или иному объекту определяется на основании
его аргументов. Например, вместо двух методов — PlayVideo() и PlayMIDI() —
можно создать один метод Р1ау(), которому в первом случае передается видео
Характеристики объектов
С объектами связан целый ряд терминов и определений. Важно понимать их
назначение, чтобы правильно употреблять.
Классы и объекты
Программисты часто путаются в терминах "класс" и "объект". Класс — это
описание категории родственных объектов, а объект — это конкретный экземп
ры, в которую кроме переменных могут также входить и функции. Создать объ
ект класса — это то же самое, что создать переменную, в качестве типа которой
указан тэг структуры.
Замечание
До сего момента термин объект означал как класс, так и собственно объект. Это было сделано
умышленно, чтобы уменьшить путаницу. ,
В иерархии наследования есть два типа классов: надкласс (родительский класс)
и подкласс (производный, или дочерний, класс). Надкласс, находящийся на са
Атрибуты
Отдельные поля структуры становятся атрибутами в классе. Атрибуты могут
быть как скрытыми (инкапсулированными), так и опубликованными (являющимися
частью открытого интерфейса класса). Как правило, все атрибуты класса скрыты
от внешнего мира.
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Свойства
Опубликованные атрибуты класса называются свойствами. Обычно они дос
тупны только для чтения или же представлены в виде связки функций Getxxx() и
Setxxx(), позволяющих получать и задавать их значения.
Методы
Методы — это действия, которые выполняет объект. Благодаря полиморфизму
дочерние классы могут переопределять методы, унаследованные от своих роди
тельских классов. Обычно при этом вызывается исходный метод, чтобы роди
Права доступа
Права доступа определяют, кто может обращаться к той или иной части клас
са. В Java и C++ определены три уровня доступа: private, protected и public.
• private. Доступ к членам класса разрешен только из его собственных
методов.
• protected. Доступ к членам класса разрешен из его методов, а также из
методов его подклассов.
• public. Доступ к членам класса разрешен отовсюду.
Эти ограничения можно ослаблять, создавая дружественные классы и функ
ции.
Отношения
Объекты взаимодействуют друг с другом тремя различными способами. Пер
гого.
• Отношение "использует". Использование: один класс объявлен дружест
www.books-shop.com
Между двумя объектами должно существовать только одно отношение. На
Расширение объектов
Когда есть хороший объект, всегда хочется его расширить или еще улучшить.
Помните о том, что перечисленные ниже способы расширения поддерживаются
не во всех языках.
Шаблоны
Благодаря абстракции и полиморфизму можно создавать в родительских клас
кого типа будет работать этот класс. Обычно на базе шаблонов создаются кон
Постоянство
Обычно программисты имеют дело с объектами, которые прекращают свое
существование по завершении программы. Но в некоторых программах пользова
www.books-shop.com
Перегрузка
Перегрузка операторов (поддерживаемая в C++) расширяет концепцию поли
нием C++. В Java, например, она не поддерживается, хотя этот язык считается
объектно
ориентированным.
В C++ разрешается добавлять новый (не переопределять старый!) смысл к
внутренним операторам языка. С ними можно обращаться как с полиморфными
методами класса, создавая дополнительные реализации, которые работают с но
ний.
• Расширение, а не переопределение. Нельзя создавать операторы, чьи опе
Интерфейсы
В C++ существует одна проблема. Если класс А использует класс Б, структура
последнего должна быть непосредственно известна программисту или же одним
из предков класса Б должен быть класс, являющийся потомком для А. Когда
класс Б поддерживает несколько интерфейсов, возникают неприятности с множе
ственным наследованием.
В Java можно просто объявить, что класс поддерживает конкретный абстракт
ный интерфейс. Это не налагает на сам класс никаких ограничений или дополни
События и исключения
Последнее расширение связано с восстановлением работоспособности про
граммы в случае ошибок. В языке С это постоянная проблема, так как обработка
ошибок, как правило, ведется не там, где они возникают.
С помощью исключений можно задать, когда и как следует обрабатывать кон
www.books-shop.com
Особые случаи
Объектная технология сталкивается с теми же проблемами, что и другие тех
Записи и структуры
Правило гласит, что класс без методов является записью. Часто именно записи
служат основным средством представления данных. Например, в базах данных
информация хранится в виде записей.
Запись, или структура, — это неупорядоченный набор данных. Единственные
методы, присутствующие в нем, — это функции вида Get() и Set(). Объекты по
ношением в целом.
Наборы функций
Набор функций противоположен записи, т.е. набору данных. Это класс, в ко
ции выполняются над числами типа int и float, но они не связаны с ними и не
формируют с ними единый класс.
Если в результате проектирования у вас получился класс, представляющий со
www.books-shop.com
Языковая поддержка
Поддержка объектов внедрена во многие современные языки программиро
рованными можно считать также языки Java и C++, знакомству с которыми будут
посвящены две последующие главы. В то же время поддержка объектов во всех
этих языках реализована настолько по
разному, что необходимо провести их
классификацию.
Классификация объектноориентированных
языков
В действительности лишь некоторые языки являются истинно объектно
ставлять собой одну большую функцию main(). Особенностью таких языков явля
www.books-shop.com
• Абстракция. Если язык поддерживает операцию приведения типа, то аб
Резюме: объектноориентированное
мышление
Объектная технология — это основа хороших программных разработок. Пра
www.books-shop.com
Глава Сетевое
12 программирование в
Java
В этой главе...
Работа с сокетами 256
Вводвывод в Java 263
Конфигурирование сокетов 265
Многозадачные программы 266
Существующие ограничения 269
Резюме 269
www.books-shop.com
До сего момента вопросы сетевого программирования и, в частности, работы с
сокетами рассматривались применительно к языку С. Его достоинства очевидны
для системного программиста, но в результате получаются программы, которые
не всегда переносимы и не всегда допускают многократное использование.
Java — прекрасный пример объектно
ориентированного языка, в котором
можно создавать многократно используемые, переносимые компоненты. В Java
обеспечиваются два вида переносимости: на уровне исходного текста и на
уровне кода. Переносимость первого типа означает, что все программы должны
компилироваться на любой платформе, где поддерживается сам язык Java.
(Компания Sun Microsystems оставляет за собой право объявлять некоторые ин
шего использования.)
Концепция переносимости на уровне кода в действительности не нова. Прин
лагаю, что читатель знаком с Java и хочет изучать непосредственно сетевое про
граммирование.
Прежде всего мы рассмотрим, какие классы существуют в Java для работы с
сокетами, какие имеются средства ввода
вывода, как конфигурировать сокеты и
работать с потоками. /
Работа с сокетами
Многие программисты считают, что основные преимущества Java — независи
тительным сетевым протоколом в Java является TCP. С ним легче работать, чем с
дейтаграммами (протокол UDP), кроме того, это наиболее надежный протокол. В
Java можно также посылать дейтаграммы, но напрямую подобная возможность не
поддерживается базовыми библиотечными классами ввода
вывода.
www.books-shop.com
Программирование клиентов и серверов
Каналы потоковой передачи (TCP
соединения) лучше всего соответствуют
возможностям Java. Java пытается скрывать детали сетевого взаимодействия и уп
TCP$клиенты Java
Вот как, например, создается клиентский сокет:
Socket s = new Socket(String Hostname, int PortNum);
Socket s = new Socket(InetAddress Addr, int PortNum);
Самый распространенный вариант таков:
Socket s = new Socket("localhost", 9999);
Для подключения к серверу больше ничего не требуется. Когда виртуальная
машина (ВМ) создает объект класса Socket, она назначает ему локальный номер
порта, выполняет преобразование данных в сетевой порядок следования байтов и
подключает сокет к серверу. Если требуется дополнительно указать локальный
сетевой интерфейс и порт, то это делается так:
Socket s = new Socket(String Hostname, int PortNum,
InetAddress localAddr, int localPort);
Socket s = new Socket(InetAddress Addr, int PortNum,
InetAddress localAddr, int localPort);
Класс InetAddress преобразует имя узла или IP
адрес в двоичный адрес. Чаще
всего с объектом этого класса не работают напрямую, так как проще сразу вы
Прием/отправка сообщений
После создания объекта класса Socket программа еще не может посылать или
принимать через него сообщения. Необходимо предварительно связать с ним
входной (класс InputStream) и выходной (класс OutputStream) потоки:
InputStream i = s.getlnputstream();
OutputStream о = s.getOutputStream();
Чтение и запись данных осуществляются блоками:
www.books-shop.com
byte[] buffer = new byte[1024];
int bytes_read = i.read(buffer); // чтение блока данных из сокета
o.write(buffer); // запись массива байтов в сокет
С помощью метода InputStream.available() можно даже определить, поступи
ли данные во внутренние буферы ядра или нет. Этот метод возвращает число
байтов, которые программа может прочитать, не рискуя быть заблокированной.
if ( i.available() > 100 ) // чтение не производится, если
в буфере меньше 100 байтов
bytes = i.read(buffer);
После завершения работы можно закрыть сокет (а также все каналы ввода
ний. Он еще не доведен до конца, так как при попытке компиляции будет выда
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
хват исключений — это важная часть любых сетевых операций. Поэтому к пока
TCPсерверы Java
Как можно было убедиться, Java упрощает создание и закрытие сокетов, а
также чтение и запись данных через них. Работать с серверами еще проще. Сер
// Простейший эхо
сервер (из файла SimpleEchoServer.Java)
//****************************************************************
try .
{
ServerSocket s = new ServerSocket("9999"); // создаем сервер
while (true)
{
Socket с = s.accept(); // ожидаем поступления запросов
InputStream i = c.getlnputstream(); // входной поток
OutputStream о =c.getOutputStream(); // выходной поток
do
www.books-shop.com
byte[] line = new byte[100]; // создаем временный буфер
i.read(line); // принимаем сообщение от клиента
о.write(line); // посылаем его обратно
}
while ( !str.trim().equals("bye") );
c.close(); // закрываем соединение
}
}
catch (Exception err)
{
System.err.println(err);
}
Передача UDPсообщений
Иногда возникает необходимость передавать сообщения в виде дейтаграмм,
т.е. цо протоколу UDP. В Java есть ряд классов, которые позволяют работать с
UDP
сокетами. Основной из них — это класс DatagramSocket.
UDP
сокет создается очень просто:
DatagramSocket s = new DatagramSocket();
При необходимости можно указать локальный порт и сетевой интерфейс:
DatagramSocket s = new DatagramSocket(int localPort);
DatagramSocket s = new DatagramSocket(int localPort,
InetAddress localAddr);
Сразу после своего создания UDP
сокет готов к приему и передаче сообщений.
Это осуществляется в обход стандартных классов ввода
вывода. UDP
пакет форми
www.books-shop.com
DatagramSocket s = new DatagramSocket(); // создаем сокет
byte[] line = new byte[100J;
System.out.print("Enter text to send: ");
int len = System.in.read(line);
InetAddress dest = // выполняем преобразование адреса
InetAddress.getByName("127.0.0.1");
DatagramPacket pkt = // создаем дейтаграмму
new DatagramPacket(line, len, dest, 9998);
s.send(pkt); // отправляем сообщение
s.close(); // закрываем соединение
В данном примере после отправки дейтаграммы соединение сразу же закрыва
ется. Это вполне допускается делать, даже если сообщение еще не покинуло ло
кальный компьютер. Очередь сообщений будет существовать до тех пор, пока все
находящиеся в ней сообщения не будут отправлены.
Текст программы
получателя представлен в листинге 12.4.
ределенному IP
адресу, зарезервированному для групповой рассылки. Все про
www.books-shop.com
Когда групповой сокет создан, он ведет себя так же, как и обычный UDP
Листинг 12.5. Создание группового сокета, привязка его к порту 16900, присоединение к
группе и ожидание сообщений
//****************************************************************
// Простейший получатель групповых сообщений
// (из файла SimpleMulticastDestination.Java)
//****************************************************************
MulticastSocket s = new MulticastSocket{16900); /•/ Создаем сокет
ms.joinGroup(InetAddress.getByName("224.0.0.1")); // присоединение
к группе
String msg;
do
{
byte[] line = new byte[100];
DatagramPacket pkt = new DatagramPacket(line, line.length);
ms.receive(pkt);
msg = new String(pkt.getData());
System.out.println("From "+pkt.getAddress()+'4"tmsg.trim());
}
while ( !msg.trim().equalsf"close") );
ms.close(); // закрываем соединение
В этом примере создаваемый групповой сокет связывается с портом 16900, че
www.books-shop.com
В вод$вывод в Java
До сего момента в примерах использовались очень простые интерфейсы вво
да
вывода. Сила сокетов в Java заключается еще и в том, что их можно связывать
с самыми разными потоками ввода
вывода. В Java имеется целый ряд классов,
обеспечивающих различные формы
чтения и записи данных.
www.books-shop.com
Преобразование потоков
Класс Socket может напрямую работать лишь с двумя классами ввода
вывода:
InputStream и OutputStream. Они, в свою очередь, позволяют передавать и прини
мейства Reader:
Socket s = new Socket(host, port);
InputStream is = s.getInputstream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String 1 = br.readLine();
Строки 2—5 можно записать короче:
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputstream()));
Передача строк осуществляется немного проще:
String msg = new String(
"<html><body>Welcome to my Java website</body></html>");
SocketServer ss = new SocketServer(9999);
Socket s = ss.accept();
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
pw.println(msg);
s.close();
Первый параметр конструктора класса PrintWriter представляет собой ссылку
на выходной поток сокета. Второй параметр сообщает объекту о том, что при ка
стью канала. В данном случае программа явно указывает на то, когда следует по
сылать пакеты.
Если обмен данными осуществляется между двумя Java
программами, можно
воспользоваться классами ObjectlnputStream и ObjectOutputStream для прие
www.books-shop.com
Socket s = ss.accept();
ObjectInputStream ois = new ObjectInputStreatn(s.getInputStream());
String newMsg = (String) ois.readObject();
Если полученный объект не может быть приведен к указанному типу, интер
Конфигурирование сокетов
В главе 9, "Повышение производительности", рассказывалось о том, как рабо
пример:
getSoTimeout()
setSoTimeout(int timeout)
С помощью этих методов можно получить или задать значение параметра
SO_TIMEOUT, определяющего период ожидания данных. Этот параметр давно уста
www.books-shop.com
Конфигурирование групповых сокетов
Перечисленные здесь методы применимы к объекту MulticastSocket. Следую
Многозадачные программы
Некоторые методики программирования в Java существенно упрощены. Наря
ток:
www.books-shop.com
Если нужно создать несколько одновременно выполняющихся потоков, вызо
вите метод start () требуемое число раз. Но, как уже говорилось, все потоки по
терфейс Runnable. Результат будет тем же, а программа претерпит лишь незначи
тельные изменения.
public class TestThread extends Frame
implements Runnable
{
public void run()
{
/*** код потока ***/
}
public static void someMethodf)
{
Thread t = new Thread (this);
t.start();
вильно. Во
вторых, в программе создается отдельный потоковый объект. У класса
Thread имеется конструктор, который принимает указатель на объект, реализую
щий интерфейс Runnable, и делает этот объект потоковым. Вызов метода start()
будет иметь тот же эффект, что и прежде.
www.books-shop.com
Синхронизация методов
Основное преимущество потоков заключается в возможности совместного дос
ниченному ресурсу, они помещаются в очередь ожидания. После того как теку
дующий поток захватывает контроль над ресурсом. Это продолжается до тех пор,
пока потоки не завершатся.
Можно построить работу так, что поток, вошедший в критическую секцию, об
активное состояние, перемещаясь в конец очереди ожидания. Вот как это делается:
public synchronized changeSomething()
{
while ( buffer_not_full) // достаточно ли данных в буфере?
{
try { wait() } // нет, перемещаемся в конец очереди
catch (Exception err)
{ System.err.println(err); }
}
/* Отправляем сообщение */
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Существующие ограничения
В Java имеются очень удобные средства сетевого программирования, позво
вывода не так
то просто разобраться. Их слишком много и они образу
турированные IР
сокеты.
• Неполный набор параметров сокетов. Не все параметры сокетов поддер
живаются.
• Отсутствие эквивалента системного вызова fork (). Из программы
можно запустить только внешний модуль, но нельзя создать новый про
Резюме
В Java имеется большая библиотека сетевых классов. В пакет Network входят
классы, предназначенные для создания потоковых (TCP), дейтаграммных (UDP)
и групповых (UDP) сокетов. Есть классы, управляющие адресами, а также их
преобразованием.
Возможности сокетов расширяются благодаря мощному набору классов ввода
ектно
ориентированной среде. В следующей главе будет рассмотрена реализация
сокетов в Ct+.
www.books-shop.com
Глава
Программирование
сокетов в C++
13
В этой главе...
Зачем программировать сокеты в C++? 272
Создание библиотеки сокетов 273
Тестирование библиотеки сокетов 283
Существующие ограничения 287
Резюме: библиотека сокетов упрощает
программирование 287
www.books-shop.com
В предыдущей главе рассматривалось программирование сокетов в Java. Java —
очень мощный язык программирования, обладающий множеством преимуществ,
среди которых следует отметить независимость от платформы и оперативную
компиляцию. Но не всем нравится Java, в основное из
за отсутствия стабильно
C++?
Работать с библиотекой классов, инкапсулирующей функции работы с сокета
ми, удобнее, чем вызывать эти функции вручную. Наличие такой библиотеки по
ботали согласованно.
C++ значительно превосходит по своим возможностям язык С и является его
надмножеством. Для компиляции программ, написанных на C++, можно даже
применять С
компилятор cс. (Это не совсем точно. Когда компилятор cc обнару
живает файл с расширением С или срр, он вызывает утилиту д++, а не дсс.) Наряду
с дополнительными возможностями появляются и дополнительные сложности,
но преимущества перевешивают недостатки.
Создав библиотеку сокетов, можно скрыть все сложности за набором простых
интерфейсов. Чем проще интерфейс, тем легче работать с ним, а также тестиро
вать и отлаживать программу. Кроме того, выше вероятность, что такой интер
фейс, тем труднее работать с программой. Чтобы добиться простоты, очень важно
скрывать ненужные детали реализации. Это особенно важно при создании сетевой
оболочки, которую впоследствии будут использовать рядовые программисты.
www.books-shop.com
Конечно, программист захочет узнать, как автор библиотеки классов реализо
нентов
Внешние интерфейсы создают своего рода мембрану между библиотекой соке
тов и программой. Через эту мембрану должны проходить все запросы, посылае
Моделирование по необходимости
На рынке существует достаточно средств, позволяющих создавать библиотеки
классов. Труднее найти готовые библиотеки сокетов. В этой главе излагается
концепция самостоятельного написания библиотеки, которую при необходимости
можно модифицировать и доработать.
Согласно распространенной модели объектно
ориентированного анализа и
проектирования, процесс разработки программной системы должен проходить по
принципу "сверху вниз" (нисходящее программирование). Процесс начинается с
анализа исходных требований и путем последовательной детализации приводит к
получению конечного продукта. Идея заключается в минимизации числа свойств,
присущих системе, — должны быть реализованы только те свойства, которые
действительно необходимы.
Создание сетевой оболочки должно проходить иначе. Далеко не всегда можно
сказать наперед, какие возможности понадобятся конечному программисту, по
этому нужно реализовать всего по максимуму. Конечно, обилие кода может при
www.books-shop.com
Определение общих характеристик
К библиотеке должны предъявляться некие общие требования. Они задают
направления, в которых следует вести работу. Часто полученный список требова
общений, в которых требовалось, чтобы сокет находился в том или ином состоя
нии. Например, при вызове функции send() необходимо, чтобы сокет был под
тельно. Кроме того, в C++ нет понятия интерфейса, поэтому нужно создать об
тальные классы.
www.books-shop.com
Обработка исключений
Третье требование заключается в необходимости контроля над различными
исключениями и ошибками, возникающими при работе в сети. В сетевом про
Конфигурирование соединений
Последнее из основных требований состоит в возможности конфигурирования
сокета или соединения. У всех сокетов есть параметры, которые можно настраи
вать. Эти параметры влияют на поведение как сокета в целом, так и отдельно ка
налов ввода
вывода.
рет свое начало от класса Exception, реализующего базовые функции (рис. 13.1).
Иерархии классов исключений бывают очень сложными, поэтому нужно вни
www.books-shop.com
Рис. 13.1. Все классы исключений порождаются от класса
Exception, который сам по себе обрабатывает только ошиб!
ки работы со строками
менной длины.
Метод Unwrap() выполняет противоположное Действие. Он принимает блок
данных и восстанавливает его внутреннюю структуру. Иногда сообщение оказы
www.books-shop.com
Со кеты: создание, конфигурирование и подключение
Последний компонент инкапсулирует все основные функции. В его основе
лежит класс Socket, который управляет всеми соединениями и пользуется услуга
щих протоколов.
В библиотеке предполагается, что программист, использующий эти классы,
следует определенным правилам. Например, при вызове метода Send() классы не
проверяют, является ли сокет подключенным. Вместо этого просто генерируется
исключение.
Отношения
На рис. 13.2 изображены базовые компоненты библиотеки и отношения между
ними. Как можно было предположить, класс Socket связан со всеми остальными
компонентами отношениями "использует" или "генерирует".
www.books-shop.com
Рис. 13.3. Классы Datagram, MessageGroup и Broadcast связаны друг с
другом, тогда как классы Socketclient и SocketServer обособлены
Абстрактные элементы
Класс Socket, не представленный на рис. 13.3, должен быть суперклассом, т.е.
предком всех остальных классов в своей иерархии. Его назначение заключается в
обеспечении дочерних классов стандартным набором атрибутов и методов. В него
входят методы Get()/Set() для всех атрибутов, а также методы Send(), Receive() и
Close(). В то же время объекты этого класса не должны создаваться напрямую,
так как с ним не связан конкретный протокол.
www.books-shop.com
Определение задач каждого класса
Мы достигли этапа, на котором необходимо ответить на вопрос: "Для каких
целей можно использовать нашу библиотеку?" В отличие от традиционной мето
биться пользователю.
Проекты и библиотеки
Многие программисты пишут библиотеки под конкретные проекты. Их логика понятна: они ста
раются реализовать только те функции, которые действительно необходимы. Недостаток такого
подхода заключается в том, что создаваемым библиотекам зачастую недостает масштабируемо
сти. Они ограничены рамками проекта и могут предложить мало полезного для других проектов.
При написании библиотек нужно сначала определить назначение и обязанности самой библио
теки, а затем переходить к описанию конкретных классов.
рибуты классов.
Все атрибуты в основном связаны с параметрами сокетов. О них рассказыва
ходит лишь установка или сброс атрибута, поэтому с ним не связан метод типа
Get(). He все атрибуты класса являются открытыми. Обычно предоставляется
доступ к тем атрибутам, которые меняют поведение класса, а внутренние пере
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Puc. 13.5. Иерархия класса Socket с указанием методов всех классов
www.books-shop.com
Таблица 13.1. Специальные методы, реализованные в классах компонента Socket
Класс Метод Описание
Socket Bind() Вызывает функцию bind()
Send() Вызывает функию send() либо sendto()
Receive() Вызывает функцию recv() либо recvfrom()
CloseInput() Закрывает входной канал с помощью функции shutdown()
CloseOutput() Закрывает выходной канал с помощью функции shutdown()
SocketServer Accept() Вызывает функцию accept()
SocketClient Connect() Вызывает функцию connect()
MessageGroup Connect() Вызывает функцию connect()
Join() Подключает сокет к группе многоадресной доставки сообщений
Drop() Удаляет сокет из группы многоадресной доставки сообщений
У класса Broadcast нет специальных методов и атрибутов. Все свои методы он
наследует от родительских классов, но используется особым образом.
ляют начальное поведение его объектов. Например, у класса Broadcast нет допол
мять, выполнять разного рода вычисления и т.д. Например, ниже приведено опи
www.books-shop.com
Не все операции доступны в конструкторе. В частности, в нем нельзя вызы
вать методы создаваемого им объекта, если только они не бьши объявлены стати
ве статического метода вместо имени объекта можно указывать имя класса, после
которого следует оператор ::. По сути, это служебные методы класса. Они редко
используются, но в определенных ситуациях оказываются полезными. Некоторые
методы, например обработчики сигналов, не могут выполняться в контексте объ
торы всех дочерних классов также будут виртуальными. Но для ясности лучше
все же явно указывать спецификатор virtual.
лиотеки сокетов. Все они, а также файлы реализации библиотеки сокетов, име
ются на Web
узле.
www.books-shop.com
Эхоклиент и эхосервер
Первый пример (наиболее часто упоминаемый в книге) — это связка эхо
www.books-shop.com
Листинг 13.2. Эхосервер (echoserver.cpp)
вращает его обратно, пока клиент не пришлет строку "bye". Обратите внимание
на то, что в этом фрагменте есть свой блок try/catch. Это очень хорошая идея,
так как возникающие здесь ошибки связаны непосредственно с соединением и
не должны влиять на работу основного тела сервера. Если не указать этот блок,
то в случае отключения клиента сервер прекратит работу, так как в блоке
try/catch функции main() будет перехвачено исключение.
www.books-shop.com
Многозадачная одноранговая передача сооб
щений
В настоящий момент в библиотеке не реализован многозадачный режим, но
это несложно сделать. Ниже приведен классический пример UDP
модуля, кото
www.books-shop.com
Существующие ограничения
Описываемая библиотека классов является примером (хоть и незавершенным)
того, как можно программировать сокеты в C++. Она должна подсказать читате
Поддержка многозадачности
Как уже упоминалось, в библиотеке не реализован многозадачный режим.
Большинство сетевых приложений выполняет несколько действий одновременно
с целью повышения производительности. Если создавать процессы и потоки
внутри классов библиотеки, это существенно упростит задачу программиста,
пользующегося библиотекой.
В первую очередь при определении класса сокета необходимо решить, что бу
дет создаваться внутри сокета: процессы или потоки? И является ли новое зада
ние отдельным объектом? В этом случае придется порождать новый класс сразу
от двух классов: Socket и Process/Thread.
упрощает программирование
Применяя объектно
ориентированный язык, такой как C++, можно сущест
www.books-shop.com
Глава
Ограничения
объектно
ориентированного
программирования
14
В этой главе...
Правильное использование объектов 289
Объекты не решают всех проблем 293
Проблема чрезмерной сложности 294
Проблема управления проектами 296
Резюме: зыбучие пески ООП 299
www.books-shop.com
Объектная технология не решает всех проблем. У нее есть своя область при
лать система, какую роль в этом играет тот или иной объект и когда задачу мож
но считать решенной.
Начальный анализ
Создавая объектное приложение, важно правильно начать. Прежде всего необ
www.books-shop.com
Случай №9.1: "Отправка сообщения: в приеме сообщения отказано"
[Пользователь вводит сообщение и щелкает на кнопке Send]
Устанавливаем соединение с удаленным узлом
Вызываем абонента
[Абонент отказывается принять сообщение]
Сообщаем пользователю об отказе
[Пользователь получает код завершения]
Случай №9.2: "Отправка длинного сообщения пользователю сети"
[Пользователь вводит сообщение и щелкает на кнопке Send]
Устанавливаем соединение с удаленным узлом
Вызываем абонента
[Абонент подтверждает наличие связи]
Отправляем сообщение по частям
Выдаем подтверждение пользователю
[Пользователь получает код завершения]
Последний этап — на основании сценариев графически изобразить ход работы
программы. Важно, чтобы каждый сценарий был учтен и было показано, как
входные данные изменяются на пути к пункту своего назначения.
Именование объектов
После того как потребности пользователей выяснены, необходимо приступить
к анализу компонентов программной системы. Поведение каждого компонента
определяется тем, что он "знает" (атрибуты) и что он "умеет делать" (методы).
Имя объекта задает его текущее поведение и возможную эволюцию. Хорошее
имя всегда является существительным. Например, лучше назвать объект Socket,
чем NetIO, поскольку работа в сети подразумевает не только ввод
вывод. Следует
избегать употребления глаголов и отглагольных существительных, так как в этом
случае осуществляется привязка к тому, что объект делает сейчас, и ограничива
нужных деталях. Например, многие разработчики решают, что для проекта пона
добится база данных, задолго до того, как будет проведен анализ задачи. Хорошо
это или плохо?
Принятие подобных решений ставит конкретную технологию во главу угла и
заставляет корректировать все остальные решения с учетом заранее выбранной
технологии. Хороший архитектор не станет определять размер балки, не рассчи
тав предварительно нагрузку на нее. Точно так же, если системный аналитик за
являет, что программа должна быть написана на Java, не поняв всю проблему в
целом, могут возникнуть серьезные трудности при реализации проекта.
На этапе анализа предметную область нужно рассматривать на макроуровне.
Если кто
то упоминает конкретную деталь реализации, она должна быть отнесена
к этапу проектирования.
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Системные ограничения
Иногда в список системных требований входят конкретные аппаратные или программные ограниче
ния. Если их немного и они связаны с финансовым/обеспечением проекта, их можно учесть сразу.
Однако в большинстве случаев их можно проигнорировать вплоть до этапа проектирования.
В процессе анализа подразумевается, что имеются все необходимые инстру
менты реализации. Не беспокойтесь о том, как будет написан тот или иной мо
Избыточное наследование
На этапе проектирования классы, определенные на этапе анализа, наполняют
ся деталями. Здесь необходимо быть очень внимательным, так как велико иску
занностей объектов. Когда все классы связаны между собой отношениями наследо
www.books-shop.com
• Соответствует ли основное назначение класса поставленной задаче?
• Достаточно ли переписать всего один или два метода?
• Правильную ли роль играют родительские классы?
• Правильно ли обрабатываются существующие данные?
Если на один из этих вопросов дан отрицательный ответ, имеет место непра
вильное использование класса. Нужно либо найти другой класс, либо отступить
от иерархии и создать совершенно новый класс.
тивность программы, но только в том случае, если все они неразрывно связаны с
основным классом. Большинство специалистов считает наличие дружественных
классов дурным тоном и признаком плохого проектирования, как если бы в
программе присутствовал оператор goto.
Перегруженные операторы
Даже перегрузка операторов в настоящее время считается нежелательной, так
как часто приводит к усложнению библиотеки классов. Если же ее необходимо
применять, придерживайтесь следующих правил.
• Не передавайте данные по значению или через указатель — вместо этого
везде, где возможно, применяйте ссылки (s). В противном случае могут
возникнуть потерянные указатели или же произойдет снижение произ
водительности из
за постоянного вызова конструкторов и деструкторов.
• Всегда делайте перегруженным оператор присваивания — это необходимо,
чтобы правильно осуществлялось преобразование типов.
• Выбирайте оператор, соответствующий смыслу операции, — например,
запись <строка>+<строка> понятна, а запись *<стек> не обязательно
означает выражение <стек>
>Рор{).
• При необходимости делайте перегруженными операторы new и delete —
некоторые авторы рекомендуют делать это всегда, хотя это спорный во
прос.
• Никогда не перегружайте непонятные операторы — перегруженные опе
www.books-shop.com
Объекты не решают всех проблем
Некоторые люди заявляют, что ООП — единственно верная технология про
ние везде, где только возможно. Они пытаются объединить все классы в одну ие
рархию, даже в тех случаях, когда это вовсе не требуется. (Обратите внимание на
то, что подобные суждения неприменимы в отношении Java, где все классы по
Недоступный код
ООП позволяет эффективно решать многие традиционные задачи программи
достатки ООП.
Неполные классы
Первое ограничение возникает при создании неполных классов, которые за
Пустые методы
При создании дочернего класса может оказаться, что вызов определенного ме
Мутации объектов
Еще один недостаток заключается в мутации объектов, когда один объект мо
жет быть преобразован в другой без операции приведения типа. Даже в объектно
www.books-shop.com
постепенно раскрывает свои свойства, в результате чего может даже получиться
совершенно новый, ранее неизвестный в программе класс. Мутация широко
применяется при работе с объектными потоками, где имеется большой двоичный
объект (BLOB — binary large object), который нужно "расшифровать". Например,
можно создать конструктор трансляции, который в зависимости от внутренней
структуры полученного аргумента создает объект того или иного класса.
Мутация выполняется при соблюдении следующих условий.
• Большой двоичный объект должен оставаться цельным — модуль преобра
www.books-shop.com
Множественное наследование
Сложность возникает вследствие увлечения наследованием. В C++ можно
создавать классы, являющиеся потомками сразу нескольких классов. Управлять
такой иерархией достаточно сложно. В главе 11, "Экономия времени за счет объ
Разрастание кода
Применяя наследование, перегрузку и шаблоны, можно столкнуться с новой
проблемой: разрастание исходных текстов библиотеки или класса. Увеличение
размеров объектов не очень существенно, если в системе много памяти. Но чем
больше строк в программе, тем выше вероятность ошибок.
В зависимости от сложности классов ;их размер может на 20—50% превышать
размер функциональных аналогов, написанных средствами модульного програм
ния.
• Не применяйте виртуальное наследование.
• Поменьше используйте виртуальные методы.
• Старайтесь избегать множественного наследования.
www.books-shop.com
• Не злоупотребляйте inline
функциями. (Некоторые специалисты утвер
вычайно важно для успешной реализации проекта. Нужно ведь не только уло
скольких страницах. Каждый руководитель рано или поздно осознает, что успеш
www.books-shop.com
ляться прием проекта. Когда процедура проектирования будет завершена,
этот человек приступит к выполнению граничных и рабочих тестов.
• Программисты — осуществляют проектирование системы и программи
полняет четко отведенную ему роль и вносит свою лепту в повышение качества
продукта.
нимает в лучшем случае 34% времени. Так почему бы не тратить спокойно время
на анализ исходных требований и проектирование?
Чтобы успокоить спонсора и заказчика, проинформируйте их о графике работ
и постоянно держите в курсе того, как продвигается работа. Показывайте им пре
зентации и документацию.
Если времени все же не хватает, сократите этап проектирования, выполняя
проектирование оперативно, в процессе программирования. Конечно, такой под
казчику как свидетельство того, что работа движется. Они редко оказываются по
Тестирование системы
Цикл разработки программного обеспечения построен на определении по
www.books-shop.com
тировать наследование, полиморфизм, интерфейсы классов и т.д. Это заставило
многих изменить существующие подходы к тестированию.
Основная ошибка, которую делают многие, заключается в том, что разработ
Промежуточное тестирование
Выше говорилось о различных формах тестирования программного обеспече
зования.
При промежуточном тестировании испытатель больше знает о внутренней
реализации системы (вплоть до уровня отдельных модулей или функций, но не
дальше), чем в предыдущем случае. Проверка здесь также проводится на основа
мандой разработчиков, чтобы каждый участник процесса четко знал свою роль и
работал согласованно с остальными.
www.books-shop.com
www.books-shop.com
Часть
Сложные сетевые
методики
IV
В этой части...
Глава 15. Удаленные вызовы процедур (RPC)
Глава 16. Безопасность сетевых приложений
Глава 17. Широковещательная, групповая и магист
ральная передача сообщений
Глава 18. Неструктурированные сокеты
Глава 19. IPv6: следующее поколение протокола IP
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Глава Удаленные вызовы
15 процедур (RPC)
В этой главе...
Возвращаясь к модели OSI 303
Сравнение методик сетевого и процедурного
программирования 304
Связующие программные средства 307
Создание RFCкомпонентов с помощью утилиты
rpcgen 310
Учет состояния сеанса в открытых соединениях 315
Резюме: создание набора RPCкомпонентов 317
www.books-shop.com
Если смотреть с точки зрения прикладного программиста, то знать все детали
сетевого программирования достаточно затруднительно. Иногда хочется просто
сосредоточиться на разработке конкретного программного алгоритма, предоста
литы rpcgen. Но прежде необходимо вспомнить о том, что такое сетевая модель,
и понять, какое место в ней занимает RPC.
www.books-shop.com
обходимости самостоятельно взаимодействовать с сетью. Идея заключается в том,
чтобы сделать сетевое соединение по возможности автоматизированным.
рования. В последнем гораздо больше "свободы", так как ошибки не столь кри
Границы языков
Первая проблема, с которой можно столкнуться, — это сам язык сетевого
взаимодействия. Например, в языке С можно делать много такого, что не разре
www.books-shop.com
зовательскими. Без применения указателя невозможно было бы обнаружить ошиб
/* ***ЗАМЕЧАНИЯ*** */
/* getuserinfo() — получение информации о пользователе */
/* от сервера */
/* user — (входной) идентификатор пользователя */
/* host — (входной) внешнее имя сервера */
/*' data
www.books-shop.com
Сетевой вызов в своей простейшей форме не хранит информацию о состоя
Организация диалога
Первый шаг в реализации функциональных возможностей сеансового уровня
заключается в определении порядка взаимодействия и строгом его соблюдении. В
большинстве RFC
соединений требуется определенная форма аутентификации,
но сам диалог инициируется клиентом.
Начальное соединение между клиентом и сервером обычно требует регистра
тить информацию. После того как регистрация будет завершена, клиент и сервер
решат, кто должен начинать диалог.
Иногда потеря сообщения может привести к разрыву соединения: и клиент, и
сервер начинают ждать друг друга, а в результате возникает тайм
аут. Как описы
валось в главе 10, "Создание устойчивых сокетов", есть способы следить за тем,
чья сейчас очередь передавать данные. Проще всего периодически посылать вне
Контрольные точки
В некоторых соединениях, ориентированных на использование сетевых серви
Возобновляемые соединения
Для сеанса требуется одно соединение, которое будет постоянно оставаться
открытым. Это требование не всегда осуществимо, так как сетевое соединение
может в любой момент прерваться. Кроме того, библиотека Socket API не сооб
www.books-shop.com
Возобновление соединения означает не только автоматическое повторное под
ключение, но и то, что программа должна проверять живучесть канала (он акти
вен и в нем нет помех). Способы проверки каналов описывались в главе 10,
"Создание устойчивых сокетов".
Восстановить соединение легко, если подобная возможность предусмотрена в
программе. Одна из проблем при потере Связи заключается в необходимости оп
ределить, в какой точке находилась программа до этого. Эту точку следует поме
тить чем
то наподобие закладки. Такие закладки называются идентификаторами
сеансов и поддерживаются как клиентами, так и серверами. Не имея данного
идентификатора, возобновить сеанс вряд ли возможно.
Когда программа обнаруживает разрыв соединения, она просто повторно под
Автоматические подключения
Цель заключается в том, чтобы снабдить пользователя удобным интерфейсом,
позволяющим ему с наименьшими усилиями возобновлять сеанс. Некоторые
приложения не имеют этого и требуют от пользователя повторно вводить пароль
или устанавливать повторное соединение вручную.
Необходимо предоставить пользователю возможность вводить столько инфор
мации, сколько он сам захочет. Это означает, что может потребоваться хранить
регистрационную информацию на протяжении сеанса. Следует, однако, помнить
о безопасности. Запись паролей в файл на случай автоматической повторной ре
пулярности.
Таким образом, сначала нужно сформулировать общий замысел: определить
цели и намерения, подвести теоретическую базу. Затем можно переходить к соз
www.books-shop.com
Сетевые заглушки
Когда взаимодействие клиентской программы с сетевыми функциями носит
пассивный характер, интерфейс сводится к вызовам простейших процедур. В Ос
тевых вызовов.
Функции getphoto() и getuserinfo(), упоминавшиеся выше, занимаются лиШь
тем, что запрашивают данные. Эти функции могут Использоваться в системе
идентификации пользователей. Когда требуется получить информацию о ком
то
вместе с его фотографией, вызывается программа, которая Запрашивает и ото
дуль сервисных функций неактивен. Такая схема работы отлично подходит для
протокола UDP, в котором от клиента к серверу передается единственное сооб
щение.
В других ситуациях требуется постоянно открытое соединение, т.е. ведется
полноценный диалог, а не просто выполняется одиночный запрос. Это область
протокола TCP. В данном случае роль клиентских сервисных функций меняется,
так как канал следует держать открытым, к тому же клиент может вести несколь
нениях. Как правило, для этого в модуль добавляются две дополнительные функ
www.books-shop.com
вать их последовательно (помните, что ядро помещает все сообщения в очередь,
пока функция recv() не извлечет их). Но нельзя заранее сказать, как долго запрос
может находиться в очереди. На обработку предыдущих запросов может уйти
очень много времени.
Реализация многозадачного режима на сервере позволяет сократить задержки,
возникающие в системах обработки транзакций. Linux работает гораздо эффек
ки). Для каждого нового запроса будет создано отдельное задание, связанное с
его обработкой. Схема работы, профаммы будет немного иной, чем описанная в
главе 6, "Пример сервера".
1. Клиентская программа вызывает клиентский сетевой модуль.
2. Клиентский сетевой модуль упаковывает запрос и данные и посылает
их в сеть, после чего переходит в режим ожидания ответа.
3. Серверный сетевой модуль принимает запрос и переадресует его сер
верной профамме.
4. Серверная программа обрабатывает запрос и возвращает результат.
5. Серверный сетевой модуль принимает результат, упаковывает его и от
ная профамма должна определить эту процедуру как точку входа в блок обработ
общения и отправить его в сеть. Однако нет уверенности в том, что все клиенты
понимают формат данных сервера. Очевидный пример — порядок следования бай
www.books-shop.com
смотреть, на каких клиентских и серверных платформах будет работать программа.
Важно добиться, по крайней мере, независимости от порядка следования байтов.
Создание RPCкомпонентов с
В состав Linux часто входит утилита rpсgen, позволяющая создавать свои соб
ственные RFC
модули. Она существенно упрощает RFC
программирование и
помогает создавать полноценные сетевые приложения. Ниже описаны ее синтак
Технология RPC находит применение в разных областях. В частности, она служит неотьемлемой
частью технологии
файлы СОМ+, которая является
COMпроектов можнорасширением
даже более включать
старой технологии COM. В
Xфайлы.
www.books-shop.com
также имени процедуры можно присвоить произвольный идентификатор, а вот
некоторые программные идентификаторы являются зарезервированными. Можно
смело работать с числами в диапазоне 2000000
3000000.
Как можно заметить, в данном примере функция возвращает значение типа
long, а не time_t. Утилита rpcgen позволяет указывать любой тип (в действитель
димые преобразования.
Запуск утилиты должен осуществляться так:
rpcgen
a rpctime.x
Опция
а указывает на необходимость создания всех файлов, которые могут
потребоваться для работы клиента и сервера. Без этой опции будут созданы толь
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
else
printf("%d|%s", *result_l, ctime(result_l));
Добавленная инструкция просто осуществляет вывод результатов. Серверный
код будет таким:
/**********************************************/
/*** Фрагмент RFC
сервера ***/
/**********************************************/
static long result;
time(&result); /*— Пользовательский код —*/
return &result;
Утилита rpcgen добавляет раздел комментариев, указывающий на то, что сер
держать либо 0, Либо 1, хотя для ее хранения отводится больше одного бита.
Тип string требует более подробных пояснений. В языке С тип char* обозна
чает указатель на char, массив элементов типа char или строку, завершающуюся
символом NULL. Возникает Определенная неоднозначность, разрешить которую и
предназначен тип string. Определения его значений выглядят так:
string filename<100>; /*— длина до 100 символов —*/
string myname<>; /*— произвольная длина —*/
Все строки имеют произвольную длину. Если же известна предельная" длина,
то ее можно задать. Это позволит гарантировать, что подсистема XDR передаст
строго указанное число байтов.
В Х
файл можно включать определения объединений (union), которые содер
www.books-shop.com
void; /* — Если параметр Err не равен 0,
у объединения нет значения
*/
};
В данном примере объединение будет либо строкой, либо пустым множест
program RPCPROC
{
version RPCPROCVERSION
{
proc_res READPROC( string) = 1; /* — имя файла в каталоге
/proc — */
} = 1;
} = 2000025;
Сервер принимает имя файла, указывающее на элемент виртуальной файловой
системы /proc, и открывает файл. Если не возникает ошибок, сервер читает со
www.books-shop.com
значения являются статическими, чтобы, не возникало проблем со стеком. Тем не
менее строки всегда приводятся к типу char*.
При заполнении возвращаемого сообщения необходимо выделить память для
вставки строк. Это означает также, что необходимо как
то освобождать ату пa
вует структуре в языке С и служит в основном тем же целям. Есть и одно новше
нять структуры.
Определение структуры выглядит так:
/**********************************/
/*** Фрагмент программы RPCList ***/
www.books-shop.com
Учет состояния сеанса в открытых
соединениях
щие проблемы.
• Текущее состояние — в сеансе должно храниться описание уже выпол
лении. Все действия в ту или другую сторону должны быть четко опре
делены.
• Восстановление состояния — при разрыве соединения системе может по
Хранение идентификаторов
В простейшем случае в соединении хранится идентификатор сеанса. Этот
идентификатор должен быть зашифрован, чтобы только клиент и сервер понима
www.books-shop.com
Можно решить эту проблему, разделив все вызовы на три этапа: инициализа
ным.
граммах и т.д.
Сбой сеанса — неприятная проблема, если не быть к ней готовым. В первую
очередь программа должна проверять каждый идентификатор сеанса. Необходимо
также обеспечить безопасность идентификатора, истечение его срока действия и
идентификацию конкретного состояния. Можно включать в него признак по
www.books-shop.com
Резюме: создание набора RPC$
компонентов
Используя свой опыт программирования сокетов, можно помогать другим раз
www.books-shop.com
Глава
Безопасность
16 сетевых приложений
В этой главе...
Потребность в защите данных я
319
Проблема безопасности в Internet 321
Защита сетевого компьютера 323
Шифрование сообщений 329
Протокол SSL 330
Резюме: безопасный сервер 335
www.books-shop.com
Безопасная система предоставляет не больше функций, чем незащищенная,
просто последняя подвержена атакам и ее данные становятся ненадежными. Уро
цией компании.
В большинстве случаев безопасность создаваемого программного продукта
считается само собой разумеющейся. В исходных требованиях редко говорится:
"Сервер должен быть невосприимчив к сетевым атакам". Безопасность — это ба
вится все больше, поэтому зачастую безопасность продукта более важна, чем его
функциональные возможности.
С процессами сетевого взаимодействия неразрывно связаны понятия иденти
фикацию на стороне как клиента, так и сервера. Только после того как противо
формации:
Уровни идентификации
Определение уровней идентификации пользователя — важная часть процесса
создания безопасной системы. Простейшая форма защиты — аутентификация —
подразумевает обычную проверку регистрационной информации пользователя.
Это первая "дверь", в которую проходит пользователь, попадая в систему. В ка
сти пароль.
Предыдущие два уровня защиты имеют тот недостаток, что клиент не может
определить, является ли сервер "троянским конем". Сертификация обеспечивает
наивысший уровень безопасности. Она требует, чтобы доверенное третье лицо
(сервер сертификатов) гарантировало подлинность как клиента, так и сервера.
www.books-shop.com
Формы взаимообмена
Познакомившись с формами идентификации, рассмотрим, как происходит пе
вание представляет собой проблему только в том случае, если канал обмена явля
ме под чужим именем и паролем, может иметь более тяжелые последствия, так
как пользователь способен модифицировать не принадлежащие ему данные.
Очень часто обмен данными осуществляется в открытую, поскольку переда
щения из сети. Такая форма общения более защищена, чем открытая передача
данных, но все равно подвержена подсматриванию (в рамках данного сетевого
сегмента). Вторжение здесь мало вероятно, так как в подсеть обычно объединя
ние программ на других сетевых узлах. Это может осуществляться средствами RPC
или же путем прямой передачи команд (через такие утилиты, как telnet, rlogin или
rsh). Данные, передаваемые традиционным способом, называются пассивными. В
случае повреждения они меняют свой смысл или интерпретацию. Распределенные
команды считаются активными данными. Будучи искаженными, они меняют поря
ти. Можно много рассказывать о том, как создавалась глобальная сеть, но важнее
всего то, что она предназначалась для функционирования в условиях ядерной ка
www.books-shop.com
тастрофы. Это объясняет, почему пути распространения информации от компью
делов надежных зон. Надежная зона — это сеть, безопасность которой проверена.
В качестве примера можно привести магистральные сети AT&T и US Sprint.
Другие крупные корпорации покупают к ним доступ через выделенные линии.
Они также являются надежными, так как провайдер зоны предоставляет ограни
рые также строятся на базе протоколов Internet. Иногда две компании объединя
ся ее недостатком.
Лучше всего стараться избегать риска и защищать передаваемые данные. При
зашифрованном виде.
Виды атак
Защита клиента и сервера начинается с определения того, каким формам атак
они могут подвергнуться. Ниже перечислены основные из них.
• Прослушивание линии — подслушивающая программа пропускает через
себя поток сообщений, ожидая полезных для себя данных. В эту катего
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
замедляет время его реакции или вообще делает его недоступным. Это
атаки вида "нападение команды ping" b "отказ в обслуживании".
• Захват линии — вместо того чтобы затруднять соединение, программа
может принимать на себя роль одной из сторон. В некоторых стеках
протоколов, например в том, что реализован в Linux, подобная форма
атаки затруднена, так как используется система порядковых номеров па
кетов.
Могут появляться новые способы вторжений, но, как правило, они попадают
в одну из перечисленных категорий. Вирусы представляют собой совершенно
другую проблему. Они связаны с недостатками в системе защиты, но не являются
разновидностью атак на защищенные данные.
В сетевом программировании защищать нужно все, что подключено к сети.
Вопросы безопасности следует рассматривать независимо от типа операционной
системы. В Linux средства защиты встроены в систему, и это было задумано из
де всего разработчиком.
Незащищенность TCP/IP
Большинство компьютеров сегодня использует протоколы Internet. Даже ком
онных систем).
Каждый протокол в стеке TCP/IP имеет свои слабые стороны. Некоторые
специалисты по вопросам безопасности заявляют, что протокол TCP взломать
труднее всего. Они заявляют, что порядковый номер пакета служит своего рода
гарантией — если нельзя угадать следующий порядковый номер, то нельзя и за
хватить канал.
С другой стороны, протокол TCP не определяет алгоритм вычисления поряд
www.books-shop.com
Защита сетевого компьютера
Как же защитить систему от атаки и вторжения? Сетевая безопасность — это
очень сложная смесь различных инструментов и правил. Ниже будет дан ряд
практических советов, касающихся защиты компьютеров. Некоторые из них не
имеют прямого отношения к сетевой безопасности, но все же важны. Не стоит
рассматривать приведенный материал как исчерпывающее описание предмета.
Чтобы стать специалистом в области защиты данных, нужно читать много разной
литературы.
Ограничение доступа
Повысить безопасность серверных и клиентских программ можно следующи
ми средствами.
• Права доступа к файлам — в первую очередь следует убедиться, что у
файлов верные права доступа и владельцы. Некоторые программы
должны выполняться с правами пользователя root (например, для досту
па к неструктурированным IP
пакетам). Внимательно проверяйте дейст
тия IР
сокета.
• Ограничение привилегий — необходима определить, какие действия имеет
право выполнять удаленная программа в ходе сеанса. Некоторые коман
Брандмауэры
Брандмауэры формируют специальный защитный слой между внешними клиен
www.books-shop.com
ния безопасности внутренней сети. Как правило, брандмауэры функционируют, не
манд. При подобной форме фильтрации необходимо знать, какие сервисы дос
ные IP
адреса. Например, это происходит, когда в сети применяется протокол
DHCP. Брандмауэр выполняет трансляцию таких адресов (пассивную фильтрацию),
поскольку клиент не сможет начать соединение, не получив реальных адресов.
Демилитаризованные зоны
Брандмауэры увеличивают сетевую безопасность, формируя надежный канал
между клиентом и сервером. В некоторых организациях концепция брандмауэров
продвинута еще на один шаг вперед: в них создаются так называемые демилита!
ризованные зоны (сокращенно ДМЗ). Нечто подобное происходило в средние ве
ка, когда вокруг городов и замков в Европе и Средней Азии возводили двойные
или даже тройные стены, демонстрировавшие их могущество.
ДМЗ служат надежным барьером на пути незаконного проникновения. Нару
шенно безопасная система, так как в ней нет канала утечки важной информации.
Но возникают две очевидные проблемы: как сервер получает обновления и каким
образом осуществляется синхронизация внутренней и внешней информации? Од
www.books-shop.com
зоне объединяются в единое целое канал доступа к Internet, каналы обслужива
вать, что чем ближе к внутренней сети, тем слабее должны становиться меры
безопасности. Ниже перечислен ряд общепринятых правил.
• Минимизируйте число портов (предоставляемых брандмауэрами), Боль
шое число портов. Через эти порты клиенты получают только ту инфор
www.books-shop.com
Брешь в RPC
Многие сетевые программисты признают, что с увеличением числа портов возрастает риск неза
торыми они пользуются, могут создавать порты динамически. Например, программа portmap в
RРС создает порты. А на технологии RPC основаны такие технологии, как Java RMI и Microsoft
СОМ/СОМ+. Поэтому Web
мастера, которые пишут серверные сценарии, иногда не догадывают
|
ся, что с каждым новым объектом сценария появляется новая брешь в системе защиты сервера.
• Минимизируйте число сервисов (предоставляемых серверами). Самые
близкие к Internet серверы должны предлагать абсолютный минимум
сервисов. Обычно эти сервисы тесно связаны с портами, по которым
брандмауэр осуществляет пассивную фильтрацию. В качестве примера
можно привести серверы HTTP, HTTPS и FTP (только чтение).
• Ограничивайте число баз данных (внутри ДМЗ). Базы данных часто со
Защита канала
В сети есть два различных компонента, которые требуют пользовательской на
лению, он далеко не так безопасен, как некоторые думают. Даже для выделенных
www.books-shop.com
линий не сложно реализовать методику непроникающего подслушивания. Тем не
менее это хорошая стартовая площадка. Существуют разные способы физической
передачи данных. Мы рассмотрим четыре основных: электрический/оптический и
проводниковый/непроводниковый.
Электрическую проводниковую линию (витую пару или коаксиальный кабель)
прослушать проще всего. Гибкие проводники не защищены от подключения
пробника, а особенности электромагнетизма не позволяют обнаружить прослу
Перехват сообщений
Потребность в защите сообщений не столь очевидна, ведь брандмауэры
фильтруют всю информацию, поступающую из сети и уходящую в нее. Но они
используют конкретные порты и сервисы, заранее .известные клиенту. Изменение
стандартных установок и интерфейсов позволяет повысить безопасность.
Если вы не являетесь приверженцем открытого программного обеспечения, то
лучше использовать закрытые или патентованные протоколы, так как это затруд
мать от него ответы. Эффекта случайности можно достичь, если постоянно гене
www.books-shop.com
что транзакция настоящая. (Конечно, это слишком примитивный алгоритм, что
ся 128
разрядные ключи.
Шифрование сообщений
Концепция шифрования не нова. Самым знаменитым шифровальным устрой
ством была немецкая машина Enigma ("загадка"). От нее ведут свое начало мно
www.books-shop.com
Виды шифрования
В сетях применяются два различных алгоритма шифрования: с открытым и
симметричным ключом. В первом алгоритме используются два ключа: один —
для шифрования (открытый), а другой — для дешифрования (секретный). Во
втором алгоритме для обеих целей применяется один и тот же ключ. При дешиф
дают открытый ключ любому клиенту по сети. Секретный ключ хранится на сер
ный шифр.
вания с открытым ключом. Среди них наиболее известны RSA (названа по фами
Проблемы с шифрованием
С алгоритмами шифрования связан интересный круг проблем. Во
первых, до
недавнего времени в США существовал запрет на экспорт "сильных" алгоритмов
шифрования. Это одна ИЗ) причин, почему в некоторые дистрибутивы не входят
криптографические пакеты. В процессе инсталляции система пытается запросить
пакеты у зарубежного сервера. В 2000 г.. правительство сняло подобные ограниче
www.books-shop.com
Последняя проблема может показаться необычной. Шифрование в действи
тельности вызывает то, что оно по своей сути должно устранять: взломы и шпио
наж. Очевидно, в Internet есть лига хакеров, которым приятно быть героями но
востей.
Протокол SSL
Как уже упоминалось, два компьютера должны договориться о том, какие
шифры они будут использовать для безопасного общения. Этой цели служит SSL
(Secure Sockets Layer — протокол защищенных сокетов). Он позволяет сущест
Библиотека OpenSSL
Полнофункциональная, хотя и недостаточно хорошо документированная вер
ленных ниже этапов могут не работать в Mandrake и Red Hat Linux; схожие про
www.books-shop.com
8. В файле /etc/man.config добавьте в переменную среды MANPATH путе
Создание SSL$клиента
Итак, давайте попробуем написать связку клиент/сервер. Создать SSL
клиент
и SSL
сервер так же просто, как и в случае обычных сокетов, так как библиотека
OpenSSL основана на вызовах рассматривавшихся в предыдущих главах функций
сокетов.
Первый шаг заключается в инициализации библиотеки OpenSSL:
/*************************************************************/
/*** Инициализация клиента ***/
/**************************************************************/
SSL_METHOD «method;
SSL_CTX *ctx;
OpenSSL_add_all_algoritms(); /* загружаем модули шифрования */
SSL_load_error_strings(); /* загружаем и регистрируем таблицы
сообщений tf6 ошибках */
method = SSLv2_client_method(); /* создаем новый клиентский
метод */
ctx = SSL_CTX_new(method); ./* создаем новый контекст */
Если функции возвращают значение NULL или 0, следует вывести сообщение об
ошибке:
ERR_print_errors_fp(stderr); /* записываем сообщения об
ошибках в поток stderr */
Далее создается традиционный сокет:
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
struct hostent *host = gethostbyname(hostname); :
int sd = socket(PF_INET, SOCK_STREAM, 0); /* создаем сокет */
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port); /* серверный порт */
addr.sin_addr.s_addr = *(long*)(host
>h_addr); /* адрес сервера */
connect(sd, &addr, sizeof(addr)); /* подключаемся к серверу */
После установления соединения между клиентом и сервером необходимо соз
int bytes;
bytes = SSL_read(ssl, buf, s i z e o f ( b u f ) ) ; /* получаем/дешифруем */
bytes = SSL_write(ssl, msg, strlen(msg)); /* шифруем/отправляем */
В библиотеке есть множество других функций для управления потоком, изме
www.books-shop.com
Создание SSLсервера
Код клиента и сервера отличается лишь незначительно. В обоих случаях ини
вляется в два этапа: сначала загружается файл сертификата, а затем — файл сек
Примечание
Как правило, чтобы иметь возможность распространять программное обеспечение в Internet,
нужно купить сертификат у одного из центров сертификации, например фирмы VeriSign. Если же
сертификаты нужны для целей отладки, их можно создать средствами библиотеки OpenSSL (не
используйте сертификаты, поставляемые с библиотекой). Написанный на Perl сценарий CA.pl
находится в каталоге /usr/local/ssl/misc, создаваемом при инсталляции библиотеки. Если
сценарий не задает серию вопросов, значит, в переменной среды PATH не записан путь к ката
логу /usr/local/ssl/bin.
www.books-shop.com
Серверный сокет создается так же, как и клиентский:
www.books-shop.com
и шифрование — вот те средства, которые позволяют защитить Web
сервер от
нападений.
Когда два или несколько компьютеров взаимодействуют в открытой среде, такой
как Internet, необходимы определенные протоколы и алгоритмы, уменьшающие
вероятность шпионажа. Шифрование бывает различных видов и обеспечивает раз
www.books-shop.com
Глава Широковещательная,
групповая
17 и магистральная
передача сообщений
В этой главе...
Широковещание в пределах домена 337
Передача сообщения группе адресатов 339
Резюме: совместное чтение сообщений 346
www.books-shop.com
Сеть — это среда передачи сообщений из одного места в другое. В сети при
www.books-shop.com
Сетевая плата реагирует на сообщение, помещая его во внутренние буферы. По
завершении операции плата уведомляет ядро, посылая ему запрос на прерывание.
Ядро считывает пакет и проверяет IP
адрес получателя. Если .он оказывается широ
нал ввода
вывода этого сокета. В противном случае пакет удаляется. Лучше указы
if ( fork() )
Receiver(sd);
else
{
shutdown(sd, SHUT RD); /* закрываем входной канал */
Sender(sd);
}
wait(O);
www.books-shop.com
Ограничения широковещательного режима
Широковещательные сообщения имеют свои ограничения. В первую очередь
следует отметить, что сообщение доставляется тольЦо компьютерам, входящим в
подсеть. Правда, можно увеличить диапазон доставки, изменив щироковещатель
адресатов
Многоадресный режим [RFC1112] решает многие проблемы широковещатель
www.books-shop.com
Подключение к группе адресатов
Работать в многоадресном режиме так же просто, как подключаться к списку
рассылки. Программа, присоединившаяся к адресной группе, будет получать все
сообщения, публикуемые ее членами. Поэтому следует убедиться, что сущест
ния: 224.0.0.0
239.255.255.255. Этот диапазон, в свою очередь, подразделяется на
более мелкие диапазоны, определяющие область видимости адреса, т.е. как дале
ниться. Формат его такой же, как и у поля sin_addr структуры sockaddr_in. Поле
imr_interface позволяет выбрать конкретный сетевой интерфейс узла. Это напо
www.books-shop.com
чае, когда в системе имеется один
единственный сетевой интерфейс, иначе ре
единения.
Очистка групповых соединений
Многоадресный режим — один из тех немногих случаев, когда лучше вручную производить необ
ходимую очистку по окончании работы. Если выйти из программы, а ядро столкнется с какимито
проблемами, сообщений об ошибках не будет. Кроме того, даже после завершения программы
можно продолжать получать сообщения. Это сделает порт недоступным для новых экземпляров
программы или же они будут получать посторонние сообщения.
Подключиться к многоадресной группе достаточно легко. По сути, для созда
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Отправка многоадресного сообщения
Послать сообщение группе адресатов так же легко, как и отправить дейта
вляется автоматически.
пы, очень полезна. Тем самым сервер избавляется от ненужного ему трафика,
связанного с потоком групповых пакетов.
адресов в 64
или 512
битовом массиве. Когда сообщение поступает в
сеть, плата преобразует его МАС
адрес в хеш
код. Если соответствую
www.books-shop.com
• Беспорядочный режим. Некоторые сетевые платы не поддерживают мно
тов, компьютер все равно будет принимать все сообщения, адресованные группе,
к которой подключилась программа. Если работа ведется по протоколу UDP или
TCP, ядро будет автоматически осуществлять фильтрацию портов. Если исполь
Конфигурирование маршрутизатора
В Internet сообщение доставляется от отправителя получателю с использовани
ем двух адресов: IP
и МАС
адреса. Для первого имеются встроенные средства
маршрутизации, а второй редко бывает известен, пока сообщение не отправлено.
Маршрутизатор предполагает наличие только одного получателя для заданного
сообщения; если с одним адресом связаны два получателя, это обычно считается
ошибкой.
Многоадресный режим требует особой обработки. Необходимо специальным
образом конфигурировать маршрутизаторы. В первую очередь они должны пони
бе МАС
адреса и идентификаторы групп, к которым хотят подключиться или от
которых хотят отключиться компьютеры в подсети. Дело в том, что сообщение о
подключении к группе распространяется только в пределах своей области види
www.books-shop.com
сообщения в каждую подсеть в пределах заданной области видимости. Это требу
Аппаратная поддержка
Выше описывалось, как реализуется многоадресный режим в операционной
системе и на аппаратном уровне. К сожалению, некоторые сетевые платы вообще
не поддерживают многоадресный и беспорядочный режимы. А для получения
группового сообщения необходимо, чтобы плата, по крайней мере, поддерживала
беспорядочный режим.
Недостаточная производительность
При получении широковещательных или многоадресных сообщений, особен
www.books-shop.com
Кроме того, канал, по которому клиент подключается к сети, может быть не в
состоянии обрабатывать все сообщения, распространяемые в рамках группы
(например, "живое" видео при наличии модема, работающего со скоростью
56 Кбит/с). Что происходит в таком случае? Маршрутизатор хранит сообщения в
очереди до тех пор, пока срок их действия не истечет. Потенциальное решение
этой проблемы заключается в усилении поддержки со стороны маршрутизаторов.
слать групповое сообщение, даже не будучи членом группы. Это может приво
Уникальность сообщений
Как известно, при передаче дейтаграмм может возникнуть дублирование со
вее выражена, так как заранее известно, что сообщение должно копироваться в
момент перехода из одной подсети в другую.
В качестве примера представим, что компьютер подключен к Internet через
несколько маршрутизаторов. Этого не должно быть, так как в правильно спроек
сообщений
Когда необходимо распространять сообщения между несколькими компьюте
тях, даже несмотря на то, что маршрутизаторы могут посылать несколько копий
оного и того же сообщения. В отличие от широковещания, чтение групповых со
рами. Тем не менее широковещание не исчезло, так как оно находит применение
в некоторых низкоуровневых протоколах.
www.books-shop.com
Глава
Неструктурированные
18 сокеты
В этой главе...
Когда необходимы неструктурированные сокеты 348
www.books-shop.com
Иногда для реализации проекта недостаточно имеющихся функций. Приво
Когда необходимы
неструктурированные сокеты
Протокол ICMP
На уровне неструктурированных сокетов появляется доступ к ICMP (Internet
Control Message Protocol — протокол управляющих сообщений в сети Internet),
который используется для передачи управляющих сообщений и сообщений об
ошибках. Протоколы более высокого уровня, в частности TCP и UDP, не дают
возможности посылать ICMP
пакеты. Кроме того, в библиотеке Sockets API нет
такого типа сокетов, как SOCK_MSG. Необходимо создать неструктурированный со
править сообщение.
В протоколе ICMP имеется целый ряд сервисов, перечисленных в приложе
тественно, все они реализованы на очень низком уровне, так как протокол ICMP
предназначен для осуществления лишь самых простых задач.
Заполнение IPзаголовка
При самостоятельном заполнении IP
заголовка необходимо установить пара
www.books-shop.com
Ускоренная передача пакетов
Как уже упоминалось, протоколы низкого уровня обладают повышенным бы
биться труднее, чем кажется, так как напрямую размер кадра узнать нельзя. Ин
Ограничения неструктурированных
сокетов
Неструктурированные сокеты обладают большой гибкостью, но они не явля
тель должны четко представлять, что они делают и как это будет вос
www.books-shop.com
• Недоступность TCP! и UPD!пакетoв. К пакетам протоколов TCP и UDP
нельзя получить доступ на низком уровне. Можно задать любой номер
протокола, даже 6 (TCP) или 17 (UDP), но не все операционные систе
Работа с неструктурированными
сокетами
Если выбор сделан в пользу неструктурированных сокетов, необходимо узнать,
как с ними работать. Ниже описываются соответствующие алгоритмы и рассмат
Создание ICMPпакета
После создания неструктурированного сокета необходимо сформировать па
www.books-shop.com
вок — это единственное содержимое пакета, функция sendtsof) М
у пакета заголовок или нет, поэтому им нужно управлять программно.
У IСМР
пакета есть свой заголовок и блок данных. С целью упрощения про
граммирования (и для обеспечения архитектурной независимости) в Linux вклю
чен библиотечный файл, содержащий определение ICMP
заголовка:
#include <netinet/ip_icmp.h>
struct, icmphdr ,*icmp_header;
Для всего пакета необходимо определить отдельного структуру:
#define PACKETSIZE 64 /* байта '*/
struct packet_struct
{
struct icmphdr header;
char message [PACKETSIZE
sizeof( struct icmphdr)];
} packet;
Эту структуру можно использовать для приема и отправки сообщений.
лучиться нуль, так как контрольная сумма вычисляется в обратном коде. Это очень
удобный способ проверки, но у него есть свои недостатки. Основной из них заклю
чается в том, что данный алгоритм позволяет выявлять одиночные ошибки. Если
же две ошибки компенсируют друг друга, они не могут быть выявлены.
www.books-shop.com
Управление IPзаголовком
В IP
заголовке имеется собственное поле контрольной суммы, которая вычис
Сторонний трафик
Непосредственное управление IP
заголовком открывает ряд необычных воз
вер. Сервер
посредник принимает сообщение от клиента, выполняет предвари
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Принимающий модуль программы MyPing
.Текст принимающего модуля прост. После создания неструктурированного
сокета программа начинает вызывать в цикле функцию recvfrom() (листинг 18.2).
структурированные сокеты. Выше уже говорилось о том, что ядро не может опре
www.books-shop.com
/*—Приникаем все, сообщения, досылаемые ядром */
if ( recvfrom(sd, &pckt, sizeof(pсkt), ft/ &r addr, slen) > 0 )
print("***Got message!***\n");
/*— Инициализируем отправляемый пакет—*/
bzero(&pckt, sizeof(pckt)); /* обнуляем содержимое */
pckt.hdr.type = ICMP_ECHO; /^запрашиваем эхосервис */
pckt.hdr.un.echo.id = pid; /* задаем идентификатор */
for ( i = 0; i < sizeof(pckt.msg)l; i++ )
pckt.msg[i] = i+'O'; /* заполняем буфер */
pckt.msg[i] = 0; /* завершаем строку нулевым символом */
pckt.hdr.un.echo.sequence = cnt++; /* устанавливаем счетчик */
pckt.hdr.checksum = /* вычисляем контрольную сумму */
checksum(&pckt, sizeof(pckt));
if ( sendto(sd, &pckt, sizeof(pckt), 0, addr, /* отправляем! */
sizeof(*addr))<= 0 )
perror("sendto");
sleep(l); /* пауза в течение одной секунды */
}
Для того чтобы программа работала правильно, она должна изменить некото
www.books-shop.com
TTL++;
if ( setsockopt(sd, SOL_IP, IP_TTL, &TTL, /* задаем значение
TTL */
sizeof(TTL)) != 0 )
perror("Set TTL option");
сокетов
Неструктурированные сокеты позволяют работать с протоколом ICMP, ис
пользуемым IP
подсистемой для передачи сообщений об ошибках. Благодаря
этим сокетам можно также создавать свои собственные протоколы. Основное
преимущество неструктурированных сокетов — высокая скорость работы, так как
по своей структуре низкоуровневые IP
пакеты близки к физическим сетевым
кадрам. Неструктурированные сокеты находят широкое применение в программах
семейств ping и traceroute.
www.books-shop.com
Глава
IPv6: следующее
поколение
протокола IP 19
В этой главе...
Существующие проблемы адресации 358
Работа по протоколу IPv6 360
Достоинства и недостатки IPv6 366
Резюме: подготовка программ к будущим
изменениям 367
www.books-shop.com
Internet сегодня — это сеть с огромным объемом трафика. Вся сила и гибкость
глобальной сети заключается в IP
пакете, посредством которого сообщения раз
сов в IPv4 —
около двух миллиардов (исключая специальные адреса). В IPv6 ад
ресом IPv4, поскольку его размер увеличился. Каждый адрес теперь состоит из
восьми шестнадцатеричных чисел, разделенных двоеточиями, например
2FFF:80:0:0:0:0:94:1. Для краткости повторяющиеся нули можно заменять двумя
двоеточиями: 2FFF:80::94:1.
www.books-shop.com
К адресу по
прежнему можно добавлять номер порта, который представляет
собой десятичное число, записываемое через точку. Например, добавление порта
80 к приведенному выше адресу будет выглядеть так: 2FFF:80::94:1.80.
Адресное пространство IPv6 уже разделено на области, или группы [RFC 1897,
RFC2471]. В этих группах делается попытка объединить разнородные адреса (как
в IPX). На момент написания книги группы еще не были четко определены и по
стоянно менялись.
Среди всевозможных групп диапазон адресов, начинающихся с битов 001, за
Рис. 19.1. Адрес IPv6 разделен на пять основных частей, определяющих тип адре!
са, порядок маршрутизации и собственно узел
дого", которую приходилось решать при работе в сети. Эта проблема была связа
www.books-shop.com
Как ллргут совместно работать IPv4 и IPv6?
Обещанные механизмы маршрутизации делают предложенные изменения дос
дарт IPv6. Еще очень долгое время будет использоваться старый протокол IPv4.
Каким образом серверы и клиенты смогут работать сразу с двумя протоколами?
Почти все системы, которые поддерживают стек протоколов IPv6, поддержи
вают и стек IPv4. Такие двухстековые системы могут существовать до тех пор, по
ет сообщение двухстековому серверу, оно попадает в стек IPv4. После того как
IP
подсистема распакует сообщение, окажется, что оно имеет формат TCP или
UDP. Если придерживаться подобного стиля программирования, то переход от
IPv4 к IPv6 окажется безболезненным.
Протокол IPv6 включает протокол IPv4, сохраняя все его полезные особенности,
кроме тех, которые больше не нужны. Чтобы выполнить преобразование адреса из
формата IPv4 в формат IPv6, необходимо старшие 80 битов адреса IPv6 оставить
равными нулю, а оставшаяся часть должна представлять собой число OxFFFF плюс
адрес IPv4. Например, адрес 128.10.48.6 примет вид ::FFFF:128.10.48.6, т.е.
::FFFF:800A:3006 (повторюсь, что запись :: означает все нули).
Естественно, обратное преобразование выполнить нельзя, поэтому приложе
Конфигурирование ядра
Можно достаточно быстра узнать, поддерживает ли текущая версия ядра про
токол IPv6. Если имеется файловая система /ргос, загляните в каталог /proc/net.
В нем должны быть файлы igmp6 и if_inet6. Если их нет, необходимо загрузить
модуль ipv6.o.
www.books-shop.com
Ошибка утилиты ifconfig
Программа ifconfig, являющаяся частью пакета net_tools, имеет ошибку, которая не позво
help. Программа
выведет список всех поддерживаемых протоколов. Очевидно, протокол IPv6 будет
отсутствовать в списке. В этом случае необходимо установить пакет net_tools,
переконфигурировать и заново скомпилировать его. После инсталляции измените
сценарий configure.sh, включив поддержку протокола IPv6. Затем скомпилируйте
пакет и скопируйте все исполняемые файлы туда, где они должны находиться
(обычно это каталог /sbin).
После того как ядро и программные пакеты скомпилированы, необходимо пе
точно сделать лишь несколько изменений. По сути, все будет работать как и
www.books-shop.com
раньше, если при написании программ придерживаться правил, описанных в на
чальных главах.
Первое изменение произошло в структуре сокета. Теперь ее тип не
sockaddr_in, a sockaddr_in6:
struct sockaddr_in6 addr;
bzero(&addr, sizeof(addr));
Эта структура будет передаваться в функции b i n d ( ) , connect() и accept() и ряд
других. Второе изменение заключено в типе сокета. Один и тот же сокет не мо
ков был изначальный замысел функции socket(); Далее нужно указать другое се
мейство адресов:
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(MY_PORT);
if ( inet_pton(AF_INET6, "2FFF::80:9ACO:351", &addr.sin6_addr) == 0 )
perror("inet_pton failed");
Структура sockaddr_in6 имеет ряд дополнительных полей, которые можно
проигнорировать. Просто обнулите их (с помощью функции bzero() или
memset()), и все будет работать правильно. Больше ничего от программы не
требуется.
В предыдущем фрагменте программы появилась новая функция inet_pton().
Она и ее двойник inet_ntop() очень полезны для преобразования различных
форм адресов. Они поддерживают протоколы IPv4, IPv6, Rose и IPX. Правда,
нынешние GNU
версии недокументированы и поддерживают только семейства
адресов AF_INET и AF_INET6.
Прототипы функций таковы:
tinclude <arpa/inet.h>
int inet pton(int domain, const char* prsnt, void* b u f ) ;
char *inet_ntop(int domain, void* buf, char* prsnt, int len);
Функция inet_pton() преобразует адрес из символьного представления в дво
www.books-shop.com
Преобразование неструктурированных соке
union IPv6_Address
{
unsigned char u8[16];
unsigned short int ul6[8);
unsigned long int u32[4];
unsigned long long int u64[2];
};
struct IPv6_Header
{
unsigned int version:4; /* версия протокола IP (6) */
unsigned int priority:4;
unsigned int flow_label:24;
unsigned int payload_len:16; /* число байтов после
заголовка */
unsigned int next_header:8; /* протокол (б для TCP) */
unsigned int hop_limit:8; /* то же, что ТТL */
union IPv6_Address source;
union IPv6_Address dest;
};
Только три поля требуют пояснений. Поле priority является эксперименталь
даются маленькие пакеты (менее 100 Кбайт), канал большей частью работает
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
вхолостую. Если задать это поле равным ,0, между заголовком и телом пакета бу
struct Jumbo_Payload
{
unsigned char option; /* равно 194 */
unsigned char length; /* равно 4 (байта) */
unsigned long bytes; ' /* длина сообщения */
};
Благодаря этой записи появляется возможность отправлять пакеты размером
до 4 Гбайт.
Протокол ICMPv6
Физическая структура заголовка протокола ICMPv6 [RFC2463] такая же, как и
в ICMPv4, но содержание полей type и code существенно изменилось. К примеру,
эхо
запрос и эхо
ответ теперь имеют другие номера (128 и 129 соответственно).
Некоторые коды больше не поддерживаются. Полный список кодов приведен в
приложении А, "Информационные таблицы".
венный МАС
адрес. Измененный МАС
адрес формируется на основе запраши
ваемого группового адреса: подсистема IPv6 берет четыре последних байта адреса
и добавляет к ним префикс 33:33. Например, если запрашиваемый групповой ад
вает на то, что это групповой адрес. Следующий байт разделен на два 4
битовых
поля, содержащих дополнительную информацию о типе группы и деталях мар
рес является конкретным, и единице, если это переходный адрес. Остальные три
флага в настоящее время зарезервированы.
Второе поле определяет область видимости адреса (локальный он или гло
бальный). Чем выше номер, тем шире диапазон. В IPv4 область видимости опре
делялась на основании значения TTL (чем оно меньше, тем скорее пакет устаре
ет). Кроме того, весь диапазон адресов был поделен на четыре области видимо
www.books-shop.com
Таблица 19.2. Поле области видимости в IPv6
Значение Область Описание
1 Узел Локальная область в пределах того же компьютера (как 12 7.0.0.1)
2 Канал Сообщения остаются в пределах группы, определенной маршрутиза
тором; маршрутизатор не позволяет таким сообщениям пройти даль
ше
5 Сервер Сообщения остаются в пределах сервера
8 Организация Сообщения остаются в пределах организации
14 Глобальная Все маршрутизаторы пропускают сообщения в глобальную сеть (пока
срок их действия не истечет)
Наконец, правила маршрутизации (см. главу 17, "Широковещательная, груп
ется тот же протокол IGMP, что и в IPv4, для передачи запросов на подключение
к группе и отключение от нее. Поскольку распространение сообщений осуществ
www.books-shop.com
Достоинства и недостатки IPv6
Протокол IPv6 избавился от ряда устаревших особенностей протокола IPv4.
Во
первых, что самое очевидное, благодаря 128
разрядной адресации существен
мальный размер пакета составляет 65535 байтов, а в IPv6 (не в режиме Jumbo) —
65535+40 байтов.
В IPv6 больше не поддерживается широковещание. На самом деле это не про
временных проектах.
Наконец, в IPv6 в заголовок пакета не включена контрольная сумма. Это уп
ной системы.
Технология 6bопе
Подобно тому как в IPv4 существовали проблемы с поддержкой многоадресного режима, кото
рые привели к появлению технологии Mbone (многоадресная магистраль), многие маршрутиза
торы не поддерживают протокол IPv6. В результате энтузиасты IPv6 создали технологию 6bоnе, в
которой пакет IPv6 помещается внутрь пакета IPv4, передаваемого от одного маршрутизатора к
другому. Эта технология находит все большее применение в Европе и на Дальнем Востоке.
И последнее замечание: в связи со своей экспериментальностью протокол IPv6
может вызывать появление брешей в системе защиты компьютера. Лучше не
применять его там, где внутренняя сеть соединена с Internet.
www.books-shop.com
Резюме: подготовка программ
к будущим изменениям
Протокол IPv6 устраняет многие ограничения, присущие схеме адресации
IPv4. Расширяя диапазон адресов на несколько порядков, IPv6 становится стан
дартом ближайшего будущего для Internet. Он уже принят в сетях IPX, использу
рационных систем, скорее всего, будут два стека протоколов, чтобы можно было
осуществлять трансляцию адресов обоих типов.
Ядро Linux, по крайней мере версия 2.2.0, поддерживает IPv6, но не во всех
дистрибутивах это ядро соответствующим образом скомпилировано. В этой главе
описывалось, что нужно делать в подобной ситуации.
Кроме того, в данной главе рассказывалось, как преобразовать существующие
программы с учетом нового стандарта. Рассматривались две новые функции, вы
www.books-shop.com
www.books-shop.com
Часть
Приложения
V
В этой части...
Приложение А. Информационные таблицы
Приложение Б. Сетевые функции
Приложение В. APIфункции ядра
Приложение Г. Вспомогательные классы
www.books-shop.com
Приложение Информационные
А таблицы
В этом приложении...
Домены: первый параметр функции socket() 371
Типы: второй параметр функции socket() 375
Определения протоколов 376
Стандартные назначения Internetпортов (первые
100 портов) 377
Коды состояния HTTP 1.1 378
Параметры сокетов (функции get/setsockopt()) 380
Определения сигналов 386
Коды ICMP 388
Диапазоны групповых адресов IPv4 389
Предложенное распределение адресов IPv6 389
Коды ICMPv6 390
Поле области видимости в групповом адресе IPv6 391
Поле флагов в групповом адресе IPv6 392
www.books-shop.com
В данном приложении сведены справочные таблицы, имеющие отношение к
программированию сокетов.
socket()
В табл. АЛ перечислены значения первого параметра функции socket(). Эти
же константы можно использовать в функции bind(), хотя считается, что в ней
все константы должны иметь префикс AF_. В настоящее время оба семейства кон
www.books-shop.com
Продолжение табл. A.I
Семейство Описание и пример Адресная структура
PF_IPX Семейство протоколов Novell struct sockaddr ipx
#include <linux/ipx.h>
sa_family_t sipx_family;
u16 sipx_port;
u32 sipx_network;
unsigned char
sipx_node[IPX_NODE_LEN];
u8 sipx_type;
/* выравнивание */
unsigned char sipx_zero;
struct sockaddr_x25 {
sa_family_t sx25 family;
/* Адрес Х.121 */
x25_address sx25_addr;
};
www.books-shop.com
Продолжение табл. А. 1
Семейство Описание и пример Адресная структура
PF_INET6 Семейство протоколов IPv6 struct in6_addr
#include <linux/in6.h> {
union {
_u8 u6_addr8[16];
_u16 u6_addr16[8];
_u32 u6 addr32[4];
#if ("OUL) >~ox£fffffff
#ifndef _RELAX_IN6_ADDR_ALIGNMENT
/* б 4 разрядное выравнивание
не поддерживается.
Но лучше выполнять
принудительное выравнивание,
когда это возможно */
_u64 u6_addr64[2];
#endif
#endif
} in6_u;
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
#define s6_addr64 in6u.u6_addr64
struct sockaddr_in6 {
unsigned short int sin6_family;
_ u16 sin6_port;
_ u32 sin6_flowinfo;
struct in6_addr sin6_addr;
struct full_sockaddr_rose {
sa family t srose_family;
rose_address srose_addr;
ax25_address srose_call;
unsigned int srose ndigis;
ax25 address
srose_digis[ROSE_MAX_DIGIS];
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Продолжение табл. А. 1
Семейство Описание и пример Адресная структура
PF_DECnet (Зарезервировано для проекта
DECnet)
PF_ NETBEUI (Зарезервировано для проекта #define NB_NAME_LEN 20
802.2LLC)
#include <linux/netbeui.h> struct sockaddr_netbeui {
sa_family snb_family;
char
snb _name[NB_NAME_LEN];
char
snb_devhint[IFNAMSIZ];
struct sockaddr_ll
{
unsigned short sll_family;
unsigned short sll_protocol;
int sll_ifindex;
unsigned short sll_hatype;
unsigned char sll_pkttype;
unsigned char sll_halen;
unsigned char sll_addr[8];
www.books-shop.com
Окончание табл. А. 1
Семейство Описание и пример Адресная структура
PF_ECONET Семейство Acorn Econet struct ec_addr
# include <linux/if ec.h> {
/* Номер станции */
unsigned char station
/* Номер сети */
unsigned char net;
};
struct sockaddr_ec
{
unsigned short sec_family;
unsigned char port;
/* Байт флагов */
unsigned char cb;
/* Тип сообщения */
unsigned char type;
struct ec_addr addr;
unsigned long cookie;
};
PF_ATMSVC Коммутируемые виртуальные каналы
ATM
PF_SNA Проект Linux SNA
PF_IRDA Сокеты IRDA struct sockaddr_irda {
#include <linux/irda.h> sa_family_t sir_family
/* Селектор LSAP/TSAP */
unsigned char sir_lsap_sel;
/* Адрес устройства */
unsigned int sir_addr;
/* Обычно <cepвис:IrDA:TinyTP */
char sir_name[25];
};
socket()
Во втором параметре функции socket() выбирается тип протокола. Некоторые
из констант, перечисленных в табл. А.2, лишь указывают на то, что протоколы
данного типа станут поддерживаться в будущем.
www.books-shop.com
Таблица А.2. Типы протоколов для второго параметра функции socket()
Тип протокола Описание
SOCK_STREAM (TCP) Надежное двустороннее потоковое соединение. Сокеты данного вида можно
использовать при вызове высокоуровневых функций, которым передаются аргумен
ты топа FILE*. Потоковый протокол позволяет устанавливать виртуальные соедине
ния с сетью через порты и выделенные клиентские каналы. После установления со
единения функция accept() возвращает дескриптор сокета, связанного с новым
клиентом
SOCK_DGRAM (UDP) Ненадежное взаимодействие без установления соединения. Все сообщения
передаются независимо друг от друга, и каждое из них может быть потеряно. Этот
протокол также поддерживает понятие порта
SOCK_RAW (IP) Доступ к внутренним полям сетевых пакетов. С помощью сокетов данного типа
создаются ICMPсообщения. Доступ разрешен только пользователю root
SOCK_RDM (RDM) Надежная доставка сообщений. Гарантируется доставка каждого пакета, но
их порядок может быть неправильным. (Еще не реализован в Linux и других версиях
UNIX.)
SOCK_SEQPACKET Последовательная; надежная доставка дейтаграмм фиксированного размера. (Еще
не реализован в Linux.)
SOCK_PACKET (Физический уровень). Сокет переводится в беспорядочный режим (если таковой
поддерживается), в котором получает все сетевые пакеты. Этот тип сокетов специ
фичен для Linux. Доступ разрешен только пользователю root. (Создавать такого
рода сокеты не рекомендуется— лучше использовать семейство протоколов
PF_PACKET.)
Определения протоколов
В листинге А.1 приведен фрагмент файла /etc/protocols [RFC2292], в котором
идентифицируются наиболее распространенные сетевые протоколы. Редактиро
www.books-shop.com
Standard — Internet Datagram Protocol)
rdp 27 RDP протокол RDP (Reliable Datagram Protocol)
isotp4 29 ISOTP4 протокол ISO Transport Protocol (класс 4)
xtp 36 XTP протокол XTP (Xpress Transfer Protocol)
ddp 37 DDP протокол DDP (Datagram Delivery Protocol)
idprcmtp 39 IDPRCMTP протокол IDPRCMTP (InterDomain Policy
Routing Control Message Transport
Protocol)
rspf 73 RSPF протокол RSPF (Radio Shortest Path First)
vmtp 81 VMTP протокол VMTP (Versatile Message
Transaction Protocol)
ospf 89 OSPFIGP протокол OSPF IGP (Open Shortest Path
First — Interior Gateway Protocol)
ipip 94 IPIP еще один протокол инкапсуляции IPпакетов
encap 98 ENCAP еще один протокол инкапсуляции IPпакетов
www.books-shop.com
chargen 19/udp ttytst source
ftpdata 20/tcp
ftp 21/tcp
fsp 21/udp fspd
ssh 22/tcp система SSH (Secure Shell)
ssh 22/udp система SSH (Secure Shell)
telnet 23/tcp
# 2 4 — закрытый
smtp 25/tcp mail
1 26 — не назначен
time 37/tcp timeserver
time 37/udp timeserver
rip 39/udp resource протокол RLP (Resource
Location Protocol)
nameserver 42/tcp name документ IEN 116
whois 43/tcp nicname
remailck 50/tcp протокол RMCP (Remote Mail
Checking Protocol)
remailck 50/udp протокол RMCP (Remote Mail
Checking Protocol)
domain 53/tcp nameserver сервер доменных имен (DNS)
domain 53/udp nameserver
mtp 57/tcp устарел
bootps 67/tcp сервер ВООТР
bootps 67/udp
bootpc 68/tcp клиент ВООТР
bootpc 68/udp
tftp 69/udp
gopher 70/tcp сервер Gopher
gopher 70/udp
rje 77/tcp netrjs
finger 79/tcp
www 80/tсp http протокол HTTP (HyperText
Transfer Protocol)
www 80/udp протокол HTTP (HyperText
Transfer Protocol)
link 87/tcp ttylink
kerberos 88/top kerberos5 krb5 Kerberos v5
kerberos 88/udp kerberos5 krb5 Kerberos v5
supdup 95/tcp
linuxconf 98/tcp
100 — зарезервирован
www.books-shop.com
Таблица А.З. Коды состояния HTTP
Класс Имя класса Конкретный код и описание
Информационные сообщения 100 Continue
101 Switching Protocols
2хх Успешное завершение 200 OK
201 Created
202 Accepted
203 NonAuthoritative Information
204 No content
205 Reset Content
206 Partial Content
Зхх Перенаправление 300 Multiple Choices
301 Moved Permanently
302 Moved Temporarily
303 See Other
304 Not Modified
305 Use Proxy
4хх Клиентская ошибка 400 Bad Request
401 Unauthorized
402 Payment Required
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable
407 Proxy Authentication Required
408 Request Timeout
409 Conflict
410 Gone
411 Length Required
412 Precondition Failed
413 Request Entity Too Large
414 RequestURI Too Long
415 Unsupported Media Type
5хх •Серверная ошибка 500 Internal Server Error
50% Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
505 HTTP Version Not Supported
www.books-shop.com
Параметры сокетов (функции
get/setsockopt())
В табл. А.4—А.7 перечислены всевозможные параметры сокетов. В разных вер
метр IP_TTL в Linux имеет тип int, но заполняется только первый байт. В AIX тот
же самый параметр имеет тип char. (Колонка со звездочкой "*" означает под
www.books-shop.com
Продолжение табл.А.4
Уровень Параметр Описание Ч 3 Значение Тип
SOL_SOCKET SO_RCVTIMEO Период таймаута входно Да Да Да Время struct
го буфера timeval
SOL_SOCKET SO_REUSEADDR Повторное использование Да Да Да Булево int
адреса
SOL_SOCKET SO_REUSEPORT Повторное использование Нет — — Булево int
адреса (групповое веща
ние)
SOL_SOCKET SO_SECURITY Параметры аутентифика Нет — — Целое int
AUTHENTICATION ции
SOL_SOCKET SO_SECURITY_ENC Параметры шифрования Нет — — Целое int
RYPTION_NETWORK соединения
SOL_SOCKET SO_SECURITY ENC Параметры шифрования Нет — — Целое int
RYPTIONJTRANSPORT передаваемых данных
SOL_SOCKET SO SNDBUF Размер выходного буфера Да Да Да Целое int
SOL_SOCKET SO_SNDLOWAT Нижний порог выходного Да Да Да Целое int
буфера
SOL_SOCKET SO_SNDTIMEO Период таймаута выход Да Да Да Время struct
ного буфера timeval
SOL_S0CKET SO_TYPE Тип сокета Да Да Да Целое int
www.books-shop.com
Продолжение табл. А.5
Уровень Параметр Описание * Ч 3 Значение Тип
SOL_IP IP_PKTINFO Разрешение сбора инфор Да Да Да Булево int
мации о пакете
SOL_IP IP_PKTOPTIONS Опции пакета Нет — — Опции int[]
SOL_IP IP_RECVERR Разрешение на получение Да Да Да Булево int
пакетов с описанием оши
бок
SOL_IP IP_RECVOPTS Разрешение опций посту Да Да Да Булево int
пающих пакетов
SOL_IP IP_RECVTOS Определение типа обслу Да Да Да Целое int
живания поступающих па
кетов
SOL_IP IP RECVTTL Определение значения TTL Да Да Да Целое int
поступающих пакетов
SOL_IP IP_RETOPTS Разрешение опций воз Да Да Да Булево int
вращаемых пакетов
SOL_IP IP_ROUTER_ALERT Разрешение на получение Нет _ — Булево int
сообщений от маршрути
затора
SOL_IP IPJTOS Тип обслуживания Да Да Да Целое int
SOL_IP IP_TTL Значение TTL Да Да Да Целое int
www.books-shop.com
Продолжение табл. А.6
Уровень Параметр Описание • Ч 3 Значение Тип
SOL_IPV6 IPV6_MULTICAST_H Число переходов для мно ? ? ? Целое int
OPS гоадресного режима
SOL_IPV6 IPV6_MULTICAST_I Исходящий интерфейс ? ? ? Целое int
F группового вещания
SOL_IPV6 IPV6_MULTICAST_ L Разрешение групповой об ? ? ? Булево int
OOP ратной связи
SOL_IPV6 IPV6_NEXTHOP Разрешение на определе ? ? 7 Булево int
ние следующего перехода
SOL_IPV6 IPV6_PKTINFO Получение информации о ? ? ? Булево int
пакете
SOL_IPV6 IPV6_PKTOPTIONS Опции пакета ? ? ? Опции int[]
SOL_IPV6 IPV6_ ROUTER_ALER Разрешение на получение ? ? ? Булево int
Т сообщений от маршрутиза
тора
SOL_IPV6 IPV6_RXSRCRT Получение исходного мар ? ? ? Булево int
шрута
SOL_IPV6 IPV6_UNICAST_HOP Предельное число перехо ? ? ? Целое int
S дов
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Окончание табл. А. 7
Уровень Параметр Описание * Ч 3 Значение Тип
SOL_TCP TCP_KEEPCNT Предельное число повтор Да Да Да int
ных подключений
SOL_TCP TCP_SYNCNT Число передаваемых сим Да Да Да int
волов синхронизации
SOL_TCP TCP_LINGER2 Время жизни в состоянии Да Да Да int
FINWAIT2
SOL_TCP TCP_DEFER_ACCEPT Активизировать модуль Да Да Да int
прослушивания, только ко
гда приходят данные
SOL_TCP TCP_WINDOW_CLAMP Границы окна сообщений Да Да Да int
Определения сигналов
В табл. А.8 перечислены стандартные сигналы и приведено их описание. Если
во второй колонке указаны три разных номера, то первый из них соответствует
BSD, второй — Linux, а третий — System V.
www.books-shop.com
Окончание табл. А. 8
Сигнал Номер Дейртвие Описание
SIGSTOP 17, 19, 23 ГДЕ . Останов процесса
SIGTSTP 18, 20, 24 Г Сигнал останова, полученный от терминала
SIGTTIN 21,21,26 г Ввод данных с терминала для фонового процесса
SIGTTOU 22, 22, 27 г Вывод данных на терминал от фонового процесса
SIGIOT 6 ВЖ Синоним сигнала SIGABRT
SIGEMT 7,, 7 Ж Аппаратная ошибка
SIGBUS 10,7,10 АЖ Ошибка на шине
SIGSYS 12,, 12 Ж Неправильный аргумент системного вызова (SVID)
SIGSTKFLT , 16, АЖ Ошибка стека в сопроцессоре
SIGURG 16, 23, 21 БЖ Сигнал о срочном сообщении в сокете (4.2 BSD)
SIGIO 23, 29, 22 АЖ Вводвывод теперь возможен (4.2 BSD)
SIGPOLL АЖ Синоним сигнала SIGIO (System V)
SIGCLD ,,8 Ж Синоним сигнала SIGCHLD
SIGXCPU 24, 24, 30 АЖ Исчерпан лимит времени на доступ к процессору (4.2 BSD)
SIGXFSZ 25, 25, 31 АЖ Исчерпан лимит на размер файла (4.2 BSD)
SIGVTALRM 26, 26, 28 АЖ Сигнал виртуального таймера (4.2 BSD)
SIGPROF 27, 27, 29 АЖ Таймер профилировщика
SIGPWR 29, 30, 19 АЖ Сбой питания (System V)
SIGINFO 29, , Ж Синоним сигнала SIGPWR
SIGLOST АЖ Потеря ресурса
SIGWINCH 28, 28, 20 БЖ Сигнал изменения размеров окна (4.3 BSD, Sun)
SIGUNUSED ,31, АЖ Неиспользуемый сигнал
www.books-shop.com
Коды ICMP
В табл. А.9 перечислены различные типы пакетов ICMP [RFC792] и указано,
что они означают.
www.books-shop.com
Окончание табл. А,9
Тип Код Описание
0 Неправильный IPзаголовок
1 Отсутствует требуемая опция
13 0 Запрос на получение метки времени
14 0 Ответ на запрос о получении метки времени
15 0 Информационный запрос
16 0 Ответ на информационный запрос
17 0 Запрос адресной маски
18 0 Ответ на запрос адресной маски
Предложенное распределение
адресов IPv6
В табл. А.11 перечислены предложенные в стандарте IPv6 диапазоны адресов с
указанием битовых префиксов адреса.
www.books-shop.com
Окончание табл. А. 11
Диапазон Префикс адреса
(не назначен) 0000 01 1
(не назначен) 0000 1
(не назначен) 0001
Агрегированный глобальный однонаправленный адрес 001
(не назначен) 010
(не назначен) 01 1
(не назначен) 100
(не назначен) 101
(не назначен) 110
(не назначен) 1110
(не назначен) 11110
(не назначен) 1111 10
(не назначен) 1111 110
(не назначен) 1111 11100
Локальный однонаправленный адрес уровня рабочей группы 11111110 10
Локальный однонаправленный адрес уровня сервера 1111 111011
Групповой адрес 1111 1111
Коды ICMPv6
В табл. А. 12 представлены новые ICMPкоды для стандарта IPv6.
www.books-shop.com
Окончание табл. А. 12
Тип Код Описание
1 Нераспознанный следующий заголовок
2 Нераспознанная опция
128 0 Эхозапрос (ping)
129 0 Эхоответ (ping)
130 0 Запрос на членство в группе
131 0 Сообщение о членстве в группе
132 0 Прекращение членства в группе
133 0 Запрос от маршрутизатора
134 0 Уведомление от маршрутизатора
135 0 Запрос от соседа
136 0 Уведомление от соседа
137 0 Перенаправление
www.books-shop.com
Поле флагов в групповом адресе IPv6
В табл. А.14 дана принятая в настоящий момент интерпретация поля флагов в
групповом адресе IPv6.
www.books-shop.com
Сетевые функции Приложение
Б
В этом приложении...
Подключение к сети 394
Взаимодействие по каналу 400
Разрыв соединения 409
Преобразование данных в сети 410
Работа с сетевыми адресами 415
Управление сокетами 419
www.books-shop.com
В этом приложении описаны все библиотечные и системные сетевые функции.
Подключение к сети
В библиотеке Socket API имеется целый ряд функций, предназначенных для
создания сокетов и подключения к другим компьютерам.
socket()
Функция socket () формирует двунаправленный канал связи, как правило, с
другой сетью. Дескриптор этого канала можно передавать как специализирован
Прототип
#include <resolv.h>
#include <sys/socket.h>
#include <sys/types.h>
int socket(int domain, int type, int protocol);
Возвращаемое значение
При успешном завершении функция возвращает корректный дескриптор соке
Параметры
domain Задает семейство (домен) сетевых протоколов (см. приложение А,
"Информационные таблицы")
type Задает сетевой уровень работы сокета (см. приложение А,
"Информационные таблицы")
protocol Задает конкретный протокол и обычно равен 0 (см. приложение А,
"Информационные таблицы")
Возможные ошибки
EPROTONOSUPPORT Тип протокола или указанный протокол не поддерживается в данном домене
ENFILE Недостаточно памяти ядра, чтобы создать новую структуру сокета
EMFILE Переполнение в таблице дескрипторов файлов
EACCES и ENOBUFS Отсутствует разрешение на создание сокета указанного типа или протокола
ENOMEM Недостаточно памяти; сокет не может быть создан, пока не будет освобож
дено достаточное количество ресурсов
EINVAL Неизвестный протокол, либо семейство протоколов недоступно
www.books-shop.com
Примеры
/*** Создание ТСР
сокета ***/
int sd;
sd = socket(PF_INET, SOCK_STREAM, 0);
bind()
Функция bind() задает порт или имя файла для привязки сокета. В большин
стве случаев ядро автоматически вызывает данную функцию, если это не было
сделано явно, причем номер порта при каждом последующем запуске программы
может быть другим.
Прототип
#include <sys/socket.h>
#include <resolv.h>
int bind(int sockfd, struct sockaddr *addr, int addrlen);
Возвращаемое значение
В случае успешного завершения возвращается 0. Если возникла ошибка, ее
код можно узнать в переменной errno.
Параметры
sockfd Дескриптор сокета
addr Номер порта или имя файла
addrlen Длина структуры addr
Возможные ошибки
EBADF Указан неверный дескриптор сокета
EINVAL Сокет уже связан с определенным адресом
EACCES Запрашиваемый адрес доступен только пользователю root
ENOTSOCK Указан дескриптор файла, а не сокета
Пример
/*** Привязка порта ft 9999 к сокету с любым IPадресом ***/
struct sockfd;
struct sockaddr_in addr;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
bzero(Saddr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(9999); /* любой порт по желанию */
/* привязка к любому сетевому интерфейсу */
addr.sin_addr.s_addr = INADDR_ANY;
/* если выбирается конкретный интерфейс, следует поступить так: */
/* inet_aton("128.l.l.l", &addr.sin_addr); */
if ( bind(sockfd, saddr, sizeof(addr)) != 0 )
perror("bind");
listen()
Функция listen() переводит сокет в режим ожидания запросов на подключе
ние. Это возможно только для сокетов типа SOCK_STREAM. Функция также создает
очередь запросов.
Прототип
#include <sys/socket.h>
#include <resolv.h>
int listen(int sockfd, int queue_len);
Возвращаемое значение
В случае успешного завершения возвращается 0. Если возникла ошибка, ее
код можно узнать в переменной errno.
Параметры
Возможные ошибки
Пример
/*** перевод сокета в режим прослушивания; ***/,
/*** длина очереди 10 позиций ***/
int sockfd;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
/*— Привязка к порту с помощью функции bind() —*/
listen(sockfd, 10); /* создание очереди с 10ю позициями */
www.books-shop.com
acceptQ
Функция accept() ожидает поступление запроса на подключение. Когда при
Прототип
#include <sys/socket.h>
#include <resolv.h>
int accept(int sockfd, struct sockaddr *addr, int *addr_len);
Возвращаемое значение
Если возвращается неотрицательное значение, то это дескриптор нового соке
та, в противном случае это признак ошибки, код которой содержится в перемен
ной errno.
Параметры
sockfd Дескриптор сокета, который связан с портом и переведен в режим прослуши
вания
addr Если этот параметр не равен нулю, функция помещает в него адрес клиента
addr_len Ссылка на переменную, содержащую размер адресной структуры; в эту же пе
ременную функция записывает реальный размер адреса
Возможные ошибки
Примеры
/*** Принятие запроса на подключение, ***/
/*** игнорирование адреса клиента ***/
int sockfd = socket(PF_INET, SOCK_STREAM, 0 ) ;
/* Привязка сокета к порту с помощью функции bind() */
/* Перевод сокета в режим прослушивания с помощью функции
listen()
*/
for (;;)
www.books-shop.com
{ int client;
client = accept(sockfd, 0, 0) ;
/*
взаимодействие с клиентом — */
close (client);
*/
/*
*/
for (;;)
{ struct sockaddr_in addr;
int client, addr_len = addr;
clientsd = accept(sockfd, &addr, &addr_len);
printf ("Connected: %s:%d\n", inet_ntoa (addr.sin_addr) ,
ntohs(addr.sin_port) );
/*
взаимодействие с клиентом
*/
close (client) ;
connect()
Функция connect() подключает сокет к одноранговому узлу или серверу. Дан
ную функцию можно вызывать для сокетов типа SOCK_DGRAM и SOCK_STREAM. В пер
вом случае (протокол UDP) функция просто запоминает номер порта, по кото
Прототип
#include <sys/socket.h>
#include <resolv.h>
int connect(int sockfd, struct sockaddr *addr, int addr_len);
Возвращаемое значение
В случае успешного завершения возвращается 0. Если возникла ошибка, ее
код можно узнать в переменной errno.
Параметры
sockfd Дескриптор только что созданного сокета; можно предварительно вызвать функ
цию bind() для привязки сокета к порту (если этого не сделать, ядро автоматиче
ски назначит сокету ближайший доступный порт)
addr Адрес и порт узла, с которым устанавливается соединение
addr_len Длина параметра addr
www.books-shop.com
Возможные ошибки
Пример
/*** Подключение к TCPсерверу ***/
int sockfd;
struct sockaddr_in addr;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 13; /* сервис текущего времени */
inet_atoi("127.0.0.1", &addr.sin_addr);
if ( connect(sockfd, saddr, sizeof(addr)) != 0 )
perror("connect");
socketpair()
Функция socketpair() создает пару сокетов, которые связаны друг с другом
механизмом, напоминающим UNIX
канал. Это подобно вызову функции pipe(),
но в распоряжении сокетов оказываются все средства библиотеки Socket API.
Функция socketpair() поддерживает только домены PF_UNIX И PF_LOCAL. Создан
www.books-shop.com
Прототип
#include <sys/socket.h>
#include <resolv.h>
int socketpair(int domain, int type, int protocol,
int sockfds[2]);
Возвращаемое значение
В случае успешного завершения возвращается 0. Если возникла ошибка, ее
код можно узнать в переменной errno.
Параметры
domain Должен содержать значение PF_LOCAL или PF_UNIX
type Должен содержать значение SOCK_STREAM; благодаря этому сокеты будут
функционировать подобно каналу, который в некоторых версиях UNIX являет
ся двунаправленным, хотя стандарт POSIX.1 этого не требует
protocol Должен содержать 0
sockfds [ 2 ] Массив целых чисел, в котором функция сохраняет дескрипторы созданных
сокетов
Возможные ошибки
EMFILE Слишком много дескрипторов файлов используется этим процессом
EAFNOSUPPORT Указанное семейство адресов не поддерживается на данном компьютере
EPROTONOSUPPORT Указанный протокол не поддерживается на данном компьютере
EOPNOSUPPORT Указанный протокол не поддерживает создание связанной пары сокетов
EFAULT Адрес массива sockf ds не является корректным
Пример
/*** Создание связанной пары сокетов ***/
int sockfd[2];
struct sockaddr_ux addr;
if ( socketpair(PF_LOCAL, SOCK_STREAM, 0, sockfd) != 0 )
perror("socketpair") ;
Взаимодействие по каналу
www.books-shop.com
send()
Функция send() посылает сообщение подключенному одноранговому компью
теру, клиенту или серверу. Она напоминает системный вызов write(), но допол
Прототип
#include <sys/socket.h>
#include <resolv.h>
int send(int sockfd, void *buffer, int msg_len, int options);
Возвращаемое значение
Подобно функции write(), функция send() возвращает число записанных бай
тов. Это число может быть меньше, чем значение параметра msg_len. В этом слу
чае функцию нужно вызывать до тех пор, пока требуемые данные не будут пол
Параметры
sockfd Дескриптор подключенного сокета, имеющего тип SOCK_DGRAM или SOCK_STREAM
buffer Отправляемые данные
msg_len Число посылаемых байтов
options Набор флагов, указывающих на особые режимы обработки сообщений:
* MSG_OOB. Режим внеполосной передачи (срочное сообщение);
* MSG_DONTROUTE Запрет маршрутизации сообщения, т.е. оно доставляется адресату напрямую;
если адресат недостижим, будет получено сообщение об ошибке;
* MSG_DONTWAIT. Не допускать блокирования программы, т.е. не ждать завершения функции
send(); если запись невозможна, в переменную errno будет записано значение EAGAIN;
* MSG_N0SIGNAL He посылать сигнал SIGPIPE локальному компьютеру, если по какойто причине
соединение разрывается досрочно
Возможные ошибки
EBADF Указан неверный дескриптор
ENOTSOCK Указанный дескриптор связан с файлом, а не с сокетом
EFAULT Для аргумента buffer указан неправильный адрес
EMSGSIZE Функция не смогла завершить работу, так как сокет попросил ядро послать сообщение
единым блоком, но размер сообщения оказался слишком велик
EAGAIN Сокет установлен в режим неблокируемой передачи, а запрашиваемая операция приведет
к блокировке
ENOBUFS Система не сумела выделить блок памяти; операция сможет продолжиться, когда освобо
дятся буферы
EINTR Получен сигнал
ЕNOМЕМ Не хватает памяти
www.books-shop.com
EINVAL Получен неправильный аргумент
EPIPE Противоположный конец локального сокета был закрыт; программа также получит сигнал
SIGPIPE, если только не был установлен флаг MSG_NOSIGNAL
Пример
/*** Отправка сообщения (TCP, UDP) подключенному узлу ***/
int sockfd;
int bytes, bytes_wrote = 0;
/* Создание сокета, подключение к серверу/узлу */
while ( (bytes = send(sockfd, buffer, msg_len, 0)) > 0 )
if ( (bytes_wrote += bytes) >= msg_len )
break;
if ( bytes < 0 )
perror("send");
/*** Передача срочного сообщения (TCP) подключенному узлу ***/
int sockfd;
int bytes, bytes_wrote = 0;
/* Создание сокета, подключение к серверу */
if ( send(sockfd, buffer, 1, MSG_OOB)) != 1 )
perror("Urgent message");
sendto()
Функция sendto() посылает сообщение указанному адресату, не подключаясь
к нему. Обычно эта функция используется для отправки дейтафамм и неструкту
Прототип
#include <sys/socket.h>
#include <resolv.h>
int sendto(int sockfd, void* msg, int len, int options,
struct sockaddr *addr, int addr_len);
Возвращаемое значение
Возвращается число отправленных байтов или — 1, если произошла ошибка.
Параметры
sockfd Дескриптор сокета
msg Отправляемые данные
len Число посылаемых байтов
options Флаги управления (такие же, как и в функции send())
addr Адрес узла
addr_len Размер адресной структуры
www.books-shop.com
Возможные ошибки
(Те же, что и в функции send())
Пример
/*** Отправка сообщения (TCP, UDP) неподключенному узлу ***/
int sockfd;
struct sockaddr_in addr;
if ( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0 )
perror("socket");
bzero(Saddr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(DEST_PORT);
inet_aton(DEST_ADDR, &addr.sin_addr);
if ( sendto(sockfd, buffer, msg_len, 0, &addr, sizeof(addr))
<0 )
perror("sendto");
sendmsg()
Функция sendmsg() собирает сообщение из нескольких блоков данных. Если
поле msg_name структуры сообщения указывает на структуру sockaddr, функция
посылает сообщение, не устанавливая соединение. Если же поле равно NULL,
функция предполагает, что сокет подключен к узлу.
Прототип
#include <sys/socket.h>
#include <resolv.h>
#include <sys/uio.h>
int sendmsg(int sockfd, const struct msghdr *msg,
unsigned int options);
Возвращаемое значение
Возвращается общее число отправленных байтов или — 1, если произошла
ошибка (ее код записывается в переменную errno).
Параметры
sockfd Дескриптор сокета
msg Указатель на структуру msghdr, в которой находятся адрес получателя, фла
ги и блоки сообщения. Определение структуры таково:
struct iovec
{
void *iov_base; /* начало буфера */
_kernel_size_t iov_len; /* длина буфера */
};
www.books-shop.com
struct msghdr
{
_ptr_t msg name; /* Адрес получателя */
socklen_t msg_namelen; /* Длина адреса */
struct iovec *msg_iov; /* Массив буферов */
size_t msg_iovlen; /* Длина массива */
_ ptr_t msg_control; /* Служебные данные */
size_t msg_controllen; /* Длина служебных данных */
int msg_flags; /* Флаги полученного сообщения */
};
Через блок служебных данных программа может передавать, к примеру, де
скрипторы файлов
options Флаги управления (такие же, как и в функции send())
Возможные ошибки
(Те же, что и в функции send())
Пример
int i, sd, len, bytes;
char buffer[MSGS][100];
struct iovec ip[MSGS];
struct msghdr msg;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_DGRAM, 0);
bzero(&addr, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
inet_aton (&addr.sin_addr,"127.0.0.1");
bzero(&msg, sizeof (msg));
msg.msg_name = &addr;
msg.msg_namelen = sizeof (addr);
for ( i = 0; i < MSGS; i++ )
{
io[i].iov_base = buffer[i];
sprintf(buffer[i], "Buffer #%d: this is a test\n", i);
io[i].iov_len = strlen(buffer[i]);
}
msg.msg_iov = io;
msg.msg_iovlen = MSGS;
if ( (bytes = sendmsg(sd, &msg, 0)) < 0 )
perror("sendmsg");
sendfile()
Функция sendfile() реализует быстрый способ передачи файла через сокет.
Она извлекает данные из источника с дескриптором in_f d и записывает их в при
www.books-shop.com
меняется, а файла
получателя — меняется. Функция читает указанное число бай
танным байтом.
Прототип
#include <unistd.h>
int sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
Возвращаемое значение
При успешном завершении функция возвращает общее число скопированных
байтов. В случае ошибки возвращается — 1, а в переменную errno записывается
код ошибки.
Параметры
out_f d Дескриптор получателя (указатель текущей позиции файла меняется)
in_f d Дескриптор источника (указатель текущей позиции файла не меняется)
offset Указатель на переменную, в которой содержится начальное смещение
count Число отправляемых байтов
Возможные ошибки
EBADF Входной файл не был открыт для чтения или выходной файл не был открыт
для записи
EINVAL Дескриптор некорректен или заблокирован
ENOMEM Недостаточно памяти для чтения из исходного файла
ЕЮ Неопределенная ошибка при чтении из исходного файла
Пример
#include <unistd.h>
struct stat fdstat;
int client = accept(sd,0,0);
int fd = open("filename.gif", O_RDONLY);
fstat(fd, &fdstat);
sendfile(client, fd, 0, fdstat.st_size);
close(fd);
close(client);
recv()
Функция recv() принимает сообщение от подключенного однорангового ком
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Прототип
#include <sys/socket.h>
#include <resolv.h>
int recv(int sockfd, void *buf, int maxbuf, int options);
Возвращаемое значение
Функция возвращает число прочитанных байтов или
1 в случае ошибки.
Параметры
sockfd Дескриптор подключенного сокета
buf Буфер, в который будет помещено поступившее сообщение
maxbuf Размер буфера
options Набор флагов, которые можно объединять с помощью операции побитового сложения:
* MSG_OOB. Запрашивает получение внеполосных данных, которые не передаются в обычном пото
ке данных. В некоторых протоколах срочные данные помещаются в начало обычной очереди, по
этому рассматриваемый флаг не может применяться при работе с такими протоколами;
* MSG_PEEK. Заставляет функцию читать данные, не удаляя их из очереди. Таким образом, при
следующей операции чтения будут прочитаны те же самые данные;
* MSG_WAITALL. Запрашивает блокировку программы до тех пор, пока не будет получено все со
общение целиком. Тем не менее функция может завершиться досрочно, если получен сигнал,
произошла ошибка или соединение было разорвано противоположной стороной;
* MSG_ERRQUEUE. Запрашивает прием пакета из очереди ошибок сокета. Информация об ошибке
передается в служебном сообщении, тип которого зависит от протокола (для IPсокета нужно ус
тановить параметр IP_RECVERR). В теле сообщения находится структура sock_extended_error;
* MSG_NOSIGNAL. Отключает выдачу сигнала SIGPIPE в потоковом сокете при разрыве соединения
на противоположной стороне
Возможные ошибки
EBADF Указан неверный дескриптор файла
ENOTCONN Сокет не был подключен (см. функции connect() и accept())
ENOTSOCK Указанный дескриптор связан с файлом, а не с сокетом
EAGAIN Задан режим неблокируемого вводавывода, а данные недоступны, либо был установ
лен период таймаута, который превышен до того, как были получены данные
EINTR Сигнал прервал выполнение операции чтения, прежде чем данные стали доступны
EFAULT Указатель буфера чтения ссылается за пределы адресного пространства процесса
EINVAL Передан неверный аргумент
Пример
/*** Получение сообщения (TCP, UDP) от подключенного узла ***/
int sockfd;
int bytes, bytes_wrote=0;
/*— Создание сокета, подключение к серверу/узлу —*/
if ( (bytes = recv(sockfd, buffer, msg_len, 0)) < 0 )
www.books-shop.com
perror("send");
/*** Получение срочного сообщения (TCP) от подключенного узла ***/
/*** Этот код обычно находится в обработчике сигнала SIGURG ***/
int sockfd;
int bytes, bytes_wrote=0;
/*— Создание сокета, подключение к серверу —*/
if ( (bytes = recv(sockfd, buffer, msg_len, MSG_OOB)) < 0 )
perror("Urgent message");
recvfrom()
Функция recvfrom() принимает сообщение от неподключенного однорангового
компьютера (UDP и неструктурированные сокеты). В протоколе Т/ТСР эта
функция никогда не используется. Вместо нее вызывается функция accept().
Прототип
linclude <sys/socket.h>
linclude <resolv.h>
int recvfromfint sockfd, void* buf, int buf_len, int options,
struct sockaddr *addr, int *addr_len);
Возвращаемое значение
При успешном завершении возвращается число прочитанных байтов. В случае
ошибки возвращается — 1, а в переменную errno записывается код ошибки.
Параметры
sockfd Дескриптор сокета
buf Буфер для приема сообщения
buf _lеп Максимальный размер буфера (сообщение усекается, если буфер слишком мал)
options Параметры управления каналом (такие же, как и в функции recv())
addr Адрес и порт отправителя
addr_len Максимальный размер адресной структуры (адрес усекается, если буфер слишком
мал); по завершении функции в этом параметре будет содержаться реальная длина
адреса
Возможные ошибки
(Те же, что и в функции recv())
Пример
struct sockaddr_in addr;
int addr_len=sizeof(addr), bytes_read;
char buf[l024];
www.books-shop.com
int sockfd = socket (PF_INET, SOCK_DGRAM, 0);
/* — привязка к конкретному порту —*/
bytes_read = recvfrom(sockfd, buf, sizeof(buf), 0, &addr,
&addr_len);
if ( bytes_read < 0 )
perror("recvfrom failed");
recvmsg()
Функция recvmsg() принимает сразу несколько сообщений от одного источни
ка. Она может использоваться с сокетами типа SOCK_DGRAM (аналогично тому, как
это происходит в случае функции sendmsg()).
Прототип
#include <sys/socket.h>
#include <resolv.h>
#include <sys/uio.h>
int recvmsg(int sockfd, struct msghdr *msg, unsigned int options);
Возвращаемое значение
При успешном завершении возвращается общее число полученных байтов, в
противном случае возвращается — 1.
Параметры
sockfd Дескриптор сокета
msg Буфер для принимаемых данных
options Параметры управления каналом (такие же, как и в функции recv( ))
Возможные ошибки
(Те же, что и в функции recv())
Пример
char buffer[MSGS][1000];
struct sockaddr_in addr;
struct iovec io[MSGS];
struct msghdr msg;
bzero(&addr, sizeof(addr));
msg.msg_name = &addr;
msg.msg_namelen = sizeof (addr);
for ( i = 0; i < MSGS; i++ )
{
io[i].iov_base = buffer(i);
io[i].iov_len = sizeof (buffer[i]);
}
394 Часть V. Приложения
www.books-shop.com
msg.msg_iov = io;
msg.msg_iovlen = MSGS;
if ( (bytes = recvmsg(sd, &msg, 0)) < 0 )
perror("recvmsg");
Разрыв соединения
После того как программа закончила сеанс связи с внешним узлом, она долж
единения.
shutdown()
Функция shutdown() закрывает указанные части канала передачи данных. Со
правленным. Если нужно сделать его доступным только для чтения или только
для записи, то следует с помощью функции shutdown() закрыть один из концов
канала.
Прототип
#include <sys/socket.h>
int shutdown(int sockfd, int how);
Возвращаемое значение
Если все прошло успешно, возвращается 0. В случае ошибки ее код можно
найти в переменной еrrno.
Параметры
sockfd Дескриптор сокета
how флаг, указывающий на то, какую часть канала следует закрыть:
* SHUT_RD (0) — сделать канал доступным только для записи;
* SHUT _WR(1) — сделать канал доступным только для чтения;
* SHUT_RDWR (2) — закрыть обе половины канала (эквивалентно вызову функции close()).
Выполнять эти действия можно только в отношении подключенных сокетов
Возможные ошибки
EBADF Указан неверный дескриптор сокета
ENOTSOCK Указанный дескриптор связан с файлом, а не с сокетом
ENOTCONN Указанный сокет не является подключенным
www.books-shop.com
Пример
int sockfd;
struct sockaddr_in addr;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(DEST_PORT);
inet_aton (DEST_ADDR, &addr. sin_addr);
connect(sockfd, &addr, sizeof(addr));
if ( shutdown(sockfd, SHUT_WR) != 0 )
PANIC("Can't make socket inputonly");
htons(), htonl()
Функции htons() и htonl() преобразуют двоичные данные из серверного по
Прототип
#include <netinet/in.h>
unsigned short int htons(unsigned short int host_short);
unsigned long int htonl(unsigned long int host_long);
Возвращаемое значение
Преобразованный аргумент (16
или 32
разрядный).
Параметры
host_short 16разрядное значение с серверным порядком следования байтов
host_long 32разрядное значение с серверным порядком следования байтов
Возможные ошибки
(отсутствуют)
www.books-shop.com
Пример
/*** Связываем сокет с портом 1023 ***/
struct sockaddr_in addr;
addr.sin_port = htons(1023);
/*** Связываем сокет с адресом 128.1.32.10 ***/
struct sockaddr_in addr;
addr.sin_addr.s_addr = htonl(Ox8001200A);
ntohs(), ntohl()
Функции ntohs() и ntohl() преобразуют двоичные данные из сетевого порядка
следования байтов в серверный.
Прототип
#include <netinet/in.h>
unsigned short int ntohs(unsigned short int network_short);
unsigned long int ntohl(unsigned long int network_long);
Возвращаемое значение
Преобразованный аргумент (16
или 32
разрядный).
Параметры
network_short 16разрядное значение с сетевым порядком следования байтов
network_long 32разрядное значение с сетевым порядком следования байтов
Возможные ошибки
(отсутствуют)
Пример
struct sockaddr_in addr;
int client, addrlen=sizeof(addr);
client = accept(sockfd, &addr, &addrlen);
if ( client > 0 )
printf("Connected %lX:%d\n", ntohl(addr.sin_addr),
ntohs(addr.sin_port));
inet_addr()
Функция inet_addr() считается устаревшей. Она преобразует IP
адрес из то
www.books-shop.com
Прототип
#include <netinet/in.h>
unsigned long int inet_addr(const char *ip_address);
Возвращаемое значение
He нуль Если все прошло успешно, возвращается преобразованный IPадрес
INADDR_NONE (1) Аргумент является неправильным. (Это ошибка функции. Она не возвращает от
рицательное значение, а значение 255.255.255.255 обозначает обычный ши
роковещательный адрес.)
Параметры
ip_address IPадрес в традиционной, точечной нотации (например, 128.187.34.2)
Возможные ошибки
(переменная errno не устанавливается)
Пример
if ( (addr.sin_addr.s_addr = inet_addr("182.187.34.2")) ==
1 )
perror("Couldn't convert address");
inet_aton()
Функция inet_aton() преобразует IP
адрес из точечной нотации в двоичную
форму с сетевым порядком следования байтов. Она заменяет функцию
inet_addr().
Прототип
#include <netinet/in.h>
int inet_aton(const char *ip_addr, struct in_addr *addr);
Возвращаемое значение
Если все прошло успешно, возвращается ненулевое значение. В противном
случае возвращается нуль.
Параметры
ip_addr ASCIIстрока с IPадресом (например, 187.34.2.1)
addr Переменная, куда записывается адрес; обычно заполняется поле sin_addr
структуры sockaddr_in
www.books-shop.com
Возможные ошибки
(переменная errno не устанавливается)
Пример
struct sockaddr_in addr;
if ( inet_aton("187.43.32.1", &addr.sin_addr) == 0
perror("inet_aton() failed");
inet_ntoa()
Функция inet_ntoa() преобразует IP
адрес из двоичной формы с сетевым по
Прототип
iinclude <netinet/in.h>
int inet_ntoa(struct in_addr *addr);
Возвращаемое значение
Функция возвращает строку с адресом.
Параметры
addr Двоичный адрес (обычно это адресное поле структуры sockaddr_in)
Возможные ошибки
(переменная errno не устанавливается)
Пример
clientfd = accept(serverfd, &addr, &addr_size);
if ( clientfd > 0 )
printf("Connected %s:%d\n", inet_ntoa(addr.sin_addr),
ntohs(addr.sin_port));
inet_pton()
Функция inet_pton() преобразует адрес IPv4 или IPv6 из символьного пред
Прототип
Iinclude <arpa/inet.h>
int inet_pton(int domain, const char* prsnt, void* addr);
www.books-shop.com
Возвращаемое значение
В случае успешного завершения возвращается ненулевое значение. Если воз
Параметры
domain Семейство адресов (AF_INET или AF_INET6)
prsnt ASCII
строка с IP
адресом (например, 187.34.2.1 или FFFF:8090:АОЗ:3245)
addr Переменная, куда записывается адрес; обычно заполняется поле sin_addr структуры
sockaddr_in или поле sin6_addr структуры sockaddr_in6
Возможные ошибки
(переменная errno не устанавливается)
Пример
struct sockaddr_in addr;
if ( inet_pton(AF_INET, "187.43.32.1", &addr.sin_addr) == 0 )
perror("inet pton() failed");
inet_ntop()
Функция inet_pton() преобразует адрес из двоичного представления с сетевым
порядком следования байтов в символьную форму. Функция поддерживает се
Прототип
#include <arpa/inet.h>
int inet_ntop(int domain, struct in_addr *addr, char* str,
int len);
Возвращаемое значение
Функция возвращает строку str.
Параметры
domain Семейство адресов (AF_INET или AF_INET6)
addr Двоичный адрес (обычно это адресное поле структуры sockaddr in)
str Буфер для строки адреса
len Размер буфера
www.books-shop.com
Возможные ошибки
(переменная errno не устанавливается)
Пример
char str[100];
clientfd = accept(serverfd, &addr, &addr_size);
if ( clientfd > 0 )
printf("Connected %s:%d\n",
inet_ntop(AF_INET, addr.sin_addr, str, sizeof(str)),
ntohs(addr.sin_port));
ния имен.
getpeername()
Функция getpeername() определяет адрес или имя компьютера, подключенного
к противоположному концу сокета с дескриптором sockfd. Результат помещается
в буфер addr. Параметр addr_len определяет размер адресного буфера. Это та же
самая информация, которую можно получить с помощью функции accept().
Прототип
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr,
socklen_t *addr_len);
Возвращаемое значение
В случае успешного завершения возвращается нуль. Если произошла ошибка,
ее код помещается в переменную errno.
Параметры
sockfd Дескриптор подключенного сокета
addr Буфер, в который помещается адресная структура
addr_len Размер буфера; этот параметр передается по ссылке, так как функция записывает
сюда реальный размер адреса
Возможные ошибки
EBADF Указан неверный дескриптор
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
ENOTSOCK Указанный дескриптор относится к файлу, а не к сокету
ЕNOТСОNN Сокет не подключен
ENOBUFS В системе недостаточно ресурсов для выполнения операции
EFAULT Указатель addr ссылается за пределы адресного пространства процесса
Пример
struct sockaddr_in addr;
int addr_len = sizeof(addr);
if { getpeername(client, &addr, &addr_len) != 0 )
perror("getpeername() failed");
printf("Peer: %s:%d\n", inet_ntoa(addr.sin_addr),
ntohs(addr.sin_port));
gethostname()
Функция gethostname() возвращает имя локального узла. Результат помещается
в буфер name, размер которого определяется параметром len.
Прототип
#include <unistd.h>
int gethostname(char *name, size_t len);
Возвращаемое значение
В случае успешного завершения возвращается нуль. Если произошла ошибка,
ее код помещается в переменную errno.
Параметры
name Буфер, в который записывается имя узла
len Размер буфера
Возможные ошибки
EINVAL Параметр len является отрицательным либо, если функция выполняется на платформе
Linux/i386, значение параметра len оказалось меньше, чем реальный размер имени
EFAULT Параметр name содержит неправильный адрес
Пример
char name[50];
if ( gethostname(name, sizeof(name)) != 0
perror("gethostname() failed");
printf("My host is: %s\n", name);
www.books-shop.com
gethostbyname()
Функция gethostbyname() ищет имя узла в базе данных DNS
сервера и преоб
разует его в IP
адрес. Функции может передаваться как имя, так и сам адрес. Во
втором случае поиск не осуществляется; вместо этого адрес возвращается в полях
h name и h addr list[0] структуры hostent.
Прототип
#include <netdb.h>
struct hostent *gethostbyname(const char *name);
Возвращаемое значение
Функция возвращает указатель на структуру hostent. В случае неудачи возвра
Параметры
name Имя узла для поиска или IPадрес
Возможные ошибки
ENOTFOUND Указанный узел не найден
NO_ADDRESS, NO_DATA Указанное имя является корректным, но с ним не связан IPадрес
NO_RECOVERY Произошла фатальная ошибка сервера имен
EAGAIN Произошла временная ошибка сервера имен, повторите попытку позднее
Пример
int i;
struct hostent *host;
host = gethostbyname("sunsite.unc.edu");
if ( host != NULL )
{
printf("Official name: %s\n", host>h_name);
for ( i = 0; host>h_aliases[i] != 0; i++)
printf(" alias[%d]: %s\n", i+1, host>h_aliases[i]);
www.books-shop.com
printf("Address type=%d\n", host>h_addrtype);
for ( i = 0; i < host>h_length; i++)
printf("Addr[%d]: %s\n", i+1,
inet_ntoa(host>h_addr_list[i]));
}
else
perror("sunsite.unc.edu");
getprotobyname()
Функция getprotobyname() просматривает файл /etc/protocols в поиске прото
ра портов.
Прототип
#include <netdb.h>
struct protoent *getprotobyname(const char* pname);
Возвращаемое значение
В случае успешного завершения функция возвращает указатель на структуру
protoent. В противном случае возвращается NULL.
struct protoent {
char *p_name; /* официальное имя протокола */
char **p_aliases; /* список псевдонимов */
int p_proto; /* номер порта */
};
Параметры
pname Имя протокола; это может быть любое известное имя протокола или псевдоним
Возможные ошибки
(переменная errno не устанавливается)
Пример
#include <netdb.h>
int i;
struct protoent *proto = getprotobyname("http");
if ( proto != NULL )
{
printf("Official name: %s\n", proto>name);
printf("Port!: %d\n", proto>p proto);
for ( i = 0; proto>p aliases[I] != 0;
www.books-shop.com
printf("Alias[%d]: %s\n", i+1, proto>p_aliases[i]);
else
perror("http");
Управление сокетами
Когда сокет открыт, можно менять самые разные его параметры. Для этой це
setsockopt()
Функция setsockopt() изменяет поведение сокета. У каждого параметра сокета
есть значение (некоторые доступны только для чтения). Полный список парамет
Прототип
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sd, int level, int optname,
const void *optval, socklen_t optlen);
Возвращаемое значение
В случае успешного завершения возвращается нуль. Если произошла ошибка,
ее код помещается в переменную errno.
Параметры
sd Дескриптор сокета
level Уровень параметра сокета (SOL_SOCKET, SOL_IP, SOL_IPV6, SOL_TCP)
optname Имя параметра сокета
optval Указатель на новое значение параметра сокета
optlen Длина параметра сокета в байтах
Возможные ошибки
EBADF Указан неверный дескриптор сокета
ENOTSOCK Указанный дескриптор относится к файлу, а не к сокету
ENOPROTOOPT Указанный параметр сокета не известен на данном уровне
EFAULT Указатель optval ссылается за пределы адресного пространства процесса
Примеры
const int TTL=128;
/*— Задание предельного числа переходов равным 128 —*/
www.books-shop.com
if ( setsockopt(sd, SOL_IP, SO_TTL, &TTL, sizeof(TTL)) != 0 )
perror("setsockopt() failed");
getsockopt()
Функция getsockopt() возвращает значение указанного параметра сокета.
Прототип
#include <sys/types.h>
iinclude <sys/socket.h>
int getsockopt(int sd, int level, int optname, void *optval,
socklen_t *optlen);
Возвращаемое значение
В случае успешного завершения возвращается нуль. Если произошла ошибка,
ее код помещается в переменную errno.
Параметры
sd Дескриптор сокета
level Уровень параметра сокета (SOL_SOCKET, SOL_IP, SOL_IPV6, SOL_TCP)
optname Имя параметра сокета
optval Буфер для значения параметра сокета
optlen Длина буфера; это значение передается по ссылке, так как функция записывает
сюда реальную длину буфера
Возможные ошибки
EBADF Указан неверный дескриптор сокета
ENOTSOCK Указанный дескриптор относится к файлу, а не к сокету
ENOPROTOOPT Указанный параметр сокета не известен на данном уровне
EFAULT Указатель optval или optlen ссылается за пределы адресного пространства
процесса
Пример
int error, size = sizeof(error);
if ( getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &size)) != 0 )
perror("getsockopt() failed");
printf("socket error=%d\n", error);
www.books-shop.com
Приложение
APIфункции ядра
В
В этом приложении...
Задания 408
Потоки 415
Блокировка 418
Сигналы 421
Работа с файлами 424
www.books-shop.com
В этом приложении приведена справочная информация, касающаяся библио
Задания
К заданиям относятся процессы и потоки. Функции библиотеки Pthreads,
предназначенные для работы с потоками, будут рассматриваться в следующем
разделе. Здесь же речь пойдет о функциях, применяемых к процессам.
fork()
Функция fork() создает новый процесс (независимое задание). Дочерний про
цесс выполняет тот же программный файл, что и его предок. Необходимо преду
полняемого кода.
Прототип
#include <unistd.h>
pid_t fork(void);
Возвращаемое значение
0 Текущее задание является дочерним
>0 Текущее задание является родительским
<0 Дочернее задание не удалось создать; код ошибки содержится в переменной errno
Параметры
(отсутствуют)
Возможные ошибки
EAGAIN Недостаточно памяти для копирования таблицы страниц родительского задания и создания
информационной структуры дочернего задания
ENOMEM Недостаточно памяти для создания необходимых структур ядра
Пример
int PID;
if ( (PID = fork()) == 0 )
{ /*— ПОТОМОК —*/
/*** выполняем соответствующие действия ***/
exit(status);
}
else if ( PID > 0 )
{ /*— ПРЕДОК —*/
www.books-shop.com
int status;
/*** выполняем соответствующие действия ***/
wait(Sstatus); /* эта функция может вызываться
в обработчике сигнала SIGCHLD */
}
else /*— ОШИБКА —*/
perror("fork() failed");
_clone()
Это низкоуровневый системный вызов, предназначенный для создания зада
Прототип
iinclude <sched.h>
int _clone(int (*fn)(void* arg), void* stacktop,
int flags, void* arg);
Возвращаемое значение
Функция возвращает идентификатор созданного задания. В случае неудачи
код ошибки записывается в переменную errno.
Параметры
fn Указатель на функцию потока, принимающую аргумент типа void*; когда она завер
шается, операционная система останавливает поток
stacktop Указатель на вершину стека дочернего задания (самый старший адрес блока данных);
этот стек имеет фиксированный размер и не может увеличиваться подобно стеку
обычного задания
flags Набор флагов, определяющих, какие области памяти совместно используются и какой
сигнал посылать в случае завершения дочернего задания. Поддерживаются все виды
сигналов, и при завершении задания операционная система сгенерирует любой ука
занный сигнал.
Следующие флаги определяют, какие из областей памяти задания будут доступны для
совместного использования:
* CLONE_VM. Совместное использование области данных между заданиями. Если флаг указан,
будут доступны все статические и предварительно инициализированныепеременные, а так
же блоки, выделенные в куче. В противном случае в дочернем задании будет создана копия
области данных;
* CLONE_FS. Совместное использование информации о файловой системе: о текущем катало
ге, корневом каталоге и стандартном режиме доступа к файлам (значение umask). Если флаг
не указан, задания будут вести себя независимо друг от друга;
* CLONE FILES. Совместное использование открытых файлов. Когда в одном задании пере
мещается указатель текущей позиции файла, в другом задании отразится это изменение, и
если закрыть файл в одном задании, то и в другом он станет недоступным. Если флаг не
www.books-shop.com
указан, в дочернем задании создаются новые ссылки на открытые индексные дескрипторы;
* CLONE SIGHAND. Совместное использование таблиц сигналов. Каждое задание может запре
тить обработку того или иного сигнала с помощью функции sigprocmask( ) , и это не отра
зится на других заданиях. Если флаг не указан, в дочернем задании создается копия табли
цы сигналов;
* CLONE_PID. Совместное использование идентификатора процесса. Применять данный флаг
следует осторожно, так как он не всегда поддерживается (как это имеет место в случае биб
лиотеки Pthreads). Если флаг не указан, в дочернем задании создается новый идентификатор
процесса
arg Указатель на блок данных, передаваемых в качестве параметра потоковой функции f п.
Эти данные должны находиться в совместно используемой области памяти
Возможные ошибки
EAGAIN Недостаточно памяти для копирования таблицы страниц родительского задания и соз
дания информационной структуры дочернего задания
ENOMEM Недостаточно памяти для создания необходимых структур ядра
Пример
exec()
Функции данного семейства предназначены для запуска внешних программ (это
могут быть либо двоичные исполняемые файлы, либо сценарии, начинающиеся со
строки вида #! <интерпретатор> [аргументы]). Функция замещает контекст те
www.books-shop.com
В семейство входят функции execl(), execlp(), execle(), execv() и execvp(),
которые являются надстройками к основной функции execve().
Прототип
#include <unistd.h>
int execve(const char* path, char* const argv[],
char* const envp[]);
int execl(const char* path, const char* argv, ...);
int execlp(const char* file, const char* argv, ...);
int execle(const char* path, const char* argv, ...,
char* const envp[]);
int execv(const char* path, char* const argv[]);
int execvp(const char* file, char* const argv[]);
Возвращаемое значение
В случае успешного завершения ни одна из функций семейства не возвраща
Параметры
file Имя исполняемого файла; функция ищет программу, просматривая список каталогов,
указанный в переменной среды PATH
path Полное путевое имя исполняемого файла
argv Массив аргументов командной строки исполняемой программы; первым аргументом
всегда является имя программы, а последним — значение NULL (0)
arg Аргумент командной строки исполняемой программы; если за ним следует многото
чие (...). то есть и другие аргументы; первым всегда указывается имя программы, а
последним — значение NULL (0)
envp Массив с описанием переменных среды; каждый элемент массива имеет формат
<имя>=<значение> (например, TERM=vt100); последний элемент массива всегда
имеет значение NULL (0)
Возможные ошибки
EACCES Отсутствуют права на запуск программы или интерпретатора, нет доступа к одному
из каталогов в путевом имени либо файловая система смонтирована с указанием па
раметра поехес
EPERM Для файла установлен бит SUID или SGID, а файловая система смонтирована с ука
занием параметра nosuid либо нет прав суперпользователя
E2BIG Список аргументов слишком велик
ENOEXEC Исполняемый файл имеет незнакомый формат, предназначен для другой архитекту
ры или не может быть выполнен по какойто иной причине
EFAULT Имя файла находится в недоступном адресном пространстве
ENAMETOOLONG Имя файла слишком велико
ENOENT Файл, сценарий или интерпретатор отсутствует
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
ENOMEM Недостаточно памяти ядра для завершения операции
ENOTDIR Компонент путевого имени файла, сценария или интерпретатора не является катало
гом
ELOOP При разрешении путевого имени обнаружено слишком много символических ссылок
ETXTBUSY Исполняемый файл открыт для записи несколькими процессами
ЕЮ Ошибка вводавывода
ENFILE Достигнут лимит числа открытых файлов в системе
EMFILE Достигнут лимит числа открытых файлов в процессе
EINVAL В исполняемом файле имеется несколько сегментов PT_INTERP
EISDIR Указанное имя интерпретатора относится к каталогу
ELIBBAD Интерпретатор имеет незнакомый формат
Пример
sched_yield()
Функция sched_yield() отдает контроль над процессором без блокирования.
Она сообщает планировщику о том, что текущее выполняемое задание хочет от
вал.
Прототип
#include <sched.h>
int sched_yield(void);
Возвращаемое значение
При успешном завершении функция возвращает 0, в противном случае —
1.
Параметры
(отсутствуют)
Возможные ошибки
(не определены)
www.books-shop.com
Пример
#include <sched.h>
sched_yield();
wait(), waitpid()
Функции wait() и waitpid() дожидаются завершения дочернего процесса и
уведомляют об этом родительскую программу. Их назначение заключается в том,
чтобы предотвратить проникновение процессов
зомби в таблицу процессов и ос
чернего задания, который мог быть задан в функции exit() или опера
торе return. Этот макрос может быть вызван только в том случае, если
макрос WIFEXITED вернул ненулевое значение.
• WIFSIGNALED(статус) — возвращает true, если дочернее задание завер
шилось из
за не перехваченного сигнала.
• WTERMSIG(cтaтyc) — возвращает номер сигнала, вызвавшего завершение
потомка. Этот макрос может быть вызван только в том случае, если
макрос WIFSIGNALED вернул ненулевое значение.
• WIFSTOPPED(статус) — возвращает true, если дочернее задание приоста
новлено. Это возможно только в том случае, если функция была вызва
чернего задания. Этот макрос может быть вызван только в том случае,
если макрос WIFSTOPPED вернул ненулевое значение.
Прототип
linclude <sys/types.h>
linclude <sys/wait.h>
PID_t wait(int *status);
PID_t waitpid(PID_t pid, int *status, int options);
Возвращаемое значение
Обе функции возвращают идентификатор завершившегося дочернего задания.
www.books-shop.com
Параметры
PID Указывает, завершения какого процесса следует дождаться:
* < 1 — дождаться любого дочернего процесса, чей идентификатор группы равен идентифика
тору родительского процесса;
* == 1 — дождаться любого дочернего процесса (такое поведение соответствует работе функ
ции wait());
* == 0 — дождаться любого дочернего процесса, чей идентификатор группы равен идентифика
тору группы родительского процесса;
* > 0 — дождаться дочернего процесса, чей идентификатор равен заданному
status В этом параметре возвращается код завершения потомка; если он не равен 0 или NULL,
то содержит значение, указанное в функции exit()
options * WNOHANG. Указывает на то, что функция должна немедленно вернуть значение, если дочернее
задание еще не завершилось;
* WUNTRACED. Указывает на то, что функция должна вернуть значение, если дочернее задание
приостановлено, а его статус не был сообщен
Возможные ошибки
ECHILD Процесс с указанным идентификатором не существует или не является потомком вызы
вающего процесса (это может произойти, если в обработчике сигнала SIGCHLD установ
лен атрибут SIG_IGN)
EINVAL Указано неправильное значение параметра options
EINTR Режим WNOHANG не задан, но был получен неблокируемый сигнал или сигнал SIGCHLD;
следует повторно вызвать функцию
Пример
void sig_child(int signum) /* этот обработчик дожидается
завершения одного дочернего задания */
{ int status;
wait(&status);
if ( WIFEXITED(status) )
printf("Child exited with the value of %d\n",
WEXITSTATUS(status));
if ( WIFSIGNALED(status) )
printf("Child aborted due to signal l%d\n",
WTERMSIG(status));
if ( WIFSTOPPED(status) )
printf("Child stopped on signal #%d\n" WSTOPSIG(status));
www.books-shop.com
Потоки
Ниже описан ряд функций библиотеки Pthreads.
pthread_create()
Функция pthread_create() создает так называемый облегченный процесс ядра
— поток. Он начинает выполняться в функции, на которую ссылается указатель
start_fn (ей передается аргумент arg). Потоковая функция должна возвращать
значение типа void*, но даже если она этого не делает, поток все равно успешно
завершается, а его код завершения устанавливается равным NULL.
Прототип
#include <pthread.h>
int pthread_create(pthread_t *tchild, pthread_attr_t *attr,
void* (*start_fn)(void *), void *arg);
Возвращаемое значение
В случае успешного завершения возвращается положительное значение. Если
при создании потока возникли какие
то ошибки, функция возвращает отрица
Параметры
tchild Дескриптор нового потока (передается по ссылке); при успешном завершении функ
ция возвращает здесь дескриптор созданного потока
attr Набор атрибутов, описывающих поведение нового потока и его взаимодействие с
родительской программой
start_f n Указатель на функцию, содержащую код потока; функция должна возвращать значе
ние типа void*
arg Параметр, передаваемый потоковой функции; он должен представлять собой ссылку
на переменную, не являющуюся стековой и недоступную для совместного использо
вания (если только не планируется блокировать ее)
Возможные ошибки
EAGAIN Недостаточно системных ресурсов для создания нового потока (число активных по
токов превысило значение PTHREAD_THREADS_MAX)
Пример
www.books-shop.com
int main(void)
{ pthread_t tchild;
if ( pthread_create(&tchild, 0, child, 0) < 0 )
perror("Can't create thread!");
/* выполняем соответствующие действия */
if ( pthread_join(tchild, 0) != 0 )
perror("Join failed");
pthread_join()
Функция pthread_join() напоминает системный вызов wait(), но дожидается
завершения дочернего потока.
Прототип
#include <pthread.h>
int pthread_join(pthread_t tchild, void **retval);
Возвращаемое значение
В случае успешного завершения возвращается положительное значение. Если
возникли какие
то ошибки, функция возвращает отрицательное число, а код
ошибки записывается в переменную errno.
Параметры
tchild Дескриптор потока, завершения которого необходимо дождаться
retval Указатель на код завершения потока (передается по ссылке)
Возможные ошибки
ESRCH Не найден поток, соответствующий указанному дескриптору
EINVAL Поток был отсоединен
EDEADLK Аргумент tchild идентифицирует вызывающий поток
Пример
(см. пример для функции pthread_create())
pthread_exit()
Функция pthread_exit() завершает выполнение текущего потока, возвращая
вызывающему потоку аргумент retval. Можно также воспользоваться обычным
оператором return.
www.books-shop.com
Прототип
#include <pthread.h>
void pthread exit(void *retval);
Возвращаемое значение
(отсутствует)
Параметр
retval Значение, передаваемое вызывающему потоку; необходимо убедиться, что это не
стековая переменная
Возможные ошибки
(отсутствуют)
Пример
(см. пример для функции pthread created)
pthread_detach()
Функция pthread_detach() отключает указанный поток от родительского зада
няться независимо.
Прототип
#include <pthread.h>
int pthread_detach(pthread_t tchild);
Возвращаемое значение
В случае успешного завершения возвращается положительное значение. Если
возникли какие
то ошибки, функция возвращает отрицательное число, а код
ошибки записывается в переменную errno.
Параметр
tchild Дескриптор дочернего потока
Возможные ошибки
ESRCH Не найден поток, соответствующий указанному дескриптору
EINVAL Поток уже был отсоединен
EDEADLK Аргумент tchild идентифицирует вызывающий поток
www.books-shop.com
Пример
void* child(void *arg)
/* код потомка */
pthread exit(arg); /* завершение потока и возврат параметра */
int main(void)
{ pthread_t tchild;
Блокировка
Основное преимущество потоков заключается в возможности совместного ис
дании блокировок.
pthread_mutex_init(), pthread_mutex_destroy()
Функции pthread_mutex_init() и pthread_rautex_destroy() создают и уничтожают
объекты исключающих семафоров. Необходимость в инициализирующей функ
ции возникает редко, так как можно работать с готовыми семафорами. Функция
pthread_mutex_destroy() уничтожает любые ресурсы, связанные с исключающим
семафором, но, поскольку в Linux таковые отсутствуют, функция всего лишь
проверяет, разблокирован ли ресурс.
Прототип
#include <pthread.h>
/*— Готовые семафоры —*/
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
pthread_mutex_t errchkmutex =
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
www.books-shop.com
Возвращаемое значение
Всегда нуль.
Параметры
mutex Дескриптор создаваемого или уничтожаемого семафора
mutexattr Атрибуты семафора; если этот параметр равен NULL, используются стандартные
установки (как у семафора PTHREAD_MUTEX_INITIALIZER)
Возможные ошибки
(отсутствуют)
pthread_mutex_lock(), pthread_mutex_trylock()
Функции pthread_mutex_lock() и pthread_mutex_trylock() соответственно бло
лета на указанное место. Если другой поток пытается занять это место, он блоки
Прототип
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
Возвращаемое значение
В случае успешного завершения возвращается нуль, при возникновении ошиб
Параметр
mutex Объект исключающего семафора
Возможные ошибки
EINVAL Исключающий семафор не был инициализирован должным образом
EDEADLK (функция pthread_mutex_trylock()) Вызывающий поток уже заблокировал исклю
чающий семафор (данная ошибка выдается только для семафоров, в которых включен
режим контроля ошибок)
EBUSY (функция pthread_mutex_lock()) Вызывающий поток в настоящее время заблокирован
Пример
www.books-shop.com
if ( pthread_mutex_lock(&mutex) == 0 )
{
/*** работа с критическими данными ***/
pthread_mutex_unlock(&mutex) ;
pthread_mutex_unlock()
Функция pthread_mutex_unlock() разблокирует исключающий семафор.
Прототип
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
Возвращаемое значение
В случае успешного завершения возвращается нуль, при возникновении
ошибки — ненулевое значение. Точный код ошибки записывается в переменную
errno.
Параметр
mutex Объект исключающего семафора
Возможные ошибки
EINVAL Исключающий семафор не был инициализирован должным образом
EPERM Вызывающий поток не является владельцем исключающего семафора (данная ошибка вы
дается только для семафоров, в которых включен режим контроля ошибок)
Пример
(см. пример для функции pthread_mutex_lock())
www.books-shop.com
Сигналы
При работе с процессами и потоками программа может получать сигналы (или
асинхронные уведомления). Ниже описаны системные вызовы, позволяющие пе
signal()
Функция signal() регистрирует функцию sig_fn в качестве обработчика сигна
Прототип
#include <signal.h>
void (*signal(int signum,
void (*sig_fn)(int signum)))(int signum);
ИЛИ
typedef void (*TSigFn)(int signum);
TSigFn signal(int signum, TSigFn sig_fn);
Возвращаемое значение
При успешном завершении возвращается указатель на функцию обработки
сигнала, в противном случае возвращается нуль.
Параметры
signum Номер перехватываемого сигнала
sig_f n Функцияобработчик, вызываемая планировщиком
Возможные ошибки
(переменная errno не устанавливается)
Пример
}
}
if ( signal(SIGFPE, sig_handler) == 0 )
perrorf("signal() failed");
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
sigaction()
Подобно функции signal(), функция sigaction() регистрирует процедуру об
Прототип
#include <signal.h>
int sigaction(int signum, const struct sigaction *sigact,
struct sigaction *oldsigact);
Возвращаемое значение
При успешном завершении возвращается нуль, иначе — ненулевое значение.
Параметры
signum Номер перехватываемого сигнала
sigact Требуемое действие и функция обработки сигнала; объявление структуры sigaction
таково:
struct sigaction
{
void (*sa handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sa_handler Указатель на функцию обработки сигнала
sajnask Список сигналов, которые будут блокироваться во время выполнения обработчика сиг
налов
sa_flags Флаги, задающие порядок обработки сигнала. Можно использовать такие константы:
* SA_NOCLDSTOP. Если обрабатывается сигнал SIGCHLD, игнорировать случаи, когда дочернее
задание приостанавливает свою работу;
* SA_ONESHOT или SA_RESETHAND. Возвращаться к стандартному системному обработчику после
получения первого сигнала;
* SA_RESTART. Пытаться восстанавливать прерванный системный вызов во избежание ошибок
типа EINTR;
* SA_NOMASK или SA_NODEFER. Позволять аналогичным сигналам прерывать выполнение обра
ботчика во избежание потерь сигналов
sa_restorer Устарел и больше не используется
oldsigact Структура, в которой хранится описание предыдущего выполненного действия
www.books-shop.com
Возможные ошибки
EINVAL Указан неверный сигнал; эта ошибка генерируется также в том случае, когда делается
попытка изменить стандартную процедуру обработки сигналов SIGKILL и SIGSTOP, ко
торые не могут быть перехвачены
EFAULT Указатель sigact или oldsigact ссылается на область памяти, выходящую за пределы
адресного пространства процесса
EINTR Был прерван системный вызов
Пример
switch ( signum )
case SIGCHLD:
}
}
struct sigaction sigact;
bzero(&sigact, sizeof(sigact));
sigact.sa_handler = sig_handler; /* задание обработчика */
sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART; /* установки */
if ( sigaction(SIGCHLD, &sigact, 0) == 0 )
perror("sigaction() failed");
sigprocmask()
Функция sigprocmask() задает, какие сигналы разрешено прерывать при об
Прототип
#tinclude <signal.h>
int sigprocmask(int how, const sigset_t *sigset,
sigset_t *oldsigset);
Возвращаемое значение
При возникновении ошибки возвращается ненулевое значение, в противном
случае — нуль.
Параметры
how Определяет, как следует интерпретировать параметр sigset:
* SIG_BLOCK. Набор блокируемых сигналов определяется суммой текущего набора сигна
лов и элементов, перечисленных в параметре sigset;
* SIG_UNBLOCK. Сигналы, перечисленные в параметре sigset, удаляются из текущего на
www.books-shop.com
бора блокируемых сигналов, причем разрешается разблокировать сигнал, который не был
блокирован;
* SIG_SETMASK. Набор блокируемых сигналов задается равным списку, содержащемуся в
параметре sigset
sigset Искомый набор сигналов
oldsigset Если этот параметр не равен NULL, в него помещается предыдущий набор сигналов
Возможные ошибки
EFAULT Указатель sigset или oldsigset ссылается на область памяти, выходящую за пре
делы адресного пространства процесса
EINTR Был прерван системный вызов
Работа с файлами
Ниже описаны библиотечные и системные функции, связанные с управлением
файлами.
bzero(), memset()
Функция bzero() обнуляет указанную область памяти. Она считается устарев
Прототип
linclude <string.h>
void bzero(void *mem, int bytes);
void* memset(void *mem, int val, size_t bytes);
Возвращаемое значение
Функция bzero() не возвращает значений. Функция memset() возвращает ссыл
Параметры
mem Инициализируемая область памяти
val Значениезаполнитель
bytes Число записываемых байтов (размер области памяти)
Возможные ошибки
(отсутствуют)
www.books-shop.com
Пример
bzero(&addr, sizeof(addr));
memset(&addr, 0, sizeof(addr));
fcntl()
Функция f c n t l ( ) манипулирует дескриптором файла или сокета.
Прототип
linclude <unistd.h>
linclude <fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *flock);
Возвращаемое значение
При наличии ошибки возвращается — 1 и устанавливается переменная errno. В
случае успешного завершения возвращаемое значение зависит от типа операции.
F_DUPFD Новый дескриптор
F_GETFD Значение флага
F_GETFL Значения флагов
F_GETOWN Идентификатор владельца дескриптора
F_GETSIG Номер сигнала, посылаемого, когда становится возможным чтение или запись, либо О
в случае традиционного обработчика сигнала SIGIO
Для всех остальных команд возвращается нуль.
Параметры
fd Искомый дескриптор
cmd Выполняемая операция. Некоторые операции дублируют существующие функции, а
для некоторых требуется аргумент (arg или flock). Все операции группируются по
назначению:
* Дублирование дескриптора (F_DUPFD). То же, что и функция dup2 (arg, fd). В этой опе
рации дескриптор f d заменяется копией дескриптора arg;
* Закрытие дескриптора при завершении функции ехес() (F_GETFD, F_SETFD). Ядро не пе
редает все дескрипторы файлов дочернему процессу, созданному с помощью функции
ехес(). Посредством данных операций можно определить текущий режим работы и задать
требуемый режим;
* Манипулирование флагами дескриптора (F_GETFL, F_SETFL). С помощью данных операций
можно узнать флаги дескриптора, заданные с помощью функции ореп(), а также задать флаги
0_APPEND, O_NONBLOCK И 0_ASYNC;
* Манипулирование блокировками файла (F_GETLK, F_SETLK, F_SELKW). В операции
F_GETLK возвращается структура блокировки, наложенной на файл.
www.books-shop.com
Если файл не блокирован:
* Определение владельца сигналов ввода&вывода (F_GETOWN, F SETOWN). Определение
или задание идентификатора текущего процессавладельца сигнала SIGIO;
* Определение типа посылаемого сигнала (F GETSIG, F SETSIG). Определение или за
дание типа сигнала, если могут быть выполнены дополнительные операции ввода
вывода. По умолчанию это сигнал SIGIO
arg Устанавливаемое значение
flock Ключ блокировки
Возможные ошибки
EACCES Операция запрещена изза наличия блокировки, удерживаемой другим процессом
EAGAIN Операция запрещена изза того, что файл отображается в памяти другим процессом
EBADF Параметр f d не является дескриптором открытого файла
EDEADLK Обнаружено, что операция F_SETLKW приведет к взаимоблокировке
EFAULT Указатель flock ссылается за пределы адресного пространства процесса
EINTR Если выполнялась операция F_SETLKW, то она была прервана сигналом. Если выполня
лась операция F_GETLK или F_SETLK, то она была прервана сигналом до того, как была
проверена или получена блокировка. Обычно это происходит при дистанционной блоки
ровке файлов (через NFS)
EINVAL В случае операции F_DUPFD параметр arg является отрицательным или превышает
максимально допустимое значение. В случае операции F_SETSIG параметр arg содер
жит недопустимый номер сигнала
EMFILE В случае операции F_DUPFD превышено максимально число файлов, открытых в одном
процессе
ENOLCK Таблица блокировок переполнена или произошла ошибка при создании блокировки по
сети
EPERM Попытка сбросить флаг о APPEND для файла, у которого установлен атрибут "только
добавление"
Пример
#include <unistd.h>
linclude <fcntl.h>
www.books-shop.com
pipe()
Функция pipe() создает канал, который связан с самим собой: входной деск
риптор (fd[0]) совпадает с выходным (fd[l]). При записи данных в канал из него
будут прочитаны эти же самые данные.
Прототип
#include <unistd.h>
int pipe(int fd[2]);
Возвращаемое значение
При успешном завершении возвращается 0, в случае ошибки —
1.
Параметр
fd Массив из двух целых чисел, куда будут записаны дескрипторы созданного канала
Возможные ошибки
EMFILE В текущем процессе открыто слишком много файлов
ENFILE Системная таблица файлов переполнена
EFAULT Неправильная ссылка на память в указателе f d
Пример
int f d [ 2 ] ;
pipe(fd); /* создание канала */
poll()
Функция poll(), как и функция select(), дожидается изменения состояния
указанных каналов ввода
вывода. Управление списком дескрипторов осуществля
Прототип
#include <sys/poll.h>
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
Возвращаемое значение
Если возвращаемое значение меньше нуля, значит, произошла ошибка. Нуле
вое значение свидетельствует о том, что истек период ожидания. В случае успеш
www.books-shop.com
Параметры
ufds Массив структур pollfd. В каждой структуре описывается отдельный дескриптор:
struct pollfd
{
int fd; /* дескриптор файла */
short events; /* запрашиваемые события */
short revents; /* события, которые
произошли в канале */
};
В поле f d содержится проверяемый дескриптор файла. Поля events и revents обо
значают соответственно проверяемые и произошедшие события. События задаются с
помощью следующих констант:
* POLLIN — поступили данные;
* POLLPRI — поступили срочные (внеполосные) данные;
* POLLOUT — канал готов для записи;
* POLLERR — произошла ошибка;
* POLLHUP — отбой на другом конце канала;
* POLLNVAL — неправильный запрос, канал f d не был открыт;
* POLLRDNORM — обычное чтение (только в Linux);
* POLLRDBAND — чтение внеполосных данных (только в Linux);
* POLLWRNORM — обычная запись (только в Linux);
* POLLWRBAND — запись внеполосных данных (только в Linux)
nfds Число каналов, которые следует проверить
timeout Период ожидания в миллисекундах; если указано отрицательное значение, функция
будет ждать бесконечно долго
Возможные ошибки
ENOMEM Недостаточно памяти для создания записей в таблице дескрипторов файлов
EFAULT Указатель ссылается на область памяти, выходящую за пределы адресного простран
ства процесса
EINTR Поступил сигнал до того, как произошло одно из запрашиваемых событий
Пример
int fd_count=0;
struct pollfd fds[MAXFDs];
fds[fd_count].fd = socket(PF_INET, SOCK_STREAM, 0);
/*** вызовы функций bind() и listen)) ***/
fds[fd_count++].events = POLLIN;
for (;;)
{
if ( poll(fds, fd_count, TIMEOUT_MS) > 0 )
{ int i;
if ( (fds[0].revents & POLLIN) != 0 )
{
www.books-shop.com
fds[fd_count].events = POLLIN | POLLHUP;
fds[fd_count++].fd = accept(fds[0].fd, 0, 0);
}
for ( i = 1; i < fd_count; i++ )
{
if ( (fds[i].revents & POLLHUP) != 0 )
{
close(fds[i].fd);
/*** перемещаем дескрипторы для
заполнения пустых позиций ***/
fd_count—;
}
else if ( (fds[i].revents & POLLIN) != 0 )
/*** читаем и обрабатываем данные ***/
}
}
read()
Функция read() читает указанное число байтов из файла с дескриптором fd и
помещает результат в буфер. Эту функцию можно использовать как с сокетами,
так и с файлами, но она не обеспечивает такого контроля, как функция recv().
Прототип
#include <unistd.h>
int read(int fd, char *buffer, size_t buf_len);
Возвращаемое значение
Функция возвращает число прочитанных байтов.
Параметры
fd Дескриптор файла или сокета
buffer Буфер, в который будут записываться извлекаемые данные
buf_len Число прочитанных байтов, а также размер буфера
Возможные ошибки
EINTR Работа функции была прервана сигналом до того, как началось чтение данных
EAGAIN Задан режим неблокируемого вводавывода (с помощью флага О NONBLOCK), а данные
недоступны
ЕЮ Ошибка вводавывода; она может произойти, если фоновый процесс игнорирует или
блокирует сигнал SIGTTIN либо лишился управляющего терминала; возможна также
низкоуровневая ошибка чтения с диска или магнитной ленты
EISDIR Указанный дескриптор связан с каталогом
www.books-shop.com
EBADF Указан неверный дескриптор файла либо файл не был открыт для чтения
EINVAL Указанный дескриптор связан с объектом, чтение из которого невозможно
EFAULT Указатель buffer ссылается на область памяти, находящуюся за пределами адресно
го пространства процесса
Пример
int sockfd;
int bytes_read;
char buffer[1024];
/*— создание сокета и подключение к серверу —*/
if ( (bytes_read = read(sockfd, buffer, sizeof(buffer))) < 0 )
perror("read");
select()
Функция select() ожидает изменения статуса одного из заданных каналов
ввода
вывода. Когда в одном из каналов происходит изменение, функция завер
скрипторов.
• FD_CLR — удаляет дескриптор из списка.
• FD_SET — добавляет дескриптор в список.
• FD_ISSET — проверяет готовность канала к выполнению операции ввода
вывода.
• FD ZERO — инициализирует список дескрипторов.
Прототип
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int hi_fd, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);
Возвращаемое значение
При успешном завершении функция возвращает число каналов, в которых
произошли изменения. В случае ошибки возвращается отрицательное значение.
Если превышен допустимый период ожидания, возвращается нуль.
www.books-shop.com
Параметры
hi_f d Число, на единицу большее номера самого старшего дескриптора в списке
readfds Список дескрипторов каналов, из которых осуществляется чтение данных
writefds Список дескрипторов каналов, в которые осуществляется запись данных
exceptfds Список дескрипторов каналов, предназначенных для чтения внеполосных сообщений
timeout Число микросекунд, в течение которых необходимо ждать изменений; параметр пред
ставляет собой указатель на число; если это число (не указатель) равно нулю, функция
немедленно завершается после проверки всех дескрипторов, если же сам указатель
равен NULL (0), то период ожидания отсутствует (функция ждет бесконечно долго)
fd Дескриптор канала, добавляемого, удаляемого или проверяемого
set Список дескрипторов
Возможные ошибки
EBADF В один из списков входит неправильный дескриптор
EINTR Получен неблокируемый сигнал
EINVAL Указан отрицательный размер списка
ENOMEM Не хватает памяти для создания внутренних таблиц
Пример
int i, ports[]={9001, 9002, 9004, 1};
int sockfd, max=0;
fd_set set;
struct sockaddr_in addr;
struct timeval timeout={2,500000}; /* 2,5 секунды */
FD_ZERO(&set);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
for ( i = 0; ports[i] > 0; i
sockfd = socket(PF_INET, SOCK_STREAM, 0);
addr.sin_port = htons(ports[i] );
if ( bind (sockfd, &addr, sizeof(addr)) != 0 )
perror("bind() failed");
else
{
FD_SET(sockfd,&set);
if ( max < sockfd )
max = sockfd;
}
}
if ( select(max+l, &set, 0, &set, &timeout) > 0 )
{
for ( i = 0; i <= max; i++)
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
if ( FD_ISSET(i, &set) )
{ int client = accept(i, 0, 0);
/*** обрабатываем клиентский запрос ***/
}
}
write()
Функция write() записывает указанное число байтов из буфера в файл с деск
Прототип
#include <unistd.h>
int write(int fd, const void *buffer, size_t msg_len);
Возвращаемое значение
Функция возвращает число записанных байтов. Это число может быть мень
ше, чем значение параметра msg_len. Если функции не удалось за один заход за
писать требуемое число байтов, ее можно вызывать в цикле до тех пор, пока опе
Параметры
fd Дескриптор файла или сокета
buffer Буфер, содержащий сообщение
msg_len Длина сообщения
Возможные ошибки
EBADF Указан неверный дескриптор файла либо файл не открыт для записи
EINVAL Указанный дескриптор связан с объектом, запись в который невозможна
EFAULT Указатель buffer содержит неправильный адрес
EPIPE Дескриптор связан с каналом или сокетом, который закрыт на противоположной сторо
не; в этом случае процессу, осуществляющему запись, посылается сигнал SIGPIPE, и
если он его принимает, функция write() генерирует ошибку EPIPE
EAGAIN Задан режим неблокируемого вводавывода (с помощью флага 0_NONBLOCK), а в буфере
канала или сокета нет места для немедленной записи данных
EINTR Функция была прервана сигналом до того, как началась запись данных
ENOSPC В буфере устройства, содержащего файл с указанным дескриптором, нет места для за
писи данных
ЕЮ При модификации индексного дескриптора произошла низкоуровневая ошибка ввода
вывода
www.books-shop.com
Пример
/*** Запись сообщения (TCP, UDP, неструктурированное) ***/
int sockfd;
int bytes, bytes_wrote=0;
/*— Создание сокета, подключение к серверу —*/
while ((bytes = write(sockfd, buffer, msg_len)) > 0 )
if ((bytes_wrote += bytes) >= msg_len)
break;
if ( bytes < 0 )
perror("write");
close()
Функция close() закрывает любой дескриптор (как файла, так и сокета). Если
сокет подключен к серверу или клиенту, канал остается активным после закры
тия до тех пор, пока не произойдет очистка буферов или не будет превышен пе
Прототип
#include <unistd.h>
int close(int f d ) ;
Возвращаемое значение
В случае успешного завершения возвращается нуль. Если произошла ошибка,
ее код записывается в переменную errno.
Параметр
fd Дескриптор файла или сокета
Возможная ошибка
EBADF Указан неверный дескриптор файла
Пример
int sockfd;
sockfd = socket(PF_INET, SOCK_RAW, htons(99));
if ( sockfd < 0 )
PANIC("Raw socket create failed");
if ( close(sockfd) != 0 )
PANIC("Raw socket close failed");
www.books-shop.com
Приложение
Вспомогательн ые
Г классы
В этом приложении.
Исключения C++ 449
Служебные классы C++ 450
Классы сообщений C++ 451
Классы сокетов C++ 452
Исключения Java 456
Служебные классы Java 457
Классы ввода
вывода Java 458
Классы сокетов Java 464
www.books-shop.com
В этом приложении описаны классы, определенные в Java API, а также в поль
Исключения С++
Ниже описаны классы объектов
исключений, определенные в пользователь
Exception (надкласс)
Конструктор:
Exception(SimpleString s);
Общее описание: исключение общего характера.
Метод:
const char* Getstring(); Возвращает строку сообщения
Дочерние исключения:
RangeException Любое исключение, связанное с выходом за пределы массива; гене
рируется классом MessageGroup
FileException Любое исключение, связанное с обработкой файла; генерируется
классом Socket
NetException (класс)
Конструктор:
NetException(SimpleString s);
Общее описание: исключение общего характера при работе в сети.
Родительский класс: Exception
Дочерние исключения:
www.books-shop.com
NetConversionException Исключение, связанное с преобразованием адреса узла (функции
inet_ntop()/inet_pton()); генерируется классом HostAddress
NetDNSException Исключение, связанное с невозможностью определения имени узла;
генерируется классом HostAddress
NetIOException Исключение при выполнении функций send()/recv(); генерируется
классом Socket
NetConnectException Исключение при выполнении функции bind(), connect(),
listen() или accept(); генерируется классами SocketServer,
SocketClient и MessageGroup
NetConf igException Исключение при попытке задания/получения параметров сокета; ге
Служебные классы C+ +
Ниже описаны некоторые классы пользовательской библиотеки сокетов, но
ми классами C++.
SimpleString (класс)
Конструктор:
SimpleString(const char* s);
SimpleString(const SimpleString& s);
Общее описание: очень простой строковый тип данных.
Методы:
+ (char *); Добавляет строку к текущему экземпляру класса
+(SimpleString& );
const char* GetString(); Возвращает строку сообщения
Генерируемые исключения: (отсутствуют)
HostAddress (класс)
Конструктор:
HostAddress(const char* Name=0, ENetwork Network=eIPv4);
HostAddress(HostAddressS Address);
Общее описание: класс, предназначенный для управления адресами узлов.
Методы:
void SetPort(int Port); Задает номер порта
int GetPort(void) const; Возвращает номер порта,
ENetwork GetNetwork(void) Возвращает тип сети
const;
www.books-shop.com
struct sockaddr* Возвращает реальный адрес сокета
GetAddress(void)const;
int Getsize(void) const; Возвращает размер адреса сокета
int ==(HostAddresss Проверка на равенство
Address) const;
int !=(HostAddresss Проверка на неравенство
Address) const; '
const char* GetHost(bool Возвращает имя узла
byName=l);
Генерируемые исключения:
Exception
NetConversionException
NetDNSException
Классы сообщений C+ +
общения.
Методы:
virtual char* Wrap(int& Bytes) Интерфейс упаковки объектов
const;
bool Unwrap (char* package, int Интерфейс распаковки объектов
Bytes, int MsgNum);
TextMessage (класс)
Конструктор:
TextMessage(unsigned short Bytes);
TextMessage(const char* Msg, unsigned short Len);
Общее описание: шаблон для создания текстового сообщения.
Родительский класс: Message
www.books-shop.com
Методы:
=(const char* str); Записывает новую строку в объект
=(const TextMessage* s);
+=(const char* str); Добавляет новую строку к объекту
+=(const TextMessages s);
const char* GetBuffer(void) const; Возвращает строку сообщения
char* Wrapfints Bytes) const; Упаковывает объект для отправки
bool Unwrap(char* package, int Распаковывает полученный объект
Bytes, int MsgNum);
GetSize(void) const; Возвращает длину строки
void SetSize(int Bytes); Задает длину строки
int GetAvailable(void) const; Возвращает число байтов, доступных в буфере
Классы сокетов C+ +
Ниже описаны классы, формирующие интерфейс сокетов. Для непосредствен
Socket (надкласс)
Конструктор:
Socket(void);
Socket(int sd);
Socket(ENetwork Network, EProtocol Protocol);
Socket(Sockets sock);
Общее описание: класс, содержащий базовые функции работы с сокетами и не
предназначенный для прямого создания объектов.
Методы:
void Bind(HostAddress& Addr); Связывает сокет с портом/интерфейсом
void CloseInput(void) const; Закрывает входной поток
void CloseOutput(void) const; Закрывает выходной поток
www.books-shop.com
int Send(Message& Msg, int Посылает сообщение подключенному узяу
Options=0) const;
int Send(HostAddress& Addr, Посылает направленное сообщение
Messages Msg, int Options=0)
const;
int Receive(Message& Msg, int Принимает сообщение от подключенного узла
Options=0) const;
int Receive(HostAddress& Addr, Принимает направленное сообщение
Messages Msg, int Options=0)
const;
void PermitRoute(bool Setting); Разрешает маршрутизацию пакетов
void KeepAlive(bool Setting); Удерживает соединение активным
void ShareAddress(bool Setting); Задает режим совместного использования адреса пор
та/интерфейса
int GetReceiveSize(void); Возвращает/задает размер входного буфера
void SetReceiveSizefint Bytes);
int GetSendSize(void); Возвращает/задает размер выходного буфера
void SetSendSize(int Bytes);
int GetMinReceive(void); Возвращает/задает пороговый размер входного буфера
void SetMinReceive(int Bytes); для получения сигнала SIGIO
int GetMinSend(void); Возвращает/задает пороговый размер выходного буфера
void SetMinSend(int Bytes); для получения сигнала SIGIO
struct timeval Возвращает/задает период ожидания, по истечении ко
GetReceiveTimeout(void); торого прием данных будет прерван
void SetReceiveTimeout(struct
timevals val);
struct timeval Возвращает/задает период ожидания, по истечении ко
GetSendTimeout(void); торого отправка данных будет прервана
void SetSendTimeout(struct
timeval& val);
ENetwork GetType(void); Возвращает тип сокета (сети)
virtual int GetTTL(void); Возвращает/задает предельное число переходов
virtual void SetTTL(int Hops);
int GetError(void); Возвращает сообщение об ошибке, находящееся в оче
реди
Генерируемые исключения:
NetException
FileException
NetConnectException
NetIOException
NetConf igException
www.books-shop.com
SocketStream (класс)
Конструктор:
SocketStream(void);
SocketStrearn(int sd);
SocketStream(ENetwork Network);
SocketStream(SocketStreams sock);
Общее описание: класс, определяющий интерфейс потоковых сокетов
(SOCK_STREAM).
Родительский класс: Socket
Методы:
int GetMaxSegmentSize(void); Возвращает/задает максимальный размер сегмента
void SetMaxSegmentSize(short Bytes);
void DontDelay(bool Setting); Включает/отключает алгоритм Нейгла
Генерируемое исключение:
NetConfigException
SocketServer (класс)
Конструктор:
SocketServer(int port, ENetwork Network=eIPv4, int QLen=15);
SocketServer(HostAddressS Addr, int QLen=15);
Общее описание: TCP
сервер.
Родительский класс: SocketStream
Методы:
void Accept(void (*Servlet) (const Принимает запрос на подключение и вызывает функ
Генерируемые исключения:
Exception
NetConnectException
SocketClient (класс)
Конструктор:
SocketClient(ENetwork Network=eIPv4);
SocketClient(HostAddress& Host, ENetwork Network=eIPv4);
www.books-shop.com
Общее описание: TCP
клиент.
Родительский класс: SocketStream
Метод:
void Connect(HostAddress& Addr); Подключается к узлу по указанному адресу
Генерируемое исключение:
NetConnectException
Datagram (класс)
Конструктор:
Datagram(HostAddress& Me, ENetwork Network=eIPv4,
EProtocol Protocol=eDatagram);
Datagram(ENetwork Network=eIPv4, EProtocol Protocol=eDatagram);
Общее описание: UDP
сокет.
Родительский класс: Socket
Методы:
void MinimizeDelay(bool Setting); Запрашивает режим минимальной задержки пакета
void MaximizeThroughput(bool Запрашивает режим максимальной пропускной способ
Setting); ности
void MaximizeReliability(bool Запрашивает режим максимальной надежности
Setting);
void MinimizeCost(bool Setting); Запрашивает режим минимальной стоимости
void PermitFragNegotiation(EFrag Разрешает процедуру фрагментации
Setting);
Генерируемое исключение:
NetConfigException
Broadcast (класс)
Конструктор:
Broadcast(HostAddress& Me);
Общее описание: широковещательный сокет, работающий в рамках подсети.
Родительский класс: Datagram
Методы: (отсутствуют).
Генерируемое исключение:
NetConfigException .
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
MessageGroup (класс)
Конструктор:
MessageGroup(HostAddress& Me, ENetwork Network=eIPv4);
Общее описание: групповой сокет.
Родительский класс: Datagram
Методы;
Connect(HostAddress& Address); Подключает сокет к адресу группового вещания
void Join(HostAddress& Address, int Регистрирует сокет в адресной группе
IFIndex=0)
void Drop(HostAddress& Address); Отменяет регистрацию сокета в адресной группе
Генерируемые исключения:
NetConfigException
NetConnectException
RangeException
Исключения Java
Ниже описаны все исключения, которые Java
программа может сгенерировать
при работе с сокетами.
lOException < ProtocolException
< UnknownHostException
< UnknownServiceExceptbn
< SocketException < BindException
< ConnectException
< NoRouteToHostException
java.io.IOException (класс)
Конструктор:
IOException();
lOException(String msg);
Общее описание: исключение общего характера, произошедшее в процессе
ввода
вывода.
Родительский класс: Exception
Дочерние исключения:
java.net.ProtocolException Ошибка протокола в классе Socket
java.net.UnknownHostException Имя узла не найдено в базе данных DNS
сервера
java. net. unknownServiceException Предпринята попытка вызова неподдерживаемого сер
виса
www.books-shop.com
java.net.SocketException (класс)
Конструктор:
SocketException();
SocketException(String msg);
Общее описание: исключение, возникшее при попытке вызова функции
bind(), connect(), listen() или accept(); генерируется классами Socket, Server
тами.
java.net.DatagramPacket (класс)
Конструктор:
DatagramPacket(byte[] buf, int Деп);
DatagramPacket(byte[] buf, int len, InetAddress addr, int port);
DatagramPacket(byte[] buf, int Offset, int len);
DatagramPacket(byte[ ] buf, int Offset, int len, InetAddress addr, int port);
www.books-shop.com
int getOffset(); Воpвращает смещение данных в массиве, пред
назначенном для приёма или отправки
int getPort(); Возвращает/задает порт отпрабителя/получателя
void setPort(int port); пакета
Методы:
String getHostAddress(); Возвращает адрес узла в символьном виде
byte[] getAddress(); Возвращает адрес узла в виде массива байтов
boolean isMulticastAddress(); Проверяет, попадает ли указанный адрес в диа
пазон групповых адресов
String getHostName(); Возвращает имя узла
Генерируемое исключение:
UnknownHostException
www.books-shop.com
java.io.InputStream (абстрактный класс)
Конструктор:
InputStream();
Общее описание: простейший входной поток.
Родительский класс: Object
Методы:
int available(); Возвращает число байтов, которые можно прочесть без блокиро
вания
void close(); Закрывает канал
void mark (int readlimit); Помечает текущую позицию потока для метода reset(), задавая
максимальный размер буфера упреждающего чтения
boolean markSupportedf); Определяет, поддерживает ли потоковый объект методы
mark()/reset()
int read(); Читает одиночный байт из потока
int read(byte [] arr); Читает массив байтов из потока
int read(byte [] arr, int Читает массив байтов указанного размера, начиная с заданного
offset, int length); смещения
void reset(); Возвращается к последней помеченной позиции
long skip(); Пропускает ближайшие п байтов потока
Генерируемое исключение:
IOException
java.io.ByteArraylnputStream (класс)
Конструктор:
ByteArrayInputStream(byte[] buf);
ByteArrayInputStream(byte[] buf, int offset, int length);
Общее описание: позволяет создавать виртуальный входной поток из массива
байтов (например, из дейтаграммы).
Родительский класс: InputStream
Методы: (отсутствуют; много переопределенных методов класса InputStream).
Генерируемые исключения: (отсутствуют).
java.io.ObjectlnputStream (класс)
Конструктор:
ObjectInputstream(InputStream о);
Общее описание: с помощью этого класса можно читать передаваемые или со
www.books-shop.com
Родительский класс: InputStream
Методы:
int available(); Возвращает число байтов, которые можно прочесть без бло
кирования
void close(); Закрывает канал
void defaultReadObject(); Считывает из потока не статические и не временные поля те
кущего объекта
int read(); Считывает байт или массив байтов указанного размера, на
int read(byte[] arr, int чиная с заданного смещения; метод readFully() читает все
offset, int len); байты, необходимые для заполнения массива, блокируя про
int readFully(byte[] arr); грамму при необходимости
int readFully(byte[] arr, int
offset, int len);
boolean readBooleanf); Читает данные соответствующего типа
byte readByte();
char readChar();
double readDouble();
float readFloat();
int readInt();
long readLong();
short readShort();
int readUnsignedByte();
int readUnsignedShort();
String readUTF(); Читает экземпляр класса Object; можно определить тип
Object readObject(); объекта и выполнить соответствующую операцию приведения
Генерируемые исключения:
IOException
ClassNotFoundException
NotActiveException
OptionalDataException
InvalidObjectException
SecurityException
StreamCorruptedException
www.books-shop.com
void close)); Закрывает канал
void flush(); Выталкивает записанные данные из буферов
void write(byte b); Записывает одиночный байт в поток
int write(byte[] arr); Записывает массив байтов в поток
int write(byte[] arr, int Записывает в поток массив байтов указанного размера, на
Генерируемое исключение:
IOException
java.io.ByteArrayOutputStream (класс)
Конструктор:
ByteArrayOutputStreain();
ByteArrayOutputStream(int size);
Общее описание: позволяет записывать потоковые данные в массив байтов.
Родительский класс: OutputStream
Методы:
void reset(); Очищает буферы и обнуляет внутренний массив
int write(byte[] arr, int Записывает массив байтов указанного размера, начиная с
offset, int len); заданного смещения
byte[] toByteArray(); Возвращает массив потоковых данных
intsize(); Возвращает текущий размер буфера
String toString( String Возвращает внутренние данные в текстовом представлении
encoder);
void write (int b); Записывает одиночный байт
void writeTo(OutputStream о); Передает массив байтов через объект OutputStream'
Генерируемые исключения: (отсутствуют).
java.io.ObjectOutputStream (класс)
Конструктор:
ObjectOutputStream(OutputStream о);
Общее описание: с помощью этого класса можно передавать и сохранять объ
www.books-shop.com
writeObject() в процессе сериализации
int flush(); Выталкивает записанные данные из буферов
int reset(); Сбрасывает информацию, записанную в поток
void write(byte b); Записывает одиночный байт или массив байтов указанного
int write(byte[] arr); размера, начиная с заданной позиции
int write(byte[] arr, int
offset, int len);
void writeBoolean(boolean b); Записывает данные соответствующего типа
void writeByte(byte b);
void writeBytes(String s);
void writeChar(int c);
void writeChars(String s);
void writeDouble(double d);
void writeFloat(float f ) ;
void writeInt(int i);
void writeLong(long l);
void writeShort(int us);
void writeUTF(String s); Записывает буферизованные поля в поток
int writeFields();
void writeObject(Object o); Записывает экземпляр класса object
Генерируемые исключения:
IOException
SecurityException
java.io.BufferedReader(класс)
Конструктор:
BufferedReader(Reader i);
BufferedReader(Reader i, int size);
Общее описание: обеспечивает буферизацию входного потока, благодаря чему
Повышается производительность; обладает средствами распознавания строк тек
www.books-shop.com
String readLine(); Читает из потока строку текста (без символа конца строки)
boolean ready(); Возвращает true, если имеются данные для чтения
void reset(); Возвращается к последней помеченной позиции
long skip(long n); Пропускает ближайшие n байтов потока
Генерируемое исключение:
IOException
java.io.PrintWriter (класс)
Конструктор:
PrintWriter(Writer о);
PrintWriter(Writer о, boolean autoFlush);
PrintWriter(OutputStream о);
PrintWriter(OutputStream о, boolean autoFlush);
Общее описание: инкапсулирует выходной символьный поток; флаг autoFlush
задает автоматическое выталкивание данных из буферов при вызове метода
println().
Родительский класс: Writer
Методы:
boolean checkError(); Выталкивает содержимое буфера и возвращает true, если
произошла ошибка
void close(); Закрывает канал
int f l u s h ( ) ; Выталкивает записанные данные из буферов
void print(boolean b); Записывает данные соответствующего типа; к объекту типа
void print(char c); Object можно применить метод String.valueOf() для
void print(char[] s); преобразования данных
void print(double d ) ;
void print(float f ) ;
void print(int i);
void print(long 1);
void print(Object obj);
void print(String s);
void println(); Записывает данные соответствующего типа, добавляя в кон
void println(boolean b ) ; це символ новой строки; если установлена опция autoFlush,
void println(char c); происходит запись содержимого буфера в поток
void println(char[] s);
void println(double d);
void println(float f ) ;
void println(int i);
void println(long 1);
void println(0bject obj);
void println(String s);
void write(byte b); Записывает одиночный байт в поток
www.books-shop.com
int write(byte[] arr); Записывает массив символов в поток
int write(byte[] arr, int Записывает в поток массив символов указанного размера,
offset, int len); начиная с заданного смещения
int write(String s); Записывает строку в поток
int write(String s, int offset, Записывает в поток строку указанного размера, начиная с
int len); заданного смещения
Генерируемые исключения:
IOException
SecurityException
java.net.Socket (класс)
Конструктор:
Socket(String host, int port);
Socket(InetAddress addr, int port);
Socket(String host, int port, InetAddress lAddr, int lPort);
Socket(InetAddress addr, int port, InetAddress lAddr, int lPort);
Общее описание: класс, описывающий базовый интерфейс сетевого взаимо
действия (TCP).
Родительский класс: Object
Методы:
void close(); Закрывает сокет
InetAddress getInetAddress(); Возвращает адрес узла на противоположном конце со
единения
InputStream getInputstream(); Возвращает потоковый объект InputStream, предна
значенный для приёма сообщений
boolean getKeepAlive(); Проверяет, активизирован ли режим поддержания ак
тивности соединения
void setKeepAlive(boolean bn); Удерживает соединение активным
InetAddress getLocalAddress(); Возвращает локальный адрес, к которому подключен
сокет
int getbocalPort(); Возвращает номер локального порта
OutputStream getOutputStream(); Возвращает потоковый объект OutputStream, предна
значенный для отправки сообщений
int getPortf); Возвращает номер порта однорангового компьютера
www.books-shop.com
int getReceiveBufferSize(); Возвращает/задает размер входного буфера
void setReceiveBufferSize(int size);
int getSendBufferSize(); Возвращает/задает размер выходного буфера
void setSendBufferSize(int size);
int getSoLinger(); Возвращает/задает длительность задержки (в секун
void setSoLinger(boolean on, int дах), в течение которой ожидается очистка буферов
linger); при закрытии сокета
int getSoTimeout(); Возвращает/задает период ожидания для операций
void setSoTimeout(int timeout); вводавывода
boolean getTcpNoDelay(); Включает/отключает алгоритм Нейгла, который опреде
void setTcpNoDelay(boolean on); ляет процедуру отправки данных; если алгоритм отклю
чен, сокет может посылать данные, не дожидаясь полу
чения подтверждений
void shutdownInput(); Закрывает входной канал
void shutdownOutput(); Закрывает выходной канал
Генерируемые исключения:
IOException
SocketException
java.net.ServerSocket (класс)
Конструктор:
ServerSocket(int port);
ServerSocket(int port, int backlog);
ServerSocket(int port, int backlog, InetAddress bindAddr);
Общее описание: серверный ТСР
сокет, формирующий очередь клиентских за
просов.
Родительский класс: Object
Статический метод:
setSocketPactory(SocketImplFactory Регистрирует объект, отвечающий за создание экземп
fac); ляров сокетов
Методы:
Socket accept(); Принимает клиентский запрос и возвращает объект
класса Socket
void close(); Закрывает сокет
InetAddress getInetAddress(); Возвращает локальный адрес, к которому подключен
сокет
int getLocalPort(); Возвращает номер локального порта
int getSoTimeout(); Возвращает/задает период ожидания для операций
void setSoTimeout(int timeout); вводавывода
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
Генерируемые исключения:
IOException
SocketException
java.net.DatagramSocket(класс)
Конструктор:
DatagramSocket();
DatagramSocket(int port);
DatagramSocket(int port, InetAddress bindAddr);
Общее описание: UDP
сокет.
Родительский класс: Object
Методы:
void close(); Закрывает сокет
void connect(InetAddress addr, int Подключается к одноранговому компьютеру для неяв
port); ной отправки сообщений
void disconnect(); Отключается от однорангового компьютера
InetAddress getInetAddress(); Возвращает адрес однорангового компьютера
InetAddress getLocalAddress(); Возвращает локальный адрес, к которому подключен
сокет
int getLocalPort(); Возвращает номер локального порта
int getPort(); Возвращает номер порта однорангового компьютера
int getReceiveBufferSize(); Возвращает/задает размер входного буфера
void setReceiveBufferSizefint size);
int getSendBufferSize(); Возвращает/задает размер выходного буфера
void setSendBufferSize(int size);
int getSoTimeout(); Возвращает/задает период ожидания для операций
void setSoTimeoutfint timeout); вводавывода
void receive(DatagramPacket p); Принимает сообщение
void send(DatagramPacket p ) ; Отправляет сообщение
Генерируемые исключения:
IOException
SocketException
www.books-shop.com
java.net.MulticastSocket (класс)
Конструктор:
MulticastSocket();
MulticastSocket(int port);
Общее описание: групповой UDP
сокет.
Родительский класс: DatagramSocket
Методы:
InetAddres s getInterf асе(); Возвращает/задает локальный адрес, к которому под
www.books-shop.com
Предметный указатель
www.books-shop.com
уборка мусора 251 параметры 206
разрыв соединения 75
м сокеты
сравнение с UDP
80
81
MAC, протокол 42, 101; 327; 332, 333 структура пакета 71; 73
характеристики пакета 64
О TCP/IP
безопасность 312
OpenSSL, библиотека 320 идентификация компьютера 42
создание клиента 321 межсетевой уровень 109
создание сервера 323 межузловой уровень 110
OSI, модель 105; 108; 293 основы адресации 28; 42
канальный уровень 106 прикладной уровень 112
представительский уровень 108; 299 сравнение с моделью OSI 112
прикладной уровень 108 структура 108
сеансовый уровень 107 уровень доступа к сети 108
сетевой уровень 107 Telnet 29; 228
сравнение с TCP/IP 112
транспортный уровень
физический уровень
107
105
U
UDP, протокол
P большие сообщения
избыточность пакетов
94
96
Pthreads, библиотека 139; 145; 160 описание 110
исключающие семафоры 757; 158 передача сообщений 92
планирование заданий 160 подтверждение доставки
сообщений 94
R прием сообщений 93
проверка целостности данных 96
RDP, протокол 83 сокеты 82; 86
RPC, технология 108; 293 сравнение с TCP 81
безопасность 316 структура пакета 69
сетевые заглушки 298 упорядочение пакетов 95
усиление надежности 94
s характеристики пакета 64
www.books-shop.com
нулевой 45 Виртуальная машина Java 249
обратной связи 26 Виртуальная память 132, 190
преобразование 46 Виртуальная сеть 107
специальный 46 Внеполосная передача 37; 53; 127;
структура 43; 327 202; 206; 209
формат 29
широковещательный
Аутентификация
45; 327
226; 309
г
Групповое вещание 203; 329;
Б 333; 334
в IPv6 '205; 352
Блокировка 157 многоадресная магистраль 334
зонная 158 отправка сообщений 332; 333
нежесткая 158 подключение к группе 330
Брандмауэр 313; 318; 334 в IPv6 353
активная фильтрация 314 реализация 332
пассивная фильтрация 314 технология 6bone 354
Буфер
входной 202 3
выходной 203
уровень заполнения 180 Задание
взаимодействие с другими
в заданиями
дифференцирование
145
138
Ввод
вывод дочернее 159
асинхронный 171; 172; 208 планирование 159
алгоритм 177 приоритет 159
запись данных 180 дублирование , 138
подключение по запросу 181 зомби 159; 160
чтение по запросу 179 контекст 133
блокирование 168; 170; 171 определение 132
альтернативы 170 планирование 136
по записи 170 получение данных от потомка 755
по подключению 170 сигнализация о завершении 152
по чтению 170 таблица страниц 132
буфер 180 Зомби 159; 160
в Java 256 Зона
методики 171 демилитаризованная 314
неблокируемый 37; 182 надежная
режим опроса 172
алгоритм 172 И
запись данных 175
поглощающий цикл 173 Инкапсуляция 238
установление соединений 176 Интерфейс 244
чтение данных 77J Исключающий семафор 155; 156; 158
сигнальный 171; 177 тип PTHREAD_ERRORCHECK_
тайм
аут 172; 185 MUTEX_INITIALIZER_NP 158
файловый 208 тип PTHREAD_MUTEX_
Взаимоблокировка 97; 126; 158 INITIALIZER 757
сетевая 228
www.books-shop.com
тип PTHREAD_RECURSIVE_ описание 446
MUTEX_INITIALIZER_NP 157 OutputStreamWriter 256
PipedInputStream 256
К PipedOutputStream 256
PipedReader 256
Канал PipedWriter 256
дескриптор 147 PrintStream 256
защита 316; 317 PrintWriter 256; 257
именованный 56 описание 449
создание 146 Reader 256
Квитирование 87 SequenceInputStream 256
обратное 226 ServerSocket 252
трехфазовое 75 описание 451
Класс Socket 250, 257
BufferedReader 257 метод close() 257
описание 448 описание 450
ByteArrayInputStream 256 SocketException 443
описание 445 StringReader 256
ByteArrayOutputStream 256 StringWriter 256
описание 447 Thread 259; 260
CharArrayReader 256 Writer 256
CharArrayWriter 256 атрибуты 241
DatagramPacket 253 ввода
вывода 256
описание 443 деструктор 274
DatagramSocket 253; 255 дружественный 283
описание 452 конструктор 273
FilelnputStream 256 методы 242
FileOutputStream 256 статические 274
FileReader 256 надкласс 241
FileWriter 256 наследование 282
FilterReader 256 множественное 286
FilterWriter 256 определение 241
Frame 260 отношения 242
InetAddress 250 подкласс 241
описание 444 потоковый 259; 260
InputStream 250; 256; 257 права доступа 242
описание 445 свойства 242
InputStreamReader 256; 257 суперкласс 241
IOException 442 члены 242
MulticastSocket 254 шаблонный 243
метод getInterface() 259 Класс сети 43
метод getTimeToLive() 259 Кластер 46
метод setInterface() 259 Клиент
метод setTimeToLive() 259 "тонкий" 227
описание 453 SSL 321
ObjectInputStream 256; 257 запись данных на сервер 52
описание 445 подключение к серверу 32; 40
ObjectOutputStream 256; 257 алгоритм 26; 30
описание 447 получение ответа от сервера 35
OutputStream 250, 256; 257 разрыв соединения с сервером 39
www.books-shop.com
Код ошибки определение 241
EACCES 31; 120 правила именования 281
EAGAIN 37; 54; 124; 173; 207
EBADF 37; 39; 52; 120; 121; 124
EBUSY 158
п
EFAULT 52; 146 Пакет
EINTR 185; 215 базовая структура 59
EINVAL 31; 37; 48; 52; 120; 161 в протоколе ICMP 68
EMFILE 146 в протоколе TCP 71; 73
EMSGSIZE 54 в протоколе UDP 69
ENETUNREACH 54 замещение 60
ENOTCONN 38 зеркальное двоение 102
ENOTSOCK 38; 54 коммутация 28
EOPNOTSUPP 121; 124 неструктурированный 67
EPIPE 52; 127 поле
EPROTONOSUPPORT 31 TTL 63; 102; 343
ESRCH 161 версии протокола 61
EWOULDBLK 38; 173 данных 64
Команда длины заголовка . 61
ping 337 идентификатора 62
схема работы 341 параметров 64
traceroute 343 протокола 63
Коммутация пакетов 28 типа обслуживания 62
Компонент 267 поля фрагментации 62
Конфликт адресов 42 потеря 103
Критическая секция 155; 158 ретрансляция 63
тип обслуживания 62; 204
м типы
ускоренная передача
56; 67
338
Маршрутизатор фрагментация 38; 62; 67
конфигурирование 333 характеристики 64; 65
многоадресный 334 целостность данных 66
однонаправленный 334 Подсеть
преобразование адресов 43; 46 активная 45
Маска подсети 45; 327 маска 45; 327
Многоадресная магистраль 334 Полиморфизм 241
Многозадачность 97; 132; 134; 135 Порт 29
Мультиплексирование 107 номер 48
привилегированный 49
н привязка к сокету
совместное использование
90; 119
48;
Неразрушаюшее чтение 37 203; 206
список стандартных 365
О эфемерный 50
Порядок следования байтов
Объект обратный 50
глобальный 238 описание 50
интроспективный анализ 243 прямой 50
мутация 284 серверный 34; 50
наследование 239; 269 сетевой 50
www.books-shop.com
Поток 132 подготовка к приему запросов 189
stderr 135; 165; 221 подключение 32; 40
дескриптор 147 алгоритм 26; 30
stdin 755; 165; 221 получение ответа 35
дескриптор 147 предварительное ветвление 191
stdout 135; 165; 221 прием запросов от клиентов 122
дескриптор 147 простейший 118
блокировка 157 разрыв соединения 39
зонная 158 создание очереди ожидания 121
нежесткая 158 степень загруженности 193
вызов функции ехес() 165 типы ресурсов 25
отсоединение 160 Сервлет 193
переадресация 1 35 Сериализация 757
создание 139 Сертификация 226; 309
состояние гонки 755 Сигнал
сравнение с процессом 136 SIGALRM 184; 220
Программа SIGCHLD 152; 161; 219
ifconfig 349 SIGFAULT
init 135; 160, 220 SIGFPE 153
rpcgen 293; 300; 302; 304 SIGHUP 220
опция
а 301 SIGINT 153
синтаксис 300 SIGIO 54; 177; 180; 181;
tcpdump 76; 77 209; 210; 220
Процесс 132 SIGPIPE 52; 276; 218
взаимодействие с другими. SIGSTOP 153
процессами 146 SIGTSTP 153
идентификатор 342 SIGTTIN 153
код завершения 162 SIGTTOU 153
создание 136 SIGURG 53; 179; 210; 219
сравнение с потоком 136 взаимные помехи 101
динамика распространения 102
с затухание
обработка
107
277
Сеанс обработчик 152
возобновление 225; 226; 306 потеря 154
идентификатор 297; 305 сброс 152
контрольные точки 29,6 список стандартных 372
организация диалога 296 Синхронизация
состояние 305 блокировка 757
сохранение в активном зонная 158
состоянии 295 нежесткая 158
Сервер взаимоблокировка 158
HTTP 127 гонка 155
SSL 323 исключающий семафор 155; 156;
взаимодействие с клиентрм 123 158
вRРС 298 критическая секция 155; 158
критический 223; 224 сериализация 157
общий алгоритм 117 Соединение 28
ограничение числа клиентов 189 активное 117
отказ от обслуживания 229 алгоритм 26
www.books-shop.com
возобновляемое 296 SO_SNDTIMEO 185; 203
Сокет SO_REUSEADDR 48; 203;
аппаратного уроэня 77; 113 207;331
в Java 249 SO_TYPE 203
конфигурирование 258 SO_REUSEPORT 331
в TCP 80; 250; 252 SO_TIMEOUT 258
в UDP 82; 86; 253 TCP_NODELAY 258
групповой 254 TCP_KEEPALIVE 206
конфигурирование 259 TCP_MAXRT 206
именованный 56; 120 TCP_MAXSEG 206
неструктурированный 63; 68; TCP_NODELAY 206; 207
337; 338; 339 TCP_STDURG 206
в IPv6 351 список 368
параметр уровень SOL_IP 203; 369
IP_ADD_MEMBERSHIP 203 уровень SOL_IPV6 205; 370
IP_DROP_MEMBERSHIP 203 уровень SOL_SOCKET 201; 368
IP_HDRINCL 61; 62, 203; 337 уровень SOL_TCP 206; 371
IP_MULTICAST_IF 204,259 подключение к группе 330
IP_MULTICAST_LOOP 204 привязка к порту 90; 119
IP_MULTICAST_TTL 204,259 Стек протоколов 100
IP_MTU_DISCOVER 204 Структура
IP_OPTIONS 204 ip_mreq 330
IP_TOS 204 ipv6_mreq 353
IP_TTL 63,204 linger 202
IPV6_ADD_MEMBERSHIP 205 pollfd 184; 200
IPV6_ADDRFORM 205 protoent 339
IPV6_CHECKSUM 205 sigaction 152
IPV6_DROP_MEMBERSHIP 205 sockaddr 90
IPV6_DSTOPTS 205 заполнение полей 119
IPV6_HOPLIMIT 205 описание 33
IPV6_MULTICAST_HOPS 205 sockaddr_in 34
IPV6_MULTICAST_IF 205 sockaddr_in6 34; 350
IPV6_MULTICAST_LOOP 205 определение 245
IPV6_NEXTHOP 205
IPV6_PKTOPTIONS
IPV6_UNICAST_HOPS
205
206
т
SO_BROADCAST 201,328 Таймер 185
SO_DEBUG 201
SO_DONTROUTE 202 Ф
SO_ERROR 202, 209,217
SO_KEEPALIVE 201, 202; 206 Файл
SO_LINGER 202, 258 /etc/protocols 339; 404
SO_OOBINLINE 202; 210 описание 364
SO_PASSCRED 202 /etc/services 49; 90; 121
SO_PEERCRED 202 описание 365
SO_RCVBUF 202, 258 netinet/in.h 32; 63
SO_RCVLOWAT 202,220 sys/socket.h 32
SO_RCVTIMEO 185 sys/types.h 32
SO_SNDBUF 203 права доступа 313
SO SNDLOWAT 203 Фильтрация
www.books-shop.com
активная 314 exit() 138; 141
пассивная 314 fcntl() 175; 178; 208; 210
Функция команда F_SETFL 174
_clone() 135; 139; 160 описание 173; 425
описание 142; 409 флаг O_NONBLOCK 38; 174
флаг CLONE_FILES 143 fork() 139; 143; 160; 163; 192; 194
флаг CLONE_FS 143 возвращаемое значение 216
флаг CLONE_PID 143 описание 137; 408
флаг CLONE_SIGHAND 143 gethostbyname() 57
флаг CLONЕ_VМ 143 возвращаемое значение 216
accept() 117; 119; 121; 123; описание 403
153; 170; 177; 181; 192, 193; 229 gethostname() 402
возвращаемое значение 215 getpeername() 401
код ошибки EAGAIN 124 getpid() 137
код ошибки EBADF 124 getpriority() 159
код ошибки EOPNOTSUPP 124 getprotobyname() 339; 404
описание 122; 383 getsockopt()
alarm() 185; 220 возвращаемое значение 216
bind() 32, 49; 71; 83; 90; 92, 93; описание 201; 406
117; 119; 123; 203; 206 параметры сокетов 368
возвращаемое значение 275 htonl() 57; 91
код ошибки EACCES 120 описание 396
код ошибки EBADF 120 htons() 35; 51
код ошибки EINVAL 120 описание 396
константа inet_addr() 57
INADDR_ANY 91; 330 описание 397
описание 90; 119; 381 inet_aton() 35; 51
bzero() 35 описание 398
описание 424 inet_ntoa() 57
close() 117; 119, 202 описание 399
возвращаемое значение 216 inet_ntpp() 350; 400
код ошибки EBADF 39 inet_pton() 350; 399
описание 39; 433 kill() 154
connect() 40; 80; 82; 86; listen() 117; 119; 193
90, 91; 117; 170 возвращаемое значение 216
возвращаемое значение 215 код ошибки EBADF 727
описание 32; 384 код ошибки
ехес() 162; 163; 220 EOPNOTSUPP 727
описание 410 описание 121; 382
ехес1() 163 memset() 424
описание 410 ntohl() 51
execle() 164 описание 397
описание 410 ntohs() 57
execlp() 163 описание 397
описание 410 pipe()
execv() 164 код ошибки EFAULT 146
описание 410 код ошибки EMFILE 146
execve() 410 описание 146; 427
execvp() 164
описание . 410 описание 182; 427
Ⱦɚɧɧɚɹɜɟɪɫɢɹɤɧɢɝɢɜɵɩɭɳɟɧɚɷɥɟɤɬɪɨɧɧɵɦɢɡɞɚɬɟɥɶɫɬɜɨɦ%RRNVVKRS
ɊɚɫɩɪɨɫɬɪɚɧɟɧɢɟɩɪɨɞɚɠɚɩɟɪɟɡɚɩɢɫɶɞɚɧɧɨɣɤɧɢɝɢɢɥɢɟɟɱɚɫɬɟɣɁȺɉɊȿɓȿɇɕ
Ɉɜɫɟɯɧɚɪɭɲɟɧɢɹɯɩɪɨɫɶɛɚɫɨɨɛɳɚɬɶɩɨɚɞɪɟɫɭ[email protected]
событие POLLERR 184 описание 182; 430
событие POLLHUP 184 тайм
аут 185
событие POLLIN 184 send() 48; 83; 86; 90, 119;
событие POLLINVAL 184 172, 175; 177; 180; 203; 208
событие POLLOUT 184 возвращаемое значение 216
событие POLLPRI 184 код ошибки EAGAIN 54
тайм
аут 185 код ошибки EBADF 54
pthread_create() 143; 161 код ошибки EMSGSIZE 54
возвращаемое значение 216 код ошибки ENOTSOCK 54
описание 139; 415 код ошибки EPIPE 54
pthread_detach() описание 53; 387
код ошибки EINVAL 161 флаг MSG_DONTROUTE 53
код ошибки ESRCH 161 флаг MSG_DONTWAIT 54; 209
описание 161; 417 флаг MSG_NOSIGNAL 54; 218
pthread_exit() 416 флаг МSG_ООВ 53; 210
pthread_join() 416 sendfile() 390
pthread_mutex_destroy() 418 sendmsg() 68; 203; 205
pthread_mutex_init() 418 описание 389
pthread_mutex_lock() 757 sendto() 68; 83; 88; 90,
описание 419 203; 340, 341
pthread_mutex_trylock() 158 описание 86; 388
описание 419 setpriority() 159
pthread_mutex_unlock() 157 setsockopt() 62; 180, 330, 337; 341
описание 420 возвращаемое значение 216
read() 40; 48; 52; 71; 82; 117; описание 201; 405
119; 127; 170; 171; 203; 208 параметры сокетов 368
код ошибки EAGAIN 37 shmget() 146
код ошибки EBADF 37 shutdown() 119
код ошибки EINVAL 37 возвращаемое значение 216
описание 35; 429 описание 39; 395
recv() 40, 48; 53; 86; 90; sigaction()
119; 127; 172; 173; 177; константа SIG_DFL 153
180; 186; 203; 209; 299 константа SIG_IGN 153
возвращаемое значение 216 описание 152, 422
код ошибки ENOTCONN 38 флаг SA_NOCLDSTOP 153
код ошибки ENOTSOCK 38 флаг SA_NODEFER 153
описание 37; 391 флаг SA_NOMASK 153
флаг MSG_DONTWAIT 37 флаг SA_ONESHOT 153
флаг MSG_OOB 37; 206; 210 флаг SA_RESETHAND 153
флаг МSG_РЕЕК 37 флаг SA_RESTART 153;
флаг MSG_WAITALL 37; 209 186; 216
recvfrom() 78; 83; 90; 93; signal() 152
122, 203; 343 описание 421
длина адреса 87 sigprocmask() 423
описание 86; 393 sleep() 186
recvmsg() 203 socket() 37; 40; 52; 80, 113;
описание 394 117; 119; 121; 203
sched_yield() 412 возвращаемое значение 216
select() 177; 195; 196; 197; 199; 228 домен PF_INET 33
коллизия выбора 198 домен PF_INET6 33; 350
www.books-shop.com
домен PF_IPX 33 write() 40; 48; 71; 82; 117; 119;
домен PF_PACKET 31 184; 203; 207; 208
домены, таблица 359 код ошибки EBADF 52
код ошибки EACCES 31 код ошибки EFAULT 52
код ошибки EINVAL 31 код ошибки EINVAL 52
код ошибки код ошибки EPIPE 52
EPROTONOSUPPORT 31 описание 52; 432
описание З0, 380 преобразования данных 50
основные параметры 30
сокет SOGK_PACKET 77; 113
сокет SOCK_RAW 63; 68;
ш
337; 339 Широковещание 201; 327; 328; 329
сокет SOCK_STREAM 119 Шифрование 318; 319
типы протоколов, таблица 363 алгоритмы 319
фильтры 78 виды 319
socketpair() 385 двустороннее 318
sysctl() 206 ключ 318
vfork() 165 одностороннее 318
wait() 152; 160; 219 с открытым ключом 319
описание 413 с симметричным ключом 319
waitpid()
макрос WEXITSTATUS() 162
макрос WIFEXITEP() 162
описание 219; 413 Экстрасеть 311
www.books-shop.com
Научно!популярное издание
Шон Уолтон
www.books-shop.com