Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Формат представления — либо точечно-десятичная форма записи адреса IPv4, либо шестнадцатеричная форма записи адреса IPv6, за которой следует завершающий символ (мы используем точку, как в программе netstat), затем десятичный номер порта, а затем завершающий нуль. Следовательно, размер буфера должен быть равен как минимум INET_ADDRSTRLENплюс 6 байт для IPv4 (16 + 6 - 22) либо INET6_ADDRSTRLENплюс 6 байт для IPv6 (46 + 6 - 52).
Обратите внимание, что при статическом хранении результата функция не допускает повторного вхождения (не является повторно входимой) и не может быть использована несколькими программными потоками (не является безопасной в многопоточной среде — thread-safe). Более подробно мы поговорим об этом в разделе 11.18. Мы допустили такое решение для этой функции, чтобы ее было легче вызывать из простых программ, приведенных в книге.
В листинге 3.8 представлена часть исходного кода, обрабатывающая семейство AF_INET.
Листинг 3.8. Наша функция sock_ntop
//lib/sock_ntop.c
5 char *
6 sock_ntop(const struct sockaddr *sa, socklen_t salen)
7 {
8 char portstr[7];
9 static char str[128]; /* макс. длина для доменного сокета Unix */
10 switch (sa->sa_family) {
11 case AF_INET: {
12 struct sockaddr_in *sin = (struct sockaddr_in*)sa;
13 if (inet_ntop(AF_INET, &sin->sin_addr. str, sizeof(str)) == NULL)
14 return (NULL);
15 if (ntohs(sin->sin_port) != 0) {
16 snprintf(portstr, sizeof(portstr), ntohs(sin->sin_port));
17 strcat(str, portstr);
18 }
19 return (str);
20 }
Для работы со структурами адресов сокетов мы определяем еще несколько функций, которые упростят переносимость нашего кода между IPv4 и IPv6.
#include "unp.h"
int sock_bind_wild(int sockfd , int family );
Возвращает: 0 в случае успешного выполнения функции, -1 в случае ошибки
int sock_cmp_addr(const struct sockaddr * sockaddr1 ,
const struct sockaddr * sockaddr2 , socklen_t addrlen );
Возвращает: 0, если адреса относятся к одному семейству и совпадают, ненулевое значение в противном случае
int sock_cmp_port(const struct sockaddr * sockaddr1 ,
const struct sockaddr * sockaddr2 , socklen_t addrlen );
Возвращает: 0, если адреса относятся к одному семейству и порты совпадают, ненулевое значение в противном случае
int sock_get_port(const struct sockaddr * sockaddr , socklen_t addrlen );
Возвращает: неотрицательный номер порта для адресов IPv4 или IPv6, иначе -1
char *sock_ntop_host(const struct sockaddr * sockaddr , socklen_t addrlen );
Возвращает: непустой указатель в случае успешного выполнения функции, NULL в случае ошибки
void sock_set_addr(const struct sockaddr * sockaddr ,
socklen_t addrlen , void * ptr );
void sock_set_port(const struct sockaddr * sockaddr ,
socklen_t addrlen , int port );
void sock_set_wild(struct sockaddr * sockaddr , socklen_t addrlen );
Функция sock_bind_wildсвязывает универсальный адрес и динамически назначаемый порт с сокетом. Функция sock_cmp_addrсравнивает адресные части двух структур адреса сокета, а функция sock_cmp_portсравнивает номера их портов. Функция sock_get_portвозвращает только номер порта, а функция sock_ntop_hostпреобразует к формату представления только ту часть структуры адреса сокета, которая относится к узлу (все, кроме порта, то есть IP-адрес узла). Функция sock_set_addrприсваивает адресной части структуры значение, указанное аргументом ptr, а функция sock_set_portзадает в структуре адреса сокета только номер порта. Функция sock_set_wildзадает адресную часть структуры через символы подстановки. Как обычно, мы предоставляем для всех этих функций функции- обертки, которые возвращают значение, отличное от типа void, и в наших программах обычно вызываем именно обертки. Мы не приводим в данной книге исходный код для этих функций, так как он свободно доступен (см. предисловие).
3.9. Функции readn, writen и readline
Потоковые сокеты (например, сокеты TCP) демонстрируют с функциями readи writeповедение, отличное от обычного ввода-вывода файлов. Функция readили writeна потоковом сокете может ввести или вывести немного меньше байтов, чем запрашивалось, но это не будет ошибкой. Причиной может быть достижение границ буфера для сокета в ядре. Все, что требуется в этой ситуации — чтобы процесс повторил вызов функции readили writeдля ввода или вывода оставшихся байтов. (Некоторые версии Unix ведут себя аналогично при записи в канал (pipe) более 4096 байт.) Этот сценарий всегда возможен на потоковом сокете при выполнении функции read, но с функцией writeон обычно наблюдается, только если сокет неблокируемый. Тем не менее вместо writeмы всегда вызываем функцию writenна тот случай, если в данной реализации возможно возвращение меньшего количества данных, чем мы запрашиваем.
Введем три функции для чтения и записи в потоковый сокет.
#include "unp.h"
ssize_t readn(int filedes , void * buff , size_t nbytes );
ssize_t writen(int filedes , const void * buff , size_t nbytes );
ssize_t readline(int filedes , void * buff , size_t maxlen );
Все функции возвращают: количество считанных или записанных байтов, -1 в случае ошибки
В листинге 3.9 представлена функция readn, в листинге 3.10 — функция writen, а в листинге 3.11 — функция readline.
Листинг 3.9. Функция readn: считывание n байт из дескриптора
//lib/readn.c
1 #include "unp.h"
2 ssize_t /* Считывает n байт из дескриптора */
3 readn(int fd, void *vptr, size_t n)
4 {
5 size_t nleft;
6 ssize_t nread;
7 char *ptr;
8 ptr = vptr;
9 nleft = n;
10 while (nleft > 0) {
11 if ((nread = read(fd, ptr, nleft)) < 0) {
12 if (errno == EINTR)
13 nread = 0; /* и вызывает снова функцию read() */
14 else
15 return (-1);
16 } else if (nread == 0)
17 break; /* EOF */
18 nleft -= nread;
19 ptr += nread;
20 }
21 return (n - nleft); /* возвращает значение >= 0 */
22 }
Листинг 3.10. Функция writen: запись n байт в дескриптор
//lib/writen.c
1 #include "unp.h"
2 ssize_t /* Записывает n байт в дескриптор */
3 writen(int fd, const void *vptr, size_t n)
4 {
5 size_t nleft;
6 ssize_t nwritten;
7 const char *ptr;
8 ptr = vptr;
9 nleft = n;
10 while (nleft > 0) {
11 if ((nwritten = write(fd, ptr, nleft)) <= 0) {
12 if (errno == EINTR)
13 nwritten = 0; /* и снова вызывает функцию write() */
14 else
15 return (-1); /* ошибка */
16 }
17 nleft -= nwritten;
18 ptr += nwritten;
19 }
20 return (n);
21 }
Листинг 3.11. Функция readline: считывание следующей строки из дескриптора, по одному байту за один раз
//test/readline1.с
1 #include "unp.h"
/* Ужасно медленная версия, приводится только для примера */
Интервал:
Закладка: