# Mein eigener Messenger und dessen Probleme



## Jatoll (9. Feb 2011)

Hallo,
da ich noch öfter Frage zu meinem selbst gebauten Messenger haben werde dachte ich mir ist ein eigenes Thema am besten dazu geeignet Hilfe zu suchen 
Erstmal, die Grundlage ist "O'Reilly - Java von Kopf bis Fuß"... dort stand ein ganz einfacher chatserver und client beschrieben und das habe ich alles abgetippt und erweitere es nun und passe es meinen Vorstellungen an.
gerade habe ich folgendes Problem:

Client:

```
private void netzwerkEinrichten() {
		try {
			sock = new Socket("127.0.0.1", 63551);
			InputStreamReader streamReader = new InputStreamReader(sock
					.getInputStream());
			reader = new BufferedReader(streamReader);
			writer = new PrintWriter(sock.getOutputStream());
			writer.println("**** "+meinName +" joined the conversation - ("+ this.getDateAndTime()+") ****");
			writer.flush();
			System.out.println("Netzwerkverbindung steht");

		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
```

Die Zeit wird hier mit der Methode this.getDateAndTime abgefragt und an den String im writer geklatscht... das passiert allerdings im Client... bei Nachrichten hab ich das selbe gemacht. genau so wird auch der Name (Name des Rechners) über den writer mit übergeben... das würde ich aber alles gerne in der Server Datei tun... ich weiß nur nicht wie...
also ich muss die möglichkeit finden den Namen irgendwie mit zu übergeben aber halt nicht im Text selber, denn die Ausgabe soll Später so aussehen:



> ABC - (09.02.2011 10:45:13):
> Hallo Leute



Die Zeit soll vom Server gesetzt werden weil es schon zu unterschieden kommt wenn die rechner nicht synchonisiert sind und das is ja doof wenn die nachrichten so rüber kommen:



> ABC - (09.02.2011 10:45:13):
> Hallo Leute
> XYZ - (09.02.2011 10:39:08):
> Ja dir auch hallo


----------



## Blakh (9. Feb 2011)

Verstehe das Problem nicht ganz. Die Zeit, wann welche Nachricht gesendet/empfangen wird, wird normalerweise doch auch vom Client festgelegt. Da muss nix synchronisiert werden. Die Zeit sollte ja auch nicht mitgeschickt werden, sondern der Client fügt selbständig immer diese hinzu.

Und wo genau liegt jetzt das Problem?


----------



## Jatoll (9. Feb 2011)

die zeit is eher weniger das problem... ich will halt den namen des Rechners mit übergeben, nur halt nicht im text selber... das mit der zeit krieg ich schon irgendwie hin...


----------



## DerEisteeTrinker (9. Feb 2011)

dann hilft dir wohl nur eine Datenstruktur die bis zu einem gewissen Trennzeichen den Namen des PCs angibt oder stylischer wäre ein XML


----------



## Jatoll (9. Feb 2011)

XML sagt mir leider gar nix ... also gehört hab ichs schon öfter aber was genau das ist und wofür weiß ich ehrlich gesagt nich


----------



## DerEisteeTrinker (9. Feb 2011)

frag mal google nach "XML Wiki", dann lesen was da steht und ein AHA-Effekt haben


----------



## HoaX (9. Feb 2011)

Dann tipps doch mal bei Google ein. Und dann kannst du dir auch gleich mal XMPP anschauen. Das ist ein fertiges Protokoll fürs Chatten.


----------



## Jatoll (9. Feb 2011)

ich würd aber gern bei java bleiben... oder hat das damit nix zu tun?


----------



## Jatoll (9. Feb 2011)

Neue Frage:

ich will die verbindung zum server trennen wenn das programm geschlossen wird oder abschmiert (am besten noch mit ner meldung)... wie mach ich sowas?


----------



## DerEisteeTrinker (9. Feb 2011)

Dafür erschuf der Programmierer das finally, hilft aber nicht unbedingt, wenn das Programm abschmiert. Wenn eine Anwendung "abschmiert" ist das ein Zeichen für schlechte Programmierung, denn man sollte als Programmierer immer mit dem daU (dümmster anzunehmender User) rechnen. Ich habe gelernt, dass man nicht dumm genug denken kann, Stichwort: Chaos-Theorie.


----------



## Jatoll (9. Feb 2011)

ok, das weiß ich ja... nur wie siehts aus mit taskmanager und Prozess beenden? ... das hat ja nicht viel mit ne dummen user zu tun...
aber wie genau funktioniert dieses finally?
is das eine extra methode?


----------



## Java-Freak (9. Feb 2011)

wenn java per task manager beendet wird kannst du das nicht abfangen, wäre auch sinnlos
finally ist ein block den du an ein try/catch konstrukt dranhängst und der immer ausgeführt wird, egal ob catch eine exception gefangen hat oder nicht
Galileo Computing :: Java ist auch eine Insel (8. Auflage) – 8 Exceptions


----------



## Jatoll (10. Feb 2011)

ich versteh nicht ganz wie und wo ich diesen finally bock einsetzen muss... er wird ja IMMER ausgeführt, ob etwas klappt oder nicht und irgendwie macht ers nicht wenn das programm geschlossen wird sondern direkt nach dem Try - Catch ... hab ich das soweit schonmal richtig verstanden?


----------



## Jatoll (18. Feb 2011)

wie kann ich eine "online-users" liste erstellen?
also ich will zumindest die IPs abgreifen können die gerade auf den Server zugreifen... aber ich weiß nicht wie das geht und finde da auch nicht wirklich was zu.


----------



## DerEisteeTrinker (18. Feb 2011)

Dazu wirst du wohl auf dem Server speichern müssen, wer sich angemeldet hat und noch nicht abgemeldet hat. Das Problem ist nur, dass du eventuell nicht mitbekommst, wenn ein Client außerplanmäßig geschlossen wurde


----------



## Gonzo17 (18. Feb 2011)

DerEisteeTrinker hat gesagt.:


> Dazu wirst du wohl auf dem Server speichern müssen, wer sich angemeldet hat und noch nicht abgemeldet hat. Das Problem ist nur, dass du eventuell nicht mitbekommst, wenn ein Client außerplanmäßig geschlossen wurde



Man kann doch von Zeit zu Zeit mal nen Ping zum Client schicken, wenn keine Antwort kommt, ist er eben nicht mehr da.


----------



## DerEisteeTrinker (18. Feb 2011)

> Man kann doch von Zeit zu Zeit mal nen Ping zum Client schicken, wenn keine Antwort kommt, ist er eben nicht mehr da.



Kann man, aber da würde ich mich nicht drauß verlassen. Denn was ist, wenn du sagst, dass die Firewall einen Ping ins Nirvana verschwinden lassen soll. Dann hast das Problem, dass der Server den Client als Offline registriert, der Client aber immernoch da ist, dann musst du dich laufend neu Anmelden und da sind die Schreie der Benutzer schon vorprogrammiert.


----------



## HoaX (18. Feb 2011)

Es muss ja kein ICMP Ping sein. Wenn man doch eh eine Verbinddung hat, dann nutzt man diese dafür...


----------



## Jatoll (18. Feb 2011)

ja soweit hab ich mir das auch schon gedacht... auch mit dem ping schicken... nur wie mach ich das?


----------



## xehpuk (18. Feb 2011)

DerEisteeTrinker hat gesagt.:


> Das Problem ist nur, dass du eventuell nicht mitbekommst, wenn ein Client außerplanmäßig geschlossen wurde


Doch, dann fliegt ne SocketException mit der Message "Socket closed" beim Server.


----------



## Jatoll (18. Feb 2011)

also der server merkt wenn jemand nicht mehr verbunden ist ... dann kommt ne exception und in diese hab ich schon eingebaut, dass ne nachricht kommt "Jemand hat den Chat verlassen!" ... nur will ich unter anderem halt hinkriegen, dass da dann der name kommt.


----------



## Gonzo17 (18. Feb 2011)

Jatoll hat gesagt.:


