- •Сетевое программирование
- •Материал этих методических указаний предпологает использование языка cи, возможностей ос Linux, cистемных вызовов этой операционной системы, и ее библиотек.
- •Краткие теоретические сведения.
- •1.1 Создание socket'а
- •1.2 Связывание socket'а
- •1.3 Ожидание установления связи
- •1.4 Запрос на установление соединения
- •1.5 Прием запроса на установление связи
- •1.6 Формирование адреса узла сети
- •1.7 Функции обмена данными
- •1.8 Посылка данных
- •Получение данных
- •Функции закрытия связи
- •Цикл лабораторных работ «Сетевое программирование на базе
- •Примеры использования socket-интерфейса для организации клиент – серверного взаимодействия
- •Программа-сервер
- •Программа-клиент
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
- •Возвращаемое значение
- •Прототип
- •Описание
Описание
Как только сокет создан и подключен, можно принимать из него данные вызовами recv() (для TCP SOCK_STREAM сокетов) и recvfrom() (для UDP SOCK_DGRAM сокетов).
Обе функции принимают дескриптор сокета s, указатель на буфер buf, длину буфера в байтах len, и набор флагов flags, определяющих работу функций.
Дополнительно, recvfrom() принимает struct sockaddr* from, указывающую откуда принимать данные и запишет в fromlen размер struct sockaddr. (Можно тоже инициализировать fromlen размером from или struct sockaddr.)
Возвращаемое значение
Возвращает число действительно принятых данных (что может быть меньше затребованного в параметре len), или -1 при ошибке (errno будет установлен соответственно.)
Если удалённая сторона закрыла соединение, то recv() вернёт 0. Это нормальный способ определения того, что удаленная сторона закрыла соединение.
Пример
/ потоковые сокеты и 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);
select()
Проверяет готовы ли дескрипторы сокетов к чтению - записи.
Прототип
#include <sys/select.h>
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() как долго проверять эти массивы. Она вернёт управление при истечении таймаута или при возникновении события, смотря что раньше. В struct timeval есть два поля: tv_sec это количество секунд, к которому добавляется tv_usec, количество микросекунд
(1 000 000 микросекунд в секунде.) Вспомогательные макросы делают следующее:
