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

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

Проблема с SendText в ctBlocking сокете [D7, WinXP]


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

Alex_C ©   (2009-05-12 22:39:00) 

Некоторое время назад я спрашивал о проблемах создания Socket'а в треде. Блягодаря примеру, взятому от сюда
http://www.delphimaster.ru/cgi-bin/forum.pl?id=1227604925&n=4
все прекрасно работает. На прием.
Проблема возникает когда надо сделать SendText:

В методе Execute потока у меня:

 while (not Terminated) and TelnetSocket.Socket.Connected do
 begin
    DataLength := TelnetSocket.Socket.ReceiveBuf(Buf[0], 64000);
    if DataLength


FireMan_Alexey   (2009-05-23 12:28:00) [1]

Не забывай, что при CanRead необходимо проверить ReceiveLength(Сколько можно прочитать) и когда CanRead=False читать не нужно, а то будет как у тебя в TelnetSocket!


Alex_C   (2009-05-22 21:13:00) [2]


> FireMan_Alexey


Спасибо! Очень интересная реализация! Буду изучать!
После чего задам вопросы )))


FireMan_Alexey   (2009-05-22 14:45:00) [3]

Да еще забыл, там нужна строчка:

If TvoySocket.Disconnected Then Break;


FireMan_Alexey   (2009-05-22 14:00:00) [4]

Да и:
If TvoySocket.ErrorPresent Then //Error ! ;
Можно первым поставить :)


FireMan_Alexey   (2009-05-22 13:54:00) [5]

If Not TvoySocket.Connect Then
Begin
   Error:=TvoySocket.Error;
   TvoySocket.Close;
    Exit;//Забыл :)
End;


FireMan_Alexey   (2009-05-22 13:46:00) [6]

Используем так:
...
TvoySocket:=TNewSock.Create;

TvoySocket.Host:='127.0.0.1';
TvoySocket.Port:=???;
If Not TvoySocket.Connect Then
 Begin
    Error:=TvoySocket.Error;
    TvoySocket.Close;
 End;
While not Terminated Do
 Begin
    TvoySocket.GetStatus;
    If TvoySocket.CanRead Then  //Chitaem ;
    If tvoySocket.CanWrite Then //Pishem;
    If TvoySocket.ErrorPresent Then //Error!;  
 End;
TvoySocket.Free;
...


FireMan_Alexey   (2009-05-22 13:41:00) [7]

За код не ругать делал для себя:

unit NewSock;

interface
Uses WinSock;

CONST
 SOCKET_ERROR=WINSOCK.SOCKET_ERROR;
   
Type
 TNewSock=Class
 Private
   FSock    :Integer;
   FClose   :Boolean;
   FError   :Integer;
   FCanRead :Boolean;
   FCanWrite:Boolean;
   FOnError :Boolean;
   FAddr    :TSockAddr;
   FHost    :String;
   FPort    :Word;
 Public
   Procedure Close;
   Procedure GetStatus(Const TimeOut:Integer=50);
   Function  GetRemoteAddress:Integer;
   Function  GetRemoteHost:String;
   Function  Connect:Boolean;
   Function  Write(Var Buff; Size:Integer):Integer;
   Function  Read (Var Buff; Size:Integer):Integer;
   Function  ReceiveLength:Integer;
   Constructor Create(Const Sock:Integer=Invalid_Socket);
   Destructor  Destroy;Override;
   Property Host:String Read FHost Write FHost;
   Property Port:Word Read FPort Write FPort;
   Property CanRead:Boolean Read FCanRead;
   Property CanWrite:Boolean Read FCanWrite;
   Property Disconnected:Boolean Read FClose;
   Property ErrorPresent:Boolean Read FOnError;
   Property Error:Integer Read FError;
   Property SocketHandle:TSocket Read FSock;
 End;

Function InetAddr(Addr:Integer):String;

implementation

Constructor TNewSock.Create;
Begin
 Inherited Create;
 FClose:=Sock=Invalid_Socket;
 FSock:=Sock;
 {IF Not FClose Then
   Begin
     FCanWrite:=True;
   End
 Else}
 FCanWrite:=False;
 FCanRead:=False;
 FOnError:=False;
 FError:=0;    
End;

Destructor  TNewSock.Destroy;
Begin
//
 Close;
 Inherited Destroy;
End;

Procedure TNewSock.Close;
Begin
 If FSock<>Invalid_Socket Then
   Begin
     If CloseSocket(FSock)<>0 Then
       Begin
         FError:=WSAGetLastError;
         FOnError:=True;
       End;
     FSock:=Invalid_Socket;
     FCanRead:=False;
     FCanWrite:=False;
   End;
 FClose:=True;
End;

Procedure TNewSock.GetStatus;
Var
 FD_READS,FD_WRITES,FD_ERROR:TFDSET;
 T:TTimeVal;
 R:Integer;
