![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Можно ли перевести сокет из блокирующего в неблокирующий режим? [D4, D5, NT4, Win2k, WinXP]
Repeater © (2002-11-01 12:46:00)Привет!
Такая трабла. Надо бы перевести сокет из блокирующего режима в неблокирующий. Если конкретнее то мне надо сделать такое. Допустим ко мне обращается клиент на серверный сокет. Я в свою очередь создаю блокирующий клиентский сокет и соединяюсь с удаленным сервером. После этого происходит небольшой диалог с удаленным сервером. Диалог состоит в том что я посылаю команды и жду пока сервер ответит. Потом снова посылаю команды и мнова жду ответа. Все это время серверный сокет должен ждать окончания этого диалога, а как только он закончится надо перевести клиентский сокет в неблокирующий режим. Можно такое сделать? Если можно то как?
Digitman (2002-11-13 08:24:00) [1]О штатном дисконнекте по инициативе партнера говорит событие OnDisconnect(). Но событийная нотификация работает только в асинхр.неблок.режиме.
В блок.режиме и при нештатном обрыве трансп.петли коннекта факт дисконнекта можно определить только вызвав один из транспортных методов (например, send(), recv()) и получив при этом отказ с соотв.кодом ошибки
Repeater (2002-11-12 20:17:00) [2]Спасибо.
Но у меня еще вопрос. Как лучше всего проверить работает еще сокет или он уже закрыт (произошел дисконнект и т.д.)
Digitman (2002-11-12 10:35:00) [3]
> Странная, конечно, логика. Зачем ему надобно эту структуру
> "держать".
Хотя бы за тем, что существует асинхр.режим вызова Lookup-ф-ций Winsock.
Когда ты задаешь целевой хост по имени, а не по адресу, требуется разрешение имени. Взгляни в реализацию
procedure TCustomWinSocket.CMLookupComplete(var Message: TCMLookupComplete);
станет понятно, почему так, а не иначе
Repeater (2002-11-12 10:23:00) [4]Спасибо, буду разбираться. Странная, конечно, логика. Зачем ему надобно эту структуру "держать". Пускай бы загрузил себе данные из нее в свое хранилище и "отпустил".
Я тут наткнулся на интересный примерчик
http://delphi.mastak.ru/cgi-bin/forum.pl?look=1&id=1033251921&n=4
Сейчас с ним разбираюсь. Наверное так же сделаю.
Digitman (2002-11-12 08:30:00) [5]
> Разве эта переменная используется все время пока сокет работает?
Да, разумеется ! Только не "все время пока сокет работает", а "пока существует гнездо как ОС-объект", т.е. с момента вызова socket() до момента вызова closesocket().
Ты взгляни в код Борланда - ведь не спроста у него структура SOCKADDR_IN вынесена членом класса. А время жизни экземпляра класса TCustomWinSocket (в контексте методов которого и вызываются socket() ,closesocket()) заведомо больше, нежели время, проходящее между этими вызовами.
Учись на коде Борланда ! Для того исходники в scktcomp.pas и даны ! Не нравится ? Много лишнего для твоей задачи ? На здоровье ! Проанализируй и выкинь лишнее ! Добавь свое ! Но центральная логика у борланда 100%-но верна в этих компонентах, и - поверь уж - нет никакого резона изменять ее радикальным образом без осмысленной необходимости !
Repeater (2002-11-11 18:35:00) [6]Ошибочка, все работает. Я не туда смотрел. ;-))
Но есть другой трабл. После выхода из своей функции (я приводил ее еще раньше ConnectToProxy)выскакивает Access violation. Я проверил и выяснилось, что эта ошибка выскакивает только когда когда я описываю rmaddr:SOCKADDR_IN; локально в функции. Когда я глобально описал эту переменную, то ошибка больше не выскакивает. Соответственно вопрос. Очевидно, что ее, эту переменную, кто то еще хотел использовать, но так как функция завершилась и все лок. переменные были уничтожены, то получилось ошибочное обращение к памяти. Разве эта переменная используется все время пока сокет работает?
Repeater (2002-11-11 18:12:00) [7]>То, что connect() возвращает WSAEWOULDBLOCK в данной ситуации -
>это АБСОЛЮТНО НОРМАЛЬНО для неблок.режима !
Да, я это тоже в MSDN читал. Но тогда у меня все остальное не работает. WSAWaitForMultipleEvents, Send и recv возвращают ошибки.
>- циклическое ожидание сигнала события с возможным параллельным
>ожиданием оконных сообщений (MsgWaitForMultipleObjects)
Вот как это сделать? Это меня больше всего интересует.
Digitman (2002-11-11 18:06:00) [8]То, что connect() возвращает WSAEWOULDBLOCK в данной ситуации - это АБСОЛЮТНО НОРМАЛЬНО для неблок.режима !
Бросай слепые эксперименты и читай внимательно документацию по Winsock.
Digitman (2002-11-11 18:04:00) [9]последовательность д.б. такова :
- инициировать сессию (WSAStartup)
- создать гнездо (socket)
- создать событие (WSACreateEvent)
- ассоциировать гнездо с событием (WSASelectEvent)
- попытка коннекта (connect), результат практически всегда равен -1 и WSAGetLastError() = 10035, и это нормально для неблок.режима
- циклическое ожидание сигнала события с возможным параллельным ожиданием оконных сообщений (MsgWaitForMultipleObjects)
- анализ и обработка сетевых событий (WSAEnumNetworkEvents) + анализ и диспетчеризация/обработка оконных сообщений (если нужно)
...
БЕЗУСЛОВНЫЕ ШАГИ :
- закрытие гнезда (closesocket)
- закрытие события (closehandle)
- завершение сессии (WSACleanup)
любой из шагов должен сопровождаться анализом результата и следом, если необходимо, кодом отказа
Repeater (2002-11-11 17:56:00) [10]Разве я сказал, что WSAEventSelect() возвращает эту ошибку? Я же сказал, что connect возвращает эту ошибку. А WSAEventSelect() возвращает 0, как и WSAGetLastError.
Ладно, более подробно. После выполнения такого кода
err:=WSAEventSelect(CliSock,MainEvent, FD_CONNECT or FD_READ or FD_CLOSE);
gle:=WSAGetLastError;
If err<>0 Then MessageBox(MainWnd,"WSAEvent Failed","Socker Error!!!",0);
err:=connect(CliSock, @rmaddr,sizeof(rmaddr));
gle:=WSAGetLastError;
err=4294967295; gle=10035
(После вызова WSAEventSelect err=0;gle=0;)
После такого кода
//не вызываем WSAEventSelect
err:=connect(CliSock, @rmaddr,sizeof(rmaddr));
gle:=WSAGetLastError;
err=0; gle=0.
Так более понятно?
Digitman (2002-11-11 17:41:00) [11]Ну и долго ты эксперементировать будешь еще ?
WSAEWOULDBLOCK = 10035
Не выдумывай.
WSAEventSelect() не может вернуть такой ошибки
Вот черным по белому описание ошибок, возвращаемых ф-цией :
Error Codes
WSANOTINITIALISED A successful WSAStartup must occur before using this function.
WSAENETDOWN The network subsystem has failed.
WSAEINVAL Indicates that one of the specified parameters was invalid, or the specified socket is in an invalid state.
WSAEINPROGRESS A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.
WSAENOTSOCK The descriptor is not a socket.
У тебя полная путаница с логикой последовательности вызова ф-ций
err:=WSAEventSelect(..); // вызвали ф-цию , сохранили результат
If err<>0 Then ; если результат = SOCKET_ERROR, то выясняем причину отказа
begin
gle:=WSAGetLastError; // вот она - причина ! Код отказа !
... здесь что-то предпринимаем, в зависимости от кода отказа
end
else
.. все ОК, движемся дальше
Repeater (2002-11-11 17:23:00) [12]Я наткнулся на такое.
Если делать так
err:=WSAEventSelect(CliSock,MainEvent, FD_CONNECT or FD_READ or FD_CLOSE);
gle:=WSAGetLastError;
If err<>0 Then MessageBox(MainWnd,"WSAEvent Failed","Socker Error!!!",0);
err:=connect(CliSock, @rmaddr,sizeof(rmaddr));
gle:=WSAGetLastError;
То connect вызывает ошибку (err=4294967295; gle=10035)
А если не вызывать WSAEventSelect, то connect проходит без ошибок.
Digitman (2002-11-11 17:03:00) [13]
> он какой то кривой
> там не так описана функция connect
Чушь.
Winsock.pas :
function connect(s: TSocket; var name: TSockAddr; namelen: Integer): Integer; stdcall;
Winsock2.pas :
function connect( const s: TSocket; const name: PSockAddr; namelen: Integer): Integer; stdcall;
Где ПРИНЦИПИАЛЬНАЯ разница ?
Repeater (2002-11-11 16:37:00) [14]Я скачал его, но мне кажется, что он какой то кривой. Например там не так описана функция connect и другие функции. В результате теперь ф-ция connect не хочет коннектиться и всегда возвращает ошибку. А старая функция connect работала без проблем
Digitman (2002-11-11 15:50:00) [15]набери в www.filesearch.com модель поиска "winsock2.pas" и качай себе на здоровье !
Repeater (2002-11-11 15:42:00) [16]Мне вообще то не sconnect.pas нужен. Мне просто нужны были некоторые функции и я воспользовался тем что было.
Если у тебя есть какие либо другие модули (pas) с декларациями WinSocks2-функций, то, пожалуйста, пришли их мне на адрес aos@inbox.ru
Digitman (2002-11-11 15:02:00) [17]Да, это ответ, если ты используешь sconnect.pas .
А вот нахрена он тебе нужен, я пока не пойму. Прокомментируй.
Repeater (2002-11-11 14:59:00) [18]Ладно, по поводу WSAWaitForMultipleEvents я еще подумаю. Попробую твой вариант.
По поводу точек входа смотри в sconnect.pas.
var
WSACreateEvent: function: THandle stdcall;
WSAResetEvent: function(hEvent: THandle): Boolean stdcall;
WSACloseEvent: function(hEvent: THandle): Boolean stdcall;
WSAEventSelect: function(s: TSocket; hEventObject: THandle; lNetworkEvents: Integer): Integer stdcall;
................
function LoadWinSock2: Boolean;
const
DLLName = "ws2_32.dll";
begin
Result := hWinSock2 > HINSTANCE_ERROR;
if Result then Exit;
hWinSock2 := LoadLibrary(PChar(DLLName));
Result := hWinSock2 > HINSTANCE_ERROR;
if Result then
begin
WSACreateEvent := GetProcAddress(hWinSock2, "WSACreateEvent");
WSAResetEvent := GetProcAddress(hWinSock2, "WSAResetEvent");
WSACloseEvent := GetProcAddress(hWinSock2, "WSACloseEvent");
WSAEventSelect := GetProcAddress(hWinSock2, "WSAEventSelect");
end;
end;
Это ответ на твой вопрос?
Digitman (2002-11-11 14:46:00) [19]Вот она-то (WSAWaitForMultipleEvents) как раз тебе на хрен не нужна) ... Довольно бестолковая ф-ция, и ее с гораздо большей эффективностью и успехом заменяют MsgWaitForMultipleObjects() + WSAEnumNetworkEvents()
та-а-ак ... ну и ? Откуда у тебя взялись ни с того ни с сего декларации и актуальные точки входа для ф-ций WSAEventSelect, WSACreateEvent, если НИ явно НИ неявно они у тебя нигде не присутствуют ? Раз ты не используешь Winsock21.pas ?
Куда, по какому адресу, спрашивается, будет передано управление при вызове той же WSAEventSelect, если нигде НИ статически НИ динамически не вызывалась GetProcAddr() для этой (и иных нестандартных для Winsock.pas) ф-ции ?
Repeater (2002-11-11 14:38:00) [20]Ага, я кажись тоже понял. Сначала наверное надо было выполнить LoadWinSock2 из модуля sconnect.