1.3 Создание клиента Программа клиента делается аналогично до момента создания сокетов.
Cоздайте сокет так, как описано выше, но не пользуйтесь командой bind:
SOCKADDR_IN anAddr;
anAddr.sin_family = AF_INET;
anAddr.sin_port = htons(80);
anAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
Заполнение структуры производится почти также но во время инициализации переменной anAddr необходимо указать IP-адрес сервера ( пример 127.0.0.1 ) .
Для обращения программы-клиента к серверу с запросом на установление логической соединения используется системный вызов connect, имеющий следующий прототип:
int connect(SOCKET s, const struct sockaddr *peer, int peer_len));
Аргумент s задает дескриптор сокета, через который программа обращается к серверу с запросом на соединение.
Аргумент addr должен указывать на структуру данных, содержащую адрес, приписанный сокету программы-сервера, к которой делается запрос на соединение. Для сетей TCP/IP такой структурой является sockaddr_in.
Для формирования значений полей структуры sockaddr_in удобно использовать функцию gethostbyname.
Аргумент addrlen задает размер (в байтах) структуры данных, указываемой аргументом addr.
Для того, чтобы запрос на соединение был успешным, необходимо, по крайней мере, чтобы программа-сервер выполнила к этому моменту системный вызов listen для сокета с указанным адресом.
При успешном выполнении запроса системный вызов connect возвращает 0, в противном случае - "-1" (устанавливая код причины неуспеха в глобальной переменной errno).
Примечание. В режиме взаимодействия без установления соединения необходимости в выполнении системного вызова connect нет. Однако, его выполнение в таком режиме не является ошибкой - просто меняется смысл выполняемых при этом действий: устанавливается адрес "по умолчанию" для всех последующих посылок дейтаграмм.
2 Лабораторная работа №1 Работа с сокетами Беркли Лабораторная работа №1 выполняется после изучения материала, посвященного описанию принципов работы с сокетами Беркли.
Цель работы:
написать консольные приложения на языке C/C++, реализующие работу протоколов Time, DayTime и Finger.
Рекомендуемая литература:
Глава 1 Работа с WinSocket данного пособия.
Снайдер Й. Эффективное программирование TCP/IP. Библиотека программиста. – Спб: Питер, 2002. – 230 с.: ил.
Электронный вариант лекций «Основы операционных систем.
Практикум». Лекция 10 «Семейство протоколов TCP/IP. Сокеты (sockets) в UNIX и основы работы с ними». Лекции можно найти в архиве, в файле osintropractice.zip.
Описание протокола Time в спецификации RFC-868 (Archive\Documents\ RFC\rfc868.txt)
Описание протокола Finger в спецификации RFC-1288 (Archive\Documents\RFC\rfc1288.txt)
Примитивы сокетов Беркли Сокет обычно трактуется как дескриптор файла и указывается в качестве параметра практически во всех примитивах.
Примитивы сокетов для TCP
SOCKET (гнездо) Дескриптор сокета, именование ресурса
BIND (связать) Связывает ресурсы (сокет) с локальным адресом хоста
LISTEN (ожидать) Возможность принять данные с указанием размера очереди
ACCEPT (принять) Блокирует сервер до попытки соединения клиента
CONNECT (соединить) Попытка установить соединения
SEND (переслать) Посылает данные по соединению
RECEIVE (получить) Получает данные у соединения
CLOSE (закрыть) Разрывает соединение на стороне сервера и клиента примитивы используются в следующем порядке:
Сторона сервера:
Вызовы примитивов осуществляются в следующем порядке:
SOCKET - создает дескриптор (примерно так же как OPEN при открытии файла) и выделяет для него место в таблице транспортного объекта. При этом сообщается, какая служба необходима, канальная (TCP) или дейтаграммная (UDP).
BIND – привязывает конкретный сетевой адрес к сокету, после чего становится возможной подключение клиентов.
LISTEN – выделяет место для очереди входящих вызовов на случай одновременного обращения нескольких клиентов.
ACCEPT – сервер переходит в режим ожидания (блокируется) до прихода блока с запросом соединения от клиента. При этом создается новый сокет, с теми же параметрами, что и оригинального сокета. Сервер может распараллелить процесс обработки нового сокета, что бы иметь возможность вернуться к ожиданию новых соединений.
Сторона клиента:
SOCKET - аналогично серверу.
CONNECT – блокирует вызывающего до завершения процесса фазы установки соединения, т.е. до подтверждения сервера, что соединение установлено.
Далее между сервером и клиентом идет обмен с использованием RECEIVE и SEND.
Соединение разрывается, если обе стороны используют примитив CLOSE.
Примечание. Для того чтобы выполнить преобразование символьного имени сервера или IP-адреса, в формат, используемый при работе с сетевыми функциями, следует использовать функции gethostbyname и inet_addr соответсвенно.
Также, следует помнить, что аргументы и типы приведённых выше функций, могут различаться в разных ОС, поэтому, для получения более полной информации следует воспользоваться системой помощи в данной системе (команда man в UNIX или MSDN в Windows).
При работе с сокетами в Windows, не забывайте подгрузить, а по окончании работы выгрузить библиотеку реализующую поддержку сокетов через функции WSAStartup/WSACleanup.
Протокол передачи времени Time Данный протокол предназначен для передачи показаний времени по сети. В сети работают так называемые time-серверы, у которых можно запросить точное время. В ответ на запрос клиента, сервер возвращает время в секундах (32х битное двоичное число), прошедшее с 00:00:00 1 января 1900 года.
Этот протокол может использовать в качестве транспортной службы как UDP-протокол, так и TCP-протокол. Стандартный порт протокола - 37.
Если в качестве транспортной службы используется TCP, взаимодействие осуществляется так:
SERVER: прослушивает 37 порт, ожидая соединений;
CLIENT: запрашивает соединение с портом 37 сервера;
SERVER: посылает время в виде двоичного 32х битного числа;
CLIENT: получает время;
SERVER: закрывает соединение;
CLIENT: закрывает соединение;
Если сервер по каким-либо причинам не может определить время на своей стороне, он отказывается от соединения, не посылая ничего.
Если в качестве транспортной службы используется UDP, взаимодействие осуществляется так:
SERVER: прослушивает 37 порт, ожидая соединений;
CLIENT: посылает серверу пустой UDP-пакет, номер порта = 37;
SERVER: получает пустой UDP-пакет;
SERVER: посылает UDP-пакет, содержащий время в виде двоичного 32х битного числа;
CLIENT: получает UDP -пакет, содержащий время;
Если сервер по каким-либо причинам не может определить время на своей стороне, он отбрасывает полученный пустой UDP-пакет и не посылает ничего в ответ.
Протокол Finger Сетевой протокол Finger предназначен для предоставления информации о пользователях удалённого компьютера. Стандартный порт протокола - 79. Используя данный протокол, вы можете получить данные о списке пользователей, которые работают в данный момент на интересующей вас ЭВМ, о конкретном пользователе (дата последнего сеанса входа в систему и т.д.), о списке загруженных задач, о типах интерфейсов (например, терминалов). Протокол Finger обеспечивает интерфейс для удаленной информационной программы пользователя (RUIP - Remote User Information Program).
Данный протокол базируется на TCP. Локальная ЭВМ осуществляет TCP-соединение с удаленным узлом через указанный порт. После этого становится доступной программа RUIP и пользователь может посылать ей свои запросы. Каждый запрос представляет собой строку текста. RUIP, получив запрос, анализирует его и присылает ответ, после чего соединение закрывается.
Любые пересылаемые данные должны иметь формат ASCII, не иметь контроля по четности и каждая строка должна завершаться последовательностью CRLF (ASCII 13, за которым следует ASCII 10).
Диалог двух машин через TCP выглядит следующим образом:
SERVER: слушает порт 79;
CLIENT: устанавливает соединение на 79 порт;
CLIENT: посылает имя пользователя;
SERVER: посылает информацию о запрашиваемом пользователе;
CLIENT: принимает;
SERVER: закрывает соединение;
CLIENT: закрывает соединение;
Пример реализации протокола Daytime
В качестве примера, приводится текст программы, реализующей работу Daytime Protocol’а. #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define strHost "212.192.122.109"
#define Port 13
int main(void) { int s, res;
int nCharRecv;
struct sockaddr_in clnt_sin;
char timebuf[128];
if ((s=socket(AF_INET, SOCK_STREAM, 0)) == -1) { printf("Cannot create socket!!!!\n");
return -1;
}
memset ((char *)&clnt_sin, '\0', sizeof(clnt_sin));
clnt_sin.sin_family = AF_INET;
clnt_sin.sin_addr.s_addr = inet_addr(strHost);
clnt_sin.sin_port = htons(Port);
res = connect (s, (struct sockaddr *)&clnt_sin, sizeof(clnt_sin));
if (res == -1) { perror("connect");
}
else { printf("Connected\n");
}
memset(timebuf, '\0', 128);
nCharRecv = recv(s, &timebuf, sizeof(timebuf), 0);
if (nCharRecv == -1) { printf("Cannot get info from server!!!!\n");
perror("Receive");
return -1;
}
close(s);
printf("Time: %s\n", timebuf);
return 0;
}
Задание на лабораторную работу: Ознакомившись с протоколом в RFC 868 написать программу, запрашивающую время с удаленного сервера через Time Protocol.
Полученное от сервера 32 – битное число необходимо преобразовать в строку и вывести на экран.
Написать программу, запрашивающую информацию о пользователе от удаленного сервера через протокол Finger, описанный в RFC 1288.
Полученную информацию необходимо вывести на экран. Имя пользователя программа должна принимать из командной строки.
Пользоваться готовыми классами, компонентами, библиотеками, реализующими работу с сетью, ЗАПРЕЩЕНО. Все программы должны использовать элементарные функции, работающими с сокетами Беркли.
|