> also der server merkt wenn jemand nicht mehr verbunden ist ... dann kommt ne exception und in diese hab ich schon eingebaut, dass ne nachricht kommt "Jemand hat den Chat verlassen!" ... nur will ich unter anderem halt hinkriegen, dass da dann der name kommt.



Weiß jetzt nicht wie das konkret aussieht, aber ist die Information nicht irgendwie in der Exception enthalten? Da müsste doch irgendwie stehen, bei welcher Verbindung es zu Problemen kam, oder?


----------



## xehpuk (18. Feb 2011)

Also der Socket müsste in dem Kontext direkt ansprechbar sein, da die Exception ja wegen des Zugriffs auf den Socket ausgelöst wird.


----------



## Java-Freak (18. Feb 2011)

die SocketException musst du eig nur abfangen und dann den user auf offline setzen...


----------



## Jatoll (18. Feb 2011)

xehpuk hat gesagt.:


> Also der Socket müsste in dem Kontext direkt ansprechbar sein, da die Exception ja wegen des Zugriffs auf den Socket ausgelöst wird.



nee da steht überall was mit Unknown Source...




> die SocketException musst du eig nur abfangen und dann den user auf offline setzen...



wie geht das denn, versteh nicht so ganz was du jetzt meinst?


----------



## xehpuk (18. Feb 2011)

Jatoll hat gesagt.:


> nee da steht überall was mit Unknown Source...


Das sagt dir ja nur, dass nicht auf den Quelltext zugegriffen werden konnte.
Ich weiß jetzt auch nicht, wie du das überhaupt machst, könntest ja deinen Code zeigen.

Hier mal ein abgespecktes Beispiel:

```
public class ChatServer extends Thread {
	private final ServerSocket serverSocket;
	
	public ChatServer(final ServerSocket serverSocket) {
		this.serverSocket = serverSocket;
	}
	
	@Override
	public void run() {
		while (!isInterrupted()) {
			final Socket socket;
			try {
				socket = serverSocket.accept();
				System.out.println("In: " + socket);
				new Thread() {
					@Override
					public void run() {
						try {
							final BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
							String input;
							while ((input = reader.readLine()) != null)
								System.out.println(socket + ": " + input);
						} catch (IOException e) {
							System.out.println("Out: " + socket);
						}
					}
				}.start();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) throws IOException {
		new ChatServer(new ServerSocket(1337)).start();
	}
}
```


----------



## Jatoll (18. Feb 2011)

ich würd den quelltext ungern öffentlich hier rein stellen.

wofür ist eigentlich dieses @Override?


----------



## Jatoll (18. Feb 2011)

@xehpuk:
die ip fragste jetzt aber auch nicht ab oder?

EDIT:
ich will ja nur wissen wie ich in der Serverdatei die IPs der angemeldeten Clients einlesen kann...


----------



## xehpuk (18. Feb 2011)

Jatoll hat gesagt.:


> wofür ist eigentlich dieses @Override?


Das ist eine Annotation.


> *@Override*—the @Override annotation informs the compiler that the element is meant to override an element declared in a superclass (overriding methods will be discussed in the the lesson titled "Interfaces and Inheritance").
> 
> ```
> // mark method as a superclass method
> ...


Annotations (The Java™ Tutorials > Learning the Java Language > Classes and Objects)
Kurz gesagt: Sie sagt dem Compiler, dass diese Methode eine andere überschreibt. Stellt der Compiler fest, dass dies nicht stimmt, gibt er einen Fehler aus. Der Zweck dahinter ist, dass man so Tippfehler sofort entdeckt und wenn man die Signatur der Operation in der Superklasse/dem Interface ändert oder sie gar löscht, meckert der Compiler dann auch, sodass man darauf aufmerksam gemacht wird, dass dies weitere Auswirkungen hat.



Jatoll hat gesagt.:


> @xehpuk:
> die ip fragste jetzt aber auch nicht ab oder?


Indirekt schon, denn ich gebe sie auf der Konsole aus. Probiers mal mit den Gettern, die "Address" beinhalten. Davon wirds wohl eine sein.


----------



## Java-Freak (20. Feb 2011)

Also man sollte noch dazu sagen, dass @Override für das Programm völlig unwichtig ist und nur zum besseren Verständnis des Codes da ist.


----------



## Jatoll (21. Feb 2011)

xehpuk hat gesagt.:


> Das sagt dir ja nur, dass nicht auf den Quelltext zugegriffen werden konnte.
> Ich weiß jetzt auch nicht, wie du das überhaupt machst, könntest ja deinen Code zeigen.
> 
> Hier mal ein abgespecktes Beispiel:
> ...



Also ich hab das mal in Eclipse rüber kopiert und gestartet... da wird nix ausgegeben... ich kann nur schreiben und dann stehts da... versteh ich jetzt nicht so wirklich, wie ich da die ip erfahren kann ???:L


----------



## Jatoll (21. Feb 2011)

Ok, in der Zwischenzeit mal was anderes:


```
frame.setDefaultCloseOperation(this.closingOnX());
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	}
private int closingOnX(){
	writer.println("**** " + meinName + " left the conversation - ("
	+ this.getDateAndTime() + ") ****");
writer.flush();
return 0;
}
```

ich will nun das hier ausgeben wenn jemand den Chat verlässt... Problem ist aber, dass es direkt zu beginn ausgeführt wird... also die Methode closingOnX().
sie soll aber nur ausgeführt werden wenn jemand das Fenster schließt... kann mir da jemand helfen?


----------



## HoaX (21. Feb 2011)

Dein Client sollte nicht dafür zuständig sein diese Meldung zu bringen. Und ein richtiges Protokoll wäre auch nicht schlecht, z.B. IRC oder XMPP.

Dein Server sollte User-Objekte halten, in denen Name, Socket, usw gespeichert ist. Der Client braucht immer nur normal die Verbindung trennen und der Server kann kann anhand des User-Objektes herausfinden welcher Benutzer nun gegangen ist und eine entsprechende Nachricht verteilen.


----------



## Jatoll (21. Feb 2011)

ja ich höre hier ja schon viel davon was der server alles tun sollte nur sagt mir keiner wie es gemacht wird ... bis jetzt hab ich nur, dass der Server ne exception schmeißt wenn jemand geht (die allerdings gefangen wird) und dann ne meldung kommt "jemand hat den chat verlassen" ... und deshalb hab ich ja schon ein paar mal gefragt wie man dann überhaupt die ip oder sonst was zur zuordnung der clients abrufen kann (vom server aus)


----------



## Gonzo17 (21. Feb 2011)

Jatoll hat gesagt.:


> und deshalb hab ich ja schon ein paar mal gefragt wie man dann überhaupt die ip oder sonst was zur zuordnung der clients abrufen kann (vom server aus)





HoaX hat gesagt.:


> Dein Server sollte User-Objekte halten, in denen Name, Socket, usw gespeichert ist.



Beantwortet das nicht die Frage? In einem User-Objekt stehen die Informationen und bei einer SocketException müsstest du doch auch wissen vol welchem Socket das kommt. Darüber kannst du es dann zuordnen. Das Code-Beispiel von xehpuk zeigt dir ja auch, wie du es machen kannst.


----------



## Jatoll (21. Feb 2011)

ich hab jetzt endlich mal herausgefunden, dass man mit .getInetAddress() vom server aus die IP der clients abrufen kann (mehr wollte ich doch auch gar nicht wissen) ... jetzt werde ich damit eine Hashmap oder Arraylist erstellen, in denen IP und Name sind und dann einfach beim ein und ausloggen aktualisieren und neu ausgeben in dem "Online-Users" fenster...
ich denke Hashmap eignet sich da besser oder?


----------



## Gonzo17 (21. Feb 2011)

Wie gesagt, mach lieber eine Liste mit User-Objekten. Denn wenn du eine HashMap hast und da zB die IP-Adresse der Key ist, dann wirst du umgekehrt nicht an die IP-Adresse kommen, wenn du nur den Namen (oder andere Infos des Benutzers) hast. Bei einer Liste von Usern kannst du dann über die Liste iterieren, jedes User-Objekt nach einer bestimmten Information abfragen und hast dann hinterher wirklich den "ganzen User in der Hand".


----------



## Jatoll (21. Feb 2011)

ja ok, klingt ganz logisch, aber ich weiß gerade nicht ob ich das auch so umsetzen kann wie du sagst... ich hab sowas wie "user anlegen" noch nie gemacht... Hashmap würd ich noch hinkriegen.


----------



## HoaX (21. Feb 2011)

Bei der HashMap mit IP wirst du Probleme bekommen, wenn zwei Benutzer hinter dem selben NAT-Gateway deinen Chat betreten, da dann beide die selbe IP haben.


----------



## Gonzo17 (21. Feb 2011)

Wie man das konkret am besten umsetzt, weiß ich selbst auch nicht, aber ich stells mir recht einfach vor. Einfach mal eine Klasse "User" anlegen, die bestimmte Attribute hat, die du eben brauchst: Name, IP-Adresse/Socket, usw. Dann machst du einen oder mehrere Konstruktoren und für die Attribute jeweils Getter und Setter (falls notwendig, Name wird wahrscheinlich zB eher nicht verändert, falls du das in deinem Chat fest pro registrietem Benutzer hast).


----------



## Jatoll (21. Feb 2011)

dann brauch ich ja aber trotzdem noch sowas wie eine hashmap, in die die ganzen objekte dann einen eintrag schreiben und aus der sie beim logout wieder gelöscht werden oder?


----------



## Gonzo17 (21. Feb 2011)

Ja, aber keine HashMap, sondern eine normale Liste. Du brauchst ja keinen Key, um an das User-Objekt zu kommen.


----------



## Jatoll (21. Feb 2011)

wie lösche ich denn so einen user der offline geht?


----------



## Gonzo17 (21. Feb 2011)

In dem Moment, in dem du "merkst", dass der User offline ist, gibst du eine Nacht im Chat aus ("Karlchen hat den Chat verlassen.") und löschst den User "Karlchen" aus der Liste. Falls deine Namen eindeutig und nicht veränderbar sind, kannst du nach einem User mit dem Namen "Karlchen" suchen. Ansonsten musst du dir etwas ausdenken.


----------



## Jatoll (21. Feb 2011)

daher ja die hashmap, die die ip als key nimmt ...


----------



## Gonzo17 (21. Feb 2011)

Jatoll hat gesagt.:


> daher ja die hashmap, die die ip als key nimmt ...



1.



HoaX hat gesagt.:


> Bei der HashMap mit IP wirst du Probleme bekommen, wenn zwei Benutzer hinter dem selben NAT-Gateway deinen Chat betreten, da dann beide die selbe IP haben.



2. Möchtest du ja evtl auf einen User zugreifen, auch wenn du seine IP-Adresse zu diesem Zeitpunkt nicht kennst.


----------



## Jatoll (21. Feb 2011)

gut aber ich kann beim login bzw. anlegen eines neuen users prüfen ob die IP schon vergeben ist und dann sagen "Sorry aber IP is schon weg!" oder? und was is überhaupt ein NAT-Gateway? ich dachte immer jedes gerät im netz hat eine eindeutige IP?


----------



## Gonzo17 (21. Feb 2011)

Jatoll hat gesagt.:


> ich dachte immer jedes gerät im netz hat eine eindeutige IP?



Nein, davon sollte man nicht ausgehen. Schau dir zum Beispiel mal das an: Proxy
Kurz beschrieben heißt das, dass man über einen Proxy (quasi ein "Vermittler") ins Internet gehen kann, dh alle Anfragen (zB im Browser) werden nicht direkt ins Internet geleitet, sondern gehen über den Proxy, somit "sieht" man dich nicht im Internet. Gehen mehrere Leute über einen Proxy ins Internet, haben sie auch alle die gleiche IP-Adresse.


----------



## Java-Freak (22. Feb 2011)

Definiere lieber ein Klasse "User", die eine IP hat und einen Anzeigenamen und nehm dann eine LinkedList<User> (wahlweise auch ArrayList). Du kannst ja dann Getter/Setter schreiben, z.b. userxy.getIP() und userxy.getName()


----------

