Avatar billede hrc Mester
17. august 2007 - 13:18 Der er 15 kommentarer og
2 løsninger

Socket uden svar

Jeg har lavet følgende klasse der laver en socket til en server for at hente data. Den sender fint men socket'ens OnReceive event bliver ikke kaldt. Hvad er der galt?

Skal jeg selv loope efter kaldet og vente på OnReceive kaldes eller hvad?

  TOpslag = class
  private
    fSocket: TTcpClient;
    fPort: string;
    fIP: string;
    fOnReceive: TSocketDataEvent;
    fLastErrorRec: TErrorRec;
    procedure SocketReply(aSender: TObject; aBuf: pchar; var aDataLen: integer);
  public
    constructor Create(const aIP, aPort : string);
    destructor Destroy; override;
    property IP : string read fIP;
    property Port : string read fPort;
    function SendRequest(aKey : string) : boolean;
    property LastErrorRec : TErrorRec read fLastErrorRec;
    property OnReceive : TSocketDataEvent read fOnReceive write fOnReceive;
  end;

implementation

{ TOpslag }

constructor TOpslag.Create(const aIP, aPort: string);
begin
  inherited Create;
  fSocket := TTcpClient.Create(nil);
  fSocket.OnReceive := SocketReply; // Attaching event here
  fSocket.RemoteHost := aIP;
  fSocket.RemotePort := aPort;
end;

destructor TOpslag.Destroy;
begin
  try
    fSocket.Close;
    fSocket.Free;
  finally
    inherited;
  end;
end;

function TOpslag.SendRequest(aKey: string) : boolean;
var
  Stream : TMemoryStream;
  Req : TRequestRec;
begin
  result := false;

  fLastErrorRec := ErrorRecs[-1]; // Undefined!

  // Construct Request
  FillChar(Req,sizeof(TRequestRec),0);
  Req.Header.xyz := IntToStr(integer(fReqType))[1];
  StringToArray(aKey,Req.Data.Key,sizeof(Req.Data.Key));

  fSocket.Open;
  try
    Stream := TMemoryStream.Create;
    try
      Stream.WriteBuffer(Req,sizeof(TRequestRec));
      Stream.Seek(0,soFromBeginning);
      if fSocket.Connected then
      begin
        fSocket.SendStream(Stream); // Fire away!
        result := true;
      end;
    finally
      Stream.Free;
    end;
  finally
    fSocket.Close;
  end;
end;

// Never called!
procedure TOpslag.SocketReply(aSender: TObject; aBuf: pchar; var aDataLen: integer);
var
  i : integer;
  Found : boolean;
  ReplyRec : PReplyRec;
  RepError : integer;
  ErrorRec : TErrorRec;
begin
  ReplyRec := PReplyRec(aBuf);

  RepError := StrToInt(ReplyRec^.RepError);

  fLastErrorRec := ErrorRecs[0]; // OK is standard
  for i := low(ErrorRecs) to high(ErrorRecs) do
    if ErrorRecs[i].Nr = RepError then
    begin
      fLastErrorRec := ErrorRecs[i];
      break;
    end;

  if assigned(fOnReceive) then
    fOnReceive(aSender, aBuf, aDataLen);
end;
Avatar billede kroning Nybegynder
17. august 2007 - 13:24 #1
Kan den modtage noget når du til slut i SendRequest kalder fSocket.Close;
Avatar billede hrc Mester
17. august 2007 - 13:26 #2
Brugen af klassen er følgende:

function DoRequest(aKey, aServer, aPort : pchar) : integer;
var
  Opslag : TOpslag;
begin
  Result:=-4;

  if (StrLen(aServer) = 0) or (StrLen(aPort) = 0) then
    Exit;

  Opslag := TOpslag.Create(aServer, aPort);
  try
    try
      // Opslag.LastErrorRec er altid "Undefined" da dens OnReceive aldrig udføres
      // Kommer der et svar som jeg bare ikke venter på?

      if Opslag.SendRequest(aCpr) then
      begin
        if Opslag.LastErrorRec.Nr = 0 then
          Result := 0;
      end;

      with Opslag.LastErrorRec do
        Log.Add(Format('Fejlkode %d: %s',[Nr,Text]));
    except
      on e: exception do
        MessageDlg(e.Message, mtError, [mbOK], 0);
    end;
  finally
    Opslag.Free;
  end;
end;
Avatar billede hrc Mester
17. august 2007 - 13:27 #3
Som du kan se lukker jeg socketen samtidig med jeg frigiver klassen. Det må jeg teste.
Avatar billede hrc Mester
17. august 2007 - 13:29 #4
... det passer ikke helt. Min SendRequest lukker socketen, men efterfølgende tjek på LastErrorRec viser at OnReceive aldrig kaldes.
Avatar billede kroning Nybegynder
17. august 2007 - 13:44 #5
Det er nok bare mig der ikke helt har forstået hvordan det virker men:

Du sender data med: fSocket.SendStream(Stream); // Fire away!
Og lige derefter lukker du med: fSocket.Close;

Hvordan skal SocketReply så blive kaldt når du har lukket socketen?
Avatar billede hrc Mester
17. august 2007 - 13:50 #6
Det troede jeg skete når jeg frigav den.
Avatar billede hrc Mester
17. august 2007 - 13:54 #7
Skal jeg så loope bagefter SendStream indtil der tikker en OnReceive-event ind?
Avatar billede martinlind Nybegynder
17. august 2007 - 13:57 #8
Du skal ihvert fald ikke køre en Close på din socket, den laver en Disconnect :)
Avatar billede hrc Mester
17. august 2007 - 14:00 #9
Noget i retning af dette (SendRequest)?

  fSocket.Open;
  try
    Stream := TMemoryStream.Create;
    try
      Stream.WriteBuffer(Req,sizeof(TRequestRec));
      Stream.Seek(0,soFromBeginning);
      if fSocket.Connected then
      begin
        fSocket.SendStream(Stream);
        TickStart := GetTickCount;
        repeat
          Sleep(200); // Vil sleep dræbe en OnReceive event?
        until (GetTickCount - TickStart > TimeOut) or (fLastErrorRec.Nr <> -1);
        result := fLastErrorRec.Nr <> -1;
      end;
    finally
      Stream.Free;
    end;
  finally
    fSocket.Close;
  end;
Avatar billede kroning Nybegynder
17. august 2007 - 14:03 #10
Ja du skal på en eller anden måde vente med at kalde fSocket.Close indtil du har modtaget data i SocketReply eller til der er gået så lang tid at du ikke mener der kommer data.

Du kunne evt. benytte blocking socket for at undgå at skulle loope, jeg syntes det at skulle loope for at vente på data virker lidt fusket.
Avatar billede hrc Mester
17. august 2007 - 14:08 #11
Også jeg, men jeg fandt ikke nogle gode eksempler der passede mig. Desuden troede jeg det var en blocking socket. Jeg forventede jo at eventen OnReceive blev kaldt automatisk.

Nu har jeg sat denne linje ind

  fSocket.BlockMode := bmBlocking;

og min SendRequest ser sådan ud:

  fSocket.Open;
  try
    Stream := TMemoryStream.Create;
    try
      Stream.WriteBuffer(Req,sizeof(TRequestRec));
      Stream.Seek(0,soFromBeginning);
      if fSocket.Connected then
      begin
        fSocket.SendStream(Stream);
        result := fLastErrorRec.Nr = 0;
      end;
    finally
      Stream.Free;
    end;
  finally
    fSocket.Close;
  end;

... men i dokumentationen står at bmBlocking er default.
Avatar billede kroning Nybegynder
17. august 2007 - 14:28 #12
Nu fandt jeg TTcpClient i min Delphi 7 og jeg kan mindes at dengang jeg skiftede fra D4 til D7 og prøvede at bruge TTcpClient og TTcpServer men fik det aldrig til at virke. Derfor installerede jeg TServerSocket og TClientSocket som jeg kendte fra D4 og det virkede fint, jeg har dog kun brugt TServerSocket og TClientSocket som NonBlocking. De få gange jeg har haft brug for blocking socket har jeg brugt Indy.

Når du bruger blocking skal du i din SendRequest lige efter:
fSocket.SendStream(Stream);
have en linie der læser
fSocket.ReceiveStream(Stream, timeout); // eller hvad den nu hedder

og fSocket.OnReceive skal slet ikke benyttes når det er blocking socket vil jeg mene.
Avatar billede hrc Mester
17. august 2007 - 15:13 #13
Der er en ReceiveBuf som så ud til at kunne bruges, men ved at kigge i koden kan jeg se at den selv trigger en fSocket.OnReceive hvis bare result <> SOCKET_ERROR. Der burde altså ikke være forskel der.

Nu er det altså ligefør jeg dropper koden projektet og kører over i Indy. Tanken var ellers en low-level implementering uden tredjeparts pakker.
Avatar billede martinlind Nybegynder
17. august 2007 - 17:31 #14
Jeg mener også at kunne huske at Indy eller ICS er et langt bedre bud, med mindre du vil lave det hele selv, Delphi's implementering er skod, men de valgte jo også at sende en Indy pakke med ud, sikkert fordi de ikke gad bruge til på en ordenlig impl. :)

Jeg har selv brugt ICS's componenter som forøvrigt også er freeware og med source ( http://www.overbyte.be )
Avatar billede hrc Mester
18. august 2007 - 08:16 #15
Denne socketkompoent ser ikke særlig god ud. Jeg vil se om Indy kan gøre det for mig. Smid et svar begge to.
Avatar billede martinlind Nybegynder
18. august 2007 - 11:08 #16
Svar :)
Avatar billede kroning Nybegynder
18. august 2007 - 11:33 #17
k
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester