Рудофф Эндрю М. - UNIX: разработка сетевых приложений. 3-е изд. стр 33.

Шрифт
Фон

Существует множество сетевых протоколов, основанных на использовании строк текста: SMTP, HTTP, FTP, finger. Поэтому соблазн работать со строками будет терзать вас достаточно часто. Наш совет: мыслить в терминах буферов, а не строк. Пишите код таким образом, чтобы считывать содержимое буфера, а не отдельные строки. Если же ожидается получение строки, ее всегда можно поискать в считанном буфере.

В листинге 3.12 приведена более быстрая версия функции readline, использующая свой собственный буфер (а не буферизацию stdio). Основное достоинство этого буфера состоит в его открытости, благодаря чему вызывающий процесс всегда знает, какие именно данные уже приняты. Несмотря на это, использование readlineвсе равно может вызвать проблемы, как мы увидим в разделе 6.3. Системные функции типа selectничего не знают о внутреннем буфере readline, поэтому неаккуратно написанная программа с легкостью может очутиться в состоянии ожидания в вызове select, при том, что данные уже будут находиться в буферах readline. По этой причине сочетание вызовов readnи readlineне будет работать так, как этого хотелось бы, пока функция readnне будет модифицирована с учетом наличия внутреннего буфера.

Листинг 3.12. Улучшенная версия функции readline

//lib/readline.c

1 #include "unp.h"

2 static int read_cnt;

3 static char *read_ptr;

4 static char read_buf[MAXLINE];

5 static ssize_t

6 my_read(int fd, char *ptr)

7 {

8 if (read_cnt <= 0) {

9 again:

10 if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {

11 if (errno == EINTR)

12 goto again;

13 return(-1);

14 } else if (read_cnt == 0)

15 return(0);

16 read_ptr = read_buf;

17 }

18 read_cnt--;

19 *ptr = *read_ptr++;

20 return(1);

21 }

22 ssize_t

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 } else

37 return(-1); /* ошибка, read() задает значение errno */

38 }

39 *ptr = 0; /* завершающий нуль, как в fgets() */

40 return(n);

41 }

42 ssize_t

43 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. Резюме

Структуры адресов сокетов являются самоопределяющимися, поскольку они всегда начинаются с поля family, которое идентифицирует семейство адресов, содержащихся в структуре. Более новые реализации, поддерживающие структуры адресов сокетов переменной длины, также содержат поле, которое определяет длину всей структуры.

Две функции, преобразующие IP-адрес из формата представления (который мы записываем в виде последовательности символов ASCII) в численный формат (который входит в структуру адреса сокета) и обратно, называются inet_ptonи inet_ntop. Эти функции являются зависящими от протокола. Более совершенной методикой является работа со структурами адресов сокетов как с непрозрачными (opaque) объектами, когда известны лишь указатель на структуру и ее размер. Мы разработали набор функций sock_, которые помогут сделать наши программы не зависящими от протокола. Создание наших не зависящих от протокола средств мы завершим в главе 11 функциями getaddrinfoи getnameinfo.

Сокеты TCP предоставляют приложению поток байтов, лишенный маркеров записей. Возвращаемое значение функции read может быть меньше запрашиваемого, но это не обязательно является ошибкой. Чтобы упростить считывание и запись потока байтов, мы разработали три функции readn, writenи readline, которые и используем в книге. Однако сетевые программы должны быть написаны в расчете на работу с буферами, а не со строками.

Упражнения

2. Почему и функция readn, и функция writenкопируют указатель void*в указатель char*?

3. Функции inet_atonи inet_addrхарактеризуются традиционно нестрогим отношением к тому, что они принимают в качестве точечно-десятичной записи адреса IPv4: допускаются от одного до четырех десятичных чисел, разделенных точками; также допускается задавать шестнадцатеричное число с помощью начального 0xили восьмеричное число с помощью начального 0 (выполните команду telnet 0xe, чтобы увидеть поведение этих функций). Функция inet_ptonнамного более строга в отношении адреса IPv4 и требует наличия именно четырех чисел, разделенных точками, каждое из которых является десятичным числом от 0 до 255. Функция inet_ptonне разрешает задавать точечно- десятичный формат записи адреса, если семейство адресов AF_INET6, хотя существует

Ваша оценка очень важна

0
Шрифт
Фон

Помогите Вашим друзьям узнать о библиотеке