Forbindelse mellem IdTCPServer og client

Tags:    delphi

Nu bliver jeg simpelthen nød til at spørge for jeg kan bare ikke få dette til at virke.

Jeg vil gerne på en eller anden måde (skal bare have et forslag) have så jeg har et program som fungerer som server og et andet program som fungerer som klient. Fra serverprogrammet skal man så kunne skrive ip til den computer hvor klientprogrammet kører ind og så oprettes der forbindelse imellem dem. Skal lige siges at det skal være muligt at sende tekst begge veje.

Den måde jeg har forsøgt at lave det på var ved at have både en IdTCPServer og en IdTCPClient i begge programmer og så automatisk lade klientprogrammets IdTCPClient oprette forbindelse tilbage til den IP som computeren som først oprettede forbindelse har.

Men kunne ikke få det til at virke. Så håber der er nogen der kan komme med en bedre og mere fungerende metode.

Mvh.
Rune Jensen
---------------------------------------------------------------------------
##The one who laugh last is the one who think slowest!##
---------------------------------------------------------------------------



7 svar postet i denne tråd vises herunder
1 indlæg har modtaget i alt 2 karma
Sorter efter stemmer Sorter efter dato
Jeg har selv lavet et chat forum i Delphi ved brug af Indy komponenterne. Jeg kan fortælle om mine erfaringer herfra. Jeg har brugt 2 forskellige scenarier i forbindelse med Indy komponenterne.

1. En normal klient server opstilling ala en webserver/browserklient.

Normal vil det være nok at serveren kører på en maskine ved brug af et TIdTCPServer komponent. På klienten benyttes så en TIdTCPClient. I TIdTCPServerens OnExecute event laves så en AThread.Connection.ReadLn. Da Indy komponenterne bruger blocking socket calls vil tråden blive suspenderet indtil serveren modtager en tesktstreng fra klienten. Når en tekst streng modtages (en kommando fra klient til server) forsætter afviklingen af programmet i OnExecute event handleren. Den modtaget kommando bearbejdes og et svar sendes tilbage til klienten ved hjælp af en AThread.Connection.WriteLn.

På klienten benyttes selvsagt et TidTCPClient komponent. Der sendes simpelthent en kommando til serveren via en WriteLn. Serveren tråd vil jo være suspenderet indtil denne tekst bliver sendt. Herefter benyttes en ReadLn i klienten. Tråden suspenderes indtil et svar er modtaget fra serveren. Når svaret er modtaget kan det benyttes til hvad det nu end var for en forespørgsel man sendte til serveren.

2. En aktiv server der skal kunne sende uopfordret beskeder til klienterne.

Hvis det er nødvendigt for serveren at sende beskeder til klienten uden at klienten har lavet en forspørgsel først, er det som du er inde på nødvendigt at benytte flere komponenter, idet der jo benyttes bloking socket calls. Serveren tråde vil normalt være suspenderet og stå og vente på forspørgsler fra klienterne. Således er det ikke umiddelbart muligt for serveren at sende noget. Måden jeg løste problemet var at bruge 2 TidTCPClient komponenter på klienten. Den ene fungere som lytte forbindelse og den anden som sende forbindelse. Lytte forbindelsen bruger en ReadLn. Når der så modtages fra serveren bearbejdes beskeden fra serveren og der lyttes igen ala den måde serverens tråd fungere på i eksempel 1. Der sendes ikke beskeder til serveren på denne forbindelse. Derimod oprettes en sende forbindelse fuldstændig på samme måde som i eksempel 1. På denne forbindelse sendes forespørgsler til serveren og et svar modtages umiddelbart efter. Serveren er herefter klar til at modtage en ny forspørgsel.

Den største udfordring i denne opsætning, er at der på serveren skal holdes styr på hvilke tråde (forbindelser) der benyttes til at lytte og hvilke der benyttes til at sende på. Jeg løste problemet ved at den første kommando der blev sendt til serveren fra klienten var en login kommando hvor der samtidig var angivet om det var en sende eller lytte forbindelse der ønskes oprettet. Endvidre oprettede jeg en klasse til at holde styr på hver enkelt klient. Denne klasse har så 2 felter (fields). En til at gemme lytte forbindelsen og en til at gemme sende forbindelsen. Endvidre kan oprettes andre felter til at identificere klienten. Så er det simpelthen bare at gemme alle disse klient objekter i en TObjectList. Jeg oprettede en TClientList klasse til at håndtere dette. Når der så ønskes en besked sendt fra serveren til alle klienterne er det simpelthen bare at benytte denne TClientList. Den har jo en liste af alle klienter hvor hver klient har en send forbindelse. Bemærk at det der er en send forbindelse på serveren jo er en lytte forbindelse på klienten og omvendt.

Et naturligt sted at oprette klient objekterne på er i serveren OnConnect event. Objekt referencen kan gemmes i TIdPeerThread objektes Data property. Herved kan klient objektet let identificeres i OnExecute. Dette er nødvendigt hvis der skal holdes styr på om en klient er logget ind. Er klienten ikke logget ind skal der jo ikke være adgang til serveren kommandoer. Dette er selvfølgelig kun nødvendigt hvis bruger adgangen ønskes styret. Objektet bliver automatisk nedlagt når forbindelsen afbrydes og tråden nedlægges.

Jeg har som sagt lavet et chat program samt et spille forum ved at benytte tekniken beskevet i eksempel 2. Dette fungere fint og er så vidt jeg kan gennemskue den mest simple måde. At benytte et server komponent på alle klienter virker som at skyde over målet da serverkompontet er indrettet til at tage sig af mange forbindelser hvorimod klient komponentet er specielt indrettet til at tage sig af een forbindelse, nemlig forbindelsen til serveren.

Som en sidste bemærkning kan jeg nævne at det er muligt at sende flere strenge af sted i en serie hvis det er nødvendigt i forbindelse med en kommando. Der skal blot være det samme antal ReadLn på serveren som der er WriteLn på klienten. Der er endvidre mulighed for at sende andre datatyper som fx. integers. Her gælder igen blot den simple regel at der skal være det samme antal ReadInteger som der er SendInteger. Dette er jo nødvendigt for at holde server og klient synkoniseret.

3. Det skal være muligt for serveren at tage initiativ til at oprette forbindelse til klienterne

Denne opsætning er meget speciel. Men det er det du snakker om. Hvis det skal være muligt for serveren at forbinde sig til en klient, er det nødvendigt at benytte et TidTCPServer komponent på klienten. Det er nødvendigt da dette komponent er det eneste der kan oprette en lytte socket. Jeg forstår ikke helt ideen i en server skal kunne forbinde sig med en klient. Normalt vil det jo være klienten opgave at forbinde sig til en server. Det virker lidt omvendt. Definitionen på en server er jo et program eller en maskine der stiller services til rådighed for en eller flere klienter.

Men er det det du ønsker er den ide du har den helt rigtige. Forstår bare ikke hvorfor du vil have en server til at tage initiativet til at oprette en forbindelse. Der virker som om det du kalder et server program i virkligheden er et klient program. Skal klienten stadig komme med forspørgsler til serveren eller er det du i virkligheden ønsker at lave et klientprogram som kan forbinde sig til mange forskellige serverer? Der er herefter muligt at sende diverse kommandoer til serverne fra klienten.

Det der er essensen i Indys klient og server komponenter er følgende:
1. Kun en klient kan tage initiativ til at oprette en forbindelse.
2. Serveren står og lytter på klienter der ønsker at oprette forbindelser. Når en ny klient ønsker at forbinde sig, sender den en forspørgsel på serverens lytteport. Serveren oprettet herefter en forbindelse til klient på en anden port. Lytte porten er så ledig igen. Hver klient har sin egen forbindelse og tråd på serveren.
3. Klienten kan kun forbinde sig til een server. Serveren derimod kan håndtere mange forbindelser (klienter).

Jeg vil meget gerne komme med et mere kvalificeret svar men det kræver at du beskriver opgaven du ønsker løst bedre.

Håber at det giver mening.

-TheHawk

[Redigeret d. 18/03-05 02:50:26 af Carsten Sonne Larsen]



Jeg tror jeg vil vælge løsningen med at lave den som jeg kaldte for server programmet til et klient program og så det andet program som et server program.

På den måde burde jeg jo kunne lade mit program oprette forbindelse til en af serverne og så sende en forespørgsel dertil hvorefter serveren udførte en opgave.

Men jeg vill gerne have et (bare et lille) kode eksempel på hvordan jeg så via af min IdTCPClient kunne oprette forbindelse til en IdTCPServer og så sende teksten 'hej' til den hvorefter IdTCPServeren skulle tjekke om teksten der blev modtaget var hej og så vise en besked hvis det var.

Det ville bare være super hvis du gad det :)

Mvh.
Rune Jensen
---------------------------------------------------------------------------
##The one who laugh last is the one who think slowest!##
---------------------------------------------------------------------------



Fejl - Glem dette indlæg

[Redigeret d. 19/03-05 01:26:39 af Carsten Sonne Larsen]



Fejl - glem dette indlæg

[Redigeret d. 19/03-05 01:27:15 af Carsten Sonne Larsen]



Hej Rune

Jeg tåger lige lidt rundt i funktionaliteten på Udvikleren.dk. Men er koden du beder om.

Server:
Fold kodeboks ind/udKode 


Klient:

Fold kodeboks ind/udKode 


Jeg har ikke testet koden men skulle mene at det virker 100%

Hvis du er interreseret kan jeg sende dig den fulde source kode til mit chat program :)

-TheHawk

[Redigeret d. 19/03-05 01:28:45 af Carsten Sonne Larsen]



Det virker sørme :)
Super!
Men hvad så hvis jeg med serveren vil sende et svar tilbage for eksempel hej, når hej bliver modtaget og så skal klienten vise beskeden hej når hej bliver modtaget?

Mvh.
Rune Jensen
---------------------------------------------------------------------------
##The one who laugh last is the one who think slowest!##
---------------------------------------------------------------------------

[Redigeret d. 21/03-05 16:03:13 af Rune Jensen (RoZ)]



Så skal du lade være med at disconnecte i clienten, og samtidig sige at hvis serveren modtager en tekst der hedder 'hej' skal den Athread.connection.writeln('goddag');

Klienten skal naturligvis lytte efter beskeder fra serveren så..


Sidder selv og har lidt problemer med Indy komponenterne, så i må ikke slå mig hvis det her ikke er rigtigt...


Mvh...
Allan Nørgaard Kristensen



t