UNIX: разработка сетевых приложений
Часть 34 из 399 Информация о книге
void sock_set_wild(struct sockaddr *<i>sockaddr</i>, socklen_t <i>addrlen</i>);Функция
связывает универсальный адрес и динамически назначаемый порт с сокетом. Функцияsock_bind_wildсравнивает адресные части двух структур адреса сокета, а функцияsock_cmp_addrсравнивает номера их портов. Функцияsock_cmp_portвозвращает только номер порта, а функцияsock_get_portпреобразует к формату представления только ту часть структуры адреса сокета, которая относится к узлу (все, кроме порта, то есть IP-адрес узла). Функцияsock_ntop_hostприсваивает адресной части структуры значение, указанное аргументомsock_set_addr, а функцияptrзадает в структуре адреса сокета только номер порта. Функцияsock_set_portзадает адресную часть структуры через символы подстановки. Как обычно, мы предоставляем для всех этих функций функции- обертки, которые возвращают значение, отличное от типа void, и в наших программах обычно вызываем именно обертки. Мы не приводим в данной книге исходный код для этих функций, так как он свободно доступен (см. предисловие).sock_set_wild3.9. Функции readn, writen и readline
Потоковые сокеты (например, сокеты TCP) демонстрируют с функциями
иreadповедение, отличное от обычного ввода-вывода файлов. Функцияwriteилиreadна потоковом сокете может ввести или вывести немного меньше байтов, чем запрашивалось, но это не будет ошибкой. Причиной может быть достижение границ буфера для сокета в ядре. Все, что требуется в этой ситуации — чтобы процесс повторил вызов функцииwriteилиreadдля ввода или вывода оставшихся байтов. (Некоторые версии Unix ведут себя аналогично при записи в канал (pipe) более 4096 байт.) Этот сценарий всегда возможен на потоковом сокете при выполнении функцииwrite, но с функциейreadон обычно наблюдается, только если сокет неблокируемый. Тем не менее вместоwriteмы всегда вызываем функциюwriteна тот случай, если в данной реализации возможно возвращение меньшего количества данных, чем мы запрашиваем.writenВведем три функции для чтения и записи в потоковый сокет.
#include "unp.h"ssize_t readn(int <i>filedes</i>, void *<i>buff</i>, size_t <i>nbytes</i>);ssize_t writen(int <i>filedes</i>, const void *<i>buff</i>, size_t <i>nbytes</i>);ssize_t readline(int <i>filedes</i>, void *<i>buff</i>, size_t <i>maxlen</i>);<i>Все функции возвращают: количество считанных или записанных байтов, -1 в случае ошибки</i>В листинге 3.9 представлена функция
, в листинге 3.10 — функцияreadn, а в листинге 3.11 — функцияwriten.readlineЛистинг 3.9. Функция readn: считывание n байт из дескриптора
//lib/readn.c1 #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 else15 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.c1 #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 else15 return (-1); /* ошибка */16 }17 nleft -= nwritten;18 ptr += nwritten;19 }20 return (n);21 }Листинг 3.11. Функция readline: считывание следующей строки из дескриптора, по одному байту за один раз
//test/readline1.с1 #include "unp.h"/* Ужасно медленная версия, приводится только для примера */2 ssize_t3 readline(int fd, void *vptr, size_t maxlen)