KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » А. Григорьев - О чём не пишут в книгах по Delphi

А. Григорьев - О чём не пишут в книгах по Delphi

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн А. Григорьев, "О чём не пишут в книгах по Delphi" бесплатно, без регистрации.
Перейти на страницу:

Если к серверу подключаются некорректно работающие клиенты, которые не присылают данные после подключения, операция может не завершаться очень долго, что будет мешать подключению новых клиентов. MSDN рекомендует при ожидании время от времени с помощью функции getsockopt для сокета sAcceptSocket узнавать значение целочисленного параметра SO_CONNECT_TIME уровня SOL_SOCKET. Этот параметр показывает время в секундах, прошедшее с момента подключения клиента к данному сокету (или -1, если подключения не было). Если подключившийся клиент слишком долго не присылает данных, сокет sAcceptSocket следует закрыть, что приведет к завершению операции, начатой AcceptEx, с ошибкой. После этого можно снова вызывать AcceptEx для приема новых клиентов.

Функция AcceptEx реализована таким образом, чтобы обеспечивать максимальную скорость подключения. Ранее мы говорили, что сокеты, созданные функциями accept и WSAAccept, наследуют параметры слушающего сокета (например, свойства асинхронного режима). Для повышения производительности сокет sAcceptSocket по умолчанию не получает свойств сокета sListenSocket. Но он может унаследовать их после завершения операции с помощью следующей установки параметра сокета SO_UPDATE_ACCEPT_CONTEXT:

setsockopt(sAcceptSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, PChar(@sListenSocket), SizeOf(sListenSocket));

Ha сокет sAcceptedSocket после его подключения к клиенту накладываются ограничения: он может использоваться не во всех функциях WinSock, а только в следующих: send, WSASend, recv, WSARecv, ReadFile, WriteFile, TransmitFile, closesocket и setsockopt, причем в последней — только для установки параметра SO_UPDATE_ACCEPT_CONTEXT.

В 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 явно, а ограничитесь компонентами. информация, изложенная в этой главе, вам все равно пригодится.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*