Имя Пароль
Зарегистрироваться


* При перепечатке материалов ссылка на www.SeoLiga.ru обязательна! RSS



Новые функции
19 марта 2009

В этом разделе мы рассмотрим некоторые новые функции, появившиеся в WinSock 2. Большинство из них позволяет выполнять уже знакомые нам по предыдущей статье действия, но предоставляет большие возможности, чем стандартные сокетные функции.
Для создания сокета предназначена функция WSASocket со следующим прототипом:
SOCKET WSASocket(int af,int SockType,int protocol,
LPWSAPROTOCOL_INFO lpProtocolInfo,GROUP g,DWORD dwFlags);

function WSASocket(AF,SockType,Protocol:Integer;
lpProtocolInfo:PWSAProtocolInfo;g:TGroup;dwFlags:DWORD):TSocket;

Первые три параметра совпадают с тремя параметрами функции Socket. Параметр lpProtocolInfo указывает на структуру WSAPROTOCOL_INFO, содержащую информацию о протоколе, для которого создаётся сокет. Если этот указатель равен nil, функция создаёт сокет на основании первых трёх параметров так же, как это делает функция Socket. С другой стороны, если этот параметр не равен nil, то структура, на которую он указывает, содержит всю информацию, необходимую для создания сокета, поэтому первые три параметра должны быть равны константе From_Protocol_Info (-1). Параметр g зарезервирован для использования в будущем и должен быть равен нулю (тип TGroup совпадает с DWORD). Последний параметр dwFlags определяет, какие дополнительные возможности имеет создаваемый сокет. Вызов функции Socket эквивалентен вызову функции WSASocket с флагом WSA_Flag_Overlapped, который показывает, что данный сокет можно использовать для перекрытого ввода-вывода. Остальные флаги используются при многоадресной рассылке (не все из них допустимы при использовании протоколов TCP и UDP). Эти флаги мы рассмотрим в соответствующем разделе.
При использовании TCP и UDP функция WSASocket даёт следующие преимущества по сравнению с функцией Socket. Во-первых, через параметр lpProtocolInfo появляется возможность явно указать провайдера, который будет использован программой. Во-вторых, если программа не использует перекрытый ввод-вывод, можно создавать сокеты без флага WSA_Flag_Overlapped, экономя при этом некоторое незначительное количество ресурсов. Кроме того, как это будет обсуждаться ниже, с помощью WSASocket две разных программы могут использовать один и тот же сокет.
Функция WSAConnect является более мощным аналогом Connect. Её прототип выглядит следующим образом:
int WSAConnect(
SOCKET s,
const struct sockaddr FAR *name,
int namelen,
LPWSABUF lpCallerData,
LPWSABUF lpCalleeData,
LPQOS lpSQOS,
LPQOS lpGQOS);

function WSAConnect(
S:TSocket;
var Name:TSockAddr;
NameLen:Integer;
lpCallerData,lpCalleeData:PWSABuf;
lpSQOS,lpGQOS:PQOS);

typedef struct __WSABUF {
u_long len;
char FAR *buf;} WSABUF, FAR * LPWSABUF;

type PWSABuf=^TWSABuf;
TWSABuf=packed record
Len:Cardinal;
Buf:PChar
end;

Функция WSAConnect устанавливает соединение со стороны клиента. Её первые три параметра совпадают с параметрами функции Connect. Параметры lpCallerData и lpCalleeData служат для передачи данных от клиента серверу и от сервера клиенту при установлении соединения. Они оба являются указателями на структуру TWSABuf, которая содержит размер буфера Len и указатель на буфер Buf. Протоколы стека TCP/IP не поддерживают передачу данных при соединении, поэтому при использовании TCP и UDP lpCallerData и lpCalleeData должны быть равны nil. Параметры lpSQOS и lpGQOS являются указателями на структуры, с помощью которых программа передаёт свои требования к качеству обслуживания, причём параметр lpGQOS связан с не поддерживаемым в настоящий момент групповым качеством и всегда должен быть равен nil. Параметр lpSQOS также должен быть равен nil, если программа не предъявляет требований к качеству обслуживания. Так как рассмотрение качества обслуживания выходит за рамки данной статьи, мы не приводим здесь определение структуры SQOS. При необходимости это определение легко найти в MSDN'е.
Между функциями Connect и WSAConnect существует небольшое различие при работе с сокетами, не поддерживающими соединение. Как вы знаете из предыдущей статьи, функция Connect может использоваться с такими сокетами для задания адреса отправки по умолчанию и автоматической фильтрации входящих пакетов. Для того, чтобы отменить такое "соединение", нужно при вызове функции Connect указать адрес InAddr_Any и нулевой порт. При использовании же WSAConnect для отмены "соединения" требуется, чтобы все без исключения поля структуры Name, включая sin_family, были нулевыми. Это сделано для того, чтобы обеспечить независимость от протокола: в этом случае для разрыва "соединения" должно использоваться одно и то же значение Name при любом протоколе.
Если программа не предъявляет требований к качеству обслуживания, то при использовании протоколов TCP и UDP функция WSAConnect не предоставляет никаких преимуществ по сравнению с функцией Connect.
Функция Accept из стандартной библиотеки сокетов позволяет серверу извлечь из очереди соединений информацию о подключившемся клиенте и создать сокет для его обслуживания. Эти действия выполняются безусловно, для любых подключившихся клиентов. Если сервер допускает подключение не любых клиентов, а только тех, которые отвечают некоторым условиям (для протокола TCP эти условия могут заключаться в том, какие IP-адреса и какие порты допустимо использовать клиентам), сразу после установления соединения его приходится разрывать, если клиент не удовлетворяет этим условиям. Для упрощения этой операции в WinSock 2 предусмотрена функция WSAAccept, имеющая следующий прототип:
SOCKET WSAAccept(
SOCKET s,
struct sockaddr FAR *addr,
LPINT addrlen,
LPCONDITIONPROC lpfnCondition,
DWORD dwCallbackData);

function WSAAccept(
S:TSocket;
Addr:PSockAddr;
AddrLen:PInteger;
lpfnCondition:TConditionProc;
dwCallbackData:DWORD):TSocket;

По сравнению с уже известной нам функцией Accept функция WSAAccept имеет два новых параметра: lpfnCondition и dwCallbackData. lpfnCondition является указателем на функцию обратного вызова. Эта функция объявляется и реализуется программой. WSAAccept вызывает её внутри себя и в зависимости от её результата принимает или отклоняет соединение. Параметр dwCallbackData не имеет смысла для самой функции WSAAccept и передаётся без изменений в функцию обратного вызова. Тип TConditionProc должен быть объявлен следующим образом:
typedef (int*)(
LPWSABUF lpCallerId,
LPWSABUF lpCallerData,
LPQOS lpSQOS,
LPQOS lpGQOS,
LPWSABUF lpCalleeId,
LPWSABUF lpCalleeData,
GROUP FAR *g,
DWORD dwCallbackData) LPCONDITIONPROC;

type TConditionProc=function(
lpCallerId,lpCallerData:PWSABuf;
lpSQOS,lpGQOS:PQOS;
lpCalleeID,lpCalleeData:PWSABuf;
g:PGroup;
dwCallbackData:DWORD):Integer;stdcall;

Параметр lpCallerId указывает на буфер, в котором хранится адрес подключившегося клиента. В случае использования стека TCP/IP lpCallerId^.Len будет равен SizeOf(TSockAddr), а lpCallerId^.Buf будет указывать на структуру TSockAddr, содержащую адрес клиента. Параметр lpCallerData определяет буфер, в котором хранятся данные, переданные клиентом при соединении. Как уже отмечалось выше, протоколы стека TCP/IP не поддерживают передачу данных при соединении, поэтому для них этот параметр будет равен nil.
Параметры lpSQOS и lpGQOS задают требуемое клиентом качество обслуживания для сокета и для группы соответственно. Т.к. группы сокетов в текущей реализации WinSock не поддерживаются, параметр lpGQOS будет равен nil. Параметр lpSQOS тоже будет равен nil, если клиент не задал качество обслуживания при соединении.
Параметр lpCalleeId содержит адрес интерфейса, принявшего соединение (поля структуры при этом используются так же, как у параметра lpCallerId). В предыдущей статье обсуждалось, что сокет, привязанный к адресу InAddr_Any, прослушивает все сетевые интерфейсы, имеющиеся на компьютере, но каждое подключение, созданное с его помощью, использует конкретный интерфейс. Параметр lpCalleeId содержит адрес, привязанный к конкретному соединению. Параметр lpCalleeData указывает на буфер, в который сервер может положить данные для отправки клиенту. Этот параметр также не имеет смысла при использовании протокола TCP, не поддерживающего отправку данных при соединении.
Описание параметра g в MSDN'е отсутствует (видимо, по ошибке). Но так как имеющаяся версия WinSock не поддерживает группы сокетов, его значение всё равно должно игнорироваться функцией обратного вызова.
И, наконец, через параметр dwCallbackData в функцию обратного вызова передаётся значение параметра dwCallbackData, переданное в функцию WSAAccept. Программист должен сам решить, как ему интерпретировать это значение.
Функция должна вернуть CF_Accept (0), если соединение принимается, CF_Reject (1), если оно отклоняется, и CF_Defer (2), если решение о разрешении или запрете соединения откладывается. Если функция обратного вызова вернула CF_Reject, WSAAccept завершается с ошибкой WSAEConnRefused, если CF_Defer - с ошибкой WSATry_Again (в последнем случае соединение остаётся в очереди, и информация о нём вновь будет передана в функцию обратного вызова при следующем вызове WSAAccept). Обе эти ошибки не являются фатальными, сокет остаётся в режиме ожидания соединения и может принимать подключения от новых клиентов.
В предыдущей статье обсуждалось, что функция Connect на стороне клиента считается успешно завершённой тогда, когда соединение встало в очередь, а не тогда, когда оно реально принято сервером через функцию Accept. По умолчанию для клиента, соединение с которым сервер отклонил, нет разницы, использовал ли сервер функцию WSAAccept и сразу отклонил соединение, или установил его с помощью Accept, а потом разорвал. В обоих случаях клиент сначала получит информацию об успешном соединении с сервером, а потом это соединение будет разорвано. Но при использовании WSAAccept можно установить такой режим работы, когда сначала выполняется функция, заданная параметром lpCondition, и лишь потом клиенту отправляется разрешение или запрет на подключение. Включается этот режим установкой параметра слушающего сокета SO_Conditional_Accept следующим образом:
var Cond:BOOL;
begin
Cond:=True;
SetSockOpt(S,SOL_Socket,SO_Conditional_Accept,PChar(@Cond),SizeOf(Cond));

Использование этого режима снижает нагрузку на сеть и повышает устойчивость сервера против атак, заключающихся в многократном подключении-отключении посторонних клиентов, поэтому в серьёзных серверах рекомендуется использовать эту возможность.
Из вышесказанного следует, что при использовании протокола TCP функция WSAAccept по сравнению с Accept даёт два принципиальных преимущества - позволяет управлять качеством обслуживания и запрещать подключение нежелательных клиентов.
Некоторые протоколы поддерживают передачу информации не только при установлении связи, но и при её завершении. Для таких протоколов в WinSock2 предусмотрены функции WSASendDisconnect и WSARecvDisconnect. Так как протокол TCP не поддерживает передачу данных при закрытии соединения, для него эти функции не дают никаких преимуществ по сравнению с использованием функции Shutdown, поэтому мы не будем их здесь рассматривать.
Далее мы рассмотрим несколько новых функций, унифицирующих работу с различными протоколами.
Функция Inet_Addr, как это уже упоминалось выше, жёстко связана с протоколом IP и не имеет смысла при использовании других протоколов. WinSock 2 предлагает использовать вместо неё функцию WSAStringToAddress, имеющую следующий прототип:
INT WSAStringToAddress(
LPTSTR AddressString,
INT AddressFamily,
LPWSAPROTOCOL_INFO lpProtocolInfo,
LPSOCKADDR lpAddress,
LPINT lpAddressLength);

function WSAStringToAddress(
AddresString:PChar;
AddressFamily:Integer;
lpProtocolInfo:PWSAProtocolInfo;
var Address:TSockAddr;
var AddressLength:Integer):Integer;

Данная функция преобразует строку, задающую адрес сокета, в адрес, хранящийся в структуре TSockAddr. Параметр AddressString указывает на строку, хранящую адрес, параметр AddressFamily - на семейство адресов, для которого осуществляется трансляция. Если есть необходимость указать конкретного провайдера для протокола, в функцию может быть передан параметр lpProtocolInfo, в котором указан идентификатор провайдера. Если же программу устраивает провайдер по умолчанию, параметр lpProtocolInfo должен быть равен nil. Адрес возвращается через параметр Address. Параметр AddressLength при вызове функции должен содержать размер буфера, переданного через Address, а на выходе содержит реально использованное число байт в буфере.
Функция возвращает 0 в случае успешного выполнения и Socket_Error в случае ошибки.
Допустимый формат строки определяется протоколом (некоторые протоколы вообще не поддерживают текстовую запись адреса, и для них функция WSAStringToAddress неприменима). Для семейства AF_Inet, к которому относятся TCP и UDP, адрес может задаваться в виде "IP1.IP2.IP3.IP4:Port" или "IP1.IP2.IP3.IP4", где IPn - n-ая компонента IP-адреса, записанного в виде четырёх байтовых полей, Port - номер порта. Если порт явно не указан, используется нулевой номер порта.
Таким образом, чтобы в структуре TSockAddr оказался, например, адрес 192.168.100.217 и порт с номером 5000, необходимо выполнить следующий код:
var Addr:TSockAddr;
AddrLen:Integer;
begin
AddrLen:=SizeOf(Addr);
WSAStringToAddress('192.168.100.217:5000',AF_Inet,nil,Addr,AddrLen);

Существует также функция WSAAddressToString, обратная к WSAStringToAddress. Её прототип выглядит следующим образом:
INT WSAAddressToString(
LPSOCKADDR lpsaAddress,
DWORD dwAddressLength,
LPWSAPROTOCOL_INFO lpProtocolInfo,
LPTSTR lpszAddressString,
LPDWORD lpdwAddressStringLength);

function WSAAddressToString(
var Address:TSockAddr;
dwAddressLength:DWORD;
lpProtocolInfo:PWSAProtocolInfo;
lpszAddressString:PChar;
var AddressStringLength:DWORD):Integer;

Как нетрудно догадаться по названию функции, она преобразует адрес, заданный структурой TSockAddr, в строку. Адрес задаётся параметром Address, параметр dwAddressLength определяет длину буфера Address. Необязательный параметр lpProtocolInfo содержит указатель на структуру TWSAProtocolInfo, с помощью которой можно определить, какой именно провайдер должен выполнить преобразование. Параметр lpszAddressString содержит указатель на буфер, заранее выделенный программой, в который будет помещена строка. Параметр AddressStringLength на входе должен содержать размер буфера, заданного параметром lpszAddressString, а на выходе содержит длину получившейся строки.
Функция возвращает ноль в случае успеха и Socket_Error в случае ошибки.
В предыдущей статье мы обсуждали различные форматы представления целых чисел, а также то, что формат, предусмотренный сетевым протоколом, может не совпадать с форматом, используемым узлом. Для преобразования из сетевого формата в формат узла используются функции HToNS, NToHS, HToNL и NToHL. Эти функции привязаны к протоколам стека TCP/IP - другие протоколы могут использовать другой формат представления чисел. WinSock 2 предлагает аналоги этих функций WSAHToNS, WSANToHS, WSAHToNL и WSANToHL, которые учитывают особенности конкретного протокола. Мы здесь рассмотрим только функцию WSANToHL, преобразующую 32-битное целое из сетевого формата в формат узла. Остальные три функции работают аналогично. Прототип функции WSANToHL выглядит так:
int WSANtohl(SOCKET s,u_long netlong,u_long FAR *lphostlong);

function WSANToHL(S:TSocket;NetLong:Cardianl;var HostLong:Cardinal):Integer;

Параметр S задаёт сокет, для которого осуществляется преобразование. Так как сокет всегда связан с конкретным протоколом, этого параметра достаточно, чтобы библиотека могла определить, по какому закону преобразовывать число из сетевого формата в формат хоста. Число в сетевом формате задаётся параметром NetLong, результат преобразования помещается в параметр HostLong. Функция возвращает ноль в случае успешного выполнения операции и Socket_Error в случае ошибки.
Если программа работает только с протоколами стека TCP/IP, старые варианты функций удобнее новых, потому что старые возвращают непосредственно результат преобразования, который можно использовать в выражениях. При использовании же новых функций для получения результата следует заводить отдельную переменную, поэтому новые функции преобразования удобно применять только тогда, когда программа должна единым образом работать с разными протоколами.
Последняя функция, которую мы здесь рассмотрим, не имеет прямых аналогов среди старых функций. Называется она WSADuplicateSocket и служит для копирования дескриптора сокета в другой процесс. Прототип функции WSADuplicateSocket выглядит следующим образом:
int WSADuplicateSocket(
SOCKET s,
DWORD dwProcessId,
LPWSAPROTOCOL_INFO lpProtocolInfo);

function WSADuplicateSocket(
S:TSocket;
dwProcessID:DWORD;
var ProtocolInfo:TWSAProtocolInfo):Integer;

Параметр S задаёт сокет, дескриптор которого нужно скопировать, параметр dwProcessID - идентификатор процесса, для которого предназначена копия. Функция помещает в структуру ProtocolInfo информацию, необходимую для создания копии дескриптора другим процессом. Затем эта структура должна быть каким-то образом передана другому процессу, который передаст её в функцию WSASocket и получит свою копию дескриптора для работы с данным сокетом.
Функция WSADuplicateSocket возвращает ноль при успешном завершении и Socket_Error при ошибке.
В предыдущей статье мы обсуждали то, что сокет является объектом, внутренняя структура которого остаётся скрытой от использующей его программы. Программа манипулирует только дескриптором сокета - некоторым уникальным идентификатором этого объекта. Функция WSADuplicateSocket позволяет другой программе получить новый дескриптор для уже существующего сокета. Старый и новый дескриптор становятся равноправными. Чтобы освободить сокет, нужно закрыть все его дескрипторы с помощью функции CloseSocket.
Если во входной буфер сокета поступают данные, их получит та программа, которая первой вызовет соответствующую функцию чтения, поэтому совместное использования одного сокета разными программами требует синхронизации их работы. MSDN рекомендует использовать такую схему работы, при которой одна программа только создаёт сокет и устанавливает соединение, а затем передаёт сокет другой программе, которая использует его для ввода-вывода. Первая программа при этом закрывает свой дескриптор. Такой алгоритм работы позволяет полностью исключить проблемы, возникающие при совместном доступе разных программ к одному сокету.
Отметим, что функция WSADuplicateSocket используется только для копирования дескрипторов между разными процессами. Разные нити одного процесса не нуждаются в этой функции, так как, находясь в одном адресном пространстве, они могут использовать один и тот же дескриптор.


Теги: asus socket, IP, borland delphi, User Datagram Protocol, Unix, SQL-сервер, socket, Создание сокета, сокет, sockets Borland Delphi

Статьи по теме:

SetRange
Событие AfterPrint для TQuickRep
Метод Preview
Размер стека
CheckDbfFieldDefs
Чтение сообщений
Использование полей базы данных в выражениях
Качество интерфейса
Гипертекстовые и мультимедийные информационные технологии
Свойство AutoSize
ТЕХНОЛОГИЧЕСКИЙ ПРОЦЕСС ОБРАБОТКИ И ЗАЩИТЫ ДАННЫХ
ClearCalcFields
Компонент TQuickRep
Свойство Bands
Свойство RecordCount
| Borland Delphi | Alex |
 


Пн Вт Ср Чт Пт Сб Вс
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31


     



Rambler's Top100

Данный сайт или домен продается ICQ: 403-353-727

© 2009 Seoliga.ru | Borland Delphi | Новые функции. Регион сайта: Москва и Санкт-Петербург