Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.08.01;
Скачать: CL | DM;

Вниз

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

 
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;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.067 c
9-1074707880
Zak3D[@Tm]
2004-01-21 20:58
2004.08.01
MP3 и Gif ы в Delphi???


14-1089823533
GanibalLector
2004-07-14 20:45
2004.08.01
Цены на авиабилеты и все такое...


1-1090411114
BillyJeans
2004-07-21 15:58
2004.08.01
Совместимость 7 и 5 версии Delphi...


1-1090147493
Алексей
2004-07-18 14:44
2004.08.01
HTML и Delphi


1-1090260090
k-sergey
2004-07-19 22:01
2004.08.01
у динамич. созд. TImage отловить событие onCliсk и получть его им