Чтение онлайн

ЖАНРЫ

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

В WinSock не документируется, в какую именно часть буфера помещаются адрес клиента и принявшего его сокета. Вместо этого предоставляется функция

GetAcceptExSockAddrs
, прототип которой приведен в листинге 2.83.

Листинг 2.83. Функция
GetAcceptExSockAddrs

procedure GetAcceptExSockAddrs(lpOutputBuffer: Pointer; dwReceiveDataLength: DWORD; dwLocalAddressLength: DWORD; dwRemoteAddressLength: DWORD; var LocalSockaddr: PSockAddr; var LocalSockaddrLength: Integer; var RemoteSockaddr: PSockAddr; var RemoteSockaddrLength: Integer);

Примечание

В Delphi до 7-й версии включительно модуль WinSock содержит ошибку — параметры

LocalSockaddr
и
RemoteSockaddr
функции
GetAcceptExSockAddrs
имеют в нем тип
TSockAddr
вместо
PSockAddr
. Из-за этой ошибки функцию
GetAcceptExSockAddrs
в этих версиях Delphi необходимо самостоятельно импортировать. Следует заметить, что во многих модулях для WinSock 2 от независимых разработчиков объявление этой функции скопировано из стандартного модуля вместе с ошибкой.

Первые четыре параметра функции

GetAcceptExSockAddrs
определяют буфер, в котором в результате вызова
AcceptEx
оказались данные от клиента и адреса, и размеры частей буфера, зарезервированных для данных и для адресов. Значения этих параметров должны совпадать со значениями аналогичных параметров в соответствующем вызове
AcceptEx
. Через параметр
LocalSockaddrs
возвращается указатель на то место в буфере, в котором хранится адрес привязки сокета
sAcceptSocket
, а через параметр
LocalSockaddrsLength
— длина адреса (16 в случае TCP). Адрес клиента и его длина возвращаются через параметры
RemoteSockaddrs
и
RemoteSockaddrsLength
. Следует особенно подчеркнуть, что указатели
LocalSockaddrs
и
RemoteSockaddrs
указывают именно на соответствующие части буфера: память для них специально не выделяется и, следовательно, не должна освобождаться, а свою актуальность они теряют при освобождении буфера.

Последняя из дополнительных функций,

TransmitFile
, служит для передачи файлов по сети. Ее прототип приведен в листинге 2.84.

Листинг 2.84. Функция
TransmitFile

function TransmitFile(hSocket: TSocket; hFile: THandle; nNumberOfBytesToWrite, nNumberOfBytesPerSend: DWORD; lpOverlapped: POverlapped; lpTransmitBuffers: PTransmitFileBuffers; dwReserved: DWORD): BOOL;

Функция

TransmitFile
отправляет содержимое указанного файла через указанный сокет. При этом допускаются только протоколы, поддерживающие соединение, т.е. использовать данную функцию с UDP-сокетом нельзя. Сокет задается параметром
hSocket
, файл — параметром
hFile
. Дескриптор файла обычно получается с помощью функции стандартного API
CreateFile
. Файл рекомендуется открывать с флагом
FILE_FLAG_SEQUENTIAL_SCAN
, т.к. это повышает производительность.

Параметр

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

Функция

TransmitFile
кладет данные из файла в буфер сокета по частям. Параметр
nNumberOfBytesPerSend
определяет размер одной порции данных. Он может быть равен нулю — в этом случае система сама определяет размер порции. Этот параметр критичен только в случае дейтаграммных протоколов, потому что при этом размер порции определяет размер дейтаграммы. Для TCP данные, хранящиеся в буфере, передаются в сеть целиком или по частям в зависимости от загрузки сети, готовности принимающей стороны и т.п., а какими порциями они попали в буфер, на размер пакета почти не влияет. Поэтому для TCP-сокета параметр
nNumberOfBytesPerSend
лучше установить равным нулю.

Параметр

lpOverlapped
указывает на запись
TOverlapped
, использующуюся для перекрытого ввода-вывода. Эту структуру мы обсуждали при описании функции
AcceptEx
. В отличие от
AcceptEx
, в
TransmitFile
этот параметр добыть равным
nil
, и тогда операция передачи файла не будет перекрытой.

Если параметр

lpOverlapped
равен
nil
, передача файла начинается с той позиции, на которую указывает файловый указатель (для только что открытого файла этот указатель указывает на его начало, а переместить его можно, например,
с помощью функции
SetFilePointer
; также он перемещается при чтении файла с помощью
ReadFile
). Если же параметр
lpOverlapped
задан, то передача файла начинается с позиции, заданной значениями полей
Offset
и
OffsetHigh
, которые должны содержать соответственно младшую и старшую часть 64-битного смещения стартовой позиции от начала файла.

Параметр

lpTransmitBuffers
является указателем на запись
TTransmitFileBuffers
, объявленную так, как показано в листинге 2.85.

Листинг 2.85. Тип
TTransmitFileBuffers

PTransmitFileBuffers = ^TTransmitFileBuffers;

_TRANSMIT_FILE_BUFFERS = record

 Head: Pointer;

 HeadLength: DWORD;

 Tail: Pointer;

 TailLength: DWORD;

end;

TTransmitFileBuffers = _TRANSMIT_FILE_BUFFERS;

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

Head
содержит указатель на буфер, содержащий данные, предназначенные для отправки перед файлом,
HeadLength
— размер этих данных. Аналогично
Tail
и
TailLength
определяют начало и длину буфера с данными, которые передаются после передачи файла. Если передача дополнительных данных не нужна, параметр
lpTransmitBuffer
может быть равен
nil
.

Допускается и обратная ситуация: параметр

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

Последний параметр функции

TransmitFile
в модуле
WinSock
имеет имя
Reserved
. В WinSock 1 он и в самом деле был зарезервирован и не имел смысла, но в WinSock 2 через него передаются флаги, управляющие операцией передачи файла. Мы не будем приводить здесь полный список возможных флагов (он есть в MSDN), а ограничимся лишь самыми важными. Указание флага
TF_USE_DEFAULT_WORKER
или
TF_USE_SYSTEM_THREAD
позволяет повысить производительность при передаче больших файлов, a
TF_USE_KERNEL_APC
— при передаче маленьких файлов. Вообще, при работе с функцией
TransmitFile
чтение файла и передачу данных в сеть осуществляет ядро операционной системы, что приводит к повышению быстродействия по сравнению с использованием
ReadFile
и
send
самой программой.

Функция

TransmitFile
реализована по-разному в серверных версиях Windows NT/2000 и в остальных системах: в серверных версиях она оптимизирована по быстродействию, а в остальных — по количеству необходимых ресурсов.

Данные, переданные функцией

TransmitFile
, удаленная сторона должна принимать обычным образом, с помощью функций
recv/WSARecv
.

2.3. Итоги главы

На этом мы заканчиваем рассмотрение WinSock. Многие возможности этого стандарта остались не рассмотренными и даже не упомянутыми. Но для этого существуют книги, подобные [3]. Нашей же основной задачей было последовательное знакомство с базовыми возможностями WinSock API и способам их применения в Delphi.

Следует отметить, что в Delphi не обязательно напрямую использовать WinSock API, чтобы работать с сокетами, т.к. VCL содержит компоненты для этого. Прежде всего это

TServerSocket
и
TClientSocket
, использующие асинхронные сокеты, основанные на оконных сообщениях. Начиная с Delphi 7, к ним добавились компоненты
TTCPServer
,
TTCPClient
и
TUDPSocket
, использующие блокирующие или неблокирующие сокеты. Кроме того, с Delphi поставляется библиотека Indy, которая тоже содержит компоненты для работы с сокетами. Но практика показывает, что освоить эти компоненты без знания особенностей WinSock API очень сложно, так что даже если вы никогда не будете вызывать функции WinSock API явно, а ограничитесь компонентами. информация, изложенная в этой главе, вам все равно пригодится.

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