Begin
//
 If FClose Then Exit;
 T.tv_usec:=TimeOut Mod 1000;
 T.tv_sec:=TimeOut Div 1000;
 FD_ZERO(FD_READS);
 FD_SET(FSock,FD_READS);
 FD_WRITES:=FD_READS;
 FD_ERROR:=FD_READS;

 //FD_SET(FSock,FD_WRITES);

 R:=Select(1,@FD_READS,@FD_WRITES,@FD_ERROR,@T);
 If R=Socket_Error Then
   Begin
     FError:=WSAGetLastError;
     FOnError:=True;
     Exit;
   End
 Else FError:=0;
 If R>0 Then
   Begin
     If FD_ISSET(FSock,FD_READS) Then FCanRead:=True
       Else FCanRead:=False;
     If FD_ISSET(FSock,FD_WRITES) Then FCanWrite:=True;
     If FD_ISSET(FSock,FD_ERROR) Then
       Begin
         FOnError:=True;
         R:=SizeOf(FError);
         R:=GetSockOpt(FSock,SOL_SOCKET,SO_ERROR,@FError,R);
         {If R=SOCKET_ERROR Then
           FError:=WSAGetLastError;}
       End
     Else
       FOnError:=False;  
   End;
End;

Function  TNewSock.GetRemoteAddress;
Var
 _Addr:TSockAddr;
 _Size:Integer;
Begin
 Result:=-1;
 If Not FClose Then
   Begin
     _Size:=SizeOf(_Addr);
     FError:=GetPeerName(FSock,_Addr,_Size);
     If FError=SOCKET_ERROR Then
       Begin
         FError:=WSAGetLastError;
         FOnError:=True;
         Exit;
       End;
     Result:=_Addr.sin_addr.S_addr;  
   End;
End;

Function  TNewSock.GetRemoteHost;
Var
 _Addr:Integer;
 Host:PHostEnt;
Begin
 _Addr:=GetRemoteAddress;
 Host:=GetHostByAddr(@_Addr,SizeOf(_Addr),AF_INET);
 Result:='';
 If Host<>Nil Then Result:=Host.h_name;
End;

Function  TNewSock.Write;
Var
 R:Integer;
Begin
 R:=Send(FSock,Buff,Size,0);
 If R=SOCKET_ERROR Then
   Begin
     FError:=WSAGetLastError;
     If FError=WSAEWOULDBLOCK Then
       Begin
         FCanWrite:=False;
         R:=0;
         FError:=0;
       End
     Else FOnError:=True;
   End;
 Result:=R;  
End;

Function  TNewSock.Read;
Var
 R:Integer;
Begin
 R:=Recv(FSock,Buff,Size,0);
 If R=0 Then
   Close;
 If R=SOCKET_ERROR Then
   Begin
     FError:=WSAGetLastError;
     If FError=WSAEWOULDBLOCK Then
       Begin
         FCanRead:=False;
         FError:=0;
       End
     Else FOnError:=True;  
     R:=0;
   End;
 Result:=R;  
End;

Function  TNewSock.ReceiveLength;
Begin
 If IOCTLSocket(FSock,FIONREAD,Result)=SOCKET_ERROR Then
   Begin
     FError:=WSAGetLastError;
     FOnError:=True;
   End;
End;

Function  TNewSock.Connect;
Var
 PHost:PHostEnt;
 R:Integer;
Begin
 Result:=False;
 If Not FClose Then Exit;
 If FHost='' Then Exit;
 If FPort=0 Then Exit;
 FSock:=Socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
 If FSock=Invalid_Socket Then
   Begin
     FError:=WSAGetLastError;
     FOnError:=True;
     Close;
     Exit;
   End;
 FAddr.sin_family:=AF_INET;
 FAddr.sin_port:=HToNS(FPort);
 FAddr.sin_addr.S_addr:=Inet_addr(PChar(FHost));
 If FAddr.sin_addr.S_addr=INADDR_NONE Then
   Begin
     PHost:=GetHostByName(PChar(FHost));
     If PHost=Nil Then
       Begin
         FError:=WSAGetLastError;
         FOnError:=True;
         Exit;
       End;
     Move(PHost^.h_addr_list^[0],
          FAddr.sin_addr,
          PHost^.h_length);
   End;
 R:=WinSock.Connect(FSock,FAddr,SizeOF(FAddr));
 If R=SOCKET_ERROR Then
   Begin
     FError:=WSAGetLastError;
     FOnError:=True;
     Exit;
   End;
 FClose:=False;
 Result:=True;
End;

Function InetAddr;
Var
 _Addr:TSockAddr;
Begin
 _Addr.sin_addr.S_addr:=Addr;
 Result:=inet_ntoa(_Addr.sin_addr);
End;

VAR
 WSAD:TWSAData;

Initialization
Begin
 WSAStartup($202,WSAD);
End;

Finalization
Begin
 WSACleanup;
End;

end.


Alex_C   (2009-05-22 11:26:00) [8]


> Ничего она не меняется, не выдумывай.


Честно признаюсь - выдумал )))
Сейчас все внимательно проверил и нашел у себя ошибку! Естественно событие коннект приходит первым и никак иначе.


> Об успешности коннекта можно судить только проанализировав
> Ev.iErrorCode[FD_CONNECT_BIT]. Тоже самое касается и всех
> прочих событий без исключения.


Я так и делаю:


      if GetEvent(Ev.lNetworkEvents,FD_CONNECT) then
         begin
           if Ev.iErrorCode[FD_CONNECT_BIT]<>0 Then
           begin
             if Assigned(FOnErrorEvent) then
               Synchronize(OnErrorEventProcedure);
             Break;
           end
           else
           begin
             FSocketState := wsConnected;
             if Assigned(FOnConnectEvent) then
               Synchronize(OnConnectProcedure);
           end;
         end;


Сергей М.   (2009-05-22 08:39:00) [9]


> почему меняется последовательность?
>


Ничего она не меняется, не выдумывай.

FD_CONNECT всегдавозбуждается первым, вне зависимости будут ли при этом одновременно возбуждаться FD_WRITE и FD_READ.

Поставь строку

if GetEvent(Ev.lNetworkEvents,FD_CONNECT) then

на самый верх цепочки строк-проверок, как тебе сказал FireMan_Alexey ©, и убедись в этом)

FD_WRITE же обычно возбуждается одновременно с FD_CONNECT.

Одновременно взведенные флаги FD_CONNECT | FD_WRITE показывают, что соединение установлено и готово к передаче.

При проверке событийных флагов следует обязателоьнопроверять соответствующие событиям коды ошибок. Так, например, попытка коннекта, приведшая к событию FD_CONNECT, еще не говорит об успешном коннекте. Об успешности коннекта можно судить только проанализировав Ev.iErrorCode[FD_CONNECT_BIT]. Тоже самое касается и всех прочих событий без исключения.


Alex_C   (2009-05-21 21:56:00) [10]


> Чтобы первое пришло FD_CONNECT поменяй местами If-ы :)


Ну это тут не причем  )))
Последовательность прихода сообщений от последовательности if-ов не зависит. Хотя тут вот тоже пока непонятность - почему меняется последовательность?


Alex_C   (2009-05-21 21:36:00) [11]


> FireMan_Alexey ©   (21.05.09 16:24) [36]
> Хочешь кину тебе исходник моего класса сокета специально
> для потока?
> Он сделан по другому...


Очень хочу!
Вообще хочу достаточно досканально разобраться с работой сокетов, т.к. в дальнейшем предстоит решить при их помощи достаточно серьезную задачу.


FireMan_Alexey   (2009-05-21 17:20:00) [12]


> Alex_C

Чтобы первое пришло FD_CONNECT поменяй местами If-ы :)


FireMan_Alexey   (2009-05-21 16:55:00) [13]

Я понял, поэтому и предложил использовать с определенной задержкой, а не бесконечной! :)
У Автора будет еще много вопросов :), хотя в [4] я предлагал использовать Select. Со своего опыта знаю, что с этой функцией гемора меньше :)


Сергей М.   (2009-05-21 16:48:00) [14]


> FireMan_Alexey ©   (21.05.09 16:25) [37]


Чтобы поток имел возможность проверить флаг Terminated, ф-ция должна "отвиснуть")

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

Если событие не сигналит бесконечно долгое время, вопрос сводится лишь к тому кто пошлет потоку сообщение. Судя по [13] автор ожидает, что это самое WM_QUIT свалится в очередь его потока с какой-то луны.

Потому я и заострил на этом внимание)


FireMan_Alexey   (2009-05-21 16:44:00) [15]


> Сергей М.


Извиняюсь, а Terminated у него нет :)


> Alex_C  


Подумай, что для тебя(Вас) лучше:

While True do
или
While not Terminated do ?


FireMan_Alexey   (2009-05-21 16:25:00) [16]


> Сергей М.

Смысл в Terminated!


FireMan_Alexey   (2009-05-21 16:24:00) [17]

Хочешь кину тебе исходник моего класса сокета специально для потока?
Он сделан по другому...


Сергей М.   (2009-05-21 16:21:00) [18]


> Alex_C   (21.05.09 16:18) [34]


Кто будет посылать WM_QUIT твоему потоку ?


Alex_C   (2009-05-21 16:18:00) [19]


> Способы обработки и диспетчеризации сообщений никак не касаются
> winsock-событий и никоим образом не влияют на порядок их
> возбуждения.


Да вот как бы и я это понимаю. Только пока не понял, почему такое происходит... Буду разбираться!


> Ничего, скоро тебя еще грабли ждут, но потом, как разберешься
> когда приходят события FD_READ, FD_CLOSE :)


Уже на эти грабли наступил ))) Никак не могу добиться, чтоб произошло событие FD_CLOSE.


Сергей М.   (2009-05-21 16:02:00) [20]


> FireMan_Alexey ©   (21.05.09 15:43) [32]


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


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

Наверх

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