Материал: Методические указания к практическим занятиям по дисциплине «Основы построения защищенных компьютерных сетей». Деревянко В.Н

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

Источник: https://studfile.net/preview/16565401/