Материал: Лабораторная работа 1

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

Лабораторная работа № 1

Клиент-серверное приложение для передачи сообщений с использованием протоколов TCP и UDP

1.1 Цель работы

Целью лабораторной работы является ознакомление студентов с основной структурой клиент-серверного приложения на примере написания программ передачи/приема текстовых сообщений с использованием сокетов в блокирующем режиме.

1.2 Задание на лабораторную работу

В первой лабораторной работе необходимо разработать 2 клиентсерверных приложения на основе протоколов TCP и UDP. Клиент должен подключиться к серверу и отправить ему текстовое сообщение; сервер, получив сообщение, должен вывести его на экран.

1.3 Методические указания

1.3.1 Понятие сокета

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

В программе сокет идентифицируется дескриптором - это просто переменная типа int(в ОС Windows тип SOCKET см. таблицу 1.1). Программа получает дескриптор от операционной системы при создании сокета, а затем передаёт его сервисам socket API для выполнения того или иного действия.

1.3.2 Обзор сокетов

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

Сокеты позволяют работать с множеством протоколов и являются удобным средством межпроцессорного взаимодействия, но в рамках данного лабораторного практикума речь будет идти только о сокетах семейства протоколов TCP/IP, использующихся для обмена данными между узлами сети Интернет.

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

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

Датаграммные сокеты опираются на протокол UDP, а потоковые - на

TCP.

Для работы с библиотекой Winsock 2.х в исходный текст программы необходимо включить директиву "#include <winsock2.h>", а в командной строке линкера указать "ws2_32.lib". В Microsoft Visual Studio для этого достаточно нажать <Alt-F7>, перейти к закладке "Link" и к списку библиотек,

перечисленных в строке "Object/Library modules", добавить "ws2_32.lib",

отделив ее от остальных символом пробела.

Перед началом использования функций библиотеки Winsock, ее необходимо подготовить к работе вызовом функции "int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData)", передав в старшем байте слова wVersionRequested номер требуемой версии, а в младшем - номер подверсии.

Аргумент lpWSAData должен указывать на структуру WSADATA, в которую при успешной инициализации будет занесена информация о производителе библиотеки. Если инициализация проваливается, функция возвращает ненулевое значение.

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

1.3.3 Атрибуты сокета

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

 

Таблица 1.1

Описание функции в *nix

Описание функции в Windows

 

 

#include <sys/types.h>

#include <winsock2.h>

#include <sys/socket.h>

 

int socket (

SOCKET socket (

int domain,

int domain,

int type,

int type,

int protocol

int protocol

);

);

Домен определяет пространство адресов, в котором располагается сокет, и множество протоколов, которые используются для передачи данных. Мы будем использовать домен Internet, задаваемый константой AF_INET (префикс AF означает "address family" - "семейство адресов"). Сокеты, размещённые в этом домене, могут использоваться для работы в любой IPсети. Существуют и другие домены (AF_IPX для протоколов Novell, AF_INET6 для новой модификации протокола IP - IPv6 и т. д.).

Тип сокета определяет способ передачи данных по сети. Чаще других применяются:

-SOCK_STREAM. Передача потока данных с предварительной установкой соединения. Обеспечивается надёжный канал передачи данных, при котором фрагменты отправленного блока не теряются, не переупорядочиваются и не дублируются.

-SOCK_DGRAM. Передача данных в виде отдельных сообщений (датаграмм). Предварительная установка соединения не требуется. Обмен данными происходит быстрее, но является ненадёжным: сообщения могут теряться, дублироваться и переупорядочиваться. Допускается передача сообщения нескольким получателям (multicasting) и широковещательная передача (broadcasting).

-SOCK_RAW. Этот тип присваивается низкоуровневым (т. н. "сырым") сокетам.

Обратите внимание, что не все домены допускают задание произвольного типа сокета. Например, совместно с доменом Unix используется только тип SOCK_STREAM. С другой стороны, для Internet-домена можно задавать любой из перечисленных типов. В этом случае для реализации SOCK_STREAM используется протокол TCP, для реализации SOCK_DGRAM

-протокол UDP, а тип SOCK_RAW используется для низкоуровневой работы с протоколами IP, ICMP и т. д.

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

протокол явно. Числовые идентификаторы протоколов зависят от выбранного домена; их можно найти в документации

1.3.4 Адреса

Прежде чем передавать данные через сокет, его необходимо связать с адресом в выбранном домене (эту процедуру называют именованием сокета). Иногда связывание осуществляется неявно (внутри функций connect и accept), но выполнять его необходимо во всех случаях. Вид адреса зависит от выбранного вами домена. В Internet-домене адрес задаётся комбинацией IPадреса и 16-битного номера порта.

Для явного связывания сокета с некоторым адресом используется функция bind. Её прототип имеет вид:

 

Таблица 1.2

 

 

Описание функции в *nix

Описание функции в Windows

 

 

#include <sys/types.h>

#include <winsock2.h>

#include <sys/socket.h>

 

int bind(

int bind(

int sockfd,

SOCKET sockfd,

struct sockaddr *addr,

struct sockaddr *addr,

int addrlen

int addrlen

);

);

В качестве первого параметра передаётся дескриптор сокета, который мы хотим привязать к заданному адресу. Второй параметр, addr, содержит указатель на структуру с адресом, а третий - длину этой структуры. Посмотрим, что она собой представляет.

 

Таблица 1.3

Описание структуры в *nix

Описание структуры в Windows

 

 

struct sockaddr {

unsigned short sa_family; // Семейство адресов char sa_data[14]; // 14 байтов для хранения адреса

};

Поле sa_family содержит идентификатор домена, тот же, что и первый параметр функции socket. В зависимости от значения этого поля по-разному интерпретируется содержимое массива sa_data. Разумеется, работать с этим массивом напрямую не очень удобно, поэтому вы можете использовать вместо sockaddr структуру вида sockaddr_in. При передаче в функцию bind указатель на эту структуру приводится к указателю на sockaddr. Рассмотрим структуру

sockaddr_in.

 

Таблица 1.4

Описание структуры в *nix

Описание структуры в Windows

 

 

struct sockaddr_in {

short int sin_family; // Семейство адресов unsigned short int sin_port; // Номер порта struct in_addr sin_addr; // IP-адрес

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

};

Здесь поле sin_family соответствует полю sa_family в sockaddr, в sin_port

записывается номер порта, а в sin_addr - IP-адрес хоста. Поле sin_addr само является структурой, которая имеет вид:

 

Таблица 1.5

 

 

Описание структуры в *nix

Описание структуры в Windows

 

 

struct in_addr {

unsigned long s_addr; };

Раньше in_addr представляла собой объединение, содержащее гораздо большее число полей. Сейчас, когда в ней осталось всего одно поле, она продолжает использоваться для обратной совместимости.