Конференция "Сети" » Оценить поток на основе TClientSocket
 
  • FearG0 © (30.09.07 01:48) [0]
    Для своего приложения я использую поток. Просьба оценить его безглючность и возможные утечки в памяти.

    unit ActivationThread;

    interface

    uses
     Windows, SysUtils, Classes,dialogs,ScktComp,messages;

    type

     TActivationThread = class(TThread)
     private
       { Private declarations }
       FSock: TClientSocket;
       Freceived,Frequest: string;
       FHandle: HWND;
       FUsername,Femail,FActivationCode:String ;
       FcodeError:integer;

       procedure OnConnect(Sender: TObject; Socket: TCustomWinSocket);
       procedure OnDisconnect(Sender: TObject; Socket: TCustomWinSocket);
       procedure OnWrite(Sender: TObject; Socket: TCustomWinSocket);
       procedure OnError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
       procedure ReadEvent(Sender: TObject;  Socket: TCustomWinSocket);
     protected
       procedure Execute; override;

     public
       constructor Create(CreateSuspennded: Boolean;AUsername,Aemail,AActivationCode:String; const AHandle:HWND);
       destructor Destroy; override;
     end;

    implementation

    constructor TActivationThread.Create(CreateSuspennded: Boolean;AUsername,Aemail,AActivationCode:String; const AHandle:HWND);
    begin
     inherited Create(true);
     FUsername:= AUsername  ;
     Femail:= Aemail  ;
     FActivationCode:= AActivationCode ;
     FHandle:= AHandle;
     FreeOnTerminate:=true;
     if createSuspennded = false then resume;
    end;

    destructor TActivationThread.Destroy;
    begin
     FSock.Free;
     inherited;
    end;

    procedure TActivationThread.OnConnect(Sender: TObject; Socket: TCustomWinSocket);
    begin
     Freceived:='';
     Socket.SendText(FRequest);
    end;

    procedure TActivationThread.OnDisconnect(Sender: TObject;
     Socket: TCustomWinSocket);
    begin

     PostThreadMessage(ThreadId,WM_CLOSE,0,0);

    end;

    procedure TActivationThread.OnError(Sender: TObject; Socket: TCustomWinSocket;
     ErrorEvent: TErrorEvent; var ErrorCode: Integer);
    begin
     FCodeError := ErrorCode;
     ErrorCode := 0;
     Socket.Close;
    end;

    procedure TActivationThread.OnWrite(Sender: TObject; Socket: TCustomWinSocket);

    begin
     //Socket.Close;
    end;

    procedure TActivationThread.ReadEvent(Sender: TObject;
     Socket: TCustomWinSocket);
    begin
    FReceived:=FReceived+Socket.ReceiveText;

    end;

    procedure TActivationThread.Execute;
    var
    Msg: TMsg;
    i,j:integer;
    FContent,SitePortion:string;
    StartTime:TDatetime; SecondsCount: integer;
    arr:stringarray;
    ResState:boolean;
    begin
    try
     SetLength(arr,0);
     FSock := TClientSocket.Create(nil);
     //ShowMessage(GetAddress(Furl));
     FSock.Host:= 'www.xxxxx.ru';
     FSock.Port := 80;
     FSock.OnConnect := OnConnect;
     FSock.OnDisconnect := OnDisconnect;
     FSock.OnWrite := OnWrite;
     FSock.OnError := OnError;
     FSock.OnRead := ReadEvent;
     FContent:='username='+HTTPTran(FuserName)+'&email='+HTTPTran(Femail)+'&ActivationCode='+HTTPTran(FActivationCode);

     FContent:=
       'Content-Length: '+IntToStr(Length(FContent))+#13#10+#13#10+FContent+#13#10;

       FRequest:='POST /activation.exe HTTP/1.0'+#13#10+
                     'Content-Type: application/x-www-form-urlencoded'#13#10+
                     'Accept: */*'+#13#10+
                     'Accept-Language: ru-RU;q=1.0,en;q=0.5'+#13#10+
                     'User-Agent: MSIE 6.0'+#13#10+
                     'Host: www.xxxxx.ru'+#13#10+
                     'Connection: Close'+#13#10+FContent;

       PeekMessage(Msg,0,0,0,PM_REMOVE);
       StartTime:=Now;
       Freceived:='';
       FSock.Open;
       while GetMessage(Msg,0,0,0) do
       begin
         TranslateMessage(Msg);
         DispatchMessage(Msg);
         if Msg.message=WM_CLOSE then break;
         if Terminated then exit;
         if SecondsBetween(Now,StartTime)>30   then
         begin
           FSock.Close;
           SendMessage(FHandle,WM_USER + 3260,0,-2);
           exit;
         end;
       end;
       FSock.Close;
       Freceived:=UTF8ToStrSmart(Freceived);
       Freceived:=RemoveHeader(Freceived);

       SendMessage(FHandle,WM_USER + 3260,0,StrToInt(Freceived));

    except
       SendMessage(FHandle,WM_USER + 3260,0,-2);

    end;
    end;

    end.


  • Slym © (01.10.07 04:31) [1]
    Pesтеs... В топку код..
    1. Работа в НЕБЛОКИРУЮЩИМ сокетом в отдельном! потоке!
    2. Ржунимагу:
    begin
    inherited Create(true);
    ....
    if createSuspennded = false then resume;
    end;


    гораздо проще
    ....
    inherited Create(createSuspennded);
    end;


    3. Ты уверен что FSock всегда здесь существует? я нет :)
    destructor TActivationThread.Destroy;
    begin
    FSock.Free;
    inherited;
    end;

  • Сергей М. © (01.10.07 08:19) [2]

    > FearG0 ©   (30.09.07 01:48)


    Во избежание утечек и AV следует делать так:

    procedure TActivationThread.Execute;
    ..
    begin
    ..
    //создаем гнездо
     FSock := TClientSocket.Create(..);
     try
    .. работа с гнездом ..
     finally
       FSock.Free; //безусловно уничтожаем гнездо
     end;
    ..
    ..
    end;




    > TranslateMessage(Msg);


    Это лишнее.


    > Slym ©   (01.10.07 04:31) [1]
    >
    > Pesтеs... В топку код..
    > 1. Работа в НЕБЛОКИРУЮЩИМ сокетом в отдельном! потоке!


    И что ? Почему бы и нет ?
  • Slym © (01.10.07 11:35) [3]
    Сергей М. ©   (01.10.07 8:19) [2]
    И что ? Почему бы и нет ?

    Ничего криминального, но масломаслянное...
  • Сергей М. © (01.10.07 11:39) [4]

    > Slym ©   (01.10.07 11:35) [3]


    То что для тривиальной задачи автора это неоправданное излишество, я согласен.

    Но не исключено, что он осознанно выбрал этот режим, исходя из потенциального наращивания функциональности потока
  • Slym © (01.10.07 11:39) [5]
    Slym ©   (01.10.07 11:35) [3]
    while GetMessage(Msg,0,0,0) do

    всеравно блокируется до сообщения, уж проще сразу в блок режиме работать
  • Сергей М. © (01.10.07 11:52) [6]

    > всеравно блокируется до сообщения


    Ну и что ?
    А вдруг автору потребуется реакция потока на какие-то сообщения, кроме собственно гнездовых ?


    > уж проще сразу в блок режиме работать


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

    Это не всегда удобно и не всегда оправдано.
  • Slym © (01.10.07 12:25) [7]
    Сергей М. ©   (01.10.07 11:52) [6]
    снять с выполнения блок.операцию

    почему terminate не сделали виртуальным :(
    Но можно написать свой AbortAndTerminate в котором клозить сокет и терминироваться.
    Мой простенький вариант :) -
    unit Unit2;

    interface

    uses
    Windows, SysUtils, Classes,dialogs,ScktComp,messages;

    type

    TActivationThread = class(TThread)
    private
      FHandle: HWND;
      FUsername,Femail,FActivationCode:String;
    protected
      procedure Execute; override;
    public
      constructor Create(CreateSuspennded: Boolean;const AUsername,Aemail,AActivationCode:String; const AHandle:HWND);
    end;

    implementation

    constructor TActivationThread.Create(CreateSuspennded: Boolean;const AUsername,Aemail,AActivationCode:String; const AHandle:HWND);
    begin
    FUsername:=AUsername;
    Femail:=Aemail;
    FActivationCode:=AActivationCode;
    FHandle:= AHandle;
    FreeOnTerminate:=true;
    inherited Create(CreateSuspennded);
    end;

    procedure TActivationThread.Execute;
    var
     Sock: TClientSocket;
     SockStream:TWinSocketStream;
     Content,Request,Received,Chunk:string;
     StartTime:dword;
     arr:array of string;
    begin
     try
       SetLength(arr,0);
       Sock := TClientSocket.Create(nil);
       try
         Sock.ClientType:=ctBlocking;
         Sock.Host:= 'www.xxxxx.ru';
         Sock.Port:= 80;
         //Content:='username='+HTTPTran(FuserName)+'&email='+HTTPTran(Femail)+'&ActivationCode='+HTTPTran(FActivationCode);
         Request:='POST /activation.exe HTTP/1.0'#13#10+
                    'Content-Type: application/x-www-form-urlencoded'#13#10+
                    'Accept: */*'#13#10+
                    'Accept-Language: ru-RU;q=1.0,en;q=0.5'#13#10+
                    'User-Agent: MSIE 6.0'#13#10+
                    'Host: www.xxxxx.ru'#13#10+
                    'Connection: Close'#13#10+
                    'Content-Length: '+IntToStr(Length(Content))+#13#10#13#10+Content;

         StartTime:=GetTickCount;
         Received:='';
         Sock.Open;
         SockStream:=TWinSocketStream.Create(Sock.Socket,30*1000);
         try
           SockStream.WriteBuffer(PChar(Request)^,length(Request));
           while SockStream.WaitForData(SockStream.TimeOut-(GetTickCount-StartTime)) do
           begin
             SetLength(Chunk,256);
             SetLength(Chunk,SockStream.Read(PChar(Chunk)^,Length(Chunk)));
             if length(Chunk)=0 then break;
             Received:=Received+Chunk;
           end;
         finally
           SockStream.Free;
         end;
       finally
         Sock.Free;
       end;
       if Received='' then abort;
       //Received:=UTF8ToStrSmart(Received);
       //Received:=RemoveHeader(Received);
       SendMessage(FHandle,WM_USER + 3260,0,StrToInt(Received));
     except
       SendMessage(FHandle,WM_USER + 3260,0,-2);
     end;
    end;

    end.

  • Сергей М. © (01.10.07 12:30) [8]

    > можно написать свой AbortAndTerminate в котором клозить
    > сокет и терминироваться.


    Нафиг он нужен ?

    Все это расчудесно делается в деструкторе.
  • Slym © (01.10.07 12:51) [9]
    Сергей М. ©   (01.10.07 12:30) [8]
    Все это расчудесно делается в деструкторе

    А деструктор вызывает
     Terminate;
     if FCreateSuspended then
       Resume;
     WaitFor;// <-ТУТ И ЗАВИСАЕМ


    а еслибы procedure Terminate; virtual;
    тадабы я сделал так
    procedure CancelBlockingOperations;
    begin
     Sock.Close;
    end;

    procedure Terminate; override;
    begin
     inherited;
     CancelBlockingOperations;
    end;

  • DiamondShark © (01.10.07 13:18) [10]
    Неблокирующий режим -- это тяжёлое наследство царского режима, тобишь кооперативной многозадачности.
    В среде с вытесняющей многозадачностью есть такие прекрасные средства, как потоки и overlapped-операции.
  • Сергей М. © (01.10.07 14:13) [11]

    > ТУТ И ЗАВИСАЕМ


    Ты забыл, что деструктор виртуальный)
  • FearG0 © (02.10.07 03:22) [12]
    1. Иногда бывает что на этом цикле зависает:

    while GetMessage(Msg,0,0,0) do
      begin
        TranslateMessage(Msg);
        DispatchMessage(Msg);  
      end;
    Причем при отладке последний брейкпоинт на DispatchMessage(Msg);  и после этого вообще ничего не происходит.
    От чего может быть?

    2. Иногда бывает что сервер отдает контент не полностью, особенно когда страница размером 300 - 400 Кбайт. Как в этом случае поступать? Создавать новый поток на докачку?

    3. Такая конструкция была выбрана именно из-за маштабируемости. Помимо активации в программе еще несколько функций активно взаимодействующих с интернетом.
  • Slym © (02.10.07 04:15) [13]
    Сергей М. ©   (01.10.07 14:13) [11]
    Ты забыл, что деструктор виртуальный

    падкалол... об этом не подумал...
  • Сергей М. © (02.10.07 08:31) [14]

    > 1. Иногда бывает что на этом цикле зависает:


    При каком поступившем конкретном сообщении ?


    > 2. Иногда бывает что сервер отдает контент не полностью,
    >  особенно когда страница размером 300 - 400 Кбайт


    TCP - поточный протокол.


    > Создавать новый поток на докачку?


    И как ты себе это мыслишь ?
  • FearG0 © (02.10.07 14:50) [15]

    > И как ты себе это мыслишь ?

    Поток посылает в программу код ошибки, в программе его обработать и создать новый поток где в заголовке будет Range с указанием с какого байта выдать контент
  • Сергей М. © (02.10.07 14:59) [16]

    > в заголовке будет Range с указанием с какого байта выдать
    > контент


    А если серверу по барабану твое "указание с какого байта выдать" ? Если сервер отдает либо все либо ничего ?


    > Иногда бывает что сервер отдает контент не полностью


    Типа "Лениво мне, серверу, сегодня тебе, клиенту, все 400кб передавать, на вот тебе шматок в 300кб, остальные 100кб завтра как высплюсь так и передам, если захочешь" ?

    Так что ли ?)
  • FearG0 © (02.10.07 15:10) [17]

    > Типа "Лениво мне, серверу, сегодня тебе, клиенту, все 400кб
    > передавать, на вот тебе шматок в 300кб, остальные 100кб
    > завтра как высплюсь так и передам, если захочешь" ?

    Ну по ходу да. Попробуй зарегистрировать сайт на народе, там на втором шаге показывается капча. Сохрани ее на диск. Открой в обычном виндовском просмотровщике. Картинка будет искаженной в 70% случаев, потому что сервак яндексовский никогда ее полностью не отдает. Лень ему наврное. Но это не страшно.

    Гораздо страшнее, когда сервак яху через свой интерфейс отдает поисковую выдачу не полностью и мой анализатор теряет часть данных....
    Вот надо побороть.
  • Сергей М. © (02.10.07 15:11) [18]

    > FearG0 ©   (02.10.07 15:10) [17]


    А теперь все тоже самое , но без доморощенного жаргона ...
  • FearG0 © (02.10.07 15:49) [19]
    Короче: факт есть факт, данные не всегда приходят от сервера полностью, это касается не конкретно оей реализаци, просто так бывает. Не важно из-за чего - глюковатость сервера, проблемы на линии или ошибки при передаче.

    Осталось придумать как мою реализацию потока доработать, чтобы решить эту проблему.
 
Конференция "Сети" » Оценить поток на основе TClientSocket
Есть новые Нет новых   [134431   +15][b:0][p:0.005]