![]() |
Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Основные функции библиотеки Sockets API.
Accept() Принимает входные подключения на слушающем сокете. Прототип
#include < sys/types.h> #include < sys/socket.h> int accept(int s, struct sockaddr *addr, socklen_t *addrlen); Описание accept() вызываетcя чтобы получить новый дескриптор сокета для последующего общения с только что подключённым клиентом.
Возвращаемый accept() -ом дескриптор определяет уже открытый и подключённый к . Возвращаемое значение accept() возвращает дескриптор только что подключённого сокета, или -1 при ошибке, при этом соответствующим образом установив errno.
Пример
struct sockaddr_storage their_addr; socklen_t addr_size; struct addrinfo hints, *res; int sockfd, new_fd; // сначала заполняем адресные структуры с помощью 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, MYPORT, & hints, & res); // создать сокет, связать и слушать: sockfd = socket(res-> ai_family, res-> ai_socktype, res-> ai_protocol); bind(sockfd, res-> ai_addr, res-> ai_addrlen); listen(sockfd, BACKLOG); // теперь принять входящие подключения: addr_size = sizeof their_addr; new_fd = accept(sockfd, (struct sockaddr *)& their_addr, & addr_size); // можно беседовать по дескриптору сокета new_fd!
Bind() Связывает сокет с IP адресом и номером порта.
Прототип
#include < sys/types.h> #include < sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen); Описание Когда удалённая машина хочет связаться с вашей серверной программой, ей для этого нужны IP адрес и номер порта. Это можно сделать вызов bind(). Сначала нужно вызывать getaddrinfo(), заполнить struct sockaddr адресом назначения и информацией порта. Затем вызывается socket() чтобы получить дескриптор сокета и передаёте сокет и адрес в bind(), и вот IP адрес привязан к сокету! Если свой IP адреса неизвестен или известно, что у вашей машины только один IP адрес, или вам безразлично, какие IP адреса используются, нужно установить флаг AI_PASSIVE в параметре hints при вызове getaddrinfo(). При этом в часть IP адреса в struct sockaddr записывается специальное значение, которое указывает bind(), что ей нужно автоматически заполнить этот IP адрес хоста. Это специальное значение записывается в IP адрес struct sockaddr чтобы автоматически установить адрес текущего хоста. Но нужно помнить, что это происходит только при заполнении struct sockaddr вручную, иначе необходимо воспользоваться результатом getaddrinfo(), как указано выше. В IPv4, поле sin_addr.s_addr структуры struct sockaddr_in устанавливается INADDR_ANY. В IPv6, в поле sin6_addr структуры sockaddr_in6 записывается значение глобальной переменной in6addr_any. Или, если объявлена новая struct in6_addr, можно инициализировать её IN6ADDR_ANY_INIT. Наконец, параметр addrlen должен быть установлен в sizeof my_addr. Возвращаемое значение Возвращает 0 при успехе или -1 в случае ошибки ( errno устанавливается соответственно). Пример
// современный способ работы с getaddrinfo() struct 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); // создать сокет: // (вам нужно прогуляться по связанному списку " res" и проверить на ошибки! ) sockfd = socket(res-> ai_family, res-> ai_socktype, res-> ai_protocol); // связать с портом, переданным getaddrinfo(): bind(sockfd, res-> ai_addr, res-> ai_addrlen); // пример упаковки структуры вручную, IPv4 struct sockaddr_in myaddr; int s; myaddr.sin_family = AF_INET; myaddr.sin_port = htons(3490); // можете указать IP адрес: inet_pton(AF_INET, " 63.161.169.137", & (myaddr.sin_addr)); // или позволить выбрать его автоматически: myaddr.sin_addr.s_addr = INADDR_ANY; s = socket(PF_INET, SOCK_STREAM, 0); bind(s, (struct sockaddr*)& myaddr, sizeof myaddr);
3. Подключает сокет к серверу. Прототип
#include < sys/types.h> #include < sys/socket.h> int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen); Описание
После того, был создан дескриптор сокета вызовом socket(), можно подключить его к удалённому серверу системным вызовом connect(). Для этого нужно только передать ему дескриптор сокета и адрес сервера, с которым вам захотелось познакомиться поближе, и длину адреса, которую принято передавать таким функциям. Обычно эту информацию получают как результат вызова getaddrinfo(), но есть возможность заполнить свою собственную struct sockaddr. Если bind() ещё не вызыван с этим дескриптором сокета, он автоматически привязывается к вашему IP адресу и случайному локальному порту. Обычно это удобно, если вы не сервер, поскольку тогда безразличен номер вашего локального порта. Если номер удалённого порта важен, то необходимо указать его в параметре в serv_addr. Можновызвать bind() если необходимо, чтобы сокет вашего клиента был привязан к определённому IP адресу и порту, но это бывает редко. Как только сокет подключён ( connect() ), можно посылать ( send() ) и принимать ( recv() ). Возвращаемое значение Возвращает 0 при успехе или -1 в случае ошибки ( errno устанавливается соответственно). Пример
// соединиться с www.example.com порт 80 (http) struct addrinfo hints, *res; int sockfd; // сначала заполняем адресные структуры с помощью getaddrinfo(): memset(& hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // использовать либо IPv4 либо IPv6 hints.ai_socktype = SOCK_STREAM; // в это строке можно указать " 80" вместо " http": getaddrinfo(" www.example.com", " http", & hints, & res); // создать сокет: sockfd = socket(res-> ai_family, res-> ai_socktype, res-> ai_protocol); // соединить с адресом и портом, переданным getaddrinfo(): connect(sockfd, res-> ai_addr, res-> ai_addrlen);
Close() Закрывает дескриптор сокета.
Прототип
#include < unistd.h> int close (int s); Описание После того как закончилось использование сокета и больше нет необходимости посылать ( send() ) или принимать ( recv() ) данные, можно его закрыть. Удалённая сторона может узнать об этом одним из двух способов. Первый: Если удалённая сторона вызывает recv(), он возвращает 0. Второй: удалённая сторона вызывает send(), он примет сигнал SIGPIPE и вернёт -1, errno будет установлен в EPIPE. Возвращаемое значение Возвращает 0 при успехе или -1 в случае ошибки ( errno устанавливается соответственно). Пример s = socket(PF_INET, SOCK_DGRAM, 0); .. // куча всего...*BRRRONNNN! * .. close(s); // действительно, не очень много.
5. getaddrinfo(), freeaddrinfo(), gai_strerror()
struct sockaddr.
Прототип
Описание getaddrinfo() это удобная функция, которая возвращает информацию об имени отдельного хоста (такую как IP адрес) и заполняет struct sockaddr, заботится о мелких деталях (типа это IPv4 или IPv6.) Она заменяет старые функции gethostbyname() и getservbyname(). Имя хоста передаётся в параметре nodename. Адрес может быть именем хоста, как “www.guap.ru”, либо IPv4 или IPv6 адрес (передаваемый как строка). Этот параметр также может быть NULL если используется флаг AI_PASSIVE. Обычно параметр servname это номер порта. Он может быть номером (передаваемый строкой, как “80”), или он может быть именем сервиса, как “http” или “tftp” или “smtp” или “pop”, и т.д. Во входных параметрах есть hints. Именно здесь можно определить что функции getaddrinfo() нужноделать. Перед использованием необходимо обнулить всю структуру целиком функцией memset(). Поле ai_flags может содержать множество флагов, но основные это AI_CANONNAME и AI_PASSIVEAI_CANONNAME заставляет записать в поле ai_canonname результата каноническое (настоящее) имя хоста. AI_PASSIVE приводит к записи в IP адрес INADDR_ANY (IPv4) или in6addr_any (IPv6); из-за этого последует вызов bind() чтобы автоматически записать в IP адрес структуры struct sockaddr адрес текущего хоста. Это удобно для запуска сервера если нет необходимости использовать постоянно установленный адрес. Если нужно использовать флаг AI_PASSIVE, то в nodename можно указать NULL (поскольку bind() заполнит его позднее). В входных параметрах лучше всего установить в ai_family AF_UNSPEC чтобы getaddrinfo() искала и IPv4 и IPv6 адреса. Хотя можно ограничиться одним или другим, установив AF_INET или AF_INET6. В поле ai_socktype нужно установить SOCK_STREAM или SOCK_DGRAM, в зависимости от того, какой тип сокета нужен. Можно оставить в ai_protocol 0, чтобы автоматически выбрать тип протокола. Теперь, можно вызвать getaddrinfo(). res теперь указывает на связаный список struct addrinfo и можнопосмотреть адреса, удовлетворяющие тому, что было передано в hints. Поэтому, можно определить какие из адресов по той или иной причине не работают. Наконец, нужно вызвать freeaddrinfo() чтобы освободить память, иначе она затрется. Возвращаемое значение При успехе возвращает ноль или не-ноль при ошибке. В таком случае можно воспользоваться функцией gai_strerror() чтобы получить печатную версию кода возврата. Пример // код для подключения клиента к серверу, а именно потокового сокета к www.guap.ru на порт 80 (http) // IPv4 или IPv6 int sockfd; struct addrinfo hints, *servinfo, *p; int rv; memset(& hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // для задания IPv6 используйте AF_INET6 hints.ai_socktype = SOCK_STREAM; if ((rv = getaddrinfo(" www.guap.ru", " http", & hints, & servinfo))! = 0) { fprintf(stderr, " getaddrinfo: %s\n", gai_strerror(rv)); exit(1); } // цикл по всем результатам и подключение к первому возможному for(p = servinfo; p! = NULL; p = p-> ai_next) { if ((sockfd = socket(p-> ai_family, p-> ai_socktype, p-> ai_protocol)) == -1) {
perror(" socket" ); continue; } if (connect(sockfd, p-> ai_addr, p-> ai_addrlen) == -1) { close(sockfd); perror(" connect" ); continue; } break; // здесь мы подключились удачно } if (p == NULL) { // цикл закончился, а подключения нет fprintf(stderr, " failed to connect\n" ); exit(2); } freeaddrinfo(servinfo); // со структурой закончили / сервер, ожидающий подключений, // а именно потоковый сокет порт 3490, IP этого хоста // IPv4 либо IPv6. int sockfd; struct addrinfo hints, *servinfo, *p; int rv; memset(& hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // для выбора IPv6 используйте AF_INET6 hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // использовать мой IP адрес if ((rv = getaddrinfo(NULL, " 3490", & hints, & servinfo))! = 0) { fprintf(stderr, " getaddrinfo: %s\n", gai_strerror(rv)); exit(1); } // цикл по всем результатам и подключение к первому возможному for(p = servinfo; p! = NULL; p = p-> ai_next) { if ((sockfd = socket(p-> ai_family, p-> ai_socktype, p-> ai_protocol)) == -1) { perror(" socket" ); continue; } if (bind(sockfd, p-> ai_addr, p-> ai_addrlen) == -1) { close(sockfd); perror(" bind" ); continue; } break; // здесь мы подключились удачно } if (p == NULL) { // цикл закончился, а подключения нет fprintf(stderr, " failed to bind socket\n" ); exit(2); } freeaddrinfo(servinfo); // со структурой закончили |
Последнее изменение этой страницы: 2017-04-12; Просмотров: 653; Нарушение авторского права страницы