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

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


Скачать 1.34 Mb.
НазваниеGuide to Network Сетевое программирование от Биджа
Дата02.05.2019
Размер1.34 Mb.
Формат файлаpdf
Имя файлаbgnet_A4_rus.pdf
ТипGuide
#75918
страница12 из 13
1   ...   5   6   7   8   9   10   11   12   13
recv().
MSG_OOB Принять Out of Band данные. То есть принять данные посланные вам
send() с MSG_OOB флагом. Как у принимающей стороны у вас будет возбуждён сигнал SIGURG , говорящий о том, что появились срочные данные. В обработчике сигнала вы можете вызвать recv() с флагом
MSG_OOB.
MSG_PEEK Если вы хотите вызвать recv() просто для вида, можете указать этот флаг. Это покажет вам, что в буфере что-то есть для вызова
recv() по- настоящему (те. без флага MSG_PEEK.) Это что-то вроде закрытого просмотра перед последующим вызовом recv().
MSG_WAITALL Говорит recv() не возвращаться пока не будут получены все указанные в параметре len данные. Однако, в экстремальных ситуациях, вроде прерывания вызова сигналом, возникновения ошибки или закрытия соединения удалённой стороной, ваши пожелания будут проигнорированы. Не сумасбродствуйте с этим. Когда вы вызываете recv() он блокируется до появления каких-либо данных. Если вы не хотите блокировки, установите сокет в неблокируемый режим или проверьте есть ли данные с помощью select() или poll() до вызова recv() или recvfrom(). Возвращаемое значение
!
Возвращает число действительно принятых данных (что может быть меньше затребованного в параметре len), или -1 при ошибке (errno будет установлен соответственно) Если удалённая сторона закрыла соединение, то recv() вернёт 0. Это нормальный способ определения того, что удаленная сторона закрыла соединение. Нормально это хорошо, бунтарь
93

Beej's Guide to Network Пример потоковые сокеты и recv()
!
struct addrinfo hints, *res; int sockfd; char buf[512]; int byte_count;
!
// получить информацию хоста, создать сокет и подключиться memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // использовать либо IPv4 либо IPv6 hints.ai_socktype = SOCK_STREAM; getaddrinfo("www.example.com", "3490", &hints, &res); sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); connect(sockfd, res->ai_addr, res->ai_addrlen);
!
// Прекрасно Мы подключены и можем принимать данные byte_count = recv(sockfd, buf, sizeof buf, 0); printf("recv()'d %d bytes of data in buf\n", byte_count);
!
// дейтаграммные сокеты и recvfrom()
!
struct addrinfo hints, *res; int sockfd; int byte_count; socklen_t fromlen; struct sockaddr_storage addr; char buf[512]; char ipstr[INET6_ADDRSTRLEN];
!
// получить информацию хоста, создать сокет и подключиться к порту 4950 memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // использовать либо IPv4 либо IPv6 hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; getaddrinfo(NULL, "4950", &hints, &res); sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); bind(sockfd, res->ai_addr, res->ai_addrlen);
!
// accept() ненужен, только recvfrom():
!
fromlen = sizeof addr; byte_count = recvfrom(sockfd, buf, sizeof buf, 0, &addr, &fromlen);
!
printf("recv()'d %d bytes of data in buf\n", byte_count); printf("from IP address %s\n", inet_ntop(addr.ss_family, addr.ss_family == AF_INET?
((struct sockadd_in *)&addr)->sin_addr:
((struct sockadd_in6 *)&addr)->sin6_addr, ipstr, sizeof ipstr); Смотри также, sendto(), select(), poll(), Блокировка
!
94

Beej's Guide to Network Programming
9.19. Проверяет готовы ли дескрипторы сокетов к чтению/записи. Прототип
!
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
!
FD_SET(int fd, fd_set *set);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_ZERO(fd_set *set);
Описание*
Функция select() предоставляет способ одновременной проверки множества сокетов на предмет ожидания recv(), готовности к передаче данных через send() без блокирования или возникновения исключения. Вы заселяете ваш массив дескрипторов сокетов используя макросы вроде FD_SET(). Получив массив, вы передаёте его функции как один из следующих параметров readfds если хотите знать, готов ли какой-нибудь сокет из массива к recv(), writefds если какой-либо сокет готов к send(), и/или exceptfds если вам нужно узнать произошло ли на каком-нибудь сокете исключение. Любой их этих параметров может быть NULL если этот тип событий вам неинтересен. После возврата из select() значения в массиве будут изменены, чтобы показать, какие сокеты готовы к чтению/записи и какие имеют исключения. Первый параметр, n это наивысший номер дескриптора сокета (они просто int, помните) плюс один. Напоследок, struct timeval *timeout в конце позволяет сказать select() как долго проверять эти массивы. Она вернёт управление при истечении таймаута или при возникновении события, смотря что раньше. Весть два поля tv_sec это количество секунд, к которому добавляется tv_usec, количество микросекунд
(1 000 000 микросекунд в секунде) Вспомогательные макросы делают следующее
FD_SET(int fd, fd_set *set); Добавляет fd в set.
FD_CLR(int fd, fd_set *set); Удаляет fd из set.
FD_ISSET(int fd, fd_set *set); Возвращает true если fd есть в set.
FD_ZERO(fd_set *set); Очищает set. Возвращаемое значение
!
Возвращает количество дескрипторов с событиями в массиве, 0 если таймаут истёк и
-1 при ошибке (errno устанавливается соответственно) Кроме того, массивы изменяются чтобы показать готовые сокеты. Пример s1, s2, n; fd_set readfds; struct timeval tv; char buf1[256], buf2[256];
!
// полагаем, что здесь оба подключены к серверу
95

Beej's Guide to Network Programming
//s1 = socket(...);
//s2 = socket(...);
//connect(s1, ...)...
//connect(s2, …)...
!
// заранее очищаем массив
FD_ZERO(&readfds);
!
// добавляем наши дескрипторы в массив
FD_SET(s1, &readfds);
FD_SET(s2, &readfds);
!
// поскольку s2 создан вторым, он больше и его используем
// в параметре n в select() n = s2 + 1;
!
// ждём появления данных на каком-либо сокете (таймаут 10.5 секунд) tv.tv_sec = 10; tv.tv_usec = 500000; rv = select(n, &readfds, NULL, NULL, &tv);
!
if (rv == -1) { perror("select"); // в select() произошла ошибка
} else if (rv == 0) { printf("Timeout occurred! No data after 10.5 seconds.\n");
} else {
// на одном или обоих дескрипторах есть данные if (FD_ISSET(s1, &readfds)) { recv(s1, buf1, sizeof buf1, 0);
} if (FD_ISSET(s2, &readfds)) { recv(s1, buf2, sizeof buf2, 0);
}
} Смотри также

Beej's Guide to Network Programming
9.20. setsockopt(), Устанавливает для сокета различные опции. Прототип
#include
!
int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
Описание*
Сокеты это весьма конфигурируемые звери. В действительности они настолько конфигурируемые, что я даже не собираюсь здесь обо всём говорить. Возможно это зависит от системы. Ноя поговорю об основах. Заметно, что эти функции получают и устанавливают определённые опции сокета. На
Linux, вся информация по сокетам находится в man странице, секция 7. (Напечатайте
man 7 socket” , чтобы получить все эти прелести)
Насчёт параметров, s это сокет, о котором выговорите должен быть установлен в SOL_SOCKET. Затем, в optname установите интересующее вас имя. Опять же, насчёт всех опций посмотрите вашу man страницу, а здесь только несколько самых забавных Связывает сокет с символическим именем устройства вроде eth0 вместо использования bind() для привязки к IP адресу. Под Unix напечатайте команду ifconfig чтобы увидеть имена устройств. Позволяет другим сокетам связываться (bind()) с этим портом, несмотря на то, что уже существует активный сокет, слушающий этот порт. Это позволяет обойти сообщения
“Address already in use”, когда вы пытаетесь перезапустить ваш сервер после обрушения.
SO_BROADCAST Позволяет UDP дейтаграммным (SOCK_DGRAM) сокетам посылать пакеты по широковещательным адресами принимать и с них. На TCP потоковых сокетах ничего - НИЧЕГО - не делает Ха-ха-ха!
Насчёт параметра optval, обычно это указатель на int, показывающую значение запроса. Для булевых переменных, ноль это ложь и не-ноль это истинно. И это абсолютный факт, несмотря на отличия вашей системы. Если передавать нечего, то
optval может быть NULL. Последний параметр, optlen, заполняется getsockopt() ивам нужно указать его для setsockopt(), возможно он будет sizeof(int). Предупреждение на некоторых системах (особенно Sun и Windows), эта опция может быть char вместо int, и содержать, например, символьное значение 'вместо целого 1. Опять же, подробности смотрите в ваших man страницах командами “man setsockopt” и
man 7 socket”!
!
!
97

Beej's Guide to Network Возвращаемое значение
!
Возвращает 0 при успехе или -1 в случае ошибки (errno устанавливается соответственно. Пример optval; int optlen; char *optval2;
!
// установить SO_REUSEADDR на сокете в ИСТИННО (1): optval = 1; setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
!
// связать сокет с именем устройства (может не работать на некоторых системах optval2 = "eth1"; // 4 байта длины, итого ниже setsockopt(s2, SOL_SOCKET, SO_BINDTODEVICE, optval2, 4);
!
// посмотреть установлен ли флаг SO_BROADCAST: getsockopt(s3, SOL_SOCKET, SO_BROADCAST, &optval, &optlen); if (optval != 0) { print("SO_BROADCAST enabled on s3!\n");
} Смотри также

Beej's Guide to Network Programming
9.21. send(), Посылают данные через сокет. Прототип
#include
!
ssize_t send(int s, const void *buf, size_t len, int flags); ssize_t sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
Описание*
Эти функции посылают данные в сокет. В общем случае send() используется для TCP
SOCK_STREAM под ключ н н ы х соке тов, ад ля неподключённых дейтаграммных сокетов. Каждый раз посылая пакет по неподключённому сокету вы должны указывать место назначения, поэтому последние параметры sendto() задают куда пакет направляется. В обоих, send() и sendto(), параметр s это сокет, buf указатель на данные, которые вы хотите послать, len число посылаемых байт и flags позволяет определить дополнительную информацию как посылать данные. Установите flags в ноль, если хотите иметь нормальные данные. Вот несколько из обычно используемых флагов, но за подробностями загляните в ваши местные man страницы по send():
MSG_OOB Посылает “out of band” данные. TCP поддерживает этот способ сказать принимающей стороне, что у этих данных приоритет выше, чему нормальных. Приёмник получит сигнал SIGURG и примет эти данные без предварительной выборки нормальных данных из очереди.
MSG_DONTROUTE Не посылать эти данные через маршрутизатор, они местные.
MSG_DONTWAIT Если send() должна блокироваться из-за загруженности внешнего трафика, она вернёт EAGAIN. Это вроде Разрешить не-блокирование для этой посылки. Детали описаны в разделе по блокированию.
MSG_NOSIGNAL
send() на удалённый хост, который больше не принимает (recv()) данные, обычно возбуждает сигнал SIGPIPE. Этот флаг предотвращает возбуждение такого сигнала. Возвращаемое значение
!
Возвращает число действительно посланных байт или -1 при ошибке (errno устанавливается соответственно) Заметьте, что это число может быть меньше затребованного Вспомогательная функция в разделе по send() поможет обойти это. Также, если сокет был закрыт на противной стороне, процесс, вызвавший send(), получит сигнал SIGPIPE. (Если только send() не был вызван с флагом MSG_NOSIGNAL.) Пример spatula_count = 3490; char *secret_message = "The Cheese is in The Toaster”;
!
int stream_socket, dgram_socket; struct sockaddr_in dest; int temp;
!
99

Beej's Guide to Network Programming
// сначала с потоковым сокетом TCP:
!
// полагаем, что сокеты созданы и подключены
//stream_socket = socket(...
//connect(stream_socket, …
// преобразовать в порядок байтов сети temp = htonl(spatula_count);
!
// послать данные нормально send(stream_socket, &temp, sizeof temp, 0);
!
// послать секретное out of band сообщение send(stream_socket, secret_message, strlen(secret_message)+1, MSG_OOB);
!
// теперь с дейтаграммным сокетом UDP:
//getaddrinfo(...
//dest = ... // полагаем, что "dest" содержит адрес назначения
//dgram_socket = socket(…
!
// послать секретное послание нормально sendto(dgram_socket, secret_message, strlen(secret_message)+1, 0,
(struct sockaddr*)&dest, sizeof dest); Смотри также, recvfrom()!

!
100

Beej's Guide to Network Programming
9.22. Останавливает обмен по сокету. Прототип
!
int shutdown(int s, int how);
Описание*
Вот так Получилось На этом сокете send() больше не разрешены, но хочу всё ещё принимать (recv()) с него данные Или наоборот Как это сделать Когда вы закрываете (close()) дескриптор сокета закрываются обе стороны, для чтения и для записи, а дескриптор освобождается. Если вы хотите закрыть только одну или другую сторону, вам нужно вызвать shutdown().
Насчёт параметров, понятно, что s это сокет с которым выработаете, а что с ним делать определяет параметр how. Это может быть SHUT_RD для предотвращения дальнейших recv(), SHUT_WR для запрещения дальнейших send(), или SHUT_RDWR для обоих. Заметьте, что shutdown() не освобождает дескриптор сокета ив итоге вам нужно вызвать close() чтобы закрыть его полностью. Этот системный вызов используется редко. Возвращаемое значение
!
Возвращает 0 при успехе или -1 в случае ошибки (errno устанавливается соответственно. Пример s = socket(PF_INET, SOCK_STREAM, 0);
!
// посылаем и обрабатываем здесь и когда всё сделано запрещаем дальнейшие посылки shutdown(s, SHUT_WR); Смотри также

Beej's Guide to Network Programming
9.23. socket()!
Создаёт дескриптор сокета Прототип
#include
!
int socket(int domain, int type, int protocol);
Описание*
Возвращает дескриптор сокета, с которым вы можете творить всему присущее. Обычно это первый шаг в этом ужасном процессе написания программ с сокетами и результат можно использовать в последующих вызовах listen(), bind(), accept() или множества других функций. Обычно вы получаете значения этих параметров из вызова getaddrinfo(), как в примере ниже, но можете и заполнять их вручную, если вам так уж хочется. определяет тип нужного вам сокета. Поверьте, их может быть множество, но поскольку это руководство по сокетам, он будет для IPv4 и PF_INET6 для IPv6.
type Хотя параметр type может принимать множество значений, вы установите его в SOCK_STREAM для надёжных TCP сокетов (send(), recv()) либо
SOCK_DGRAM для ненадёжных быстрых UDP сокетов (sendto(),
recvfrom().) Другой интересный тип сокетов это SOCK_RAW, который позволяет строить пакеты вручную. Это очень круто)
protocol Последнее, параметр protocol указывает какой протокол использовать для этого типа сокетов. Как я уже сказал, например, SOCK_STREAM использует TCP. К счастью для вас, используя SOCK_STREAM или
SOCK_DGRAM, вы можете просто установить protocol в 0, ион автоматически использует правильный протокол. Иначе вы можете использовать getprotobyname() для выбора номера нужного протокола. Возвращаемое значение!
Дескриптор нового сокета для последующих вызовов или -1 при ошибке (и errno будет установлен соответственно) Пример addrinfo hints, *res; int sockfd;
!
// сначала заполняем адресные структуры с помощью getaddrinfo():
!
memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC;
// AF_INET, AF_INET6, или AF_UNSPEC hints.ai_socktype = SOCK_STREAM;
// SOCK_STREAM или SOCK_DGRAM getaddrinfo("www.example.com", "3490", &hints, &res);
!
// создаём сокет с помощью информации, которую наскребла getaddrinfo(): sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); Смотри также, bind(), getaddrinfo(), listen()!
102

Beej's Guide to Network Programming
9.24. struct sockaddr сотоварищи!
Структуры для обработки интернет адресов. Прототип
!
// Все указатели на адресные структуры сокетов часто приводятся
// к этому типу перед их использованием в различных функциях и вызовах
!
struct sockaddr {
unsigned short sa_family;
// семейство адресов, AF_xxx char sa_data[14]; // 14 байт адреса протокола
};
!
// IPv4 AF_INET сокеты:
!
struct sockaddr_in { short sin_family;
// например, AF_INET, AF_INET6 unsigned short sin_port;
// например, htons(3490) struct in_addr sin_addr;
// смотри struct in_addr, ниже char sin_zero[8]
// обнулите, если хочется
};
!
struct in_addr { unsigned long s_addr;
// заполнить с помощью inet_pton()
};
!!
// IPv6 AF_INET6 сокеты:
!
struct sockaddr_in6 { u_int16_t sin6_family;
// семейство адресов, AF_INET6
u_int16_t sin6_port;
// номер порта, Порядок Байтов Сети u_int32_t sin6_flowinfo;
// IPv6 flow information struct in6_addr sin6_addr;
// IPv6 адрес u_int32_t sin6_scope_id;
// Scope ID
};
!
struct in6_addr {
unsigned char s6_addr[16];
// заполнить с помощью inet_pton()
};
!
// Общая структура хранения адреса сокета достаточно велика для хранения
// данных struct sockaddr_in или struct sockaddr_in6:
!
struct sockaddr_storage {
sa_family_t ss_family;
// семейство адресов
!
// всё это расширение зависит от реализации, проигнорируйте
!
char __ss_pad1[_SS_PAD1SIZE]; int64_t __ss_align; char __ss_pad2[_SS_PAD2SIZE];
};
!
!
!
1   ...   5   6   7   8   9   10   11   12   13


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