UNIX: разработка сетевых приложений
23 readline(int fd, void *vptr, size_t maxlen)24 {25 ssize_t n, rc;26 char c, *ptr;27 ptr = vptr;28 for (n = 1; n < maxlen; n++) {29 if ((rc = my_read(fd, &c)) == 1) {30 *ptr++ = c;31 if (c== '\n')32 break; /* Записан символ новой строки, как в fgets() */33 } else if (rc == 0) {34 *ptr = 0;35 return(n - 1); /* EOF, считано n-1 байт данных */36 } else37 return(-1); /* ошибка, read() задает значение errno */38 }39 *ptr = 0; /* завершающий нуль, как в fgets() */40 return(n);41 }42 ssize_t43 readlinebuf(void **vptrptr)44 {45 if (read_cnt)46 *vptrptr = read_ptr;47 return(read_cnt);48 }Внутренняя функция2-21считывает доmy_readсимволов за один вызов и затем возвращает их по одному.MAXLINEЕдинственное изменение самой функции29заключается в том, что теперь она вызывает функциюreadlineвместоmy_read.readНовая функция42-48выдает сведения о состоянии внутреннего буфера, что позволяет вызывающим функциям проверить, нет ли в нем других данных, помимо уже принятой строки.readlinebufПРИМЕЧАНИЕК сожалению, использование переменных типа static в коде readline.c для поддержки информации о состоянии при последовательных вызовах приводит к тому, что функция больше не является безопасной в многопоточной системе (thread-safe) и повторно входимой (reentrant). Мы обсуждаем это в разделах 11.18 и 26.5. Мы предлагаем версию, безопасную в многопоточной системе, основанную на собственных данных программных потоков, в листинге 26.5.
3.10. Резюме
Структуры адресов сокетов являются неотъемлемой частью каждой сетевой программы. Мы выделяем для них место в памяти, заполняем их и передаем указатели на них различным функциям сокетов. Иногда мы передаем указатель на одну из этих структур функции сокета, и она сама заполняет поля структуры. Мы всегда передаем эти структуры по ссылке (то есть передаем указатель на структуру, а не саму структуру) и всегда передаем размер структуры в качестве дополнительного аргумента. Когда функция сокета заполняет структуру, длина также передается по ссылке, и ее значение может быть изменено функцией, поэтому мы называем такой аргумент «значение-результат» (value-result).
Структуры адресов сокетов являются самоопределяющимися, поскольку они всегда начинаются с поля
, которое идентифицирует семейство адресов, содержащихся в структуре. Более новые реализации, поддерживающие структуры адресов сокетов переменной длины, также содержат поле, которое определяет длину всей структуры.familyДве функции, преобразующие IP-адрес из формата представления (который мы записываем в виде последовательности символов ASCII) в численный формат (который входит в структуру адреса сокета) и обратно, называются
иinet_pton. Эти функции являются зависящими от протокола. Более совершенной методикой является работа со структурами адресов сокетов как с непрозрачными (opaque) объектами, когда известны лишь указатель на структуру и ее размер. Мы разработали набор функцийinet_ntop, которые помогут сделать наши программы не зависящими от протокола. Создание наших не зависящих от протокола средств мы завершим в главе 11 функциямиsock_иgetaddrinfo.getnameinfoСокеты TCP предоставляют приложению поток байтов, лишенный маркеров записей. Возвращаемое значение функции read может быть меньше запрашиваемого, но это не обязательно является ошибкой. Чтобы упростить считывание и запись потока байтов, мы разработали три функции
,readnиwriten, которые и используем в книге. Однако сетевые программы должны быть написаны в расчете на работу с буферами, а не со строками.readlineУпражнения
1. Почему аргументы типа «значение-результат», такие как длина структуры адреса сокета, должны передаваться по ссылке?
2. Почему и функция
, и функцияreadnкопируют указательwritenв указательvoid*?char*3. Функции
иinet_atonхарактеризуются традиционно нестрогим отношением к тому, что они принимают в качестве точечно-десятичной записи адреса IPv4: допускаются от одного до четырех десятичных чисел, разделенных точками; также допускается задавать шестнадцатеричное число с помощью начальногоinet_addrили восьмеричное число с помощью начального 0 (выполните команду0x, чтобы увидеть поведение этих функций). Функцияtelnet 0xeнамного более строга в отношении адреса IPv4 и требует наличия именно четырех чисел, разделенных точками, каждое из которых является десятичным числом от 0 до 255. Функцияinet_ptonне разрешает задавать точечно- десятичный формат записи адреса, если семейство адресов —inet_pton, хотя существует мнение, что это можно было бы разрешить, и тогда возвращаемое значение было бы адресом IPv4, преобразованным к виду IPv6 (см. рис. А.6). Напишите новую функциюAF_INET6, реализующую такой сценарий: если используется семейство адресовinet_pton_looseи функцияAF_INETвозвращает нуль, вызовите функциюinet_ptonи посмотрите, успешно ли она выполнится. Аналогично, если используется семейство адресовinet_atonи функцияAF_INET6возвращает нуль, вызовите функциюinet_pton, и если она выполнится успешно, возвратите адрес IPv4, преобразованный к виду IPv6.inet_aton