AW: Chat mit PM
Zitat:
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 |
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 |
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 |
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:
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:
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;
Code:
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):
PRIVATE
hans ditter Hallo hans ditter! Dies ist eine private Nachricht
Code:
Die könnte dann so aussehen:
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
Code:
Und die Fehlermeldung z.B.:
PRIVATE-RECV
xZise Hallo hans ditter! Dies ist eine private Nachricht
Code:
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 ;) ).
PRIVATE-ERR
Benutzername "hans ditter" nicht im Netzwerk vorhanden! Ein anderes Format wäre im IRC Stil:
Code:
Hier wäre der Trenner das Leerzeichen, aber wieder sprechende Befehle, und Nicknames mit Leerzeichen sind nicht möglich.
msg <Reciever> <Content>
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 |
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:
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?).
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]; 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] |
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 |
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: |
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:
Der Server empfängt das dann wie folgt
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;
Delphi-Quellcode:
btw: Um mit TServerSocket und TClientSocket auch Unicode (UTF8) zu versenden kann man einfach diese Unit mit einbinden. (D2010 tested)
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;
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. |
AW: Chat mit PM
Moin,
Zitat:
Zitat:
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 |
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:
dauf Socket einen Pointer speichern, wenn ich den Inhalt des Pointers dann aber ausgeben wollte, kam nichts raus...
UserData^.SocketPointer:=Socket;
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:24 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