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


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]


> Понятно, что это не верно


Строго наоборот - это верно.
В каждый момент времени сокет может быть либо в состоянии приема либо передачи, но не одновременно и в том и в другом состоянии.


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

Наверх

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