Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Можно ли перевести сокет из блокирующего в неблокирующий режим? [D4, D5, NT4, Win2k, WinXP]


Страницы:   1 2 3

Repeater ©   (2002-11-01 12:46:00) 

Привет!
Такая трабла. Надо бы перевести сокет из блокирующего режима в неблокирующий. Если конкретнее то мне надо сделать такое. Допустим ко мне обращается клиент на серверный сокет. Я в свою очередь создаю блокирующий клиентский сокет и соединяюсь с удаленным сервером. После этого происходит небольшой диалог с удаленным сервером. Диалог состоит в том что я посылаю команды и жду пока сервер ответит. Потом снова посылаю команды и мнова жду ответа. Все это время серверный сокет должен ждать окончания этого диалога, а как только он закончится надо перевести клиентский сокет в неблокирующий режим. Можно такое сделать? Если можно то как?


Repeater   (2002-11-11 14:34:00) [21]

Использую такие юниты

winsock, sconnect, ScktComp;


Вручную импортирую только такую функцию

Function WSAWaitForMultipleEvents(cEvents:DWord;const lpEvents:PDWORD;fWaitAll:Boolean;dwTimeOut:DWORD;fAlertable:Boolean):DWORD; external "ws2_32.dll" name "WSAWaitForMultipleEvents";


Digitman   (2002-11-11 14:23:00) [22]

Я, похоже, догадываюсь, почему у тебя че-то куда-то там "скачет"
Ты самостоятельно импортируешь ф-ции, отсутствующие в составе деклараций в Winsock.pas ? Ведь декларации WSAEventSelect, WSACreateEvent там отсутствуют. Или ты пользуешь Winsock2.pas ?
Если "вручную" импортируешь, покажи, как ты это делаешь.


Repeater   (2002-11-11 14:16:00) [23]

2 Polevi

Поменял, результат тот же.

2 Digitman
>Какой функции ?

WSAEventSelect. Я проверяю ее результат. Но я не могу проверять ее результат непосредственно, так как весь код, какой бы он ни был, после этой функции пропускается и осуществляется переход на последнюю строчку. Пока я проверяю ее так
err:=WSAEventSelect(...);
А потом после выполнения и перехода на последнюю строчку жму Ctrl-F7 и в окне пишу err. Значение этой переменной равно 0 Значит ошибки нету. Также вызываю там же WSAGetLastError и тоже получаю 0.

>ты потрудись-таки взглянуть в исх.код TCustomWinSocket и обрати
>внимание, что при вызове практически ЛЮБОЙ Winsock-ф-ции
>Борланд либо явно проверяет результат ее выполнения либо это
>делается в ф-ции CheckWSAResult(). Считаешь , что это бред>
>сивой кобылы ?

Нет я так не считаю, но сколько я не искал я не нашел вызова этой функции ни в одном исходнике от Борланда. Может ты имел ввиду CheckSocketResult. Но она делает проверку результата на 0, а я и сам это делаю, но как я уже выше говорил, получаю 0, то есть - нет ошибки.

>Какой вообще смысл в установке неблок.режима, если в конеч.
>итоге весь этот твой код будет работать синхронно из-за
>блокировки выполнения в момент вызова ф-ций ожидания
>трансп.событий ?

Хорошо, объясню еще раз. Клиент коннектится. Я в свою очередь коннекчусь к серверу и провожу с ним небольшой диалог. Во время диалога я задаю серверу команды, а он отвечает. И я должен дождаться ответа сервера. А это возможно только в блок. режиме. Все это время клиент должен ждать когда же ему ответят. А как только я договорюсь с сервером я перехожу в прозрачный режим. Тоесть перехожу в неблок режим и действую так. Пришла инфа от клиента - передаю ее серверу. Пришла инфа от сервера - передаю ее клиенту.
Причем у меня не один а допустим 10 клиентов одновременно подконнекчиваются.


Digitman   (2002-11-10 08:25:00) [24]


> результат, возвращаемый этой функцией, я уже проверял. Он
> равен 0


Какой функции ?

ты потрудись-таки взглянуть в исх.код TCustomWinSocket и обрати внимание, что при вызове практически ЛЮБОЙ Winsock-ф-ции Борланд либо явно проверяет результат ее выполнения либо это делается в ф-ции CheckWSAResult(). Считаешь , что это бред сивой кобылы ? Борланд с дури это делает, по-твоему, и ты умней его, когда игнорируешь результаты, считая их ВСЕГДА только такими, какими тебе хочется из видеть ?

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

Вникни в то, что говорит <Polevi>. Сначала гнездо переводится в некий режим, а уж затем вызываются прочие ф-ции, результат работы которых ЗАВИСИТ от режима


Polevi   (2002-11-09 14:56:00) [25]

//коннектимся к серверу
connect(CliSock, rmaddr,sizeof(rmaddr));
//выбираем блокируемый режим (устанавливаем event, который будет срабатывать в случае) если сервер вернул результат
WSAEventSelect(CliSock,MainEvent, FD_CONNECT or FD_READ or FD_CLOSE);

местами поменяй


Repeater   (2002-11-08 16:11:00) [26]

>Быть того не может)
Я знаю, но факт налицо.

>Второе. Ты в курсе, что практически ВСЕ вызовы WinsockAPI
>являют собой вызовы ф-ций, а у ф-ции возвращают результат ?
Да, знаю. Но этот результат может быть опущен, компилятор позволяет это делать. Это во первых. А во вторых результат, возвращаемый этой функцией, я уже проверял. Он равен 0. И в третьих если бы была какая то ошибка то WSAGetLastError должен бы вернуть код ошибки, а он возвращает 0.

>Где (опять же !) вызов WSAStartup(), WSACleanup().
Они вызываются при инициализации приложения за пределами этой функции. Поэтому этих вызовов нет в самой функции, но эти вызовы точно производятся.

>Прокомментируй КАЖДУЮ строчку в своем коде - где, что, для чего
>делаешь и какой результат от вызова очер.ф-ции из состава
Да причем здесь коментарии. Весь остальной код не имеет значения ни для кого кроме меня. Это простой диалог моей проги с сервером. Тем болле, что он почему-то пропускается. Но могу привести коментарии первой части функции, если надо.


Function ConnectToProxy:Boolean;
Var rmaddr:SOCKADDR_IN;
sd:String;
p:pointer;
Begin
//Если возникнет какя то ошибка автоматически возвращаем False
Result:=False;
//Заполняем структуру для соединения с сервером
rmaddr.sin_family := AF_INET;
rmaddr.sin_addr.s_addr := inet_addr(PChar(OUT_IP));//адрес
rmaddr.sin_port := htons (OUT_PORT);//порт
//создаем сокет
CliSock := socket(AF_INET,SOCK_STREAM,0);
//если ошибка показываем сообщение
if CliSock = INVALID_SOCKET Then MessageBox(MainWnd,"INVALID_SOCKET","Socker Error!!!",0);
//создаем event для ожидания ответта от сервера
MainEvent:=CreateEvent(0,True,False,"1234567");
//коннектимся к серверу
connect(CliSock, rmaddr,sizeof(rmaddr));
//выбираем блокируемый режим (устанавливаем event, который будет срабатывать в случае) если сервер вернул результат
WSAEventSelect(CliSock,MainEvent, FD_CONNECT or FD_READ or FD_CLOSE);

//ждем соединения с сервером
WSAWaitForMultipleEvents(1,@MainEvent,False,INFINITE,False);
//когда соединились отсылаем запрос
sd:=#10#26#134#87;
p:=@sd[1];
Send(CliSock,p^,Length(sd),0);
//ждем ответа
WSAWaitForMultipleEvents(1,@MainEvent,False,INFINITE,False);
//как только сервер ответил считываем его ответ
Recv(CliSock,buf,MAX_DATA,0);
...................


Digitman   (2002-11-08 15:56:00) [27]

Быть того не может)

Это - раз.

Второе. Ты в курсе, что практически ВСЕ вызовы WinsockAPI являют собой вызовы ф-ций, а у ф-ции возвращают результат ?

Где у тебя анализ результатов этих ф-ций ? За исключением socket() ? Где (опять же !) вызов WSAStartup(), WSACleanup() ?

Прокомментируй КАЖДУЮ строчку в своем коде - где, что, для чего делаешь и какой результат от вызова очер.ф-ции из состава WinsockAPI в своем коде ты ожидаешь...


Repeater   (2002-11-08 15:49:00) [28]

Причем WSAGetLastError возвращает 0.


Repeater   (2002-11-08 15:47:00) [29]

Хорошо. Привожу полный код.


Function ConnectToProxy:Boolean;
Var rmaddr:SOCKADDR_IN;
sd:String;
p:pointer;
Begin
Result:=False;
rmaddr.sin_family := AF_INET;
rmaddr.sin_addr.s_addr := inet_addr(PChar(OUT_IP));
rmaddr.sin_port := htons (OUT_PORT);
CliSock := socket(AF_INET,SOCK_STREAM,0);
if CliSock = INVALID_SOCKET Then MessageBox(MainWnd,"INVALID_SOCKET","Socker Error!!!",0);
MainEvent:=CreateEvent(0,True,False,"1234567");
connect(CliSock, rmaddr,sizeof(rmaddr));
WSAEventSelect(CliSock,MainEvent, FD_CONNECT or FD_READ or FD_CLOSE);

WSAWaitForMultipleEvents(1,@MainEvent,False,INFINITE,False);
sd:=#10#26#134#87;
p:=@sd[1];
Send(CliSock,p^,Length(sd),0);
WSAWaitForMultipleEvents(1,@MainEvent,False,INFINITE,False);
Recv(CliSock,buf,MAX_DATA,0);
sd:=#5#1#0#1#149#16#9#22#31#144;
p:=@sd[1];
Send(CliSock,p^,Length(sd),0);
WSAWaitForMultipleEvents(1,@MainEvent,False,INFINITE,False);
Recv(CliSock,buf,10,0);
If buf[1]<>#0 Then Begin
MessageBox(MainWnd,PChar("Cant connect"+#13#10+"Error # "+Int2Str(Ord(buf[1]))),"Error",0);
Exit;
End;
WSAAsyncSelect(CliSock,MainWnd,WM_ASYNC_PROXYEVENT, FD_ACCEPT or FD_READ or FD_CLOSE);
Result:=True;
End;


Ну так вот после вызова

WSAEventSelect(CliSock,MainEvent, FD_CONNECT or FD_READ or FD_CLOSE);

Сразу же осуществляется переход на последнюю строчку

Result:=True;


Digitman   (2002-11-08 15:42:00) [30]


> весь последующий код пропускается


Какой код-то ?? "Догадайся , мол, сама" ?)


Repeater   (2002-11-08 15:34:00) [31]

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


Digitman   (2002-11-08 15:12:00) [32]


> переход на последнюю строчку функции


Как это понимать ?


Repeater   (2002-11-08 14:58:00) [33]

Такой еще вопрос. Когда вызываю функцию

WSAEventSelect(CliSock,MainEvent, FD_CONNECT or FD_READ or FD_CLOSE);

почему то сразу же происходит переход на последнюю строчку функции, которая вызвала WSAEventSelect. То же самое происходит если вызвать функцию WSACreateEvent.


Repeater   (2002-11-08 14:54:00) [34]

Последний вопрос отменяется. Уже нашел ошибку.


Digitman   (2002-11-08 14:53:00) [35]


> Создаю сокет


Прежде чем обращаться к любым ф-циям из состава WinsockAPI, необходимо инициализировать сессию вызовом WSAStartup(). завершение сессии - соответственно WSACleanup()


> Хотя точно известно, что на машине 10.0.0.1 есть открытый
> порт 1080


Вызов ф-ции socket() предназначен ТОЛЬКО для создания гнезда.
Для осуществления попытки коннекта к целевому хосту/сервису необходимо вывзвать ф-цию connect()


Repeater   (2002-11-08 14:37:00) [36]

В чем может быть проблема? Создаю сокет


Var rmaddr:TSockAddrIn;
CliSock:=TSocket;
Begin
rmaddr.sin_family := AF_INET;
rmaddr.sin_addr.s_addr := inet_addr(PChar("10.0.0.1));
rmaddr.sin_port := htons(1080);
CliSock := socket(AF_INET,SOCK_STREAM,0);
..............


Но все время почему то получаю CliSock = INVALID_SOCKET.
Хотя точно известно, что на машине 10.0.0.1 есть открытый порт 1080. По крайней мере TClientSocket без проблем коннектится.


Repeater   (2002-11-07 17:10:00) [37]

Спасибо, попробую.


Digitman   (2002-11-02 14:44:00) [38]

Используй единый режим и не занимайся ерундой с переключениями.
Все это решаемо в любом из режимов.

Для клиентской стороны прокси останови выбор либо на ctBlocking (с созданием доп.кодового потока для чтения/записи) либо на ctNonBlocking (никакие доп.потоки не нужны)

Для серверной стороны прокси останови выбор либо на stThreadBlocking (доп.кодовые потоки для чтения/записи создаются и диспетчеризуются автоматически компонентом TServerSocket) либо на stNonBlocking (никакие доп.потоки не нужны)


Repeater   (2002-11-01 20:07:00) [39]

>А нахрена тебе все это надо, скажи на милость ? Все, что ты
>задумал, даже самое изощренное, делается элементарно без всяких
>переключений, в любом режиме.

Ок. Как тогда лучше всего сделать такое. Есть что то типа прокси. К нему конектятся клиенты, а он отсылает запросы на удаленный сервер. Алгоритм работы такой. Клиент конектится к прокси. Прокси в ответ на это конектится к удаленному серверу, совершает авторизацию и потом служит в качестве гейта для клиентов. То есть все что посылают клиенты отсылает удаленному серверу, а все, что посылает удаленный сервер отсылает клиентам. Я сделал так. При конекте клиента создается блокирующий сокет. Он коннектится к удаленному серверу. Потом происходит некий диалог авторизации между сервером и этим сокетом. Диалог происходит так. прокси отсылает запрос серверу и ждет пока сервер ответит. После считки ответа прокси снова отсылает запрос и ждет ответа. Ну а уже после авторизации сокет переводится в неблок. режим и дальше все идет по такому алгоритму. Когда прокси получает данные от клиента (окну приходит месага) он считывает данные и отсылает серверу. Когда сервер присылает данные для клиента (окну снова приходит месага) прокси считывает эти данные и отсылает их клиенту. Вроде все нормально, но есть одна проблемма. Когда клиент коннектится, то идет коннект от блок. сокета к серверу и пока не пройдет авторизация все замирает потому что все сообщения от сокетов стоят в очереди. Ведь работа идет в процедуре окна. Но я не могу позволить им (другим сокетам) работать пока удаленный сервер отвечает на авторизацию. Ведь среди них будет и тот, в ответ на коннект которого пошла авторизация к серверу и он должен ждать пока сервер ответит. Что можно сдесь сделать? Можно ли как то для каждого клиента открывать отдельную нить и получать все относящиеся к этому клиенту сообщения в нее?


Digitman   (2002-11-01 16:55:00) [40]

А нахрена тебе все это надо, скажи на милость ? Все, что ты задумал, даже самое изощренное, делается элементарно без всяких переключений, в любом режиме.

К тому же я, в первую очередь, упомянул эти ф-ции вовсе не для того, стобы "скакать козлом" между блок. и неблок. режимами.
В неблок.режиме гнездо в TCustomWinSocket инициализируется Борландом для асинхронной статус-нотификации с использованием оконных сообщений. Не нравится ? Да на здоровье ! Создал гнездо, реинициализировал его с использованием WSAEventSelect() - и получай нотификации иным образом - через сист.объект синхронизации типа Event !

Ну а уж если тебя прижала проблема, что TCustomWinSocket не "знает" о смене режима с блок. на неблок. (или наоборот), возьми да пользуй транспортные гнездовые ф-ции непосредственно - send(), recv() и т.п. Хэндл-то у тебя есть)..


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо