Протоколы, не требующие соединения — КиберПедия 

Опора деревянной одностоечной и способы укрепление угловых опор: Опоры ВЛ - конструкции, предназначен­ные для поддерживания проводов на необходимой высоте над землей, водой...

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

Протоколы, не требующие соединения

2021-03-17 130
Протоколы, не требующие соединения 0.00 из 5.00 0 оценок
Заказать работу

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

Приемник

Процесс получения данных на сокете, не требующем соединения, прост. Сначала создают сокет функцией socket. Затем выполняют привязку сокета к интерфейсу, на котором будут принимать данные, функ­цией bind (как и в случае протоколов, ориентированных на сеансы). Разни­ца в том, что нельзя вызвать listen или accept: вместо этого нужно просто ожидать приема входящих данных. Поскольку в этом случае соединения нет, принимающий сокет может получать дейтаграммы от любой машины в сети. Простейшая функция приема — recvform:

 

int recvfrom(

SOCKET s,

char* buf,

int len

int flags,

struct sockaddr* from,

int* fromlen

);

 

Первые четыре параметра такие же, как и для функции recv, включают до­пустимые значения для flags: MSG_OOB и MSG_PEEK. Параметр from — струк­тура SOCKADDR для данного протокола слушающего сокета, на размер струк­туры адреса ссылается fromlen. После возврата вызова структура SOCKADDR будет содержать адрес рабочей станции, которая отправляет данные.

Отправитель

Есть два способа отправки данных через сокет, не требующий соединения. Первый и самый простой — создать сокет и вызвать функцию sendto или WSASendTo. Рассмотрим сначала функцию sendto:

 

int sendto(

SOCKET s

const char* buf,

int len,

int flags,

const struct sockaddr * to,

int tolen

);

 

Параметры этой функции такие же, как и у recvfrom, за исключением buf— буфера данных для отправки, и len — показывающего сколько байт от­правлять. Параметр to — указатель на структуру SOCKADDR с адресом при­нимающей рабочей станции.

Протоколы, ориентированные на передачу сообщений

Большинство протоколов, требующих соединения, — потоковые, а не тре­бующих соединения — ориентированы на передачу сообщений. Поэтому при отправке и приеме данных нужно учесть ряд факторов. Во-первых, по­скольку ориентированные на передачу сообщений протоколы сохраняют границы сообщений, данные, поставленные в очередь отправки, блокируют­ся до завершения выполнения функции отправки. Если отправка не может быть завершена, при асинхронном или неблокирующем режиме ввода-вы­вода функция отправки вернет ошибку WSAEWOULDBLOCK. Это означает, что базовая система не смогла обработать данные и нужно попытаться вызвать функцию отправки повторно. Главное — в ориен­тированных на сообщения протоколах запись происходит только в резуль­тате самостоятельного действия.

Освобождение ресурсов сокета

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

Дополнительные функции API

Рассмотрим API-функции Winsock, которые пригодятся вам при создании сетевых приложений.

Функция getpeername

Эта функция возвращает информацию об адресе сокета партнера на под­ключенном сокете:

 

int getpeername(

SOCKET s,

struct sockaddr* name,

int* namelen

);

 

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

Функция getsockname

Эта функция противоположна getpeername и возвращает адресную инфор­мацию для локального интерфейса определенного сокета:

 

int getsockname(

SOCKET s,

struct sockaddr* name,

int* namelen

);

 

Используются те же параметры, что и для getpeername, однако возвраща­ется информация о локальном адресе. В случае TCP адрес совпадает с соке­той сервера, слушающим на заданном порте и IP-интерфейсе.

 

Ввод-вывод в Winsock

Эта глава посвящена управлению вводом-выводом через сокеты в Windows-приложениях. В Winsock такое управление реализовано с помощью режимов работы и моделей ввода-вывода. Режим (mode) сокета определяет поведение функций, работающих с сокетом. Модель (model) сокета описывает, как при­ложение производит ввод-вывод при работе с сокетом. Модели не зависят от режима работы и позволяют обходить их ограничения.

Winsock поддерживает два режима: блокирующий (blocking) и неблоки­рующий (nonblocking). Эти режимы подробно описаны в начале главы, здесь же демонстрируется их использование в приложениях для управления вво­дом-выводом. Далее приведен ряд интересных моделей, которые помогают приложению управлять несколькими сокетами одновременно в асинхрон­ном режиме: select, WSAAsyncSelect, WSAEventSelect, перекрытый ввод-вывод (overlapped I/O) и порт завершения (completion port).

Режимы работы сокетов

