Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Информационные сети и коммуникации. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
1 Мб
Скачать

while(total < len)

{

n = send(s, buf+total, len-total, flags); if(n == -1) { break; }

total += n;

}

If(n==-1) return -1; else return total;

}

Получение данных

int recv(int sockfd, void *buf, int len, int flags);

Возврат: -1, если ошибка Возврат: 0, если соединение завершено

Возврат: количество прочитанных байт

Особенность recv – программа «останавливается» на этом операторе и ожидает получения «len» байт из сокет.

while(1)

{

bytes_read = recv(sock, buf, 1024, 0); if(bytes_read <= 0) break; send(sock, buf, bytes_read, 0);

}

В случае работы в режиме «сырого сокета» для обмена данными используется

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

Реализация сокета, соответствующего стандарту IPv6, потребует изменений:

Структура sockaddr превращается в sockaddr_in6:

31

struct in6_addr { union {

uint8_t u6_addr8[16]; uint16_t u6_addr16[8]; uint32_t u6_addr32[4];

} in6_u;

 

#define s6_addr

in6_u.u6_addr8

#define s6_addr16

in6_u.u6_addr16

#define s6_addr32

in6_u.u6_addr32

};

 

struct sockaddr_in6 {

 

sa_family_t sin6_family;

/* AF_INET6 */

in_port_t sin6_port;

/* Порт транспортного уровня # */

uint32_t sin6_flowinfo;

/* IPv6 flow information */

struct in6_addr sin6_addr; /* IPv6 address */

uint32_t sin6_scope_id;

/* IPv6 scope-id */

};

 

Фактически при разработке кода можно подменить все элементы структур, характерных для IPv4 на IPv6. Однако для разработки совместимых на уровне кода приложений в будущем рекомендуется использовать универсальную структуру sockaddr_storage,

/* Structure large enough to hold any socket address (with the historical exception of

AF_UNIX). 128 bytes reserved. */

#if ULONG_MAX > 0xffffffff

#define __ss_aligntype __uint64_t #else

#define __ss_aligntype __uint32_t #endif

#define _SS_SIZE

128

#define _SS_PADSIZE

(_SS_SIZE - (2 * sizeof (__ss_aligntype)))

32

struct sockaddr_storage

 

{

 

sa_family_t ss_family;

/* Address family */

__ss_aligntype __ss_align; /* Force desired alignment. */ char __ss_padding[_SS_PADSIZE];

};

ПРАКТИЧЕСКАЯ РАБОТА

Создайте файл с текстом программы на Си в любом текстовом редакторе, выполняющий действия в соответствии с вариантом.

Откомпилируйте программу при помощи компилятора GNU

GCC:

gcc ./yourprog.c -o ./yourprog.bin

Выполните отладку программы.

Запротоколируйте сетевойобмен (tcpdump) и приведите его вотчет.

В процессе выполнения работы пользуйтесь справкой man <функция>.

В случае досрочного выполнения задания рекомендуется незамедлительно переходить к выполнению следуюшей темы.

ВАРИАНТЫ ЗАДАНИЙ

Для всех вариантов разработать программу, которая создаст UDPсокет и отправит данные на IP-адрес, указанный преподавателем, следующего содержания:

0-й байт: фиксирован и равен 129;

1-й байт: день месяца;

2-й байт: месяц;

3-й байт: номер вашей бригады; 4-й – 40-й байт: латинскими буквами фамилия первого члена бри-

гады (&buf[6]),

41-й – 60-й байт: латинскими буквами фамилия второго члена бригады;

61-й – 80-й байт: латинскими буквами фамилия третьего члена бригады;

81-й байт: сумма по «исключающему или» всех байт посылки.

33

В ответ сервер преподавателя пришлет посылку с паролем и кодом согласно листингу в приложении.

Необходимо получить пароль двумя методами:

с помощью чтения из сокета;

с помощью команды tcpdump.

• Для всех вариантов модифицировать программу для работы в сети IPv6.

Передайте преподавателю:

Текст программы на языке Си.

Байты пакета, который будет отправлен на компьютер преподавателя.

Байты пакета, который был получен в ответ.

Значение кодового слова – ответа.

Для выполнения работы потребуется произвольная версия операционной системы Линукс. Автор настоящего учебного пособия рекомендует использовать для этой цели версию Ubuntu с длительным сроком поддержки. Компиляцию программы следует производить при помощи компилятора gcc, который обычно входит в базовую поставку операционной системы. Компьютер преподавателя с установленной и запущенной на нем программой, размещенной в приложении, должен быть подключен к сети и находиться в пределах одного физического сегмента с компьютерами студентов.

КОНТРОЛЬНЫЕ ВОПРОСЫ

1.Объясните назначение полей структуры sockadd.

2.Какую функцию выполняет вызов listen()?

3.Какую функцию выполняет вызов bind()?

4.Нужно ли выполнять bind для сокета «активной» стороны (сторона клиента)?

5.Какие изменения потребуются для реализации вашей программы для сети IPv6?

34

5. РАЗРАБОТКА ПРИЛОЖЕНИЯ СЕТЕВОГО СЕРВЕРА НА ЯЗЫКЕ СИ

КРАТКИЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ

Ранее вы приобрели базовые навыки разработки сокет-приложений, работающих в режиме клиента. Здесь пойдет речь уже о несколько более сложной конструкции – системе, работающей в режиме сервера. Сетевое приложение, работающее в режиме сервера, является пассивной стороной, т. е. стороной, ожидающей соединения. Рассмотрим процесс установки соединения.

Стороной, инициирующей подключение, является сторона клиента. При этом программное обеспечение на стороне сервера находится в режиме ожидания соединений, т. е. в состоянии, когда сокет привязан (bind) и готов работать с входящими подключениями (accept).

Для установки соединения клиенту и серверу необходимо обменяться пакетами с установленными флагами SYN и ACK. Обрыв соединения без завершающих «рукопожатий» приводит к возникновению так называемых «зомби» – сокет, гнезд, которые не принадлежат ни одному процессу. Задача операционной системы – своевременно обнаруживать и разрешать подобные ситуации, освобождая ресурсы.

Вызывающая сторона отправляет запрос на подключение путем вызова функции connect. Следует обратить внимание, что в случае, если bind на стороне сервера не был корректно закрыт, то он остается в состоянии «зомби» в течение ориентировочно 40…120 с. Для того чтобы повторно выполнить привязку к сокету, который находится в состоянии «зомби», придетсядождаться его освобождения.

Возможность обработки параллельно нескольких подключений реализуют тремя способами: путем организации нескольких параллельных нитей (thread), оператора select и путем перебора значений в цикле. В процессе выполнения работы необходимо воспользоваться вторым или третьим способом.

Выполнить инициализацию объекта «Сокет»

#include <sys/types.h> #include <sys/socket.h>

int socket(int domain, int type, int protocol);

35

Domain – пространство имен и протоколов:

IPV4 IPV6 loopback.

Наиболее часто используются:

AF_UNIX(AF_LOCAL) – ФС ос unix- «локальный сокет» – обмен данными через специальный объект ФС

AF_INET – обмен данными через сетевой стек (в том числе через loopback)

AF_PACKET – низкоуровневый обмен

Выполнить объявление сокета

int socket(int domain, int type, int protocol);

Type – указывает способ обмена данными по сети. SOCK_STREAM – протокол TCP, требуется установка соединения.

Может быть использован, только если взаимодействующая сторона SOCK_STREAM с назначаемым IP адресом

SOCK_DGRAM – протокол UDP, не требует соединения, допускает multicast, brodcast, network адреса.

SOCK_RAW – низкоуровневое формирование ip. Возможна обработка icmp.

Параметры объединяются через ||

SOCK_NONBLOCK – неблокирующий сокет – точка для обслуживания нескольких клиентов из одного треда.

Заполнить структуру с адресом и выполнить привязку сокета к IP-адресу и порту

#include <sys/types.h> #include <sys/socket.h>

int bind(int sockfd, struct sockaddr *addr, int addrlen);

Sockfd – дескриптор сокета, то, что вернула команда socket на предыдущем шаге

struct sockaddr {

unsigned short sa_family; // Семейство адресов, AF_xxx

36

char

sa_data[14];

// 14 байт для хранения адреса

};

 

 

 

sa_family –

идентификатор домена, аналогичный первому пара-

метру функции socket.

 

Адрес sa_data -> указатель на структуру

struct sockaddr_in {

 

short int

 

sin_family;

// Семейство адресов

unsigned short int sin_port; // Номер порта

struct in_addr

sin_addr;

// IP-адрес

unsigned char

sin_zero[8]; // "Дополнение" до размера струк-

туры sockaddr

 

 

 

};

 

 

 

Обратите внимание, что адрес перевернут младшим «вперед», struct in_addr {

unsigned long s_addr;

};

Таким образом, потребуется преобразование порядка следования байт в словах.

Существует два формата представления адреса: порядок «хоста»

(host byte order) и «сетевой» (net byte order), отличающихся порядком следования старших/младших байт в слове. В сокет API используется сетевой порядок следования, значит, требуется преобразование.

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);

Для создания серверного сокета потребуется инициализировать очередь запросов на соединение. Сокет прослушивает порт и ожидает нового соединения:

int listen(int sockfd, int sizeoflisten);

sockfd – знакомый нам дескриптор сокета;

37

int sizeoflisten – длина очереди. 1 – всего один клиент сможет подключиться параллельно к сокету;

0 – все в порядке – новое соединение готово принимать данные. –1 – произошла ошибка.

После инициализации сервер готов обслужить сокет – при помощи функция accept

#include <sys/socket.h>

int accept(int sockfd, void *addr, int *addrlen);

В структуру void *addr будет записан адрес клиента, который установил соединение.

int accept(int sockfd, null, null); – нам совершенно не интересно, «кто». Возврат – дескриптор, с которым можно делать send и receive

или –1

На стороне клиентского сокета потребуется:

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

где все структуры и параметры нам уже знакомы. Возврат – дескрип-

тор для read и wrire или –1.

Если не связывать по bind, клиентский сокет автоматически получит номер из пространства 1024-65535. Однако можно привязать и его, если это необходимо.

ОБМЕН ДАННЫМИ С СОКЕТАМИ

Отправка данных

int send(int sockfd, const void *msg, int len, int flags);

flags – манипулирование флагами пакета (например, TOS). Может быть NULL.

Возврат – количество байт, или –1.

Функция send отправит только то количество байт, которое сможет, и совершенно не обязана отправить весь буфер одной посылкой. Возврат 0 не является ошибкой.

38

Пример функции, которая корректно отправляет данные в сокет: int sendallbuf(int s, char *buf, int len, int flags)

{

int total = 0; int n;

while(total < len)

{

n = send(s, buf+total, len-total, flags); if(n == -1) { break; }

total += n;

}

if(n==-1) return -1; else return total;

}

Получение данных

int recv(int sockfd, void *buf, int len, int flags);

Возврат: –1 если ошибка; 0, если соединение завершено;

количество прочитанных байт.

Особенность recv – программа «останавливается» на этом операторе и ожидает получения «len» байт из сокет.

while(1)

{

bytes_read = recv(sock, buf, 1024, 0); if(bytes_read <= 0) break; send(sock, buf, bytes_read, 0);

}

В случае работы в режиме «сырого сокета» для обмена данными используется:

39

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

ПРАКТИЧЕСКАЯ РАБОТА

Изучите обмен данными с сервисом «HTTP-сервер» путем соединения терминалом telnet. Протоколировать результат в отчет. Запросить гипертекстовый файл, двоичный файл, изображение.

Изучите обмен данными с сервисом «FTP-сервер» путем соединения терминалом telnet.

Модифицируйте программу для работы в сетях IPv6.

Создайте файл с текстом программы на Си в любом текстовом редакторе, выполняющий действия в соответствии с вариантом.

Откомпилируйте программу при помощи компилятора GNU GCC: gcc ./yourprog.c -o ./yourprog.bin.

Выполните отладку программы.

Запротоколируйте сетевой обмен (tcpdump) и приведите его.

Прервите процесс обмена данными между сервером и клиентом путем «убивания» процесса-сервера командой kill. Сделайте листинг процессов и сокет-гнезд, находящихся в состоянии «зомби». Используйте команду tcpdump.

В процессе выполнения работы пользуйтесь справкой man <функция>.

Для вариантов 1–3, 8 можно протестировать функциональность программного обеспечения при помощи терминала (telnet). Для вариантов 4–7 – функциональность путем соединения из интернет-браузера.

Разработать программу на языке Си, ожидающую соединения с компьютера преподавателя, и реализовать следующие функциональные возможности в соответствии с вариантом.

Вариант 1

Ожидать от клиента соединения. Вывести клиенту запрос на имя пользователя (Login:, Password:). Ожидать от клиента передачу идентификационных данных пользователя (admin 123). Если имя верное, передать пользователю текущее время и завершить соединение, в противном случае завершить соединение.

40

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]