Avatar billede hugopedersen Nybegynder
09. november 2009 - 09:57 Der er 32 kommentarer og
1 løsning

TIdTCPServer

Jeg sidder og roder med at lave et program der kan modtage kommandoer via TCP. Til det bruger jeg Indy TIdTCPServer og det virker som sådan også fint. Dog mangler jeg at finde ud af at svar fra serveren kun skal gå til 1 bestemt klient - den der har sendt forespørgslen.
Jeg har ikke kunnet finde den røde tråd i nogle af de eksempler jeg har kunnet opstøve på nettet, så hvis du ligger inde med et eksempel, så er jeg meget interesseret.
Avatar billede preppydude Nybegynder
09. november 2009 - 13:32 #1
Jeg kan godt lave et simpelt eksempel til dig - hvad skal det kunne? :)
Avatar billede hugopedersen Nybegynder
09. november 2009 - 14:22 #2
Mit 'problem' er at jeg godt vil at klienterne identificerer sig og jeg så kun sender beskeder til den klient der afgiver en kommando.

Jeg skal bruge det til fjernstyring og da er det jo rarest at den der bestiller vare 1 får svar på det og ikke på vare 2 som naboen har bestilt.
Avatar billede preppydude Nybegynder
09. november 2009 - 15:33 #3
I dens OnExecute event skulle der gerne være et object der hedder AContext. Det er det som du skal bruge til at læse bufferen og svare med, fx:

procedure TMainForm.TestServerExecute(AContext: TIdContext);
var
  strRequest: String;
begin
  strRequest := AContext.Connection.IOHandler.ReadLn();
  if (strRequest='ping') then
    AContext.Connection.IOHandler.WriteLn('pong');
end;
Avatar billede hugopedersen Nybegynder
09. november 2009 - 16:41 #4
Det er på plads det hele - men jeg ville gerne kunne se hvem der sender beskeder og så kun svare til den.  Men det er ikke sikkert at det er muligt.
I så fald må jeg bare begrænse det til en 1 bruger version.

Jeg har et andet issue med at jeg gerne vil have slået standard reply helt fra - det kan jeg ikke lige se hvordan jeg gør. Jeg har fjernet alle de steder jeg kan se det, men uden held.
Avatar billede preppydude Nybegynder
09. november 2009 - 17:08 #5
Det kan sagtens lade sig gøre. Tilknyt en client struct til AContext.Data når forbindelsen bliver oprettet. Bare husk at slette den igen når de disconnecter. :)

type
  PClient = ^TClient;
  TClient = packed record
    // Basic information
    IP:          String;
    DNS:        String;
    LastAction:  TDateTime;
    ConnectedAt: TDateTime;
    // ... andet information du måske ville bruge
  end;

procedure TMainForm.ServerConnect(AContext: TIdContext);
var
  Client: PClient;
begin
  Client := AllocMem(SizeOf(TClient));
  Client.LastAction  := Now;
  Client.ConnectedAt := Now;
  Client.IP          := AContext.Binding.PeerIP;
  Client.DNS        := GStack.HostByAddress(Client.IP);
  AContext.Data      := TObject(Client);
end;

procedure TMainForm.ServerExecute(AContext: TIdContext);
var
  strRequest: String;
  Client: PClient;
begin
  Client := PClient(AContext.Data);
  if not (Client<>nil) then Exit;
  Client.LastAction := Now;

  strRequest := AContext.Connection.IOHandler.ReadLn();
  if (strRequest='ping') then
    AContext.Connection.IOHandler.WriteLn('pong');
end;

procedure TMainForm.ServerDisconnect(AContext: TIdContext);
var
  Client: PClient;
begin
  Client := PClient(AContext.Data);
  FreeMem(Client);
  AContext.Data := nil;
end;
Avatar billede hugopedersen Nybegynder
10. november 2009 - 10:08 #6
Så mange gode input fortjener point.

Dog har jeg droppet ideen om kun at sende til 1 klient - det kan man vist ikke. Jeg har i stedet begrænset det til 1 bruger ad gangen som også er rigeligt i mit tilfælde.
Avatar billede preppydude Nybegynder
10. november 2009 - 10:37 #7
Man kan sagtens lave det så du kan klare en masse klienter på én gang. Giv mig et specifikt eksempel på hvad du er ved at lave, så laver jeg noget du kan bygge videre på. :)
Avatar billede hugopedersen Nybegynder
10. november 2009 - 10:48 #8
Jeg har en 'server' der står og lytter
En klient connecter sig og afsender nogle fjernstyringskommandoer.
Disse behandler serveren og sender svar tilbage til klienten.
Efterfølgende disconnecter serveren forbindelsen.

Mere avanceret er det såmænd ikke.
Avatar billede preppydude Nybegynder
10. november 2009 - 10:53 #9
Ah okay, så skulle du se på den her: http://delphi.about.com/od/indy/ss/screen_thief2.htm

Den bruger Indy 9, så det kan godt være du skal rette nogle ting hvis du bruger Indy 10 som jeg gør.

Men den giver dig i hvert fald en ide om hvordan du håndterer flere klienter på en gang.
Avatar billede hugopedersen Nybegynder
10. november 2009 - 11:12 #10
OK - det vil jeg da lige prøve at se på.
Avatar billede hugopedersen Nybegynder
10. november 2009 - 11:24 #11
Var lige ved at se på den - der er rigtig meget der skal ændres for bare at få den til at virke :-)
Avatar billede preppydude Nybegynder
10. november 2009 - 11:41 #12
Så du bruger Indy 10? :)
Avatar billede hugopedersen Nybegynder
10. november 2009 - 12:55 #13
Ja og en af de væsentlige forskelle så vidt jeg kan se er TIdPeerThread som ikke er i 10'eren at jeg kan finde. Det må være på en anden måde det er lavet.
Avatar billede preppydude Nybegynder
10. november 2009 - 13:04 #14
Det er lidt anderledes ja, men generelt kan du erstatte TIdPeerThread med TIdContext og så tage den derfra. :)
Avatar billede hugopedersen Nybegynder
10. november 2009 - 13:12 #15
OK - desværre er jeg lidt i tidsnød lige nu så jeg får ikke tid til at lege så meget med multidelen. Men det spiller også som single.

Havde dog lidt issues med at programmet hængte når jeg ville lukke serverprogrammet. Men det var vist nok fordi connectionen ikke var disconnected.
Avatar billede kroning Nybegynder
10. november 2009 - 15:29 #16
Jeg sidder lidt og undre mig over hvordan du har fået idTCPServer til kun at virke som "enkeltbruger", idTCPServer er jo en server og kan uden noget extra kode klare mange brugere på samme tid. Jeg bruger selv komponenten og har op til 40 brugere logget på på samme tid som sender kommandoer til serveren og får svar retur, det eneste man skal gøre er at modtage kommandoen i ServerExecute og sende svaret til klienten i ServerExecute som preppydude også har givet et eks. på.
Avatar billede hugopedersen Nybegynder
10. november 2009 - 15:32 #17
Det er sådan set også det jeg gør - jeg vil bare ikke have svar ud til alle der connecter. Kun til den rigtige.

Og enkeltbruger er bare at sætte maxconnections til 1, så får andre en connection refused
Avatar billede kroning Nybegynder
10. november 2009 - 15:39 #18
Jamen hvis du sender svar i ServerExecute som i preppydude´s eksempel så er det kun den ene klient der får svar uanset hvor mange der er logget på.
Avatar billede preppydude Nybegynder
10. november 2009 - 15:47 #19
Ja, OnExecute kaldes kun for den klient som har sendt noget til serveren, og det er kun den klient du svarer. Alle andre bliver kørt seperart i et andet event, da IdTcpServer er multi-threaded.
Avatar billede hugopedersen Nybegynder
10. november 2009 - 19:30 #20
OK - det gør jeg så ikke. Jeg bruger commandhandlerne da jeg har flere kommandoer. Men det vil jeg da prøve at ændre og se hvad der sker.
Avatar billede hugopedersen Nybegynder
11. november 2009 - 08:10 #21
Det er da fordi jeg har siddet og rodet med den forkerte komponent hele tiden. Jeg har brugt den der hedder idCmdTCPServer i stedet for idTCPServer.  Det giver noget helt andet resultat. idTCPServer kan styres lidt strammere i kode.

Smid et par svar så vi kan lukke for nu.
Avatar billede preppydude Nybegynder
11. november 2009 - 09:40 #22
Her er svaret. :)
Avatar billede kroning Nybegynder
11. november 2009 - 10:35 #23
Jeg springer over, har jo kun gentaget hvad preppy har skrevet :)
Avatar billede hugopedersen Nybegynder
11. november 2009 - 14:18 #24
Tillæg - Hvorfor har jeg store problemer med at lukke programmet nå jeg smider en TIdAntiFreeze på?
Avatar billede preppydude Nybegynder
11. november 2009 - 14:40 #25
Husk at disconnecte samtlige klienter der er tilknyttet serveren før du lukker. :)
Avatar billede hugopedersen Nybegynder
11. november 2009 - 14:46 #26
Det synes jeg også at jeg gør. Men det kan da være der er en eller anden fiks måde at sikre det på.

Men rent faktisk sker det også hvis programmet kører og jeg lukker uden at der har været logget nogen på. Men fjerner jeg den AntiFreeze komponent, så virker det tilsyneladende.
Avatar billede kroning Nybegynder
11. november 2009 - 15:20 #27
Er AntiFreeze ikke kun beregnet til at bruges hvis man kalder nogle Indy funktioner i main tråden for at undgå at programmet fryser mens data overføres?
Avatar billede preppydude Nybegynder
11. november 2009 - 15:33 #28
Er ikke helt inde i den, men mener generelt den bare gør så programmet ikke fryser overhovedet. Men prøv det jeg sagde og fortæl mig om det ikke hjalp på det - evt kan du prøve at lukke uden der er nogen klienter på serveren.
Avatar billede hugopedersen Nybegynder
12. november 2009 - 07:24 #29
Du har sandsynligvis ret i at det er fordi connection ikke er afbrudt til klienter for jeg kan se at hvis jeg connecter fra en klient og så prøver at lukke programmet, så går det galt.

Hvordan kan jeg force alle connections til at close?
Avatar billede hugopedersen Nybegynder
12. november 2009 - 09:47 #30
Hvad er sidste officielle version af Indy?

Den jeg har er 10.2.5
Avatar billede preppydude Nybegynder
12. november 2009 - 12:45 #31
Tror det er den nyeste version du har. Har du prøvet at loope igennem din servers connections og disconnecte dem en efter en?
Avatar billede hugopedersen Nybegynder
12. november 2009 - 12:54 #32
Try
    If idTCPServer.Contexts <> Nil Then
      Begin
        With idTCPServer.Contexts.LockList Do
        Try
          For intX := 0 To Count - 1 Do
            Begin
              TIdContext(Items[intX]).Connection.IOHandler.WriteLn(conCode_Status_Disconnected);
              TIdContext(Items[intX]).Connection.Disconnect(False);
            End;
        Finally
          idTCPServer.Contexts.UnlockList;
        End;
      End;
    Sleep(idTCPServer.TerminateWaitTime);
    idTCPServer.Active := False;
  Finally
  End;
Avatar billede preppydude Nybegynder
12. november 2009 - 13:24 #33
Ja, eller noget lignende:

procedure TMainForm.FormDestroy(Sender: TObject);
var
  List: TList;
begin
  List := Server.Contexts.LockList;
  try
    Application.ProcessMessages;
    while (List.Count>0) do
    begin
      try
        TIdContext(List.Items[0]).Connection.Disconnect;
      except
        on E:Exception do
          TIdContext(List.Items[0]).Destroy;
      end;
    end;
  finally
    Server.Contexts.UnlockList;
  end;
  Sleep(Server.TerminateWaitTime);
  Server.Active := False;
end;
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