В блокирующем режиме функции ввода-вывода, такие как send и recv, перед завершением ожидают окончания операции. В неблокирующем — работа функций завершается немедленно. Приложения, выполняемые на платфор­мах Windows СЕ и Windows 95 (в случае Winsock 1), поддерживают очень мало моделей ввода-вывода и требуют от программиста описать блокирова­ние и разблокирование сокетов в разных ситуациях.

Блокирующий режим

При блокировке сокета необходима осторожность, так как этом режиме лю­бой вызов функции Winsock именно блокирует сокет на некоторое время. Большинство приложений Winsock следуют модели «поставщик — потреби­тель», в которой программа считывает или записывает определенное количе­ство байт и затем выполняет с ними какие-либо операции.

 

SOCKET sock;

char buff[256];

int done = 0;

while(!done) {

nBytes = recv(sock, buff, 65);

if (nBytes == SOCKET_ERROR) {

printf("recv failed with error %d\n", WSAGetLastError());

return;

}

DoComputationOnData(buff);

}

 

Проблема в том, что функция recv может не завершиться никогда, так как для этого нужно считать какие-либо данные из буфера системы. В такой си­туации некоторые программисты могут соблазниться «подглядыванием» данных (чтение без удаления из буфера), используя флаг MSG_PEEK в recv или вызывая ioctlsocket с параметром FIONREAD. Подобный стиль програм­мирования заслуживает резкой критики. Издержки, связанные с «подгляды­ванием», велики, так как необходимо сделать один или более системных вы­зовов для определения числа доступных байт, после чего все равно прихо­дится вызывать recv для удаления данных из буфера.

Чтобы этого избежать, следует предотвратить замораживание приложе­ния из-за недостатка данных (из-за сетевых проблем или проблем клиента) без постоянного «подглядывания» в системные сетевые буферы. Один из ме­тодов — разделить приложения на считывающий и вычисляющий потоки, совместно использующие общий буфер данных. Доступ к буферу регулиру­ется синхронизирующим объектом, таким как событие или мьютекс. Задача считывающего потока — постоянно читать данные из сети и помещать их в общий буфер. Считав минимально необходимое количество данных, этот поток инициирует сигнальное событие, уведомляющее вычисляющий поток, что можно начинать вычисления. Затем вычисляющий поток удаля­ет часть данных из буфера и производит с ними необходимые операции. В следующем листинге реализованы две функции: для приема данных (ReadThread) и их обработки (ProcessThread).

 

// Перед созданием двух потоков,

// инициализируется общий буфер (data)

// и создается сигнальное событие (hEvent)

CRITICAL_SECTION data;

HANDLE   hEvent;

TCHAR    buff[MAX_BUFFER_SIZE];

int    nbytes;

// Считывающий поток void ReadThread(void) {

int nTotal = 0, nRead = 0, nLeft = 0 nBytes = 0;

while (!done)

{

nTotal = 0;

nLeft = NUM_BYTES_REQUIRED;

while (nTotal!= NUM_BYTES_REQUIRED)

{

EnterCriticalSection(&data);

nRead = recv(sock, &(buff[MAX_BUFFER_SIZE - nBytes]), nLeft);

if (nRead == -1) {

printf("error\n"); ExitThread();

nTotal += nRead;

nLeft -= nRead;

 

nBytes += nRead;

LeaveCriticalSection(&data);

}

SetEvent(hEvent); } }

// Вычисляющий поток void ProcessThread(void)

WaitForSingleObject(hEvent);

EnterCriticalSection(&data);

DoSomeComputationOnData(buff);

// Удаление обработанных данных из буфера

// и сдвиг оставшихся в начало массива

nBytes -= NUM BYTES REQUIRED;

}

LeaveCriticalSection(&data);

}

 

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

Неблокирующий режим

Альтернатива описанному режиму — режим без блокировки. Он несколько сложнее в использовании, но обеспечивает те же возможности, что и режим блокировки, плюс некоторые преимущества. В следующем листинге показано, как создать сокет и перевести его в неблокирующий режим.

SOCKET s;

unsigned long ul = 1;

int nRet;

s = socket(AF_INET, SOCK_STREAM, 0);

nRet = ioctlsocket(s, FIOBIO, (unsigned long *)&ul);

if (nRet == SOCKET ERROR)

{

// Не удалось перевести сокет в неблокирующий режим

}

Если сокет находится в неблокирующем режиме, функции Winsock завер­шаются немедленно. В большинстве случаев они будут возвращать ошибку


Поделиться с друзьями:

Адаптации растений и животных к жизни в горах: Большое значение для жизни организмов в горах имеют степень расчленения, крутизна и экспозиционные различия склонов...

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

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого...

Историки об Елизавете Петровне: Елизавета попала между двумя встречными культурными течениями, воспитывалась среди новых европейских веяний и преданий...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.027 с.