Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2004.07.25;
Скачать: [xml.tar.bz2];

Вниз

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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.53 MB
Время: 0.037 c
3-1088406454
denmin
2004-06-28 11:07
2004.07.25
Как узнать координаты ячейки грида?


14-1089283653
TohaNik
2004-07-08 14:47
2004.07.25
Вот не знаю... радоваться или огорчаться?


6-1085580515
cherrex
2004-05-26 18:08
2004.07.25
FTP Server


14-1089014841
Паниковский
2004-07-05 12:07
2004.07.25
Помогите курсовая горит!


4-1087154940
DjOne
2004-06-13 23:29
2004.07.25
Tabs





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский