![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Проблема с SendText в ctBlocking сокете [D7, WinXP]
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.