Delphi-PRAXiS
Seite 2 von 4     12 34      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Chat mit PM (https://www.delphipraxis.net/154972-chat-mit-pm.html)

xZise 6. Okt 2010 07:27

AW: Chat mit PM
 
Zitat:

Zitat von hans ditter (Beitrag 1053984)
@Zacherl
Ok, ich kann dir soweit folgen, dass ich vom Client ein Record mit IP und Nachricht schicken sollte. Dass diese dann mit einer TList verglichen wird.
Aber mir ist nicht klar, wie ich ein Record sende. Wie gesagt, ich bin noch ganz frisch in der Materie.

Freue mich auf weitere Antworten,
hans ditter

Einen Rekord wirst du wohp schwer selbst versenden können. Stattdessen überlegst du dir ein Chat Protokoll.

Dabei gibt es verschiedene Verfahren. jabber beziehungsweise XMPP nutzen xml um Empfänger Text und alles andere zu definieren.

Ein Vorschlag der Recht einfach zu implementieren ist, ist das du den Empfänger direkt schreibst. Dann machst du ein Zeichen welches eindeutig zum Trennen geeignet ist. Zum Beispiel einen Umbruch. Dann schreib du den Text. Fertig. Vom Server dann kommt das so ähnlich, nur das ganz am Anfang der Absender geschrieben wird. Dadurch kannst sogenanntest spoofing verhindern. Also das sind jemand als jemand anderes ausgibt.

Aber da kannst du dir was selber überlegen.

Und zu deinen Pointe: Irgendwo musst du doch deine Sockets speichern. Und diese verknüpfst du mit einen Namen und einer IP dazu empfehle ich, wenn man noch nicht Su gut mit Pointern umgehen kann, dass du einfach nehmen Klasse erstellst und darin die IP, Namen und zugehörige Verbindung speichert. Diese speicherst du dann in einer TObjectList.

MfG
Fabian

hans ditter 6. Okt 2010 17:13

AW: Chat mit PM
 
erstmal vielen Dank für deine Antwort xZise. Ich würde aber gerne das gleich "korrekt" lösen. Wenn ich Zacherl richtig verstanden hab, dann wäre dein Vorschlag auch keine "saubere" Lösung...
Wenn man Pointer etc. aber nicht so einfach erklären kann, dann probier ich erstmal deine Variante aus.

Eine Frage hätte ich aber noch. Dieses eindeutige Zeichen wird in ein Terminator-Zeichen-Protokoll verwendet, ist das korrekt? Und wie kann ich mir so ein Protokoll vorstellen? Ist das eine Unit mit verschiedenen Befehlen, die überprüft werden können?

hans ditter

hans ditter 6. Okt 2010 18:07

AW: Chat mit PM
 
Ok, ich hab mich grad nochmal mit meinem diiiicken Delphibuch auseinandergesetzt und glaube verstanden zu haben, wie ich das mit den Pointer und der TList hinbekomme.
Aber mir ist gleich das nächste Problem unter die Finger gekommen: Wo bekomme ich den Socket Pointer her?

Hoffe auf schnelle Antwort!!
hans ditter

xZise 6. Okt 2010 18:09

AW: Chat mit PM
 
Moin,
Also meine Lösung ist nur eine einfachere als die von Zacherl. Weil du immer weißt womit du hantierst und nicht mit Pointern arbeitest.

Vielleicht habe ich das übersehen, aber womit überträgst du die Daten? Zum Beispiel wenn du IdTCP (oder IdUDP) nutzt dann hätte ich es so gespeichert:
Delphi-Quellcode:
type
  TClientConnection = class
  private
    FSocket : TIdTCP;
    FNickname : string;

    function GetIP : string; // Hier halt die Ip mithilfe des Sockets zurückgeben oder irgendwie anders :)
  public
    property Socket : TIdTCP read FSocket;
    property Nickname : string read FNickname write FNickname;
    property IP : string read GetIP;
  end;

  TClientList = class
  private
    FClients : TObjectList; // Im Konstruktor erstellen & im Destruktor freigeben.
  public
    procedure ClientConnect(Connection : TClientConnection);
    procedure ClientDisconnect(Connection : TClientConnection);
  end;
Und wie du dein Protokoll schreibst, musst du wie gesagt immer selber wissen. Meine Möglichkeit hat den Vorteil, dass du sie mit einer TStringList lesen/interpretieren kannst. So könnte zum Beispiel eine private Nachricht aussehen:
Code:
PRIVATE
hans ditter
Hallo hans ditter! Dies ist eine private Nachricht
PRIVATE als Kennzeichner, dass es eine PM ist, dann der Nickname und dann der Text. Die Nachricht bekommst du dann und musst dann nur noch folgendes machen (Pseudoquelltext):
Code:
Wenn Typ = PM dann:
  Suche in der Clientliste nach den Nickname
  Ist der Nickname vorhanden
    Schicke die PM an den Client (Empfänger)
  ansonsten
    Schicke eine Fehlermeldung an den Sender
Die könnte dann so aussehen:
Code:
PRIVATE-RECV
xZise
Hallo hans ditter! Dies ist eine private Nachricht
Und die Fehlermeldung z.B.:
Code:
PRIVATE-ERR
Benutzername "hans ditter" nicht im Netzwerk vorhanden!
Bei diesem System hat man den Nachteil, dass man viel Platz verschwendet, durch die Zeilenumbrüche und den sprechenden Befehlen (PRIVATE, PRIVATE-RECV und PRIVATE-ERR). Aber dafür kann jeder Administrator sehen, wenn was schief geht was schief geht (z.B. hat jemand PRIVAT statt PRIVATE stehen oder so ;) ).
Ein anderes Format wäre im IRC Stil:
Code:
msg <Reciever> <Content>
Hier wäre der Trenner das Leerzeichen, aber wieder sprechende Befehle, und Nicknames mit Leerzeichen sind nicht möglich.

Ansonsten kannst du dir auch XMPP anschauen. Die nutzen XML, was natürlich noch mehr Platz verschwendet. Aber dadurch ist es sehr leicht zu sehen, was verschickt wird, und das Protokoll kann einfach erweitert werden.

[redbox]Irgendwo speicherst du doch, die verschiedenen Verbindungen oder?[/redbox]

MfG
Fabian

hans ditter 6. Okt 2010 18:24

AW: Chat mit PM
 
tja, also bis jetzt hab ich es nur geschafft, an alle eine Nachricht zu schicken.
Wenn sich ein Client mit dem Server verbindet, dann schreib ich in eine ListBox die IP des Clienten und ausgehen von der IP schreibe ich den Username in eine 2. ListBox.
Aber das Socket oder so speicher ich noch nicht.
Ich hab jetzt mal ein bisschen gebastelt, mit Pointern.
Delphi-Quellcode:
type
  PClientData = ^TClientData;
  TClientData = record
    UserNick: string;
    IP: string;
    SocketPointer: pointer;
  end;

...

var
  Form4: TForm4;
  UserList: TList;
  UserData: PClientData; //Pointer auf TClientData (Record)

implementation

...

New(UserData);//neuer Pointer auf TClientData
    UserData^.IP:=Socket.RemoteAddress;
    UserData^.SocketPointer:=Cardinal(Socket);
    UserData^.UserNick:=UserNick.Items.Strings[UserNick.Items.Count];
Das ist das, was ich bisher habe. Allerdings gibt mir Delphi natürlich beim SocketPointer eine Fehlermeldung, da wäre nochmal gut zu wissen, wohin der Pointer eig zeigen soll (also, wie bringe ich da eine Pointer auf das entsprechende Socket unter?).

Zu dem Protokoll muss ich sagen, dass ich gerade total auf der Leitung stehe... :oops::pale:

[Edit]Was du damit meinst, wie ich die Daten übertrage, ist mir auch nicht ganz klar. Ich denke mal mit TCP/IP und einer ganz normalen TClientSocket bzw. TServerSocket... war es das was du meintest?[/Edit]

xZise 6. Okt 2010 18:39

AW: Chat mit PM
 
Naja irgendetwas stellt die Verbindung her. Es gibt z.B. von Indy IdTCP bzw. IdUDP, je nachdem ob du TCP oder UDP verwendest. Also die Kernkomponente wo du sagst: Schicke an die IP X.Y.Z.A den Text B.

Das was wir dir vorschlagen ist, dass du ständig einen TServerSocket oder TClientSocket hast, der einfach an die bei ihm gespeicherte IP einen bestimmten Text schickt.

Außerdem ist ein Pointer kein Cardinal. Okay beide sind 32 bit breit (noch), aber mit 64 bit muss Cardinal nicht auch auf 64 sich verbreitern. Von welchen Typ ist denn Socket (also wo du in der Zeile den auf Cardinal castest). Den gleichen Typ könntest du statt Pointer nehmen.

MfG
Fabian

hans ditter 6. Okt 2010 18:51

AW: Chat mit PM
 
Also meine zentrale Komponente ist TClientSocket / TServerSocket, die Standardkomponente von Delphi (glaub ich zumindest).

Hm, wenn ich das richtig verstanden hab mit dem Cardinal(Socket): Socket ist die Variable bei ServerSocketClientConnect(Socket: TCustomWinSocket);

Oh man, ich trau gar nicht, dass zu sagen, aber ich hab's immer noch nicht verstanden mit dem Protokoll. So wie ich mir vorstelle, läuft das so, dass User A /msg to B {content} schreibt und das an den Server schickt.
Der Server empfängt etwas, überprüft, ob am Anfang etwas steht (hier ja: /msg to B) versteht: "aha, /msg to bedeutet, dass soll eine private Nachricht sein", überprüft dann den angegebenen User, ob der angemeldet ist und schickt die Nachricht an ihn.
Ist meine Vorstellung davon überhaupt korrekt?? Ich fürchte fast nein... :(:duck:

Sir Rufo 6. Okt 2010 19:54

AW: Chat mit PM
 
Auch wenn es auf den ersten Blick komplizierter wird, solltest du die Nachrichten als JSON verschicken.

Senden vom Client geht dann z.B. so
Delphi-Quellcode:
uses
  superobject;

{...}

var
  o : ISuperObject;
begin
  o := SO; // Struktur vorbereiten
  o.S[ 'CMD' ] := 'MSG'; // Wir wollen eine Nachricht senden
  o.S[ 'MSG' ] := 'Ich grüße euch alle'; // Die Nachricht
  // Soll es nur einem Empfänger gesendet werden, dann diese Feld füllen, sonst leer lassen
  o.S[ 'TO' ] := 'Peter'; // Der Empfänger
  ClientSocket.SendText( o.AsJSON );
end;
Der Server empfängt das dann wie folgt
Delphi-Quellcode:
uses
  superobject;

{...}

procedure ClientRead( Sender : TObject; Socket : TCustomWinSocket );
var
  r : string;
  o : ISuperObject;
  idx : integer;
begin
  // Lesen vom Client
  r := Socket.ReceiveText;
  // und daraus wieder ein JSON erzeugen
  o := SO( r );
  o.S[ 'FROM' ] := ''; // Der Server könnte jetzt noch den Benutzer einfügen
  case IndexText( o.S[ 'CMD' ], [ 'MSG', 'LOGIN' ] ) of
    0 : // MSG
      begin
        o.I[ 'COUNT' ] := 0;
        // Senden
        for idx := 0 to TServerWinSocket( Sender ).ActiveConnections - 1 do
          // an alle?
          if ( ( o.[ 'TO' ] = '' ) or
            // an diesen Benutzer? 
            ( o.S[ 'TO' ] = <Deine Userabfrage> ) ) and
            // nicht an den Client der gerade sendet
            ( Socket <> TServerWinSocket( Sender ).Connections[ idx ] ) then
            begin
              TServerWinSocket( Sender ).Connections[ idx ].SendText( o.AsJSON );
              o.I[ 'COUNT' ] := o.I[ 'COUNT' ] + 1;
            end;
         // Und wieder zurück an den Client
         Socket.SendText( o.AsJSON );
      end;
    1 : // LOGIN
      begin
        // hier werden die Login-Daten überprüft
        o.B[ 'SUCCESS' ] := PruefeLogin( o.S[ 'USER' ], o.S[ 'PASS' ] );
        // Eintrag im Usermanager
        {...}
        // Rückmeldung an den Client
        Socket.SendText( o.AsJSON );
      end;
  end;
end;
btw: Um mit TServerSocket und TClientSocket auch Unicode (UTF8) zu versenden kann man einfach diese Unit mit einbinden. (D2010 tested)
Delphi-Quellcode:
unit insCustomWinSockEncoder;

interface

uses
  ScktComp;

type
  // Erlaubt dem Socket Unicode zu senden
  TCustomWinSocketEncoder = class helper for TCustomWinSocket
    function ReceiveText : string;
    function SendText( const S : string ) : integer;
  end;

implementation

uses
  SysUtils;

{ TCustomWinSocketEncoder }

function TCustomWinSocketEncoder.ReceiveText : string;
  var
    Encoding : TEncoding;
    Buffer : TBytes;
  begin
    SetLength( Buffer, ReceiveBuf( Pointer( nil )^, -1 ) );
    SetLength( Buffer, ReceiveBuf( Pointer( Buffer )^, Length( Buffer ) ) );
    Encoding := TEncoding.UTF8;
    Result := Encoding.GetString( Buffer );
    SetLength( Buffer, 0 );
  end;

function TCustomWinSocketEncoder.SendText( const S : string ) : integer;
  var
    Encoding : TEncoding;
    Buffer : TBytes;
  begin
    Encoding := TEncoding.UTF8;
    Buffer := Encoding.GetBytes( S );
    Result := SendBuf( Pointer( Buffer )^, Length( Buffer ) );
    SetLength( Buffer, 0 );
  end;

end.

xZise 6. Okt 2010 21:34

AW: Chat mit PM
 
Moin,
Zitat:

Zitat von hans ditter (Beitrag 1054156)
[...]Hm, wenn ich das richtig verstanden hab mit dem Cardinal(Socket): Socket ist die Variable bei ServerSocketClientConnect(Socket: TCustomWinSocket);[...]

Dann schreib doch statt "Pointer" einfach "TCustomWinSocket".

Zitat:

Zitat von hans ditter (Beitrag 1054156)
[...]Oh man, ich trau gar nicht, dass zu sagen, aber ich hab's immer noch nicht verstanden mit dem Protokoll. So wie ich mir vorstelle, läuft das so, dass User A /msg to B {content} schreibt und das an den Server schickt.
Der Server empfängt etwas, überprüft, ob am Anfang etwas steht (hier ja: /msg to B) versteht: "aha, /msg to bedeutet, dass soll eine private Nachricht sein", überprüft dann den angegebenen User, ob der angemeldet ist und schickt die Nachricht an ihn.
Ist meine Vorstellung davon überhaupt korrekt?? Ich fürchte fast nein... :(:duck:

Genau so! Du überlegst halt einfach, was du übertragen musst: Zum Beispiel "Schicke an alle" und "Schicke an benutzer". Das heißt du musst übertragen, welcher Typ das ist, damit der Server und die anderen Clients wissen, was für eine Nachricht war das. Dann musst du einen Empfänger definieren, wenn es eine private Nachricht ist. Und zum Schluss musst du dann noch den Inhalt dazu schreiben.

Der Server liest nun die Nachricht und entschlüsselt den Typ um heraus zu finden, was er damit machen soll und handelt dann entsprechend. Zum Beispiel wenn man eine private Nachricht verschicken will, das habe ich ja schon beschrieben.

So könntest du zum Beispiel einen Befehl definieren, um den Nickname eines Benutzers zu ändern und weitere Befehle definieren.

MfG
Fabian

hans ditter 7. Okt 2010 17:32

AW: Chat mit PM
 
Hey, dann war das ja doch gar nicht so falsch!! :D *FREU* Ich hatte das so schonmal versucht. Ich wollte /msg B Hallo B schicken. Das würde ja aber bedeuten, dass ich irgendwie erstmal das /msg rauskopieren und vergleichen und dann den User rauskopieren und vergleichen muss.
Geht das nicht einfacher?
Also vlt nach dem Motte: Wenn der Server einen String empfängt, der mit / anfängt, dass er dann weiß: "Ok, ich muss jetzt mal schauen welcher Befehlt da steht". Wenn er dann sieht, der Befehl ist msg, dass er dann automatisch nach dem User schaut. (Vielleicht könnte man das Ende des Befehls ja mit einem weiteren / markieren?)

Zu Pointer etc:
Ich hab von pointer schon auf TCustomWinSocket umgestellt. Wollte dann mit
Delphi-Quellcode:
UserData^.SocketPointer:=Socket;
dauf Socket einen Pointer speichern, wenn ich den Inhalt des Pointers dann aber ausgeben wollte, kam nichts raus...


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:24 Uhr.
Seite 2 von 4     12 34      

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