Главная страница
Навигация по странице:

  • Блокировка , send() !83

  • Описание* Эти функции непригодны потому что они не работают с IPv6! Вместо них используйте inet_ntop() или inet_pton()

  • 9.15. Говорит сокету слушать входящие подключения. Прототип lude int listen(int s, int backlog); Описание*

  • Описание* Очень много функций при ошибке возвращают -1 и записывают в errno

  • 9.17. Одновременно проверяет события на множестве сокетов. Прототип lude int poll(struct pollfd *ufds, unsigned int nfds, int timeout); Описание*

  • Описание* Как только вы создали и подключили сокет, вы можете принимать из него данные вызовами recv()

  • Guide to Network Сетевое программирование от Биджа


    Скачать 1.34 Mb.
    НазваниеGuide to Network Сетевое программирование от Биджа
    Дата02.05.2019
    Размер1.34 Mb.
    Формат файлаpdf
    Имя файлаbgnet_A4_rus.pdf
    ТипGuide
    #75918
    страница11 из 13
    1   ...   5   6   7   8   9   10   11   12   13
    fcntl() чем я себе позволяю, ноя пытаюсь оставаться сокет- ориентированным)
    O_NONBLOCK Делает сокет неблокируемым. См. раздел по блокировке.
    O_ASYNC Переключает сокет на асинхронный ввод/вывод. Когда данные на сокете готовы к чтению возбуждается сигнал SIGIO. Это встречается редко и выходит за рамки данного документа. И я думаю, это доступно только на некоторых системах. Возвращаемое значение!
    Возвращает 0 при успехе или -1 в случае ошибки (errno устанавливается соответственно. Различные вызовы fcntl() возвращают различные значения, ноя их здесь не рассматриваю, поскольку они не относятся к сокетам. Смотри fcntl() man страницы. Пример s = socket(PF_INET, SOCK_STREAM, 0);
    !
    fcntl(s, F_SETFL, O_NONBLOCK); // установить не-блокировку fcntl(s, F_SETFL, O_ASYNC);
    // установит асинхронный ввод/вывод Смотри также!
    Блокировка, send()
    !
    83

    Beej's Guide to Network Programming
    9.12. htons(), htonl(), ntohs(), Преобразуют многобайтные целые типы из порядка байтов хоста в порядок байтов сети и наоборот. Прототип
    !
    uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);
    Описание*
    Чтобы сделать вас по-настоящему несчастными, различные компьютеры используют внутри различный порядок байт для хранения многобайтных целых (те. больше char.) В итоге, когда вы посылаете двухбайтное short int из Intel машины в Mac (я имею ввиду до того как они тоже стали Intel), то если один компьютер думает, что это число 1, то другой думает, что это число 256, и наоборот. Способ обойти эту проблему это всем отложить разногласия и договориться как сделали Motorola и IBM, и Intel тоже сделала это загадочным способом, так что мы все перед посылкой преобразуем наш порядок байт в “big-endian”. Поскольку Intel это “little-
    endian” машины, то намного политкорректнее будет называть наш предпочитаемый порядок байт Порядком Байтов Сети. Вот эти функции и выполняют преобразование из вашего врождённого порядка в порядок байтов сети и наоборот. Это означает, что на Intel эти функции меняют все байты местами, а на PowerPC они ничего не делают поскольку байты уже в Порядке Байтов Сети. Нов своих программах вы должны использовать их всегда, потому что кто-нибудь может захотеть скомпилировать их на Intel машине и иметь правильно работающий вариант) Заметьте, что все использованные типы это 32-битные (4 байта, возможно int) и 16- битные (2 байта, очень похоже на short) числа. 64-битные машины возможно имеют
    htonll() для 64-битных int, ноя таких не видел. Вам нужно просто написать свои собственные. В любом случае, что этим функциям делать решаете вы, преобразовывать ли
    из
    порядка байт хоста (ваша машина) или из порядка байтов сети. Если “host”, то первая буква функции, которую вы собираетесь вызвать будет “h”. Иначе это “n” для “network”. В середине имени функции всегда стоит “to” (в) потому что вы всегда преобразуете
    из
    одного в другое, предпоследняя буква показывает во что вы преобразуете. Последняя буква это размер данных, “s” для short, или “l” для long. Таким образом htons() host to network short htonl() host to network long ntohs() network to host short ntohl() network to host long Возвращаемое значение
    !
    Каждая функция возвращает преобразованное значение. Пример some_long = 10; uint16_t some_short = 20; uint32_t network_byte_order;
    !
    84

    Beej's Guide to Network Programming
    // преобразовать и послать network_byte_order = htonl(some_long); send(s, &network_byte_order, sizeof(uint32_t), 0); some_short == ntohs(htons(some_short)); // это выражение истинно
    !
    !
    85

    Beej's Guide to Network Programming
    9.13. inet_ntoa(), inet_aton(), Преобразуют строковые IP адреса в формате цифр-и-точек в struct in_addr и назад. Прототип
    #include
    #include
    // ЭТО ВСЁ УСТАРЕЛО Взамен используйте inet_pton() или inet_ntop()!! char *inet_ntoa(struct in_addr in);
    int inet_aton(const char *cp, struct in_addr *inp); in_addr_t inet_addr(const char *cp);
    Описание*
    Эти функции непригодны потому что они не работают с IPv6! Вместо них используйте
    inet_ntop() или inet_pton()! Они включены здесь потому что их до сих пор можно встретить в природе. Все эти функции преобразуют struct in_addr (вероятней всего часть вашей struct sockaddr_in) в строковый формат цифр-и-точек (например, “192.168.5.10”) и наоборот. Если вы получили IP адрес в командной строке или ещё как, то это самый простой способ получения struct in_addr для connect() или любых других нужд. Если вам нужно больше власти, испробуйте какую-нибудь DNS функцию, вроде gethostbyname() или устройте в вашей стране coup d'État (госпереворот). Функция inet_ntoa() преобразует сетевой адрес изв строку формата цифр-и-точек. По историческим причинам буква “n” в “ntoa” означает сеть, и “a” означает ASCII (так что это “Network To ASCII” - у суффикса “toa” есть похожий друг в библотеке C, именуемый atoi(), который преобразует строку ASCII в целое) Функция inet_aton() обратная, она преобразует строку с цифрами-и-точками в in_addr_t (это тип поля s_addr в struct in_addr.) Напоследок, функция inet_addr() более старая функция, делающая практически тоже самое, что и inet_aton(). Теоретически она непригодна, новы будете многократное встречать и полиция вас не арестует, если вы её используете. Возвращаемое значение возвращает не-ноль, если адрес действительный, и ноль, если нет.
    inet_ntoa() возвращает строку цифр-и-точек в статическом буфере, который при каждом вызове функции перезаписывается.
    inet_addr() возвращает адрес в формате in_addr_t, или
    -1 при ошибке. (Тот же результат вы получите, если попытаетесь преобразовать строку “255.255.255.255”, которая является действительным IP адресом. Вот почему inet_aton() лучше) Пример sockaddr_in antelope; char *some_addr; inet_aton("10.0.0.1", &antelope.sin_addr);
    // запомнить IP в antelope
    !
    some_addr = inet_ntoa(antelope.sin_addr); // возвращаем IP printf("%s\n", some_addr);
    // печатает “10.0.0.1"
    !
    // этот вызов подобен вызову inet_aton() выше antelope.sin_addr.s_addr = inet_addr("10.0.0.1"); Смотри также, inet_pton(), gethostbyname(), gethostbyaddr()
    !
    86

    Beej's Guide to Network Programming
    9.14. inet_ntop(), Преобразуют IP адреса в читаемую форму и назад. Прототип
    !
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); int inet_pton(int af, const char *src, void *dst);
    Описание*
    Эти функции предназначены для работы считаемыми адресами и преобразования их в двоичное представление для последующего использования с различными функциями и системными вызовами. Буква “n” означает “network” (сеть, и “p” -
    “presentation” (представление. Или “text presentation” (текстовое представление. Новы можете думать он м, как “printable” (печатное. “ntop” это “network to printable” (сеть в печатное. Видите Иногда, глядя на IP адрес, вы не хотите смотреть на кучу цифр. Вы хотите видеть чудную печатную строку, как 192.0.2.180 или 2001:db8:8714:3a90::12. В этом случае,
    inet_ntop() к вашим услугам. В параметре af inet_ntop() принимает семейство адресов (AF_INET или
    AF_INET6). Параметр src должен быть указателем на struct in_addr либо struct in6_addr, содержащую адрес, который вы хотите преобразовать в строку. Наконец, dst и size это указатели на строку назначения и длина этой строки. Какой должна быть максимальная длина строки dst? Какова максимальная длина IPv4 и IPv6 адресов К счастью есть пара макросов, которые вам помогут. Максимальные длины это INET_ADDRSTRLEN and INET6_ADDRSTRLEN. В другой разу вам может быть строка, содержащая IP в читаемой форме, ивы хотите упаковать её вили. В этом случае обратная функция inet_pton() это то, что выищете также принимает семейство адресов (AF_INET или AF_INET6) в параметре af. Параметр src это указатель на строку, содержащую IP адрес в печатной форме. Наконец, параметр dst указывает где нужно сохранить результат, это может быть struct in_addr или struct in6_addr. Эти функции не выполняют DNS поиск - для этого нужна getaddinfo(). Возвращаемое значение возвращает параметр dst при успехе или NULL в случае отказа (установлена.
    inet_pton() возвращает 1 при успехе и -1 если была ошибка (установлена, либо 0 если задан недействительный IP адрес. Пример пример inet_ntop() и inet_pton() с IPv4 struct sockaddr_in sa; char str[INET_ADDRSTRLEN];
    // запомнить этот IP адрес в sa: inet_pton(AF_INET, "192.0.2.33", &(sa.sin_addr));
    // извлекаем и печатаем inet_ntop(AF_INET, &(sa.sin_addr), str, INET_ADDRSTRLEN);
    !
    printf("%s\n", str); // печатает "192.0.2.33"
    87

    Beej's Guide to Network Programming
    !
    // пример inet_ntop() и inet_pton() св основном тоже самое кроме кучи разбросанных 6-ок)
    !
    struct sockaddr_in6 sa; char str[INET6_ADDRSTRLEN];
    !
    // запомнить этот IP адрес в sa: inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(sa.sin6_addr));
    !
    // извлекаем и печатаем inet_ntop(AF_INET6, &(sa.sin6_addr), str, INET6_ADDRSTRLEN);
    !
    printf("%s\n", str); // печатает “2001:db8:8714:3a90::12"
    !
    // Полезная вспомогательная функция
    !
    // Преобразует адрес изв строку, IPv4 и IPv6:
    !
    char *get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen)
    { switch(sa->sa_family) { case AF_INET: inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), s, maxlen); break;
    !
    case AF_INET6: inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), s, maxlen); break;
    !
    default: strncpy(s, "Unknown AF", maxlen); return NULL;
    }
    !
    return s;
    } Смотри также

    Beej's Guide to Network Programming
    9.15. Говорит сокету слушать входящие подключения. Прототип lude int listen(int s, int backlog);
    Описание*
    Вы можете взять ваш дескриптор сокета (созданный системным вызовом socket() и сказать ему слушать входящие соединения. Это и отличает серверы от клиентов, ребята. Параметр backlog может означать пару различных вещей в зависимости от вашей системы, но приближённо он означает резерв - сколько ожидающих соединений вы можете иметь до того, как ядро начнёт отбрасывать новые. Так что, как только придёт новое соединение, вам нужно быстро принять (accept()) его, чтобы не переполнять резерв. Попробуйте установить 10 или около того и если при высокой нагрузке ваши клиенты начнут получать “Connection refused” (Соединение отвергнуто, установите побольше. Перед вызовом listen() ваш сервер должен вызвать bind() чтобы подключиться к определённому номеру порта. Клиенты будут подключаться к этому порту на IP адресе сервера. Возвращаемое значение
    !
    Возвращает 0 при успехе или -1 в случае ошибки (errno устанавливается соответственно. Пример addrinfo hints, *res; int sockfd;
    !
    // сначала заполняем адресные структуры с помощью getaddrinfo():
    !
    memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // использовать либо IPv4 либо IPv6 hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // заполнить мой IP для меня
    !
    getaddrinfo(NULL, "3490", &hints, &res);
    !
    // создать сокет:
    !
    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    !
    // связать спортом, переданным getaddrinfo():
    !
    bind(sockfd, res->ai_addr, res->ai_addrlen);
    !
    listen(sockfd, 10); // теперь сервер слушает
    !
    // где-то дальше есть цикл accept() Смотри также, bind(), socket()
    !
    !
    89

    Beej's Guide to Network Programming
    9.16. perror(), Распечатывают ошибку как читаемую строку. Прототип
    #include // для strerror()
    !
    void perror(const char *s); char *strerror(int errnum);
    Описание*
    Очень много функций при ошибке возвращают -1 и записывают в errno некоторое число, и было бы замечательно если бы вы могли легко распечатать его в некоторой понятной форме. И perror() благосклонно это делает. Если вы хотите добавить описание перед сообщением об ошибке, передайте указатель на него в параметре s или оставьте s как
    NULL и ничего дополнительно не напечатается) Коротко, эта функция берёт значение errno, вроде ECONNRESET, и любезно печатает его как “Connection reset by peer.” Функция strerror() подобна perror(), за исключением того, что она возвращает указатель на строку с сообщением об ошибке для заданного параметром errnum значения (обычно вы передаёте переменную errno.) Возвращаемое значение возвращает указатель на строку с сообщением об ошибке. Пример s;
    !
    s = socket(PF_INET, SOCK_STREAM, 0);
    !
    if (s == -1) { // ошибка вышла
    // печатает "socket error: " + сообщение об ошибке perror("socket error");
    }
    !
    // подобно if (listen(s, 10) == -1) {
    // это печатает "an error: " + сообщение об ошибке из errno: printf("an error: %s\n", strerror(errno));
    } Смотри также
    90

    Beej's Guide to Network Programming
    9.17. Одновременно проверяет события на множестве сокетов. Прототип lude int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
    Описание*
    Эта функция подобна select() в том, что обе отслеживают события в массивах дескрипторов, такие как входные данные готовы к recv(), сокет готов к send(), данные сокета внеполосного (out-of-band) канала готовы к recv(), ошибки и т.д. Основная идея в том, что вы передаёте массив из nfds структур struct pollfd в параметре ufds, вместе с таймаутом в миллисекундах (1000 миллисекунд это 1 секунда)
    timeout может быть отрицательным если вы хотите ждать вечно. Если за время таймаута ни с одним дескриптором событий не происходит, poll() возвращает управление. Каждый элемент массива структур struct pollfd представляет один дескриптор сокета и состоит из следующих полей struct pollfd { int fd;
    // дескриптор сокета short events; // биткарта интересующих событий short revents; // биткарта произошедших событий при возврате из poll()
    }; Перед вызовом poll() запишите дескриптор сокета в поле fd (если записать в fd отрицательное число, то эта struct pollfd игнорируется ив поле revents записывается ноль) и заполните поле events поразрядным ИЛИ следующих макросов
    POLLIN Известить меня о готовности данных к recv() на этом сокете.
    POLLOUT Известить меня, что я могу вызвать send() на этом сокете без блокировки.
    POLLPRI Известить меня о готовности out-of-band данных к recv(). По возвращению из poll() поле revents будет содержать поразрядное ИЛИ этих полей, показывая, какое событие произошло с этим дескриптором. Дополнительно могут быть установлены следующие поля
    POLLERR На этом сокете произошла ошибка.
    POLLHUP
    Удалённая сторона соединения зависла.
    POLLNVAL Что-то не то с дескриптором сокета fd - может он не инициализирован Возвращаемое значение
    !
    Возвращает количество элементов массива ufds для которых обнаружены события оно может быть равно нулю, если истёк таймаут. Также при ошибке возвращает -1
    (errno будет установлена соответственно) Пример nt s1, s2; int rv; char buf1[256], buf2[256]; struct pollfd ufds[2];
    !
    s1 = socket(PF_INET, SOCK_STREAM, 0);
    91

    Beej's Guide to Network Programming
    s2 = socket(PF_INET, SOCK_STREAM, 0);
    !
    // полагаем, что здесь оба подключены к серверу
    //connect(s1, ...)...
    //connect(s2, ...)...
    // заполняем массив файловых дескрипторов.
    //
    // хотим узнать когда обычные или out-of-band
    // готовы к приёму (recv())…
    !
    ufds[0].fd = s1; ufds[0].events = POLLIN | POLLPRI;
    // обычные или out-of-band ufds[1] = s2; ufds[1].events = POLLIN;
    // только обычные данные
    !
    // ждём события на сокете, таймаут 3.5 секунды rv = poll(ufds, 2, 3500);
    !
    if (rv == -1) { perror("poll");
    // в poll() произошла ошибка
    } else if (rv == 0) { printf("Timeout occurred! No data after 3.5 seconds.\n");
    } else {
    // проверка события на s1: if (ufds[0].revents & POLLIN) { recv(s1, buf1, sizeof buf1, 0);
    // принимаем обычные данные
    } if (ufds[0].revents & POLLPRI) { recv(s1, buf1, sizeof buf1, MSG_OOB); // out-of-band данные
    }
    !
    // проверка события на s2: if (ufds[1].revents & POLLIN) { recv(s1, buf2, sizeof buf2, 0);
    }
    } Смотри также

    Beej's Guide to Network Programming
    9.18. recv(), Принимают данные из сокета. Прототип
    #include
    !
    ssize_t recv(int s, void *buf, size_t len, int flags); ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
    Описание*
    Как только вы создали и подключили сокет, вы можете принимать из него данные вызовами recv() (для TCP SOCK_STREAM сокетов) и recvfrom() (для UDP SOCK_DGRAM
    сокетов). Обе функции принимают дескриптор сокета s, указатель на буфер buf, длину буфера в байтах len, и набор флагов flags, определяющих работу функций. Дополнительно, принимает struct sockaddr* from, указывающую откуда принимать данные и запишет в fromlen размер struct sockaddr. (Вы тоже можете инициализировать fromlen размером from или struct sockaddr.) Так что же за дивные флаги вы можете передавать в эту функцию Вот некоторые из них, но вам нужно осведомиться в свои man страницах, какие из них действительно поддерживаются вашей системой. Вы объединяете их поразрядным ИЛИ либо просто устанавливаете flags в
    0 если хотите получить обычный скучный
    1   ...   5   6   7   8   9   10   11   12   13


    написать администратору сайта