Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Основные функции библиотеки Sockets API.



 

 

Accept()

Принимает входные подключения на слушающем сокете.

Прототип

 

#include < sys/types.h>

#include < sys/socket.h>

int accept(int s, struct sockaddr *addr, socklen_t *addrlen);

Описание

accept() вызываетcя чтобы получить новый дескриптор сокета для последующего общения с только что подключённым клиентом.

 

s addr
Дескриптор слушаемого сокета. Заполняется адресом подключающегося сайта.
addrlen Заполняется размером (sizeof())структуры, возвращённой в параметре addr. Его можно игнорировать, если полагаете, что получили назад struct sockaddr_in, потому что именно такой тип был передан в параметре addr.

Возвращаемый 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.
connect()

Подключает сокет к серверу.

Прототип

 

#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; Просмотров: 617; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.048 с.)
Главная | Случайная страница | Обратная связь