AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Delphi Clientverwaltung mit TServerSocket
Tutorial durchsuchen
Ansicht
Themen-Optionen

Clientverwaltung mit TServerSocket

Ein Tutorial von Arnulf · begonnen am 4. Sep 2005 · letzter Beitrag vom 4. Mai 2006
Antwort Antwort
Seite 2 von 2     12   
Arnulf
Registriert seit: 28. Okt 2004
Viele verstehen nicht ganz, wie man Clients mit TServerSocket verwaltet.
Ich verwende dafür Sourcen aus meine Projekt, einfach aus Faulheit.
Also bitte nicht verwirren lassen von den Client Daten.
Der Beitrag erklärt nicht wie man die Sockets verwendet, sondern es geht um die reine Verwaltung der Clients.

Die Serverseitige Clientverwaltung beginnt einfach mal damit, daß man einen Prototypen der Clients erstellt.
Delphi-Quellcode:
type TConnectionInfo = record Used : Boolean;
                              PlayerName : string[255];
                              PlayerPort : string[32];
                              ClientVersion : string[32];
                              AlliedSkin : string[64];
                              AxisSkin : string[64];
                              LoginTime : TDateTime;
                              LastPingTime : TDateTime;
                              Status : integer;
                              OldStatus : integer;
                              CheatStatus : integer;
                              ScreenSize : int64;
                              ScreenTimeOut : TDateTime;
                              ScreenFileName : string[255];
                              Socket : TCustomWinSocket; // den socket hier zu speichern ist wichtig!
                       end;
Prototyp allein genügt nicht, also:
Delphi-Quellcode:
var
Connection: ^TConnectionInfo;
Jetzt kommen wir zum connect event.
Wenn ein Client zum Server verbindet, dann müssen wir für den Client Speicher bereit stellen in Form unseres Prototyps.
Ich hab ein connection maximum für meinen Server - der eigentliche Client teil beginnt also erst nach dem else:

Delphi-Quellcode:
procedure TfMain.ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket);
var ConnI : integer;

begin
      AddLogEntry( 'Client ' + GetFullAdress(Socket) + ' connected.' ); //AddLogEntry ist nur das log memo.

      ConnI := ServerSocket.Socket.ActiveConnections; // wieviele connections sind aktiv?
      if (ConnI = MAX_CONNECTIONS) // MAX_CONECTIONS ist eine constante bei mir 64 user maximal
      then begin
           try
           Socket.SendText('#DISCONNECT' );
           Socket.Close;
           AddLogEntry( 'WARNING: Connection maximum reached - user kicked' );
           except
                 AddLogEntry( 'WARNING: Conn Maximum reached and user kick failed' );
           end;

           end

      else begin
           GetMem(Connection,sizeof(TConnectionInfo));
           try
           Connection^.Used := TRUE;
           Connection^.PlayerPort := 'none';
           Connection^.ClientVersion := 'none';
           Connection^.LoginTime := Time;
           Connection^.LastPingTime := Time;
           Connection^.Socket := Socket;
           Connection^.Status := ST_WAITLOGIN;
           Connection^.OldStatus := ST_WAITLOGIN;
           Connection^.CheatStatus := 0;
           Connection^.ScreenSize := 0;
           Connection^.ScreenTimeOut := Time;
           Connection^.ScreenFileName := 'Not Assigned';
           Connection^.PlayerName := 'Not Assigned';
           Connection^.AlliedSkin := 'none';
           Connection^.AxisSkin := 'none';
           Socket.Data := Connection;
           except
           freemem(Socket.Data);
           socket.Close;
           end;
           end;
end;
Jetzt haben wir also einen Client Record erstellt - das ganze ist in weiterer folge für jeden Client in socket.data zu finden.
Ich kann also jederzeit - jeh nach informationen die ich zu dem jeweiligen Client in meinem Record speicher - auf clientdaten zugreifen, und die dem richtigen Socket zuordnen.

In meinem Fall mußte ich immer wieder einen Client finden dem ich einen bestimmten PlayerPort zugewiesen hab.
PlayerPort hat nichts mit den Tsocket Ports zu tun - das hat in dem Fall nur etwas mit den userdaten zu tun - PlayerPort soll man hier also als Synonym für die Identifikation des Users nehmen ( könnte name oder sonstwas sein ).

Als erstes beispiel mal ClientRead event - ein client schickt etwas an der server - aber welcher Client ist das? und wo soll ich den einordnen:

Delphi-Quellcode:
procedure TfMain.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket);
var tempcon : ^TConnectionInfo;
begin
     TempCon:=Socket.Data;
// was auch immer ich mit dem user mache wenn infos kommen, ich kann jetzt jederzeit
// auf den gesammten record des clients zugreifen:
     if TempCon^.PlayerPort = '1234then ........
// wenn infos kommen kann ich die genausogut dem client zuweisen:
     tempCon^.PlayerPort := floattostr(clientport);
end;
Im fall von Client Read ist das ja ganz nett, aber da hab ich den Socket ja schon und könnte gleich Antworten.
Was aber wenn ich jetzt einem bestimmten client etwas senden will und den Socket nicht gleich ansprechen kann?
Dann müssen wir die Threads durchgehen und nach der richtigen Verbindung suchen.
Ich hab mir dazu gleich eine procedure gebastelt, die mir gleich an den richtigen Client text verschickt.
Ich will also jetzt eine nachricht an den Client mit dem PlayerPort 12345 schicken.

Delphi-Quellcode:
procedure tfmain.ConnSockSend( PlayerPort : String; Text : string );

var scan: integer;
    tempcon: ^TConnectionInfo;

          begin
          try
            for scan := 0 to ServerSocket.Socket.ActiveConnections-1 do
              begin
                TempCon := ServerSocket.Socket.Connections[scan].Data;
                if (TempCon^.PlayerPort = PlayerPort) then
                   ServerSocket.Socket.Connections[scan].SendText( Text ); // break wäre gut oder?
              end;
          except
            AddLogEntry ('ERROR: Socksend method failed');
          end;
          end;
Die Routine läßt sich sicherlich noch optimieren - mit break oder wenn man while statt for verwendet ... egal.

Jedenfalls kann ich jetzt jederzeit mit
ConnSockSend ( '12345', ' Diese Nachricht ist für PlayerPort 12345' ); Text an einen bestimmten User verschicken.

Damit sind wir fast am ende des Tutorials.
Nur bevor jemand sich die Frage stellt, warum so umständlich?
Könnte ich nicht auch das ganze in ein Array oder ähnliches packen und auch dort den socket zu einem User speichern?
Ja das ginge schon, aber die Methode wird uns von TServerSocket zur Verfügung gestellt und hat den großen Vorteil, daß es Thread sicher ist.
Ich weiß einfach welcher Socket mit welchem Client noch aktiv ist und welcher nicht.
Ich erspar mir also im Prinzip einen haufen aufwand mit der Verwaltung von Threads oder Sockets.

Zum schluß nicht vergessen beim disconnect eines Clients:
Delphi-Quellcode:
procedure TfMain.ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);

begin
      freemem(Socket.Data);
      AddLogEntry( 'Client ' + GetFullAdress(Socket) + ' disconnected.' );
end;
Ich hoffe das ganze ist nicht zu kompliziert geworden, aber es demonstriert wohl wie man TServerSocket eigentlich verwendet

Alles Gute
Arnulf
 
Hybrid
 
#11
  Alt 28. Dez 2005, 14:48
Hm, jetzt komm ich mir total dumm vor, da ich nicht weiß was damit gemeint ist
  Mit Zitat antworten Zitat
Hybrid
 
#12
  Alt 28. Dez 2005, 14:55
Hm ok, ich habe jetzt (nach nem bisschen googlen ^^) einen Haltepunkt auf
AddLogEntry('Client ' + (Socket.LocalAddress) + ' connected.'); und
GetMem(Connection,sizeof(TConnectionInfo)); gesetzt, aber das Programm wurde nirgens angehalten.


/////////////////////////////////////////////////


Ok, nachdem ich die Prozeduren im Objektinspektor entfernt und wieder neu hinzugefügt habe, werden sie auch vom ServerSocket benutzt.
ich wundere mich nur woran das lag.
hm naja, Danke nochma für die Hilfe.
  Mit Zitat antworten Zitat
nitschchedu

 
Delphi 7 Professional
 
#13
  Alt 4. Mai 2006, 10:53
Was mich mal Interessiert ist das mit dem "PingTime" was ich rigendt wo oben gesehen habe kannst du das mal näher erklären ? oder Poste mal den Cod
  Mit Zitat antworten Zitat
Muetze1
 
#14
  Alt 4. Mai 2006, 11:35
Sende was hin, merke dir die Zeit. Der Empfänger sendet wieder zurück. Wenn du die Antwort wieder empfängst, dann die Zeitdifferenz ausgeben.

Und noch was zum Tutorial bzw. 1. Beitrag: Wenn du mit GetMem() dir den Speicher mit den Records besorgst und im Except-Block wieder freigibst, dann beachte folgendes:

1. Du alloziierst den Speicher und legst den Zeiger in "Connection" ab
2. Du weist den Zeiger erst am Ende der Data Eigenschaft des Sockets zu
3. Der Fehlerfall wird wahrscheinlich _vor_ dieser Zuweisung auftreten

Ok, damit zu dem Problem in dem Code:

Im Fehlerfall versuchst du die Data Eigenschaft frei zu geben, welche dann noch sehr wahrscheinlich NIL ist (somit gibst du gar nix frei)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:02 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz