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

Вниз

Socket -> Thread   Найти похожие ветки 

 
FireMan_Alexey ©   (2004-05-23 10:33) [0]

Ув. Мастера!
Использую сокет в потоке и хотел задать вопрос по поводу обработки событий.

Вот заголовок класса:

TEvent=Procedure (Sender:TObject) of object;

TThreadSock=Class(TThread)
 Private  
   FSocket  :TSocket;
   FRead    :TEvent;
   FError   :Integer;
   FHost    :String;
   RecvMem  :TMyMemory;
   SendMem  :TMyMemory;
   Procedure Read;
   Procedure Write;
   Function  ReceiveLength(Socket:TSocket):Integer;
 Protected
   Procedure Execute;Override;
 Public
   Procedure   Lock;
   Procedure   Unlock;
   Procedure   SetSocket(Socket:TSocket);
   Constructor Create;
   Destructor  Destroy;Override;
   Property    Error       :Integer   Read FError;
   Property    SocketHandle:TSocket   Read FSocket;
   Property    Host        :String    Read FHost   Write FHost;
   Property    ReceiveData :TMyMemory Read RecvMem;
   Property    SendData    :TMyMemory Read SendMem;
   Property    OnRead      :TEvent    Read FRead   Write FRead;
 End;

Вопрос если я буду использовать вызов FRead(Self) и это будет вызываться процедура формы,
то не возникнет ли какое-нибудь исключение без использования синхронизации?
Уже пробовал такое сочетание.

Procedure Read;
Begin
 If Assigned(FRead) Then FRead(Self);
End;

А в процедуре Execute:

If (NetEvent.lNetworkEvents and FD_READ_BIT)<>0 Then Synchronize(Read);
Но такой вариант не проходит! Процедура Read даже не выполнятся!
Рассматривал модуль SCKTCOMP.Pas и видел там используют TCriticalSection. Для чего?


 
Polevi ©   (2004-05-23 19:20) [1]

>Процедура Read даже не выполнятся!
должна выполнятся, если конечно условие выполняется


 
Digitman ©   (2004-05-24 08:17) [2]

вызов обработчиков транспортных событий в осн.код.потоке (см. Synchronize) полностью лишает смысла "вынос" гнездового транспорта в доп.код.поток


 
FireMan_Alexey ©   (2004-05-24 09:27) [3]

>Digitman
Но я когда-то в книге читал, что внутри доп. кодового потока нельзя использовать оконные функции. Может я не прав?
Но как тогда возможно передавать приходящие данные от сокета?
И для чего этот класс TCriticalSection?
>Polevi
Условие выполняется т.е. приходит FD_READ, выполняется Synchronize, но как я понял сама Synchronize отсылает лишь postmessage с определенными параметрами!
Я вообще-то отказался от Synchronize сразу. Т.к. если мне прийдется ждать выполнения поочередно нескольких доп. код. потоков, то я могу сделать это и с помощью оконных функций!
Но мне нужно в отдельном кодовом потоке.


 
Digitman ©   (2004-05-24 09:54) [4]


> FireMan_Alexey ©   (24.05.04 09:27) [3]


> внутри доп. кодового потока нельзя использовать оконные
> функции


это почему же ? ничто этому не мешает


> для чего этот класс TCriticalSection


для синхронизации доступа со стороны доп.потоков одного и того же процесса к non-thread-safe ресурсу, созданному одним из код.потоков этого же процесса


 
FireMan_Alexey ©   (2004-05-24 09:59) [5]

Т.е. как я понимаю у меня все будет работать нормально без синхронизации или же мне лучше использовать TCriticalSection?


 
Digitman ©   (2004-05-24 10:04) [6]


> FireMan_Alexey ©   (24.05.04 09:59) [5]



> все будет работать нормально


что "все"-то ?

о чем вообще речь идет ? о каком ресурсе ?


 
Polevi ©   (2004-05-24 10:05) [7]

это зависит от кода
если тебе нужно работать в нем с формой, к примеру - нужен Synchonize
если просто работать монопольно с неким ресурсом - TCriticalSection


 
FireMan_Alexey ©   (2004-05-24 10:12) [8]

TEvent=Procedure (Sender:TObject) of object;
 
 TThreadSock=Class(TThread)
 Private  
   FSocket  :TSocket;
   FRead    :TEvent;
   FError   :Integer;
   FHost    :String;
   RecvMem  :TMyMemory;
   SendMem  :TMyMemory;
   FCritical:TCriticalSection;
   Procedure Read;
   Procedure Write;
   Function  ReceiveLength(Socket:TSocket):Integer;
 Protected
   Procedure Execute;Override;
 Public
   Procedure   Lock;
   Procedure   Unlock;
   Procedure   SetSocket(Socket:TSocket);
   Constructor Create;
   Destructor  Destroy;Override;
   Property    Error       :Integer   Read FError;
   Property    SocketHandle:TSocket   Read FSocket;
   Property    Host        :String    Read FHost   Write FHost;
   Property    ReceiveData :TMyMemory Read RecvMem;
   Property    SendData    :TMyMemory Read SendMem;
   Property    OnRead      :TEvent    Read FRead   Write FRead;
 End;
implementation

Const
 MaxSend=$1000;

Constructor TThreadSock.Create;
Begin
 Inherited Create(True);
 FSocket:=Invalid_Socket;
 FCritical:=TCriticalSection.Create;
End;

Destructor  TThreadSock.Destroy;
Begin
 FCritical.Free;
 Inherited Destroy;
End;

Procedure TThreadSock.SetSocket;
Begin
 If FSocket=Invalid_Socket Then FSocket:=Socket;
End;

Procedure TThreadSock.Read;
Var
 Buff:Pointer;
 Size:Integer;
Begin
//
 Lock;
 Try
   Size:=ReceiveLength(FSocket);
   GetMem(Buff,Size);
   Recv(FSocket,Buff^,Size,0);
   RecvMem.AddBuff(Buff^,Size);
   FreeMem(Buff);
   If Assigned(FRead) Then
     FRead(Self);
  Finally
    Unlock;
  End;
End;

Procedure TThreadSock.Write;
Var
 Count:Integer;
Begin
//
 Lock;
 Try
   While True do
     Begin
       Count:=SendMem.Size;
       If Count<=0 Then Exit;
       If Count>MaxSend Then Count:=MaxSend;
       Count:=Send(FSocket,SendMem.Memory^,Count,0);
       If Count>0 Then SendMem.Delete(0,Count)
         Else Exit;
     End;
 Finally
   Unlock;
 End;
End;

Function TThreadSock.ReceiveLength(Socket:TSocket):Integer;
Begin
 Lock;
 Try
   IOCTLSocket(Socket,FIONREAD,Cardinal(Result));
 Finally
   Unlock;
 End;
End;

Function GetEvent(Event,FD_XXX:Integer):Boolean;
Begin
 Result:=(Event and FD_XXX)<>0;
End;

Function  CheckError(Error:Integer):Boolean;
Begin
 Result:=False;
 If (Error<>0)and(Error<>WSAEWouldBlock) Then Result:=True;
End;

Procedure TThreadSock.Execute;
Var
 Event   :THandle;
 NetEvent:TWSANetworkEvents;
 WSAD    :TWSAData;
 Index   :Integer;
Begin
//
 If FSocket=Invalid_Socket Then Exit;
 FError:=WSAStartup($202,WSAD);
 If FError<>0 Then
   Begin
     FError:=WSAGetLastError;
     Exit;
   End;
 RecvMem:=TMyMemory.Create;
 SendMem:=TMyMemory.Create;
 Event:=WSACreateEvent;
 WSAEventSelect(FSocket,Event,FD_READ or FD_WRITE or FD_CLOSE);
 While not Terminated do
   Begin
     Index:=WSAWaitForMultipleEvents(1,@Event,False,10,False);
     Case Index of
       0:
         Begin
           FError:=WSAEnumNetworkEvents(FSocket,Event,@NetEvent);
           If GetEvent(NetEvent.lNetworkEvents,FD_READ) Then
             Begin
               FError:=NetEvent.iErrorCode[FD_READ_BIT];
               If CheckError(FError) Then Break;
               //Synchronize(Read);
               Read;
             End;
           If GetEvent(NetEvent.lNetworkEvents,FD_WRITE) Then
             Begin
               FError:=NetEvent.iErrorCode[FD_WRITE_BIT];
               If CheckError(FError) Then Break;
               Write;
             End;
           If GetEvent(NetEvent.lNetworkEvents,FD_CLOSE) Then
             Break;
         End;
       WSA_WAIT_TIMEOUT:;
       {WSA_WAIT_FAILED:
         Begin
           FError:=WSAGetLastError;
           Break;
         End;}
     End;
     If SendMem.Size>0 Then Write;
   End;
 WSACloseEvent(Event);
 CloseSocket(FSocket);
 WSACleanup;
 RecvMem.Free;
 SendMem.Free;
End;

Procedure TThreadSock.Lock;
Begin
 FCritical.Enter;
End;

Procedure TThreadSock.Unlock;
Begin
 FCritical.Leave;
End;

end.


 
FireMan_Alexey ©   (2004-05-24 10:13) [9]

type
 TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   Button2: TButton;
   Edit1: TEdit;
   procedure Button1Click(Sender: TObject);
   procedure ReadData(Sender: TObject);
   procedure Button2Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1 : TForm1;
 Sock  : TSocket;
 Client:TThreadSock;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
Var
 WSAD:TWSAData;
 Addr:TSockAddr;
 Error:Integer;
begin
 WSAStartup($202,WSAD);
 Addr.sin_family:=AF_INET;
 Addr.sin_port:=HtoNS(2000);
 Addr.sin_addr.S_addr:=Inet_addr("127.0.0.1");
 Sock:=Socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
 Error:=Connect(Sock,@Addr,Sizeof(Addr));
 If Error=Socket_Error Then
   Begin
     Error:=WSAGEtLastError;
     Form1.Caption:=IntToStr(Error);
     Halt;
   End;
 Client:=TThreadSock.Create;
 Client.SetSocket(Sock);
 Client.OnRead:=ReadData;
 Client.Resume;
end;

Procedure TForm1.ReadData;
Begin
//
 Memo1.Lines.Add(TThreadSock(Sender).ReceiveData.DataToStr);
 TThreadSock(Sender).ReceiveData.Delete(0,TThreadSock(Sender).ReceiveData.Size);
End;

procedure TForm1.Button2Click(Sender: TObject);
Var
 S:String;
begin
 If Client.Suspended Then Exit;
 Client.Suspend;
 S:=Edit1.Text;
 Client.SendData.AddBuff(S[1],Length(s));
 Client.Resume;
end;

end.


 
Polevi ©   (2004-05-24 10:23) [10]

в обработчике обращаешься к Memo - нужно вызывать его через Synchronize
а по коду - ты не анализируешь результаты вызова winsock ф-ий send и recv
к плачевным результатам сие приведет тебя


 
FireMan_Alexey ©   (2004-05-24 10:27) [11]

>Polevi
Так?

Procedure Refresh;
Begin
 Memo1.Lines.Add(TThreadSock(Sender).ReceiveData.DataToStr);
End;

Procedure TForm1.ReadData;
Begin
//
Synchronize(Refresh);
TThreadSock(Sender).ReceiveData.Delete(0,TThreadSock(Sender).ReceiveData.Size);
End;


 
Digitman ©   (2004-05-24 10:27) [12]

мне непонятен смысл отказа от использования готовых классов работы с Winsock (например, те же TClient/ServerSocket)


 
FireMan_Alexey ©   (2004-05-24 10:31) [13]

>Digitman
Я не сказал, что мне не нравятся эти компоненты!


 
Polevi ©   (2004-05-24 10:34) [14]

If GetEvent(NetEvent.lNetworkEvents,FD_READ) Then
            Begin
              FError:=NetEvent.iErrorCode[FD_READ_BIT];
              If CheckError(FError) Then Break;
              Synchronize(Read);

но смысла я не вижу в этом
лучше в читать в некий буфер в дополнительном потоке, а через синхронизе вызывать обработчик, который будет раболтать уже с готовыми данными не обращаясь к винсок ф-иям.


 
Digitman ©   (2004-05-24 10:40) [15]


> FireMan_Alexey ©   (24.05.04 10:31) [13]


но объяснение-то этому существует ? ведь там уже реализован вполне корректный алгоритм работы с гнездами на WSAPI, не требующий доп.усилий по отладке ..

еще раз - обрати внимание на [10] в части работы с ф-циями приема/передачи


 
Digitman ©   (2004-05-24 10:46) [16]

сам по себе транспорт не занимает ощутимых временных ресурсов, основное время уходит на обработку принятых/передаваемых данных

бессмысленен вынос транспорта в доп.код.поток, в то время как обработка транспортируемых данных продолжает осуществляться в осн.код.потоке

осн.код.поток предназначен для обработки польз.интерфейсных событий, в дан.случае - для отображения результатов уже обработанных транспортируемых данных


 
FireMan_Alexey ©   (2004-05-24 16:22) [17]

>Digitman
Единственное, что я понял из рассмотрения Scktcomp.pas что разница в моем коде и в борландовском только в том, что я использую функцию WSAWaitForMultipleEvents, а борланд Select.
Вызовы же моих обработчиков событий осуществляются таким же способом как и у борланда с использованием Synchronize. Так в чем же моя ошибка, в том что я не хочу присоединять к своему проекту модуль на ~48Kbyte или написать свой 4Кbyte. А при том, что приложение будет без окна, размер SCKTCOMP.DCU существенно увеличит размер выходящего файла.


 
FireMan_Alexey ©   (2004-05-24 16:30) [18]

Извини ошибся. Без модуля Forms;


 
Digitman ©   (2004-05-24 16:34) [19]


> Единственное, что я понял из рассмотрения Scktcomp.pas что
> разница в моем коде и в борландовском только в том, что
> я использую функцию WSAWaitForMultipleEvents, а борланд
> Select.


неужто ? а как же хотя бы recv() ? тоже нет разницы, по-твоему ? и ты и Борланд вызываете эту ф-цию одинаково - как процедуру ?


> Вызовы же моих обработчиков событий осуществляются таким
> же способом как и у борланда с использованием Synchronize


в упомянутых компонентах кроме OnTerminate() ни один обработчик у Борланда не вызывается с использованием Synchronize .. и то если DoTerminate не перекрыт тобой в наследнике


> модуль на ~48Kbyte или написать свой 4Кbyte


imho, если это серьезный коммерч.проект, а не "зараза", то разница в 44 кб - ловля блох, не более того.


 
FireMan_Alexey ©   (2004-05-24 18:53) [20]

procedure TServerClientThread.ClientExecute;
var
 FDSet: TFDSet;
 TimeVal: TTimeVal;
begin
 while not Terminated and ClientSocket.Connected do
 begin
   FD_ZERO(FDSet);
   FD_SET(ClientSocket.SocketHandle, FDSet);
   TimeVal.tv_sec := 0;
   TimeVal.tv_usec := 500;
   if (select(0, @FDSet, nil, nil, @TimeVal) > 0) and not Terminated then
     if ClientSocket.ReceiveBuf(FDSet, -1) = 0 then Break
     else Synchronize(DoRead);
   if (select(0, nil, @FDSet, nil, @TimeVal) > 0) and not Terminated then
     Synchronize(DoWrite);
 end;
end;

Если можно, в чем я не прав?


 
FireMan_Alexey ©   (2004-05-24 19:05) [21]

Т.е. при обработке того FD_READ или других событий в [20] тоже происходит синхронизация. Может я в чем-то не прав? Или это не код SCKTCOMP.PAS? Может мы друг-друга не до поняли?


 
Digitman ©   (2004-05-25 08:31) [22]


> FireMan_Alexey ©   (24.05.04 18:53) [20]


ах ты вон о чем !

ну этот код скорее шаблонный, нежели "рабочий", т.е. реально и исключительно применяемый на практике .. сделано это, очевидно, для того, чтобы "батонокидатели" не задумывались ни о какой синхронизации в обработчиках этих событий и смело писали в мемы да эдиты все что ни попадя .. реальные же преимущества  мультипоточного сервера при использовании ориг.кода метода TServerClientThread.ClientExecute, можно сказать, равны нулю

как правило, метод ClientExecute перекрывается в наследнике TServerClientThread, где в ЭТОМ  ЖЕ потоке происходит вызов трансп.ф-ций гнезда, далее происходит предв. буферизация/обработка/проверка и, если требуется, принятые корректные и целостные запросные структуры передаются на обработку иным доп.потокам, избавляя тем самым осн.поток от необходимости обрабатывать запросы последовательно ..

но это отнюдь не говорит о необязательности синхронизации там, где она действительно необходима .. др.вопрос, что синхронизация необязательно подразумевает использование именно метода Synchronize() - для синхронизации двух доп.потоков метод Synchronize() в отдельных случаях попросту неприменим ..



Страницы: 1 вся ветка

Текущий архив: 2004.07.25;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.025 c
1-1089284927
Артем
2004-07-08 15:08
2004.07.25
Ресурсы и Webbrowser


14-1088944223
}|{yk
2004-07-04 16:30
2004.07.25
Как заставить Excel переносить длинные строки


1-1088730903
Xerx
2004-07-02 05:15
2004.07.25
Иконки


1-1089350175
Kaginava
2004-07-09 09:16
2004.07.25
таймер в потоке


1-1089603479
Vitalik
2004-07-12 07:37
2004.07.25
TChart