Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2004.08.01;
Скачать: [xml.tar.bz2];

Вниз

Неблокирующий режим в сокетах   Найти похожие ветки 

 
kalishenko   (2004-05-28 19:39) [0]

Сокет никак не активизируется. Не пойму, почему. Вот пример:

const
     CLOSE_SOCK = 0;
     CONNECTING_SOCK = 1;
     CONNECTED_SOCK = 2;
     TIME_OUT = 30;
     maxproccess = 2;

var
 WSData: WSAData;
 Return: integer;
 Addr: TSockAddrIn;
 Buf: array [0..100] of char;
 sock : array [1..maxproccess] of TSocket;
 stat : array [1..maxproccess] of Byte;
 time : array [1..maxproccess] of Byte;
 x : integer;
 on_sock : LongInt = 1;
 off_sock : LongInt = 0;
 wfds_empty : Boolean;
 wfds : Tfdset;
 tv : Ttimeval;

procedure TForm1.Button4Click(Sender: TObject);
begin
 FillChar(Buf,100,0);

 WSAStartup($101,WSData);
 for x := 1 to maxproccess do
   stat[x] := CLOSE_SOCK;

 repeat
   for x := 1 to maxproccess do
   begin
     if stat[x] = CLOSE_SOCK then
     begin
       sock[x] := socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
       time[x] := TIME_OUT;
       Return := ioctlsocket(sock[x],FIONBIO,on_sock);
       addr.sin_family := AF_INET;
       addr.sin_port := htons(10001);
       addr.sin_addr.s_addr := INADDR_ANY;
       Return := connect(sock[x],addr,sizeof(addr));
       stat[x] := CONNECTING_SOCK;
     end;
   end;

   FD_ZERO(wfds);
   wfds_empty:=true;
   for x:=1 to maxproccess do
   begin
     if stat[x]=CONNECTING_SOCK then
     begin
       FD_SET(sock[x],wfds);
       wfds_empty:=false;
     end;
   end;

   if not wfds_empty then
   begin
     tv.tv_sec:=1;
     tv.tv_usec:=0;
     Return := select(0,nil,@wfds,nil,@tv);
   end;

   for x:=1 to maxproccess do
   begin
     if stat[x]<>CLOSE_SOCK then
     begin
       dec(time[x]);
       if time[x]=0 then
       begin
         MessageBox(0,"Ваше время вышло!","",MB_OK);
         stat[x]:=CLOSE_SOCK;
         ioctlsocket(sock[x],FIONBIO,off_sock);
         closesocket(sock[x]);
       end;
     end;

     if stat[x]=CONNECTING_SOCK then
     begin
       if FD_ISSET(sock[x],wfds) then
         stat[x]:=CONNECTED_SOCK;
     end;

     if stat[x]=CONNECTED_SOCK then
     begin
       MessageBox(0,"Привет!","",MB_OK);
       FD_CLR(sock[x],wfds);
       send(sock[x],buf,sizeof(buf),0);
       Form1.Caption := Buf;
       stat[x]:=CLOSE_SOCK;
       ioctlsocket(sock[x],FIONBIO,off_sock);
       closesocket(sock[x]);
     end;
   end;
   Application.ProcessMessages;
 until
   false;

 WSACleanup;
end;


 
SammIk ©   (2004-05-28 20:12) [1]

Попробуи
WSAStartup($0002,WSData);
Может поможет


 
kalishenko   (2004-05-28 20:25) [2]

Не, Вы наверное не так меня поняли. WSAStartUp здесь непричем. Все с ним нлормально. И сокеты открываются (sock[x] := socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);), и замечательно переходят в неблокирующий режим (Return := ioctlsocket(sock[x],FIONBIO,on_sock);), затем, как и в хелпе написано, connect выдает SOCKET_ERROR для неблок. режима (Return := connect(sock[x],addr,sizeof(addr));), но затем сокеты никак не хотят select-иться (Return := select(0,nil,@wfds,nil,@tv);), и функция FD_ISSET говорит, что сокет не установлен, поэтому условие (if FD_ISSET(sock[x],wfds) then stat[x]:=CONNECTED_SOCK;) не выполняется. А почему, не пойму.


 
Verg ©   (2004-05-28 20:30) [3]


>    addr.sin_family := AF_INET;
>        addr.sin_port := htons(10001);
>        addr.sin_addr.s_addr := INADDR_ANY;
>        Return := connect(sock[x],addr,sizeof(addr));


Так делать нельзя. Нельзя потключаться к INADDR_ANY.


 
SammIk ©   (2004-05-28 20:36) [4]

Выдержка из мсдн по поводу, select
Version: Requires Windows Sockets 2.0.
 Header: Declared in Winsock2.h.
 Library: Use Ws2_32.lib.


 
kalishenko   (2004-05-28 20:37) [5]

А к кому можно, если это сервер?


 
SammIk ©   (2004-05-28 20:38) [6]

и про FD_SET
Version: Requires Windows Sockets 2.0.
 Header: Declared in Winsock2.h.


 
SammIk ©   (2004-05-28 20:41) [7]

Есле это сервер, то надо биндить и листать, а не конектица


 
Verg ©   (2004-05-28 20:41) [8]


> [5] kalishenko   (28.05.04 20:37)
> А к кому можно, если это сервер?


Не понял. В каком месте это сервер?

Это классический клиент, если он вызывает connect - "соединиться" по-русски...


 
kalishenko   (2004-05-28 20:46) [9]

>>Sammik

По-моему, Winsock 2.0 здесь ни причем, если еще в 1.1 эти функции задекларированы.


 
SammIk ©   (2004-05-28 20:49) [10]

Тебе лучше знать чем моему sdk.


 
kalishenko   (2004-05-28 20:50) [11]

Ой, сорри, обознался. Но я попробовал и просто 127.0.0.1 - никакого эффекта.


 
SammIk ©   (2004-05-28 20:57) [12]

вопервых попробуи второи сокет $02,
а вовторых после каждого вызова сокетных ф-ии
поставь WSAGetLastError, и анализируи результат, возвращаемыи ф-ями.
Должно помочь


 
Verg ©   (2004-05-28 20:58) [13]


> [11] kalishenko   (28.05.04 20:50)
> Ой, сорри, обознался. Но я попробовал и просто 127.0.0.1
> - никакого эффекта.


Покажи код - как пробовал.

А где сервер 1001 порта на 127.0.0.1?
Он вообще есть?


 
kalishenko   (2004-05-28 21:23) [14]

Извините, протормозил. Забыл запустить сервер. :)
Тогда попутно вопрос: как реализовать неблокирующий режим для сервера?


 
Verg ©   (2004-05-28 21:45) [15]

Да точно так же
socket;
ioctlsocket(...,FIONBIO...);
bind()
listen()
Select()
accept

Ожидать готовности входящего соединения надо включив сокет в readfds набор для ф-ции Select.
Или без select - просто периодически вызывая Accept, который в случае отсутствия готовых соединений вернет INVALID_SOCKET, а WSAGetLastError будет равен WSAEWOULDBLOCK.


 
kalishenko   (2004-05-28 22:46) [16]

Вот так?

for x := 1 to maxproccess do
begin
 if stat[x] = CLOSE_SOCK then
 begin
   sock[x] := socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
   time[x] := TIME_OUT;
   ioctlsocket(sock[x],FIONBIO,on_sock);
   addr.sin_family := AF_INET;
   addr.sin_port := htons(10001);
   addr.sin_addr.s_addr := INADDR_ANY;
   Return := bind(sock[x],addr,sizeof(addr));
   Return := listen(sock[x],5);
 end;
end;

for x := 1 to maxproccess do
 if accept(sock[x],nil,nil) <> INVALID_SOCKET then stat[x]:=ACCEPTED_SOCK
 else Return := WSAGetLastError;

for x := 1 to maxproccess do
begin
 if stat[x]=ACCEPTED_SOCK then
 begin
   recv(Return,buff,sizeof(buff),0);
   Form1.Caption := Buff;
   stat[x]:=CLOSE_SOCK;
   ioctlsocket(sock[x],FIONBIO,off_sock);
   closesocket(sock[x]);
 end;
end;


 
Verg ©   (2004-05-28 23:02) [17]

Тебе никто не даст установить несколько сокетов на прослушку одного и того же порта на одном и том же локальном адресе (в данном случае INADDR_ANY)

Результатом accept в случае не INVALID_SOCKET будет новый сокет ассоциированный с установленным соединением (клиентом). Т.е. это тот сокет, посредством которого сервер в последствии будет обмениваться информацией с клиентом.
Сам же слушающий (серверный) сокет не предназначен для приема или передачи информации. Его задача только лишь принимать запросы на входящие соединения и складывать их во внутреннюю очередь, извлекать из которой установленные соединения ты и будешь ф-цией accept....

Пока встречный вопрос: а зачем тебе все это на чистом Winsock API реализовывать? Возьми TClient или TServerSocket, ну или компоненты Indy...


 
kalishenko   (2004-05-28 23:20) [18]

Просто здесь на форуме много раз высказывали мысль, что в TClientSocket и TServerSocket было обнаружено много ошибок. Да и просто захотелось узнать все изнутри. Кроме того, думаю использовать тот или иной вариант (блок. режим или неблок. режим) в консольной проге.

А приведенный мною выше код не работает, как хотелось бы. Все время вылетает INVALID_SOCKET после accept и WSAGetLastError равен WSAEWOULDBLOCK.


 
Verg ©   (2004-05-28 23:54) [19]


> А приведенный мною выше код не работает, как хотелось бы.
> Все время вылетает INVALID_SOCKET после accept и WSAGetLastError
> равен WSAEWOULDBLOCK.


Конечно, ведь соединения-то никто и не пытался установить с твоим сервером. Ты должен "ждать" либо через select либо в блокирующем режиме
просто вызвать accept - он сам подождет
.

> Просто здесь на форуме много раз высказывали мысль, что
> в TClientSocket и TServerSocket было обнаружено много ошибок.


Ошибок, не ошибки - неточности есть некоторые, но, поверь их исчезающе малое число, особенно по сравнению с тем, которе ты сам можешь наделать пытаясь "вспахать" это все с нуля.
Вот посмотри. Примерчик самого наипростейшего, однопоточного TCP сервера (ЭХО-сервер) на WinsockAPI.
Этот сервер - пожалуй, самое простое, что можно сделать, но в то же время он крайне неэффективен, так что может рассматриваться только как учебный примерчик для начала.

program EchoServer;

{$APPTYPE CONSOLE}

uses
 Windows,
 Winsock,
 Classes,
 SysUtils;

var
WSAData : TWSAData;
SSocket : TSocket;
LAddr   : TSockAddrIn;
Fds,
Rfds : TFDSet;
I : integer;

function ToOem(const S : string):string;
begin
 Result := S;
 if Result<>"" then
   CharToOemBuff(pchar(Result), pchar(Result), length(Result));
end;

procedure PrintErr(Err : DWORD; Wt : boolean);
begin
 Writeln(ToOem(SysErrorMessage(Err)));
 if Wt then ReadLn;
end;

procedure ExitErr;
begin
 if (WSAGetLastError<>NO_ERROR) and
    (WSAGetLastError<>WSAENOTSOCK) and
    (WSAGetLastError<>WSAEINTR) then
   PrintErr(WSAGetLastError, true);
 if SSocket<> INVALID_SOCKET then
   CloseSocket(SSocket);
 if WSAData.wVersion <> 0 then
   WSACleanup;
 halt(0);
end;

function checksocket(Ret : integer):integer;
begin
 Result := Ret;
 if Result = SOCKET_ERROR then
   ExitErr
end;

procedure DoRead(S : TSocket);
var Res : integer;
   Buffer : array[0..255] of byte;
begin
 Res := recv(S, Buffer[0], sizeof(Buffer), 0);
 if Res > 0 then
   Res := send(S, Buffer[0], Res, 0);
 if Res <= 0 then
 begin
   if Res < 0 then
     PrintErr(WSAGetLastError, false);
   FD_CLR(S, Fds);
   if CloseSocket(S) = SOCKET_ERROR then
     PrintErr(WSAGetLastError, false);
 end;
end;

var
 NewSocket : TSocket;
begin
 if WSAStartup(MAKEWORD(1,1), WSAData) <> NO_ERROR then ExitErr;
 SSocket := socket(PF_INET, SOCK_STREAM, 0);
 if SSocket = INVALID_SOCKET then ExitErr;
 FillChar(LAddr, sizeof(LAddr), 0);
 LAddr.sin_family := AF_INET;
 LADDR.sin_port   := htons(8088);
 LAddr.sin_addr.S_addr := htonl(INADDR_ANY);
 checksocket( bind(SSocket, LAddr, sizeof(LAddr)) );
 checksocket( listen(SSocket, SOMAXCONN) );
 FD_ZERO( Fds );
 FD_SET( SSocket, Fds );
 Repeat
   RFds := Fds;
   checksocket( select(0, @RFds, nil, nil, nil) );
   for I:=0 to RFds.fd_count -1 do
   begin
     if RFDs.fd_array[I] = SSocket then
     begin
       NewSocket := accept( SSocket, nil, nil );
       if NewSocket <> INVALID_SOCKET then
       begin
         if Fds.fd_count < FD_SETSIZE then
           FD_SET( NewSocket, Fds )
         else
           if CloseSocket(NewSocket) = SOCKET_ERROR then
             PrintErr(WSAGetLastError, false);
       end else
         PrintErr(WSAGetLastError, false);
     end else
       DoRead(RFDs.fd_array[I]);
   end;
 until False;
end.


 
kalishenko   (2004-06-01 09:21) [20]

А почему он неэффективен? Из-за "Repeat ... Until False"?


 
Digitman ©   (2004-06-01 10:29) [21]


> почему он неэффективен?


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


 
Verg ©   (2004-06-01 10:55) [22]


> [20] kalishenko   (01.06.04 09:21)
> А почему он неэффективен? Из-за "Repeat ... Until False"?


Не, из-за

for I:=0 to RFds.fd_count -1 do

Последовательный сервер... в принципе все сказано в [21]



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2004.08.01;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.52 MB
Время: 0.152 c
3-1089106576
RainKM
2004-07-06 13:36
2004.08.01
Paradox


3-1089140159
Zhekson
2004-07-06 22:55
2004.08.01
Фильтрация по полю второстепенной таблицы.


14-1089878459
}|{yk
2004-07-15 12:00
2004.08.01
Я вот не пойму


1-1090224195
Qwer
2004-07-19 12:03
2004.08.01
Как запустить процедуру ?


14-1089449926
Mell
2004-07-10 12:58
2004.08.01
Красивый toolbar





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский