Главная страница

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


Скачать 1.34 Mb.
НазваниеGuide to Network Сетевое программирование от Биджа
Дата02.05.2019
Размер1.34 Mb.
Формат файлаpdf
Имя файлаbgnet_A4_rus.pdf
ТипGuide
#75918
страница10 из 13
1   ...   5   6   7   8   9   10   11   12   13
errno устанавливается соответственно. Пример = socket(PF_INET, SOCK_DGRAM, 0);
// куча всего close(s); // действительно, не очень много. Смотри также, shutdown()
!
!
72

Beej's Guide to Network Programming
9.5. getaddrinfo(), freeaddrinfo(), Получает информацию об имени хоста и/или сервисе и записывает результат в struct sockaddr. Прототип
#include
#include
!
int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
!
void freeaddrinfo(struct addrinfo *ai);
!
const char *gai_strerror(int ecode);
!
struct addrinfo { int ai_flags;
// AI_PASSIVE, AI_CANONNAME, ... int ai_family;
// AF_xxx int ai_socktype;
// SOCK_xxx int ai_protocol;
// 0 (авто) или IPPROTO_TCP, IPPROTO_UDP
!
socklen_t ai_addrlen;
// длина ai_addr char *ai_canonname;
// каноническое имя struct sockaddr *ai_addr;
// двоичный адрес struct addrinfo *ai_next;
// следующая структура в связанном списке
}; Описание это превосходная функция, которая возвращает информацию об имени отдельного хоста (такую как IP адрес) и заполняет struct sockaddr, заботится о мелких деталях (типа это IPv4 или IPv6.) Она заменяет старые функции и getservbyname(). Описание ниже содержит много информации, которая, возможно, немного устрашает, но реальное использование достаточно просто. Может быть стоит сначала посмотреть примеры. Имя интересующего вас хоста передаётся в параметре nodename. Адрес может быть именем хоста, как “www.example.com”, либо IPv4 или IPv6 адрес (передаваемый как строка. Этот параметр также может быть если вы используете флаг см. ниже) Обычно параметр это номер порта. Он может быть номером (передаваемый строкой, как “80”), или он может быть именем сервиса, как “http” или “tftp” или “smtp” или “pop”, и т.д. Распространённые имена сервисов можно найти вили в вашем системном файле /etc/services. Напоследок, во входных параметрах есть hints. Именно здесь вы определяете что функции нужно делать. Перед использование обнулите всю структуру целиком функцией memset(). Давайте взглянем на поля, которые вам нужно заполнить до использования. Поле ai_flags может содержать множество флагов, вот два из них. (Много флагов указывается поразрядным ИЛИ оператором “
|
”). Полный список флагов приведён в вашей man странице.
73
http://www.iana.org/assignments/port-­‐numbers
43

Beej's Guide to Network заставляет записать в поле ai_canonname результата каноническое настоящее) имя хоста. приводит к записи в IP адрес INADDR_ANY
(IPv4) или in6addr_any
(IPv6); из-за этого последует вызов чтобы автоматически записать в IP адрес структуры struct sockaddr адрес текущего хоста. Это превосходно для запуска сервера если вы не хотите использовать постоянно установленный адрес. Если вы используете флаг AI_PASSIVE, тов можно указать NULL поскольку заполнит его позднее) Продолжаем с входными параметрами. Вам лучше всего установить в чтобы искала и IPv4 и IPv6 адреса. Хотя вы можете ограничить себя одним или другим, установив или AF_INET6. Следующее, в поле нужно установить или SOCK_DGRAM, в зависимости оттого, какой тип сокета вам нужен. Наконец, просто оставьте в ai_protocol
0 чтобы автоматически выбрать тип вашего протокола. Теперь, когда всё установки сделаны, вы наконец-то можете вызвать getaddrinfo()! Конечно, здесь веселье только начинается. res теперь указывает на связаный список struct addrinfo ивы можете пройтись по нему, посмотреть адреса, удовлетворяющие тому, что вы передали в hints. Теперь стало возможным определить какие из адресов по той или иной причине не работают, и, как в Linux man странице, циклически пройти по нему вызывая и или если вы запустили сервер с флагом AI_PASSIVE) до самого конца. Наконец, когда со связанным списком покончено, вам нужно вызвать
freeaddrinfo() чтобы освободить память, иначе она затеряется (утечёт) и Кое-Кто расстроится. Возвращаемое значение
!
При успехе возвращает ноль или не-ноль при ошибке. В таком случае вы можете воспользоваться функцией gai_strerror() чтобы получить печатную версию кода возврата. Пример код для подключения клиента к серверу,
// а именно потокового сокета к www.example.com напортили hints.ai_family = AF_UNSPEC;
// для задания IPv6 используйте AF_INET6 hints.ai_socktype = SOCK_STREAM;
!
if ((rv = getaddrinfo("www.example.com", "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) {
74

Beej's Guide to Network Programming
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);
// со структурой закончили Смотри также, getnameinfo()

75

Beej's Guide to Network Programming
9.6. Возвращает имя системы. Прототип
!
int gethostname(char *name, size_t len);
Описание*
У вашей системы есть имя. У всех оно есть. Это даже несколько более Unix-овая вещь, чем все остальные сетевые причиндалы, о которых мы говорили, но она до сих пор используется. Например, вы можете получить имя вашего хоста и затем вызвать gethostbyname(), чтобы получить ваш IP адрес. Параметр name должен указывать на буфер, который будет содержать имя хоста, а len это размер этого буфера в байтах. gethostname() не выйдет за пределы этого буфера он может возвратить ошибку или просто остановить запись) и вернёт строку св конце если в буфере хватило места для строки. Возвращаемое значение
!
Возвращает 0 при успехе или -1 в случае ошибки (errno устанавливается соответственно. Пример hostname[128];
!
gethostname(hostname, sizeof hostname); printf("My hostname: %s\n", hostname); Смотри также
!
76

Beej's Guide to Network Programming
9.7. gethostbyname(), Получают IP адрес хоста по имени или наоборот. Прототип
#include
!
struct hostent *gethostbyname(const char *name); // УСТАРЕЛО struct hostent *gethostbyaddr(const char *addr, int len, int type);
Описание*
ЗАМЕТЬТЕ ПОЖАЛУЙСТА эти две функции заменены на и
getnameinfo()! В частности, gethostbyname() не очень хорошо работает с IPv6. Эти функции выполняют преобразование имён хостов в IP адреса и обратно. Например, если у вас есть, вы можете использовать
gethostbyname() чтобы получить IP адрес и сохранить его в struct in_addr. И наооборот, если у вас есть struct in_addr или struct in6_addr, можете воспользоваться gethostbyaddr() чтобы получить назад имя хоста. gethostbyaddr() совместима с, но пользоваться нужно getnameinfo() поновее и поярче. Если у вас есть строка с IP адресом в формате цифр-и-точек для которой вы хотите узнать имя хоста, то вам лучше воспользоваться getaddrinfo() с флагом
AI_CANONNAME.)
gethostbyname() принимает строку вроде “www.yahoo.com” и возвращает struct hostent, которая содержит тонны информации, включая адрес. (Другая информация это официальное имя хоста, список псевдонимов, тип адреса и список адресов. Это структура общего назначения ивы увидели как очень просто использовать её в наших особых целях)
gethostbyaddr() принимает struct in_addr или struct in6_addr и выдаёт соответствующее имя хоста (если оно есть, так что это функция, обратная
gethostbyname(). Насчёт параметров, даже хотя addr
меет тип char*, в действительности вам надо передавать указатель на struct in_addr. должна быть sizeof(struct in_addr), и должен быть AF_INET. Что это за struct hostent, которую нам возвращают Она содержит множество полей, содержащих информацию о запрошенном хосте.
char *h_name Настоящее каноническое имя хоста
char **h_aliases Список псевдонимов, к нему можно обращаться, как к массиву, последний элемент содержит NULL
int h_addrtype Тип адреса результата, в нашем случае должен быть AF_INET
int length Длина адресов в байтах (4 для IPv4)
char **h_addr_list Список IP адресов этого хоста. Хоть они, в действительности это массив переодетых struct in_addr*. Последний элемент массива равен NULL.
h_addr Псевдоним h_addr_list[0]. Если вам нужен любой старый
IP адрес этого хоста (да, у них может быть несколько) используйте это поле.
!
77

Beej's Guide to Network Возвращаемое значение
!
Возвращает указатель на получившуюся struct hostent или NULL
при ошибке
Вместо нормальной perror() и всего прилагающегося для выдачи сообщений об ошибке, эти функции пишут результат в переменную h_errno, которую можно распечатать функциями herror() или hstrerror(). Это подобно классической errno,
perror(), и strerror(). Пример ЭТО УСТАРЕВШИЙ МЕТОД ПОЛУЧЕНИЯ ИМЁН ХОСТА
// взамен используйте getaddrinfo()!
!
#include
#include
#include
#include
#include
#include
#include
!
int main(int argc, char *argv[])
{ int i; struct hostent *he; struct in_addr **addr_list;
!
if (argc != 2) { fprintf(stderr,"usage: ghbn hostname\n"); return 1;
}
!
if ((he = gethostbyname(argv[1])) == NULL) { // получить информацию хоста herror("gethostbyname"); return 2;
}
!
// распечатать информацию об этом хосте: printf("Official name is: %s\n", he->h_name); printf(" IP addresses: "); addr_list = (struct in_addr **)he->h_addr_list; for(i = 0; addr_list[i] != NULL; i++) { printf("%s ", inet_ntoa(*addr_list[i]));
} printf(“\n"); return 0;
}
!
// ЭТО ЗАМЕНЕНО
// взамен используйте getnameinfo()!
!
struct hostent *he; struct in_addr ipv4addr; struct in6_addr ipv6addr;
!
inet_pton(AF_INET, "192.0.2.34", &ipv4addr); he = gethostbyaddr(&ipv4addr, sizeof ipv4addr, AF_INET); printf("Host name: %s\n", he->h_name);
!
inet_pton(AF_INET6, "2001:db8:63b3:1::beef", &ipv6addr); he = gethostbyaddr(&ipv6addr, sizeof ipv6addr, AF_INET6);
78

Beej's Guide to Network Programming
printf("Host name: %s\n", he->h_name); Смотри также, getnameinfo(), gethostname(), errno, perror(),
strerror(), struct in_addr!
!
79

Beej's Guide to Network Programming
9.8. Ищет информацию об имени хоста и сервиса по заданной struct sockaddr. Прототип
#include
!
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);
Описание*
Эта функция противоположна getaddrinfo(), что означает, она принимает уже заполненную struct sockaddr и по ней выполняет поиск имён хоста и сервиса. Она заменяет старые функции gethostbyaddr() и getservbyport(). Вам надо передать указатель на struct которая в действительности приведённая вами struct sockaddr_in либо struct sockaddr_in6) в параметре sa и длину структуры в salen. Имена хоста и сервиса в результате будут записаны в области, указанные параметрами и serv. Конечно, максимальную длину этих буферов нужно указать в и
servlen. Последнее, вы можете передать несколько флагов, но есть парочка хороших. При установленном NI_NOFQDN
host будет содержать только имя хоста, а неполное имя домена. NI_NAMEREQD вызовет отказ функции, если имя не будет найдено DNS поиском если флаг не указан и имя не найдено, getnameinfo() заполнит host строковой версией IP адреса) Как всегда, полный обзор ищите в ваших локальных man страницах. Возвращаемое значение
!
При успехе возвращает ноль и не-ноль при ошибке. Ненулевое значение можно передать gai_strerror(), чтобы получить читаемую строку. (Смотри getaddrinfo.) Пример sockaddr_in6 sa; // если хотите может быть IPv4 char host[1024]; char service[20];
!
// положим, что sa наполнена доброй информацией о хосте и порте, sizeof sa, host, sizeof host, service, sizeof service, 0);
!
printf(" host: %s\n", host);
// например, "www.example.com" printf("service: %s\n", service);
// например, "http" Смотри также, gethostbyaddr()!
!
80

Beej's Guide to Network Programming
9.9. Возвращает адресную информацию об удалённой стороне соединения. Прототип lude int getpeername(int s, struct sockaddr *addr, socklen_t *len);
Описание*
Как только вы приняли (accept()) удалённое соединение или подключились
(connect()) к серверу, у вас появляется некто, известный как ровня (пир,
peer
). Ваш пир это просто компьютер, к которому вы подключились через IP адрес и порт. Итак.
getpeername() просто возвращает, заполненную информацией о подключённой машине. Почему она зовётся именем (name)? Ну, поскольку существует много типов сокетов, а не только Интернет Сокеты, которые мы рассматриваем в данном руководстве, то имя прекрасный общий термин для всех случаев. Хотя в нашем случае имя пира это IP адрес и порт. Пусть функция возвращает размер полученного адреса в len, но вам нужно предварительно записать в длину addr. Возвращаемое значение
!
Возвращает 0 при успехе или -1 в случае ошибки (errno устанавливается соответственно. Пример подразумеваем, что s это подключённый сокет
!
socklen_t len; struct sockaddr_storage addr; char ipstr[INET6_ADDRSTRLEN]; int port;
!
len = sizeof addr; getpeername(s, (struct sockaddr*)&addr, &len);
!
// работаем с обоими IPv4 и IPv6: if (addr.ss_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)&addr; port = ntohs(s->sin_port); inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
} else {
// AF_INET6 struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; port = ntohs(s->sin6_port); inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
}
!
printf("Peer IP address: %s\n", ipstr); printf("Peer port : %d\n", port); Смотри также, gethostbyname(), gethostbyaddr()!
!
81

Beej's Guide to Network Programming
9.10. Содержит код ошибки последнего системного вызова. Прототип
!
int errno;
Описание*
Эта переменная содержит информацию об ошибках для множества системных вызовов. Если при вызове, например, socket() или listen() происходит ошибка, то возвращается
-1 ив устанавливается код, позволяющий точно определить, что случилось. В заголовочном файле errno.h перечислены все символические имена ошибок, как
EADDRINUSE, EPIPE, ECONNREFUSED и т.д. Ваши местные man страницы скажут какие коды могут быть возвращены как ошибки, ивы сможете использовать их вовремя исполнения, чтобы обрабатывать разные ошибки по разному. Или, как правило, вы вызываете perror() или strerror() чтобы получить читаемую версию ошибки.
Ещё одно замечание для энтузиастов многопоточности, в большинстве систем errno определена потокобезопасным способом. (То есть, в действительности она не глобальная переменная, но ведёт себя так, как должна вести себя глобальная переменная в однопотоковой среде) Возвращаемое значение
!
Значение переменной это код последней произошедшей ошибки, но может означать успех если последнее действие завершилось удачно. Примерили используйте strerror()
}
!
tryagain: if (select(n, &readfds, NULL, NULL) == -1) {
// ошибочка вышла
!
// если мы просто прерваны, просто перезапуск вызовом select(): if (errno == EINTR) goto tryagain; // AAAA! goto!!!
!
// иначе это ошибка посерьёзней: perror("select"); exit(1);
} Смотри также, strerror()!
!
82

Beej's Guide to Network Programming
9.11. Управляет дескрипторами сокетов. Прототип
#include
!
int fcntl(int s, int cmd, long arg);
Описание*
Обычно эта функция используется для блокировки файла и других файл- ориентированных вещей, ноу неё есть пара относящихся к сокетам функций, которые вы можете посмотреть или время от времени использовать. Параметр s это дескриптор сокета, с которым вы хотите работать, cmd должен быть установлен в F_SETFL и может быть одной из следующих команд. (Как я сказал, здесь больше
1   ...   5   6   7   8   9   10   11   12   13


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