![]() |
| ||
|
|
Новости |
Поиск |
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
Alex_C (2009-05-20 16:43:00) [41]Здравствуйте!
Всем большое спасибо за советы!
Благодаря им я написал ClientSocketThread.
Приведу текс модуля полностью - думаю он многим будет интересен:
unit HXClientSocetThreadUnit;
interface
uses
Windows, Messages, SysUtils, Classes, WinSock2, ScktComp;
type
TSocketState = (wsNone, wsConnected, wsClosed);
THXOnConnectEvent = procedure(Sender: TObject) of object;
THXOnDisconnectEvent = procedure(Sender: TObject) of object;
THXOnReadEvent = procedure(Sender: TObject; Buff: array of byte;
Count: Integer) of object;
THXOnWriteEvent = procedure(Sender: TObject) of object;
THXOnErrorEvent = procedure(Sender: TObject; Error: Integer) of object;
THXOnDestroyEvent = procedure(Sender: TObject) of object;
THXClientSocetThread = class(TThread)
Host: string;
Port: Cardinal;
private
function ProcessMessage : Boolean;
procedure ProcessMessages;
protected
FSock: Cardinal;
FSocketState: TSocketState;
FOnConnectEvent : THXOnConnectEvent;
FOnDisconnectEvent: THXOnDisconnectEvent;
FOnReadEvent : THXOnReadEvent;
FOnWriteEvent : THXOnWriteEvent;
FOnErrorEvent : THXOnErrorEvent;
FOnDestroyEvent : THXOnDestroyEvent;
procedure Execute; override;
public
constructor Create(iAddr: string; iPort: Cardinal);
destructor Destroy; override;
procedure SendData(Data: array of byte; Len: Integer);
procedure SendStr(Str: String);
procedure Disconnect;
property OnConnectEvent : THXOnConnectEvent read FOnConnectEvent write FOnConnectEvent;
property OnDisconnectEvent: THXOnDisconnectEvent read FOnDisconnectEvent write FOnDisconnectEvent;
property OnReadEvent : THXOnReadEvent read FOnReadEvent write FOnReadEvent;
property OnWriteEvent : THXOnWriteEvent read FOnWriteEvent write FOnWriteEvent;
property OnErrorEvent : THXOnErrorEvent read FOnErrorEvent write FOnErrorEvent;
property OnDestroyEvent : THXOnDestroyEvent read FOnDestroyEvent write FOnDestroyEvent;
end;
type
PWSAEvent=^TWSAEvent;
TWSAEvent=THandle;
const
fd_max_events = 10;
type
TWSANetworkEvents = record
lNetworkEvents: longint;
iErrorCode: array[0..fd_max_events-1] of integer;
end;
PWSANetworkEvents = ^TWSANetworkEvents;
implementation
uses
Unit1;
Slym (2009-05-13 12:35:00) [42]Slym © (13.05.09 12:22) [8]
переводит в неблокирующий режим!
после выяснений "кто-почем" перевести обратно в блокирующий
WSAEventSelect(ClientSocket.SocketHandle,nil,0);
ioctlsocket(ClientSocket.SocketHandle,FIONBIO,0);
Slym (2009-05-13 12:26:00) [43]или уйти в сторону overlapped операций хотя там теже евенты...
Slym (2009-05-13 12:23:00) [44]Slym © (13.05.09 12:22) [8]
SD_RECEIVE
FD_READ!!!
Slym (2009-05-13 12:22:00) [45]
const
winsocket = 'wsock32.dll';
type PWSAEvent=^TWSAEvent;
TWSAEvent=THandle;
function WSACreateEvent:TWSAEvent;stdcall; external winsocket name 'WSACreateEvent';
function WSACloseEvent(hEvent:TWSAEvent):BOOL;stdcall; external winsocket name 'WSACloseEvent';
function WSASetEvent(hEvent:TWSAEvent):BOOL;stdcall; external winsocket name 'WSASetEvent';
function WSAResetEvent(hEvent:TWSAEvent):BOOL;stdcall; external winsocket name 'WSAResetEvent';
function WSAEventSelect(hSocket: TSocket; hEventObject: TWSAEvent; lNetworkEvents: LongInt): integer;stdcall; external winsocket name 'WSAEventSelect';
procedure TPortMapClientThread.ClientExecute;
var
events:array[0..1] of THandle;
WaitResult:DWORD;
begin
try
events[0]:=WSACreateEvent;
events[1]:=WriteEvent;
WSAEventSelect(ClientSocket.SocketHandle,events[0],SD_RECEIVE);
WaitResult:=WaitForMultipleObjects(2,@events,false,INFINITE);
case WaitResult of
WAIT_OBJECT_0: Read;WSAResetEvent(events[0]);
WAIT_OBJECT_0+1: Write;WSAResetEvent(events[1]);
else
end;
..........
end;
procedure TTelnetThread.SendText(S: string);
begin
FToSend:=FToSend+S;
SetEvent(WriteEvent);
end;
Сергей М. (2009-05-13 09:23:00) [46]
> блокирующий сокет .. в отдельном треде его использовать нельзя
Сокету в любомрежиме абсолютно фиолетово, в каком потоке его используют - в любом потоке он будет успешно работать.
Собссно разничия в понятиях основнойпоток и дополнительныйпоток довольно условны.
Считается, что основным является поток, создаваемый неявно самой системой
при создании процесса. Прочие, т.е. дополнительные потоки отличаются от основного потока только тем, что создаются они при необходимости явно в прикладном коде хост-приложения или в коде библ.модулей, используемых хост-приложением.
FireMan_Alexey (2009-05-13 09:02:00) [47]Да
Function GetEvent(L,L1:Integer):Boolean;
Begin
Result:=(L and L1)<>0;
End;
Сильно не ругайте код, он был написан для Butto1Click просто для демонстрации :)
поэтому присутствуют всякие Application и другие несуразности...
Но после не долгой переработки может стать даже очень работоспособным для потока.
FireMan_Alexey (2009-05-13 08:57:00) [48]Чего-то я запарился с коментариями, они должны были идти после кода :)
FireMan_Alexey (2009-05-13 08:55:00) [49]Select помойму никто не отменял :)
> 1. Если хочешь "одновременно" прием-передача - используй
> не блокирующий сокет (а значит в отдельном треде его использовать
> нельзя) - но при этом программа "зависает" при подключении
> к серверу.
>
Кто тебе сказал, что нельзя использовать? (плюнь ему в лицо)
Вот пример использования неблок. сокета в отдельном потоке
Думаю шапку сам до пишеш :)
...
//
А если не в отдельном потоке используй оконную функцию WSAAsyncSelect,
а чтобы программа не зависала переводи в асинхронный режим до коннекта, а потом лови сообщение FD_CONNECT!
Сергей если что меня поправит :), просто я уже давно не использую оконные функции.
А конструкция с простым Select-ом тебя чем не устраивает?
Addr.sin_family:=AF_INET;
Addr.sin_port:=HtoNS(2000);
Addr.sin_addr.S_addr:=inet_addr('127.0.0.1');
EResult:=WSAStartup( $202,WSAD);
If EResult<>0 Then Exit;
Sock:=Socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
IF Sock=Invalid_Socket Then Exit;
Event:=WSACreateEvent;
WSAEventSelect(Sock,Event,FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE); //описана в Winsock2
Connect(Sock,@Addr,Sizeof(addr));
While True do
Begin
Application.ProcessMessages;
Case WSAWaitForMultipleEvents(1,@Event,False,{WSA_INFINITE}50,True) of
WSA_WAIT_TIMEOUT:;
0:Begin
//Ev.lNetworkEvents:=FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE;
EResult:=WSAEnumNetworkEvents(Sock,Event,@Ev);
IF EResult<>0 Then Break;
IF GetEvent(Ev.lNetworkEvents,FD_READ)Then
Begin
IF Ev.iErrorCode[FD_READ_BIT]<>0 Then
Begin
Log.Items.Add('READ ERROR: '+IntToStr(Ev.iErrorCode[FD_READ_BIT]));
Break;
End
Else
Begin
ioctlsocket(Sock,FIONREAD,Size);
SetLength(Buff,Size);
Recv(Sock,buff[1],Size,0);
Log.Items.Add('READ '+IntToStr(Size)+' '+Buff);
End;
End;
IF GetEvent(Ev.lNetworkEvents,FD_WRITE)Then
Begin
IF Ev.iErrorCode[FD_WRITE_BIT]<>0 Then
Begin
Log.Items.Add('WRITE ERROR: '+IntToStr(Ev.iErrorCode[FD_WRITE_BIT]));
Break;
End
Else
Log.Items.Add('WRITE');
End;
IF GetEvent(Ev.lNetworkEvents,FD_CONNECT)Then
Begin
IF Ev.iErrorCode[FD_CONNECT_BIT]<>0 Then
Begin
Log.Items.Add('CONNECT ERROR: '+IntToStr(Ev.iErrorCode[FD_CONNECT_BIT]));
Break;
End
Else
Log.Items.Add('CONNECT');
End;
IF GetEvent(Ev.lNetworkEvents,FD_CLOSE)Then
Begin
IF Ev.iErrorCode[FD_CLOSE_BIT]<>0 Then
Begin
Log.Items.Add('CLOSE ERROR: '+IntToStr(Ev.iErrorCode[FD_CLOSE_BIT]));
Break;
End
Else
Begin
Log.Items.Add('CLOSE');
Break;
End;
End;
End;
End;
End;
WSACloseEvent(Event);
CloseSocket(Sock);
WSACleanup;
...
Alex_C (2009-05-13 08:31:00) [50]
> Если нужно организовать "одновременные" прием и передачу
> с использованием одного и того же сокета, то блокирующий
> режим не подойдет.
Да Сергей! Вчера перелопатил кучу инфы по сокетам - тоже уже это понял.
Однако получается какой-то замкнутый круг:
1. Если хочешь "одновременно" прием-передача - используй не блокирующий сокет (а значит в отдельном треде его использовать нельзя) - но при этом программа "зависает" при подключении к серверу.
2. Хочешь, чтоб не зависала при подключении - используй сокет в отдельном треде, а значит блокирующий, но при этом нельзя организовать одновременно прием-передача.
Интересно, и как эту проблему в принципе решить? Или ее решить нельзя - или-или?
Сергей М. (2009-05-13 08:17:00) [51]
> куда SendText вставлять?
Если нужно организовать "одновременные" прием и передачу с использованием одного и того же сокета, то блокирующий режим не подойдет.
Сергей М. (2009-05-13 08:15:00) [52]
> Понятно, что это не верно
Строго наоборот - это верно.
В каждый момент времени сокет может быть либо в состоянии приема либо передачи, но не одновременно и в том и в другом состоянии.