Главная страница

Программирование в сетях Windows. Э. Джонс, Д. Оланд


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница17 из 50
1   ...   13   14   15   16   17   18   19   20   ...   50

ГЛАВА 7 Основы Wmsock 165
вызовах API-функций Winsock К тому же, многие программисты стал- киваются с одними и теми же проблемами при закрытии сокетов, со- стояния TCP при этом представляют наибольший интерес
Начальное состояние любого сокета — CLOSED Как только клиент инициирует соединение, серверу отправляется пакет SYN и клиентс- кий сокет переходит в состояние SYN_SENT Получив пакет SYN, сер- вер отправляет пакет SYN-and-ACK, а клиент отвечает на него пакетом
АСК С этого момента клиентский сокет переходит в состояние ESTA-
BLISHED Если сервер не отправляет пакет SYN-ACK, клиент по исте- чении времени ожидания возвращается в состояние CLOSED
Если сокет сервера связан и прослушивает локальный интерфейс и порт, то он находится в состоянии LISTEN При попытке клиента ус- тановить соединение сервер получает пакет SYN и отвечает пакетом
SYN-ACK Состояние сокета сервера меняется на SYN_RCVD Наконец,
после отправки клиентом пакета АСК сокет сервера переводится в со- стояние ESTABLISHED
Существует два способа закрыть соединение Если этот процесс начинает приложение, то закрытие называется активным, иначе —
пассивным На рис 7-2 изображены оба вида закрытия При активном закрытии соединения приложение отправляет пакет FIN Если прило- жение вызывает closesocket win shutdown (со вторым аргументом SD_
SEND), оно отправляет узлу пакет FIN, и состояние сокета меняется на
FIN_WAIT_1 Обычно узел отвечает пакетом АСК и сокет переходит в состояние FIN_WAIT_2 Если узел тоже закрывает соединение, он от- правляет пакет FIN, а компьютер отвечает пакетом АСК и переводит сокет в состояние TIME_WAIT
Активное закрытие сокета Пассивное закрытие сокета closesocket recv FIN
send FIN send ACK
V T
I
T
Рис. 7-2. Состояния закрытия сокета TCP
Состояние TIMEWAIT также называется состоянием ожидания
MSL MSL — максимальное время жизни сегмента (Maximum Segment rtetime), иными словами, время существования пакета в сети перед его
Торасыванием У каждого IP-пакета есть поле времени жизни (time-
°-live, TTL) Если оно равно О, значит, пакет можно отбросить Каждый
см след стр

166 ЧАСТЬ II Интерфейс прикладного программирования Wmsock маршрутизатор, обслуживающий пакет, уменьшает значение TTL на 1
и передает пакет дальше Перейдя в состояние TIMEWAIT, приложе- ние остается в нем на протяжении двух периодов времени, равных
MSL Это позволяет TCP в случае потери заключительного пакета АСК
послать его заново, с последующей отправкой FIN По истечении 2MSL
сокет переходит в состояние CLOSED
Результат двух других способов активного закрытия — состояние
TIME_WAIT В предыдущем случае только одна сторона отправляла FIN
и получала ответ АСК, а узел оставался свободным для передачи дан- ных до момента своего закрытия Здесь и возможны два других спо- соба В первом случае, при одновременном закрытии, компьютер и узел одновременно запрашивают закрытие компьютер отправляет узлу пакет FIN и получает от него пакет FIN
Затем в ответ на пакет FIN компьютер отправляет пакет АСК и из- меняет состояние сокета на CLOSING После получения компьютером пакета АСК от узла сокет переходит в состояние TIME_WAIT
Второй случай активного закрытия является вариацией одновре- менного закрытия сокет из состояния FIN_WAIT_1 сразу переходит в состояние TIMEJWAIT Это происходит, если приложение отправляет пакет FIN и тут же после этого получает от узла пакет FIN-ACK В та- ком случае узел подтверждает пакет FIN приложения отправкой свое- го, на которое приложение отвечает пакетом АСК
Основной смысл состояния TIMEWAIT заключается в том, что пока соединение ожидает истечения 2MSL, сокетная пара, участвующая в соединении, не может быть использована повторно Сокетная пара —
это комбинация локального и удаленного IP портов Некоторые реа- лизации TCP не позволяют повторно использовать любой из портов сокетной пары, находящейся в состоянии TIME_WAIT В реализации
Microsoft этого дефекта нет Впрочем, при попытке соединения с сокет- ной парой, находящейся в состоянии TIMEWAIT, произойдет ошибка
WSAEADDRINUSE Одно из решений проблемы (кроме ожидания окон- чания состояния TIME_WAIT пары сокетов, использующей локальный порт) — использовать параметр сокета SO_REUSEADDR Более подроб- но SOJREUSEADDR рассматривается в главе 9
И наконец, рассмотрим пассивное закрытие По этому сценарию приложение получает от узла пакет FIN и отвечает пакетом АСК. В этом случае сокет приложения переходит в состояние CLOSE_WAIT Так как узел закрыл свою сторону, он больше не может отправлять данные, но приложение вправе это делать, пока не закроет свою сторону соеди- нения Для закрытия своей стороны приложение отправляет пакет FIN,
после чего ТСР-сокет приложения переводится в состояние LAST_ACK
После получения от узла пакета АСК сокет приложения возвращается в состояние CLOSED
Подробне о протоколе TCP/IP — в RFC 793 Этот и другие RFC дос- тупны по адресу http //wivwrfc-editororg

/

, - „ ,
й р
ГЛАВА 7 Основы Winsock 167
функции connect и WSAConnect
Нам осталось обсудить собственно установление соединения Оно осуществ- ляется вызовом connect или WSAConnect Сначала рассмотрим версию Win- sock 1 этой функции
int connect(
SOCKET s,
const struct sockaddr FAR* name,
int namelen
),
Параметры практически не требуют пояснений s — действительный
ТСР-сокет для установления соединения, пате — структура адреса сокета
(SOCKADDRIN) для TCP, описывающая сервер к которому подключаются,
namelen — длина переменной пате Версия Winsock 2 этой функции опре- делена так
int WSAConnect(
SOCKET s,
const struct sockaddr FAR * name,
int namelen,
LPWSABUF lpCallerData,
LPWSABUF lpCalleeData,
LPQOS lpSQOS,
LPQOS lpGQOS
),
Первые три параметра такие же, как и в функции connect Следующие два
lpCallerData и lpCalleeData, — это строковые буферы, используемые для при- ема и отправки данных в момент установления соединения Параметр lp-
CallerData указывает на буфер, содержащий данные, отправляемые клиентом серверу вместе с запросом на соединение, lpCallerData — на буфер с данны- ми, возвращаемыми сервером в ходе установления соединения Обе пере- менные являются структурами WSABUF, и для lpCallerData поле len должно указывать длину данных передаваемого буфера buf В случае lpCalleeData
поле len определяет размер буфера buf куда принимаются данные от серве- ра Два последних параметра lpSQOS и lpGQOS, — ссылаются на структуры
QoS, определяющие требования пропускной способности отправки и при- ема данных устанавливаемого соединения Параметр lpSQOS указывает тре- бования к сокету 5, a lpGQOS — к группе сокетов На данный момент группы сокетов не поддерживаются Нулевое значение lpSQOS означает, что прило
Жение не предъявляет требований к качеству обслуживания
Если на компьютере, к которому вы подключаетесь, не запущен процесс,
прослушивающий данный порт, функция connect вернет ошибку WSAECON-
NREFUSED Другая ошибка — WSAETIMEDOUT, происходит, когда вызываемый а
Дресат недоступен, например, из-за отказа коммуникационного оборудова- ли на пути к узлу или отсутствия узла в сети
п ое l A U i b i i интерфейс прикладного программирования winsocK
Передача данных
По сути, в сетевом программировании самое главное — уметь отправлять и принимать данные. Для пересылки данных по сокету используются функции
send и WSASend. Аналогично, для приема данных существуют функции recv
и WSARecv.
Заметьте, что все буферы, используемые при отправке и приеме данных состоят из элементов типа char. To есть эти функции не предназначены для работы с кодировкой UNICODE. Это особенно важно для Windows СЕ, так как она использует UNICODE по умолчанию. Отправить строку символов UNICODE
можно двумя способами: в исходном виде или привести к типу char '. Тон- кость в том, что при указании количества отправляемых или принимаемых символов результат функции, определяющей длину строки, нужно умножить на 2, так как каждый UNICODE-символ занимает 2 байта строкового масси- ва. Другой способ: сначала перевести строку из UNICODE в ASCII функцией
WideCharToMuMByte.
Все функции приема и отправки данных при возникновении ошибки возвращают код SOCKETJERROR. Для получения более подробной информа- ции об ошибке вызовите функцию WSAGetLastError. Самые распространен- ные ошибки - WSAECONNABORTED и WSAECONNRESET. Обе возникают при закрытии соединения: либо по истечении времени ожидания, либо при зак- рытии соединения партнерским узлом. Еще одна типичная ошибка — WSAE-
WOULDBLOCK, обычно происходит при использовании неблокирующих или асинхронных сокетов. По существу, она означает, что функция не может быть выполнена в данный момент. В главе 8 будут описаны разные методы ввода-вывода Winsock, которые помогут избежать этих ошибок.
Функции send и WSASend
API-функция send для отправки данных по сокету определена так:
int send(
SOCKET s,
M
const char FAR * buf,
if
int len,

int flags
);
Параметр s определяет сокет для отправки данных. Второй параметр —
buf, указывает на символьный буфер, содержащий данные для отправки. Тре- тий — len, задает число отправляемых из буфера символов. И последний па- раметр — flags, может принимать значения О, MSGDONTROUTE, MSG_OOB,
или результат логического ИЛИ над любыми из этих параметров. При ука- зании флага MSGJDONTROUTE транспорт не будет маршрутизировать от- правляемые пакеты. Обработка этого запроса остается на усмотрение базо- вого протокола (например, если транспорт не поддерживает этот параметр",
запрос игнорируется). Флаг MSGOOB указывает, что данные должны быть отправлены вне полосы (out of band), то есть срочно.

Jl A DM I Ul.num>i vvinouv-rv
При успешном выполнении функция send вернет количество переданных иначе — ошибку SOCKET_ERROR. Одна из типичных ошибок — WSAE-
oNNABORTED, происходит при разрыве виртуального соединения из-за шибки протокола или истечения времени ожидания. В этом случае сокет ж е Н
быть закрыт, так как он больше не может использоваться. Ошибка
yrfiAECONNRESET происходит, если приложение на удаленном узле, выполнив аппаратное закрытие, сбрасывает виртуальное соединение, или неожидан- но завершается, или происходит перезагрузка удаленного узла. В этой ситу- ации сокет также должен быть закрыт. Еще одна ошибка — WSAETIMEDOUT,
зачастую происходит при обрыве соединения по причине сбоев сети или отказа удаленной системы без предупреждения.
функция Winsock версии 2 WSASend — аналог send, определена так:
int WSASend(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE
);
Сокет является действительным описателем сеанса соединения. Второй параметр указывает на структуру WSABUF или на массив этих структур. Тре- тий — определяет число передаваемых структур WSABUF. Помните, что струк- тура WSABUF включает сам символьный буфер и его длину. Может возникнуть вопрос: зачем нужно отправлять более одного буфера за раз? Это называет- ся комплексным вводом-выводом (scatter-gather I/O). Подробней мы обсудим его далее, а сейчас лишь отметим, что при использовании нескольких буфе- ров для отправки данных по сокету соединения массив буферов отправля- ется, начиная с первой и заканчивая последней структурой WSABUF.
Параметр lpNumberOfBytesSent — указатель на тип DWORD, который пос- ле вызова WSASend содержит общее число переданных байт. Параметр фла- гов dwFlags такой же, что и в функции send. Последние два указателя — lp-
Overlapped и lpCompletionROUTINE используются для перекрытого ввода-вы-
вода (overlapped I/O) — одной из моделей асинхронного ввода-вывода, под- держиваемых Winsock (см. также главу 8).
WSASend присваивает параметру ipNumberOjBytesSent количество записан- ных байт. При успешном выполнении функция возвращает 0, иначе — SO-
^KET_ERROR. Ошибки те же, что и у функции send.
Функция WSASendDisconnect
то специализированная функция используется редко. Она определена так:
i n t WSASendDisconnect (
SOCKET s, i--w
LPWSABUF lpOUT boundDisconnectData
ii интерфейс прикладного программирования Winsock
Срочные данные
Если приложению требуется отправить через потоковый сокет инфор- мацию более высокого приоритета, оно может обозначить эти сведе- ния как срочные данные (out-of-band, OOB) Приложение с другой сто- роны соединения получает и обрабатывает ООВ-данные через отдель- ный логический канал, концептуально независимый от потока данных
В TCP передача ООВ-данных реализована путем добавления 1 -би- тового маркера (называемого URG) и 1б-битного указателя в заголов- ке сегмента TCP, которые позволяют выделить важные байты в основ- ном трафике На данный момент для TCP существуют два способа вы- деления срочных данных В RFC 793, описывающем TCP и концепцию срочных данных, говорится, что указатель срочности в заголовке TCP
является положительным смещением байта, следующего за байтом срочных данных Однако в RFC 1122 это смещение трактуется, как ука- затель на сам байт срочности
В спецификации Winsock под термином ООВ понимают как неза- висимые'от протокола ООВ-данные, так и реализацию механизма пе- редачи срочных данных в TCP Для проверки, есть ли в очереди сроч- ные данные, вызовите функцию toctlsocket с параметром SIOCATMARK
Подробнее об этой функции — в главе 9
В Winsock предусмотрено несколько способов передачи срочных данных Можно встроить их в обычный поток, либо, отключив эту воз- можность, вызвать отдельную функцию, возвращающую только сроч- ные данные Параметр SOJDOBINLJNE управляет поведением ООВ-дан- ных (он подробно обсуждается в главе 9)
В ряде случаев срочные данные используют программы Telnet и
Rlogin Впрочем, если вы не планируете писать собственные версии этих программ, избегайте применения срочных данных — они не стандартизированы и могут иметь другие реализации на отличных от
Win32 платформах Если вам нужно время от времени передавать сроч- но какую-то информацию, создайте отдельный управляющий сокет для срочных данных, а основное соединение предоставьте для обыч- ной передачи данных
Функция WSASendDtsconnect начинает процесс закрытия сокета и отправ- ляет соответствующие данные Разумеется, она доступна только для протоко- лов, поддерживающих постепенное закрытие и передачу данных при его осу- ществлении Ни один из существующих поставщиков транспорта на данный момент не поддерживает передачу данных о закрытии соединения Функция
WSASendDisconnect действует аналогично shutdown с параметром SD_SEND, но также отправляет данные, содержащиеся в параметре boundDisconnectData
После ее вызова отправлять данные через сокет невозможно В случае неудач- ного завершения WSASendDisconnect возвращает значение SOCKET_ERROK
Ошибки, встречающиеся при работе функции, аналогичны ошибкам send

Г Л А В А 7 Основы Wmsock 171
функции recv и WSARecv
Функция recv — основной инструмент приема данных по сокету Она опре- делена так
int recv(
SOCKET s,
char FAR» buf,
int len,
int flags
),
Параметр s определяет сокет для приема данных Второй параметр — buf
является символьным буфером и предназначен для полученных данных, a len
указывает число принимаемых байт или размер буфера buf Последний па- раметр — flags, может принимать значения О, MSG_РЕЕК, MSGJDOB или ре- зультат логического ИЛИ над любыми из этих параметров Разумеется, 0 оз- начает отсутствие особых действий Флаг MSG_PEEK указывает, что доступ- ные данные должны копироваться в принимающий буфер и при этом оста- ваться в системном буфере По завершении функция также возвращает ко- личество ожидающих байт
Считывать сообщения таким образом не рекомендуется Мало того, что из-за двух системных вызовов (одно! о — для считывания данных, и друго- го, без флага MSGJPEEK — для удаления данных), снижается производитель- ность В ряде случаев этот способ просто не надежен Объем возвращаемых данных может не соответствовать их суммарному доступному количеству К
тому же, сохраняя данные в системных буферах, система оставляет все мень- ше памяти для размещения входящих данных В результате уменьшается раз- мер окна TCP для всех отправителей, что не позволяет приложению достичь максимальной производительности Лучше всего скопировать все данные в собственный буфер и обрабатывать их там Флаг MSGJDOB уже обсуждался ранее при рассмотрении отправки данных
Использование recv в сокетах, ориентированных на передачу сообщений или дейтаграмм, имеет несколько особенностей Если при вызове recv раз- мер ожидающих обработки данных больше предоставляемого буфера, то после его полного заполнения возникает ошибка WSAEMSGSIZE Заметьте ошибка превышения размера сообщения происходит только при использо- вании протоколов, ориентированных на передачу сообщений Потоковые протоколы буферизируют поступающие данные и при запросе приложени- ем предоставляют их в полном объеме, даже если количество ожидающих обработки данных больше размера буфера Таким образом, ошибка WSAEMS-
GSIZE не может произойти при работе с потоковыми протоколами
Функция WSARecv обладает дополнительными по сравнению с recv воз- можностями поддерживает перекрытый ввод-вывод и фрагментарные дей-
Т а граммные уведомления l n
* WSARecv(
SOCKET s,
LPWSABUF lpBuffers,

1 72 ЧАСТЬ II Интерфейс прикладного программирования Winsock
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD IpFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE
);
Параметр 5 — сокет соединения. Второй и третий параметры определяют буферы для приема данных. Указатель IpBuffers ссылается на массив структур
WSABUF, a dwBufferCount — определяет количество таких структур в массиве. Па- раметр ipNumberOfBytesReceived в случае немедленного завершения операции получения данных указывает на количество принятых этим вызовом байт. Па- раметр IpFlags может принимать значения MSGJPEEK, MSGJDOB, MSG_PARTIAL
или результат логического ИЛИ над любыми из этих параметров.
У флага MSG_PARTIAL в зависимости от способа использования могут быть разные значения и смысл. Для протоколов, ориентированных на передачу сообщений, этот флаг задается после вызова WSARecv (если все сообщение не может быть возвращено из-за нехватки места в буфере). В этом случае каждый последующий вызов WSARecv задает флаг MSG_PARTIAL, пока сообще- ние не будет прочитано целиком. Если этот флаг передается как входной параметр, операция приема данных должна завершиться, как только данные будут доступны, даже если это только часть сообщения. Флаг MSG_PARTIAL
используется только с протоколами, ориентированными на передачу сооб- щений. Запись каждого протокола в каталоге Winsock содержит флаг, указы- вающий на поддержку этой возможности (см. также главу 5). Параметры
lpOverlapped и lpCompletionROUTINE применяются в операциях перекрыто- го ввода-вывода (обсуждаются в главе 8).
Функция WSARecvDisconnect
Эта функция обратна WSASendDisconnect и определена так:
int WSARecvDisconnect(
SOCKET s,
LPWSABUF lpInboundDisconnectData
);
Как и у WSASendDisconnect, ее параметрами являются описатель сокета соединения и действительная структура WSABUF для приема данных. Функ- ция принимает только данные о закрытии соединения, отправленные с дру- гой стороны функцией WSASendDisconnect, ее нельзя использовать для при- ема обычных данных. К тому же, сразу после принятия данных она прекра- щает прием с удаленной стороны, что эквивалентно вызову shutdown с па- раметром SD_RECV.
Функция WSARecvEx
Эта функция — специальное расширение Microsoft для Winsock 1. Она иД
е
*
тична recv во всем, кроме того, что параметру7а§5 передается по ссылке. •

*
позволяет базовому поставщику задавать флаг MSG_PARTIAL.

Г Л А В А 7 Основы Winsock 173
int PASCAL FAR WSARecvEx(
SOCKET s,
char FAR « buf,
int len,
int «flags
);
Если полученные данные не составляют полного сообщения, в парамет- ре flags возвращается флаг MSG_PARTIAL. Он используется только с протоко- лами, ориентированными на передачу сообщений. Когда при принятии не- полного сообщения флаг MSG_PARTIAL передается как часть параметрами^,
функция завершается немедленно, вернув принятые данные. Если в буфере не хватает места, чтобы принять сообщение целиком, WSARecvEx вернет ошибку WSAEMSGSIZE, а оставшиеся данные будут отброшены. Обратите вни- мание на разницу между флагом MSG_PARTIAL и ошибкой WSAEMSGSIZE: в случае ошибки сообщение поступило целиком, однако соответствующий буфер слишком мал для его приема. Флаги MSG_PEEK и MSGJDOB также мож- но использовать в WSARecvEx.
Потоковые протоколы
Большинство протоколов с установлением соединения являются потоковы- ми. Важно учитывать, что при использовании любой функции отправки или приема данных через потоковый сокет нет гарантии, что вы прочитаете или запишете весь запрошенный объем данных. Скажем, требуется отправить
2048 байт из символьного буфера функцией send:
char sendbuff[2048];
int nBytes = 2048;
// Заполнение буфера sendbuff 2048 байтами данных
// Присвоение s значения действительного потокового сокета соединения
ret = send(s, sendbuff, nBytes, 0);
Возможно, функция send сообщит об отправке менее 2048 байт. Перемен- ная ret будет содержать количество переданных байт, поскольку система вы- деляет определенное количество буферного пространства на отправку и прием сообщений для каждого сокета. При отправке данных внутренние бу- феры удерживают их до момента отправки непосредственно по проводу,
фичиной неполной отправки может быть, например, передача большого количества данных, при этом все буферы слишком быстро заполнятся.
В TCP/IP также существует так называемый размер окна. Принимающая торона регулирует его, указывая количество данных, которое способна при- ь. При переполнении данными получатель может задать нулевой размер
К Н а '
ч т обы справиться с поступившими данными. Это приведет к приоста- овке отправки данных, пока размер окна не станет больше 0. В нашем слу- е
Р
а з мер буфера может оказаться равным 1024 байтам, следовательно, по-
Р уется повторно отправить оставшиеся 1024 байта. Отправку всего содер-
°го буфера обеспечит следующий фрагмент программы:

1 74 ЧАСТЬ II Интерфейс прикладного программирования Winsock char sendbuff[2048];
int nBytes = 2048,
nLeft,
ldx;
// Заполнение буфера sendbuff 2048 байтами данных
// Присвоение s значения действительного потокового сокета соединения
nLeft = nBytes;
idx = 0;
while (nLeft > 0)
{
ret = send(s, &sendbuff[idx], nLeft, 0);
if (ret == SOCKET_ERROR)
{
fy_ // Ошибка
"" l
-Як '
nLeft -= ret;
idx += ret;
}
Все, сказанное далее справедливо и для приема данных на потоковом сокете, но для нас это не очень важно. Приложение не знает, сколько дан- ных оно в очередной раз прочтет на потоковом сокете. Если вам нужно от- править дискретные сообщения по потоковому протоколу, это не составит труда. Например, если у всех сообщений одинаковый размер (512 байт), то прочитать их можно так:
char recvbuff[1024];
int ret,
nLeft,
idx;
nLeft = 512;
idx = 0;
while (nLeft > 0)
{
ret = recv(s, &recvbuff[idx], nLeft, 0);
if (ret == SOCKET_ERROR)
{
// Ошибка
}
idx += ret;
nLeft -= ret;
}
Ситуация несколько усложнится, если размер сообщений будет варьиро- ваться. Тогда потребуется реализовать собственный протокол, сообщающий получателю о размере поступающего сообщения. Пусть первые 4 байта со- общения указывают его размер в виде целого числа. Преобразовав их в чис- ло при чтении, приемник узнает длину сообщения.

Г Л А В А 7 Основы Winsock
175
Комплексный ввод-вывод
Впервые принцип комплексного ввода-вывода (Scatter-Gather I/O) был применен в функциях recv и writev сокетов Беркли (Berkeley Sockets).
В Winsock 2 его поддерживают функции WSARecv, WSARecvFrom, WS -
Send и WSASenaTo. Комплексный ввод-вывод наиболее полезен для п; i- ложений, отправляющих и принимающих данные в специфическом формате. Например, если передаваемые клиентом серверу сообщения должны состоять из фиксированного 32-байтного заголовка, опреде- ляющего некие действия, 64-байтного блока данных и заканчиваться
1б-байтной контрольной суммой. В этом случае функция WSASend мо- жет быть вызвана для массива соответствующих трех структур WSABUF.
На принимающей стороне одним из входных параметров вызываемой функции WSARecv также должны быть три структуры WSABUF, содержа- щие 32,64 и 16 байт.
При использовании потоковых сокетов операции комплексного ввода-вывода просто интерпретируют несколько буферов данных как один непрерывный. Функция принятия данных может завершиться раньше, чем будут заполнены все буферы. В сокетах, ориентированных на передачу сообщений, каждая операция получения данных прини- мает одно сообщение, длина которого не больше размера буфера. Если в буфере недостаточно места, вызов заканчивается ошибкой WSAE-
MSGSIZE, и данные усекаются до размера доступного пространства.
Разумеется, в протоколах, поддерживающих фрагментарные сообще- ния, для предотвращения потери данных можно использовать флаг
MSG PARTIAL.
Завершение сеанса
По окончании работы с сокетом необходимо закрыть соединение и освобо- дить все ресурсы, связанные с описателем сокета, вызвав функцию dosesocket.
Впрочем, ее неправильное использование может привести к потере данных.
Поэтому перед вызовом dosesocket сеанс нужно корректно завершить фун- кцией shutdown.
Функция shutdown
Правильно написанное приложение уведомляет получателя об окончании отправки данных. Так же должен поступить и узел. Такое поведение называ- ется корректным завершением сеанса и осуществляется с помощью функции
shutdown-.
int shutdown(
SOCKET s,
int how
Параметр how может принимать значения SD_RECEIVE, SD_SEND или SD_
BOTH. Значение SD_RECEIVE запрещает все последующие вызовы любых

1 76 ЧАСТЬ II Интерфейс прикладного программирования Winsock функций приема данных, на протоколы нижнего уровня это не действует
Если в очереди ТСР-сокета есть данные, либо они поступают позже, соеди- нение сбрасывается. UDP-сокеты в аналогиной ситуации продолжают при- нимать данные и ставить их в очередь. SDJSEND запрещает все последующие вызовы функций отправки данных. В случае ТСР-сокетов после подтвержде- ния получателем приема всех отправленных данных передается пакет FIN
Наконец, SDBOTH запрещает как прием, так и отправлку.
Функция closesocket
Эта функция закрывает сокет. Она определена так:
int closesocket (SOCKET s);
Вызов closesocket освобождает дескриптор сокета, и все дальнейшие опе- рации с сокетом закончатся ошибкой WSAENOTSOCK. Если не существует других ссылок на сокет, все связанные с дескриптором ресурсы будут осво- бождены, включая данные в очереди.
Ожидающие асинхронные вызовы, исходящие от любого потока данно- го процесса, отменяются без уведомления. Ожидающие операции перекры- того ввода-вывода также аннулируются. Все выполняющиеся события, про- цедура и порт завершения, связанные с перекрытым вводом-выводом, завер- шатся ошибкой WSA_OPERATION_ABORTED. (Асинхронные и неблокирующие модели ввода-вывода более подробно обсуждаются в главе 8.) Другой фак- тор, влияющий на поведение функции closesocket, — значение параметра сокета SOJLINGER (его полное описание — в главе 9).
Пример
Разнообразие функций отправки и приема данных удивительно. Но в дей- ствительности большинству приложений для приема информации требуют- ся только recv или WSARecv, а для отправки — send или WSASend. Другие функ- ции обеспечивают специальные возможности и редко используются (или поддерживаются только транспортными протоколами).
Теперь рассмотрим небольшой пример клиент-серверного взаимодей- ствия с учетом описанных принципов и функций. В листинге 7-1 приведен код простого эхо-сервера. Приложение создает сокет, привязывает его к ло- кальному IP-интерфейсу и порту, и слушает соединения клиентов. После при- ема от клиента запроса на соединение создается новый сокет, который пере- дается клиентскому потоку. Поток читает данные и возвращает их клиенту.
Листинг 7-1. Эхо-сервер
// Имя модуля: Server.с
// Описание:
// Это пример простого сервера TCP, принимающего
// соединения клиентов. После установления соединения
// порождается процесс, который получает данные клиента
// и отправляет их обратно (если параметр возврата данных
// не выключен).

Г Л А В А 7 Основы Winsock 177
Листинг 7-1. (продолжение)
II
II Параметры компиляции:
// cl -о Server Server.с ws2_32.1ib
// Параметры командной строки:
// server [-p:x] [-i:IP] [-о]
// -р:х Номер порта, на котором будут прослушиваться соединения
ц -i:str Интерфейс, на котором будут прослушиваться соединения
// -о Только приенимать данные, не возвращая их клиенту
//
«include
«include
«include **
«define DEFAULT_PORT 5150
HM
«define DEFAULT_BUFFER 4096
int iPort = DEFAULT_PORT; // Порт прослушивания клиентов
BOOL blnterface = FALSE, // Прослушивать указанный интерфейс
bRecvOnly = FALSE; // Только прием данных
char szAddress[128]; // Интерфейс прослушивания клиентов
// Функция: usage p
//
// Описание: ^
// Выводит сведения о параметрах командной строки и выходит
//
void usage()
41
{
printf("usage: server [-p:x] [-1:IP] [-o]\n\n");
printf(" -p;x Port number to listen on\n");
printf(" -i:str Interface to listen on\n");
printf(" -o Don't echo the data back\n\n");
ExitProcess(i);
// «Пункция: ValidateArgs
//
// Описание:
// Анализирует параметры командной строки и задает
/ некоторые глобальные флаги для указания выполняемых действий
*
O i d V a l i d
ateArgs(int argc, char ..argv)
;(0 Mi -U0 .ii^dst ,»oo«)voe
nt
i; sNMeuqeei -a<|0» \\ (0 «•
;Мв см. след. стр.

1 78 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Листинг 7-1. (продолжение)
for(i = 1; 1 < argc;
if ((argv[i][0] = = • - • ) || (argv[i][O] == '/•))
{
switch (tolower(argv[i][1]))
{
case 'p':
iPort = atoi(&argv[i][3]);
break;
case 'i':
blnterface = TRUE;
if (strlen(argv[i]) > 3)
strcpy(szAddress, &argv[i][3]);
break;
case 'o':
bRecvOnly = TRUE;
break;
default;
usage();
break;
// Функция: ClientThread
// •- *
II Описание:
// Вызывается в качестве потока, управляет данным клиентским
// соединением. Входным параметром является описатель сокета,
// возвращаемый функцией accept(). Функция получает данные
// от клиента и отправлает их обратно.
//
DWORD WINAPI ClientThread(LPVOID lpParam)
SOCKET
char
int
sock=(SOCKET)lpParam;
szBuff[DEFAULT_BUFFER];
ret,
nLeft,
idx;
>' w h i l e d ) те . N, Лонднак
' { г г.- v-
// Блокирующий вызов recv()
// -л» sat)
ret = recv(sock, szBuff, DEFAULT.BUFFER, 0);
if (ret == 0) // Корректное завершение ^
break;

1   ...   13   14   15   16   17   18   19   20   ...   50


написать администратору сайта