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

Учебное пособие 1381

.pdf
Скачиваний:
3
Добавлен:
30.04.2022
Размер:
1.08 Mб
Скачать

ФГБОУ ВПО «Воронежский государственный технический университет»

Кафедра систем информационной безопасности

МЕТОДИЧЕСКИЕ УКАЗАНИЯ

к практическим занятиям по дисциплине «Основы построения защищенных компьютерных сетей»

для студентов специальности 090301 «Компьютерная безопасность» очной формы обучения

Воронеж 2014

Составитель канд. техн. наук В. Н. Деревянко

УДК 681.326

Методические указания к практическим занятиям по дисциплине «Основы построения защищенных компьютерных сетей» для студентов специальности 090301 «Компьютерная безопасность» очной формы обучения / ФГБОУ ВПО «Воронежский государственный технический университет»; сост. В.Н. Деревянко. Воронеж, 2014. 51 с.

В методических указаниях изложен порядок выполнения заданий при проведении практических занятий и пояснения по их выполнению по курсу «Основы построения защищенных компьютерных сетей» для студентов специальности 090301 «Компьютерная безопасность». В результате выполнения этих работ студенты получают практические навыки по разработке сетевых программ на языках С, С++ для операционных систем

Windows и UNIX (LINUX).

Методические указания подготовлены в электронном виде в текстовом редакторе MS Word 2013 и содержатся в файле Деревянко_ПЗ_ОПЗКС.pdf.

Табл. 1. Ил. 4. Библиогр.: 5 назв.

Рецензент д-р техн. наук, проф. А.Г. Остапенко

Ответственный за выпуск зав. кафедрой д-р техн. наук, проф. А.Г. Остапенко

Издается по решению редакционно-издательского совета Воронежского государственного технического университета

© ФГБОУ ВПО «Воронежский государственный технический университет», 2014

Практическое занятие №1 Разработка клиент-серверных приложений на основе

механизма сокетов с применением протокола TCP

Цель практического занятия Изучить механизм сокетов и освоить разработку кли-

ент-серверных программ обмена данными на основе протокола TCP, получить практические навыки по разработке сетевых программ на языках С, С++.

Теоретические сведения

Базовые операции сокетов приведены в таблице ниже. Операция SOCKET создает новый сокет и учитывает его

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

 

Таблица

Базовые операции сокетов для TCP

Базовая операция

Значение

 

socket (сокет)

Создать новый сокет

 

bind (связать)

Связать локальный адрес с сокетом

 

listen (ожидать)

Объявить о желании принять соеди-

 

 

нение; указать размер очереди

 

accept (принять)

Пассивно установить входящее со-

 

 

единение

 

connect(соединить)

Активно пытаться установить соеди-

 

 

нение

 

send (послать)

Послать данные по соединению

 

receive(получить)

Получить данные у соединения

 

 

 

 

close (закрыть)

Разорвать соединение

 

Адреса созданному сокету назначаются с помощью операции bind. После того как сервер привязывает адрес к сокету, с ним могут cвязаться удаленные клиенты. Вызов socket не создает адрес напрямую, так как некоторые процессы придают адресам большое значение (например, они использовали один и тот же адрес годами, и этот адрес всем известен), тогда как другим процессам это не важно.

Следом идет вызов listen, который выделяет место для очереди входящих соединений на случай, если несколько клиентов попытаются соединиться одновременно.

В данном примере модели сокетов операция listen не является блокирующим вызовом.

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

На стороне клиента также сначала с помощью операции socket должен быть создан сокет, но операция bind здесь не требуется, так как используемый порт не имеет значения для клиента. connect блокирует вызывающего и инициирует активный процесс соединения. Когда этот процесс завершается (то есть когда соответствующий сегмент, посланный сервером, получен), процесс клиента разблокируется, и соединение считается установленным. После этого обе стороны могут использовать send и recieve для передачи и получения данных по полнодуплексному соединению. Могут также применяться стандартные UNIX-вызовы read и write, если нет нужды в использовании специальных свойств send и recieve.

В модели сокетов используется симметричный разрыв соединения. Соединение разрывается, когда обе стороны выполняют операцию close.

2

С помощью сокетов можно реализовать транспортный сервис и без установления соединения. В таком случае операция connect будет задавать адрес удаленного узла, а send и receive — отправлять и получать дейтаграммы. (Иногда используется расширенный набор вызовов — например, операции sendto и receivefrom, позволяющие приложению не ограничиваться одним транспортным узлом.) Иногда сокеты используются с протоколами, в которых вместо байтового потока применяется поток сообщений и которые могут включать или не включать управление перегрузкой.

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

/* Программа для клиента */

#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h>

#define SERVER_PORT 12345 /*По соглашению между клиентом и сервером*/

#define BUF_SIZE 4096 /* Размер передаваемых блоков */ int main(int argc, char *argv)

{

int c,s,bytes;

 

char buf[BUF_SIZE];

/*буфер для входящего файла */

struct hostent *h;

/*информация о сервере */

struct sockaddr_in channel;

/*хранит IP=адрес */

if (argc!=3) fatal("Для запуска введите: client имя_сервера имя_файла"); h = gethostbyname(argv[1]); / *поиск IP-адреса хоста*/

if(!h) fatal("Ошибка выполнения gethostbyname") s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s<0) fatal("Сокет");

memset(&channel, 0, sizeof(channel)); channel.sin_family=AF_INET;

3

memcpy (&channel.sin_addr.s_addr, h>h_addr,h>h_length); channel.sin_port=htons(SERVER_PORT);

c = connect(s, (struct sockaddr *) &channel, sizeof(channel)); if (c<0) fatal("Ошибка соединения");

/* Соединение установлено. Посылается имя файла */ write (s, argv[2], strlen(argv[2])+1);

/*Получить файл, записать на стандартное устр. вывода*/ while (1) {

bytes = read(s, buf, BUF_SIZE);

/* Читать из сокета */

if (bytes <= 0) exit(0);

/* Проверка конца файла */

write(1, buf, bytes);

/* Записать */

}

 

fatal(char *string)

 

printf(“%s\n”, string);

 

exit(1);

 

}

/* Программа для сервера */

#include <sys/types.h> #include <sys/fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h>

#define SERVER_PORT 12345 /*По соглашению между клиентом и сервером*/ #define BUF_SIZE 4096 /* Размер передаваемых блоков */

#define QUEUE_SIZE 10

int main(int argc, char *argv[]);

{

int s, b, l, fd, sa, bytes, on = 1;

 

char buf[BUF_SIZE];

/* буфер для исходящего файла */

struct sockaddr_in channel;

/* содержит IP-адрес */

/* Создать структуру адреса для привязки к сокету */

memset(&channel, 0, sizeof(channel));

/* Обнуление channel */

channel.sin_family = AF_INET;

 

4

channel.sin_addr.s_addr = htonl(INADDR_ANY); channel.sin_port = htons(SERVER_PORT);

/* Пассивный режим. Ожидание соединения */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

/* создать сокет */

if (s<0) fatal("ошибка сокета");

setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)); b = bind(s, (struct sockaddr *) &channel, sizeof(channel));

if (b<0) fatal("Ошибка связывания");

l = listen(s, QUEUE_SIZE); /* Опр. размера очереди */ if (l< 0) fatal("Ошибка ожидания");

/* Теперь сокет установлен и привязан. Ожидание и обработка соединения */

while (1) {

 

sa = accept(s, 0, 0);

// Блокировка в ожидании запроса соединения

if (sa<0) fatal("Ошибка доступа");

read(sa, buf, BUF_SIZE);

/*считать имя файла из сокета*/

/* Получить и вернуть файл */

fd = open(buf, O_RDONLY);

/*Открыть файл для отсылки */

if (fd < 0) fatal("Ошибка открытия файла");

while (1) {

 

bytes = read(fd, buf, BUF_SIZE);

/*Читать из файла*/

if (bytes <= 0) break;

/* Проверка конца файла */

write(sa, buf, bytes);

/* Записать байты в сокет */

}

 

close(fd);

/* Закрыть файл */

close(sa);

/* Разорвать соединение */

}

 

}

Программа сервера начинается с включения некоторых стандартных заголовков, последние три из которых содержат основные структуры и определения, связанные с интернетом. Значение SERVER_PORT выбрано как 12345. Подойдет любое число от 1024 до 65535, если оно не используется другим процессом; порты с номерами 1023 и ниже зарезервированы.

5

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

Затем инициализируется структура данных, которая будет содержать IP-адрес сервера. Эта структура будет связана с серверным сокетом. Вызов memset полностью обнуляет структуру данных. Последующие три присваивания заполняют три поля этой структуры. Последнее из них содержит порт сервера. Функции htonl и htons занимаются преобразованием значений в стандартный формат, что позволяет программе нормально выполняться как на машинах с представлением числовых разрядов little-endian (например, Intel x86), так и с представлением big-endian (например, SPARC).

После этого сервером создается и проверяется на ошибки (определяется по s < 0) сокет. Вызов setsockopt нужен для того, чтобы порт мог использоваться несколько раз, а сервер — бесконечно, обрабатывая запрос за запросом. Теперь IPадрес привязывается к сокету и выполняется проверка успешного завершения вызова bind. Конечным этапом инициализации является вызов listen, свидетельствующий о готовности сервера к приему входящих вызовов и сообщающий системе о том, что нужно ставить в очередь до QUEUE_SIZE вызовов, пока сервер обрабатывает текущий вызов. При заполнении очереди прибытие новых запросов игнорируется.

В основном цикле программы accept блокирует сервер на то время, пока клиент пытается установить соединение. Если вызов завершается успешно, accept возвращает дескриптор сокета, который можно использовать для чтения и записи. Однако, в отличие от однонаправленных каналов, сокеты двунаправлены, поэтому для чтения (и записи) данных из соединения можно использовать sa (принятый сокет).

После установления соединения сервер считывает имя файла. Если оно недоступно, сервер блокируется, ожидая его. Получив имя файла, сервер открывает файл и входит в цикл,

6

который читает блоки данных из файла и записывает их в сокет. Это продолжается до тех пор, пока не будут скопированы все запрошенные данные. Затем файл закрывается, соединение разрывается и начинается ожидание нового вызова.

Работа клиента начинается с проверки корректности числа аргументов (argc = 3 означает, что в строке запуска содержалось имя программы и два аргумента). Обратите внимание на то, что argv[1] содержит имя сервера и переводится в IP-адрес с помощью gethostbyname. Для поиска имени функция использует DNS.

Затем создается и инициализируется сокет, после чего клиент пытается установить TCP-соединение с сервером посредством connect. Если сервер включен, работает на указанной машине, соединен с SERVER_PORT и либо простаивает, либо имеет достаточно места в очереди listen (очереди ожидания), то соединение с клиентом будет рано или поздно установлено. По данному соединению клиент передает имя файла, записывая его в сокет.

Теперь клиентская программа входит в цикл, читает файл блок за блоком из сокета и копирует на стандартное устройство вывода. По окончании процесса она завершается.

Общая схема применения сокетов в ориентированном на соединение протоколе показана на рис. 1.

Программа-клиент также создает сокет, вызывая функцию socket. Однако в отличие от сервера, ориентированной на соединение, программе-клиенту неважен номер порта протокола, который она получит. То есть ей незачем вызывать функцию bind. Вместо этого программа-клиент, ориентированная на соединение, устанавливает соединение, вызывая функцию connect. Кроме функций write и read клиент и сервер могут использовать send и recv, а также любую другую функцию, предназначенную для работы с ориентированным на соединение протоколом.

7

Рис. 1. Сокеты в ориентированном на соединение протоколе

Типовые в арианты заданий

Написать программу, которая будет:

1)обращаться к произвольному серверу, устанавливать соединение по указанному номеру порта, считывать требуемое количество символов и выводить их на экран локальной машины;

2)обращаться к произвольному серверу, устанавливать соединение по указанному номеру порта, передавать на сервер произвольную строку, ждать отклик сервера и выводить его на экран локальной машины;

Написать серверную программу, которая будет:

3)ожидать соединения по указанному порту, а после установления соединения передавать клиенту строку, содержащую время дня и текущую дату.

8