Форум: "Сети";
Текущий архив: 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.04 c