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

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

Начнем рассмотрение функций обмена данными с отправки. Для передачи данных серверу существует функция send, а для второй версии библиотеки WinSock - WSASend. Функция send описывается следующим образом:

function send(
s: TSocket:
var Buf;
len: Integer:
flags: Integer ): Integer: stdcal1;

Рассмотрим параметры данной функции:

• s - сокет, через который будет происходить отправка данных. У вас в программе может быть открыто одновременно несколько соединений с разными серверами, и нужно четко определить, какой сокет использовать;
• buf - буфер, содержащий данные, которые необходимо отправить;
• len - длина буфера в параметре buf;
• fl ags - флаги, определяющие метод отправки. Здесь можно указывать сочетание из следующих значений:
О 0 - флаги не указаны;
О MSGD0NTR0UTE - отправляемые пакеты не надо маршрутизировать. Если транспортный протокол, отправляющий данные, не поддерживает этот флаг, то он игнорируется;
О MSG_00B - данные должны быть отправлены вне очереди (out of band), то есть срочно.

Функция WSASend доступна только во второй версии библиотеки, и нужно подключать заголовочный файл WinSock 2. Описание функции следующее:

function WSASend( s: TSocket;
IpBuffers: LPWSABUF;
dwBufferCount: DWORD;
var lpNumberOfBytesSent: DWORD;
dwFlags: DWORD:
lpOverlapped: LPWSAOVERLAPPED;
1pCompleti onRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE ): Integer: stdcal1;

Рассмотрим параметры функции WSASend:

• s - сокет, через который будет происходить отправка данных;
в 1 pBuf f ers - структура или массив структур типа WSABUF. С этой структурой мы познакомились, когда рассматривали функцию connect. Она же использовалась для отправки данных во время соединения;
в dwBuf ferCount - количество структур в параметре 1 pBuf fers;
в 1 pNumberOfBytesSent - количество переданных 6aftTOBt если операции ввода-вывода уже завершились;
в dwFl ags - определяет метод отправки и может принимать такие же значения, как параметр dwFl ags функции send;

в lpOverlapped и lpCompletionRoutine - используются при перекрытом вводе-выводе (overlapped I/O). Это одна из моделей асинхронной работы сети поддерживаемой WinSock.

Если функция send (или WSASend) отработала успешно, то она возвратит количество отправленных байтов, в противном случае - значение -1 (константа S0CKET_ ERROR). Получить код ошибок вы можете с помощью функции WSAGetLastError:

в WSANOTINITIALISED - сначала необходимо вызвать функцию WSASturtup, а потом создавать сокет;
в WSAENETDOWN - связь нарушена, возможные причины - отошел кабель или произошло отключение от Интернета;
в WSAECONNABORTED - соединение было прервано, или вышло время ожидания, или произошла другая ошибка;
в WSAEACCES - указанный адрес является широковещательным, но нужный флаг не выставлен;
в WSAEINTR - блокирующая операция была прервана функцией WSACancel Block -ingCall;
в WSAEINPROGRESS - выполняется операция в блокирующем режиме. Вы уже запустили на выполнение какую-то функцию и нужно дождаться завершения ее работы;
в WSAENETRESET - удаленный компьютер прервал соединение, необходимо закрыть сокет;
в WSAENOBUFS - нет доступных буферов;
в WSAEN0TC0NN - соединение не установлено. Или вы забыли установить соединение с сервером, или оно уже было прервано; .
в WSAE0PN0TSUPP - указан флаг MSG00B, но сокет настроен не в режиме потока (SOCK_STREAM), или не поддерживается внеочередная передача данных, или сокет однонаправленный и может только отправлять данные;
в WSAESHUTDOWN - сокет закрыт. Возможно, была вызвана функция shutdown;
в WSAEWOULDBLOCK - сокет помечен как неблокирующий, а запрошенная операция будет заблокирована;
в WSAEMSGSIZE - сокет настроен как ориентированный на сообщения (например протокол UDP), а размер пакета больше возможного. Только при использовании соединения TCP можно отправлять данные больше возможного пакета, потому что здесь разбиение происходит автоматически;
• WSAEHOSTUNREACH - удаленный узел не может быть доступен в текущий момент;
• WSAEINVAL - сокет не был привязан функцией bi nd, или указан неизвестный флаг, или установлен флаг MSG_00B для сокета с включенной опцией S0_0OBINLINE;
• WSAECONNABORTED - виртуальное соединение было прервано из-за превышения времени ожидания или другой ошибки;
• WSAECONNRESET - виртуальное соединение было закрыто. Для сокетов UDP удаленный хост не распознал предыдущую посылку и ответил сообщением "Port Unreachable* (порт недосягаем). Приложение должно закрыть сокет;

• WSAETIMEDOUT - время ожидания ответа вышло.

Для получения данных используются функции recv и WSARecv (для второй версии WinSock). Функция recv описывается следующим образом:

function recv(
s: TSocket:
var Buf:
len: Integer:
flags: Integer ): Integer: stdcall:

Параметры очень похожи на те, которые мы видели у функции send:

• s - сокет, чьи данные надо получить;
• buf - буфер, в который будут помещены принятые данные;
• 1 en - длина буфера в параметре buf;

• f 1 ags - флаги, определяющие метод получения. Здесь можно указывать сочетание из следующих значений:

О 0 - флаги не указаны;
О MSG_PEEK - данные должны быть считаны из системного буфера и не удалены. По умолчанию считанные данные удаляются из системного буфера;

О MSG00B - обработать срочные данные (out of band).

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

Если функция recv вернула нулевое значение, то есть не произошло ошибок и получено 0 байт, то это означает, что клиент завершил соединение (оборвалась связь или сеанс завершен) и сокет можно закрывать.

ПРИМЕЧАНИЕ -

В параметре len функции recv всегда указывайте размер буфера, а не размер данных, которые нужно получить. Если данных для получения будет больше, чем позволяет сохранить буфер, то может произойти эффект переполнения, о котором мы уже говорили В этом случае считываемые по сети данные выйдут за пределы буфера, таким образом возможна перезапись критически важной для системы информации.

Функция WSARecv описывается следующим образом:

function WSARecv( s: TSocket;
lpBuffers: LPWSABUF;
dwBufferCount. DWORD;
var lpNumberOfBytesRecvd: DWORD ; var lpFlags: DWORD;
lpOverlapped: LPWSAOVERLAPPED:
1pCompleti onRouti ne• LPWSAOVERLAPPED_COMPLETION_ROUTINE )- Integer;
stdcall:

Здесь также бросается в глаза сходство в параметрах с функцией WSASend. Давайте рассмотрим их назначение:

в s - сокет, через который будет происходить получение данных;
в 1 pBuffers - структура или массив структур типа WSABUF. В эти буферы будут помещены полученные данные;
в dwBufferCount - количество структур в параметре lpBuffers;
в 1 pNumberOfBytesRecvd - количество полученных байтов, если операции ввода-вывода уже завершились;
в lpFlags - определяет метод отправки и может принимать такие же значения, как и параметр dwFl ags функции recv Содержит новый флаг MSG_PARTIAL Его нужно указывать для протоколов, ориентированных на сообщения, когда данные нельзя прочитать за один прием В этом случае мы получим часть данных, а остальное сможем прочитать при следующем считывании;

в lpOver lapped и lpCompl eti onRouti ne - используются в перекрытом вводе-выводе (overlapped I/O). Это одна из моделей асинхронной работы сети, поддерживаемой WinSock.

Стоит заметить, что если при использовании функции получения данных, вы применяете протокол, ориентированный на передачу сообщений (UPD) и указали недостаточный размер буфера, функция возвратит ошибку WSAEMSGSIZE. Если протокол потоковый (TCP), то такая ошибка не может возникнуть, потому что получаемые данные кэшируются в системе и предоставляются приложению полностью. В этом случае, если указан недостаточный буфер, оставшиеся данные можно получить при последующем считывании.

Во время получения данных могут произойти следующие ошибки:

в WSANOTINITIALISED - сначала необходимо вызвать функцию WSASturtup, а потом создавать сокет;
в WSAENETDOWN - связь нарушена, возможные причины - отошел кабель или произошло отключение от Интернета;
• WSAEN0TC0NN - соединение не установлено. Или вы забыли установить соединение с сервером, или оно уже было прервано;
• WSAECONNABORTED - соединение было прервано, или вышло время ожидания, или произошла другая ошибка;
• WSAEINTR - блокирующая операция была прервана функцией WSACancel Bl ocking-Call;
• WSAEINPROGRESS - выполняется операция в блокирующем режиме. Вы уже запустили на выполнение какую-то функцию и нужно дождаться завершения ее работы;
• WSAENETRESET - удаленный компьютер прервал соединение, необходимо закрыть сокет;
• WSAEN0TS0CK - указанный дескриптор не является сокетом;
• WSAE0PN0TSUPP - указан флаг MSG00B, но сокет настроен не в режиме потока (SOCKSTREAM), или не поддерживается внеочередная передача данных, или сокет однонаправленный и может только отправлять данные;
• WSAESHUTDOWN - сокет закрыт. Возможно, что была вызвана функция shutdown;
• WSAEWOULDBLOCK - сокет помечен как неблокирующий, а запрошенная операция будет заблокирована;
• WSAEMSGSIZE - сокет настроен как ориентированный на сообщения (например, протокол UDP), а размер пакета больше возможного. Только при использовании соединения TCP можно отправлять данные больше возможного пакета, потому что здесь разбиение происходит автоматически;
• WSAEINVAL- сокет не был привязан функцией bind, или указан неизвестный флаг, или указан флаг MSG00B для сокета с включенной опцией S0 00BINLINE;
• WSAECONNABORTED - виртуальное соединение было прервано из-за превышения времени ожидания или другой ошибки;

• WSAECONNRESET - виртуальное соединение было закрыто. Для сокетов UDP удаленный хост не распознал предыдущую посылку и ответил сообщением "Port Unreachable* (порт недосягаем). Приложение должно закрыть сокет.

• WSAETIMEDOUT - время ожидания ответа вышло.

Есть еще одна интересная сетевая функция, которая появилась в WinSock 2, - TransmitFile. Она сразу отправляет целый файл по сети. Если все рассмотренные функции (без префикса WSA) относились к сетевой библиотеке и существуют не только в Windows, но и в системах Unix, то этой функции на других платформах может не существовать.

Функция TransmitFile отправляет файл по сети. Это происходит достаточно быстро, потому что отправка идет в самой библиотеке в режиме "ядра". Вам не надо заботится о последовательном чтении и проверять количество отправленных данных, потому что это гарантируется библиотекой WinSock 2.

Функция TransmitFile описывается следующим образом:

function TransmitFile( hSocket: TSocket:
hFile. THandle;
nNumberOfBytesToWrite DWORD ; nNumberOfBytesPerSend DWORD : IpOverlapped: POverlapped; 1pTransmi tBuffers : PTransmi tFi1eBuffers;
dwFlags. DWORD ): BOOL: stdcall•

Рассмотрим ее параметры:

о hSocket - сокет, через который нужно отправить данные;
о hFile - указатель на открытый файл, данные которого надо отправить;
в nNumberOfBytesToWrite - количество байтов из файла, которые надо отправить. Если указать 0, то будет отправлен весь файл;
о nNumberOfBytesPerSend - размер пакета для отправки Если указать 1024, то данные будут отправляться пакетами по 1024 байта данных. Установив 0, будет использовано значение по умолчанию;
в IpOverlapped - применяется при перекрестном вводе-вы воде;

о IpTransmitBuffers - содержит данные, которые надо отправить до и после отправки файла. По этим данным вы можете на принимающей стороне определить, что началась ияи закончилась передача данных.

о dwFl ags - флаги. Здесь можно указать следующие значенияО TFDISC0NNECT - закрыть сокет после передачи данных;
О TF_REUSE_S0CKET - подготовить сокет для повторного использования;

О TFWRITEBEHIND - разрешается завершить работу, не дожидаясь подтверждения о получении данных со стороны клиента.

Параметр IpTransmitBuffers имеет тип структуры следующего вида:

_TRANSMIT_FILE_BUFFERS = record Head Pointer. HeadLength: DWORD ; Tail. Pointer;
Tail Length: DWORD ;
end:
TTransmitFileBuffers = _TRANSMIT_FILE_BUFFERS.

Как и для других структур, здесь есть свой псевдоним TTransmitFil eBuf fers, по которому и следует обращаться к структуре. У структуры имеются такие параметры:

о Head - указатель на буфер, содержащий данные, которые надо послать клиенту до начала отправки файла;
о HeadLength - размер буфера head;
в Tail - указатель на буфер, содержащий данные, которые надо послать клиенту после завершения отправки файла;

в Tai 1 Length - размер буфера Tai 1.

6 Зак 408

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

4.6. Функции клиента || Оглавление || 4.8. Завершение соединения


Delphi в шутку и всерьез: что умеют хакеры



Новости за месяц

  • Январь
    2022
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс