PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java: simples Client-Server Programm - Meinungen gefragt


Sephiroth
2005-07-06, 21:31:26
Ich soll zum Thema Sockets, Clients und Server einen Server basteln, der das aktuelle Datum und Zeit sendet und an einen Client sendet.

Ich wollte mal eure Meinung zu meiner Implementierung hören.

Source siehe Anhang.

HellHorse
2005-07-06, 21:47:29
Server:
So gayt das nicht.
Ein Thread macht:
sockamServ=servSo.accept()
Ein anderer sendet dann die Response.
Logging (log4j oÄ) kriegt man billig und ist cool.

Client:
Den Client willst du wahrscheinlich nicht die ganze Zeit das Datum abfragen lassen.

Ich würde mir einen besseren Mechanismus überlegen das ganze zu beenden.
Das Datum würde ich bloss als long (8 bytes übertragen).
Ports und Addressen nicht hardcoden sondern Kommandzeilenargumente.

Ansonsten ist es halt eben simpel. Besser gleich kerberos5 und DES3 nehmen. X-D

Sephiroth
2005-07-06, 22:09:15
Server:
So gayt das nicht.
Ein Thread macht:
sockamServ=servSo.accept()
Ein anderer sendet dann die Response.
Logging (log4j oÄ) kriegt man billig und ist cool.

Client:
Den Client willst du wahrscheinlich nicht die ganze Zeit das Datum abfragen lassen.

Ich würde mir einen besseren Mechanismus überlegen das ganze zu beenden.
Das Datum würde ich bloss als long (8 bytes übertragen).
Ports und Addressen nicht hardcoden sondern Kommandzeilenargumente.

Ansonsten ist es halt eben simpel. Besser gleich kerberos5 und DES3 nehmen. X-D
Du sprichst in Rätseln :confused: ;(

Eigentlich war das, was in der run Methode des Threads steht, in der main des "Servers". Allerdings wollte ich unbedingt daß der Server nicht ununterbrochen das Datum sendet, sondern eben danach 1 Sekunde wartet - deswegen hab ich das als Thread gemacht, um die sleep Methode nutzen zu können.

Ansonsten: die Aufgabenstellung war eben so - Server machen der Socket erstellt und Datum sendet; Client verbindet sich zum Port und gibt die Daten aus.

HellHorse
2005-07-06, 22:50:14
Dein Server Loop ist im Moment:

auf Verbindung warten
jede Sekunde zum Client senden
gehe zu 1.


IMHO sollte er aber so aussehen

auf Verbindung warten
Thread starten der Daten sendet
gehe zu 1.

Du machst dann einen eigenen Thread, der bloss die Daten zum Client sendet.

Was machst du z.B wenn plötzlich zwei Clients zum Server verbinden wollen?
Zudem sendest du ja nicht ein Datum sondern einfach einen String und hoffst, dass der Client den ganzen auf einmal liest. Sonst verteilt sich `das Datum' über mehrere Zeilen.
Und bloss um eine Sekunde zu warten, brauchst du ja nicht extra einen Thread aufzutun.

Mal abesehen von all den while(true)...

Sephiroth
2005-07-07, 00:17:49
Dein Server Loop ist im Moment:

auf Verbindung warten
jede Sekunde zum Client senden
gehe zu 1.


IMHO sollte er aber so aussehen

auf Verbindung warten
Thread starten der Daten sendet
gehe zu 1.

Du machst dann einen eigenen Thread, der bloss die Daten zum Client sendet.

Was machst du z.B wenn plötzlich zwei Clients zum Server verbinden wollen?
Zudem sendest du ja nicht ein Datum sondern einfach einen String und hoffst, dass der Client den ganzen auf einmal liest. Sonst verteilt sich `das Datum' über mehrere Zeilen.

Und wie stell ich das an?
Ein Thread erstellt den Socket und wartet auf Verbindungen. Wenn eine Verbindung hergestellt wurde, startet dieser Thread einen weiteren, der die Daten sendet - hab ich das so richtig verstanden?

Das geht zwar über die eigentliche Aufgabenstellung hinaus (vermutlich sogar drann vorbei), aber das interessiert micht jetzt.


Schreiben Sie eine Klasse MyTimeServer, die nur aus der main()-Methode besteht, ein
ServerSocket-Objekt erzeugt und in einer endlosen while-Schleife Verbindungsversuche
behandelt, einen OutputStream erzeugt, das aktuelle Datum und die aktuelle Zeit als Date-
Objekt erzeugt und dieses in den OutputStream schreibt.
Schreiben Sie eine Klasse MyTimeClient, die nur aus der main()-Methode besteht, ein Socket-
Objekt erzeugt, dessen InputStream liest und auf dem Bildschirm ausgibt.

HellHorse
2005-07-07, 09:09:04
Und wie stell ich das an?
Ein Thread erstellt den Socket und wartet auf Verbindungen. Wenn eine Verbindung hergestellt wurde, startet dieser Thread einen weiteren, der die Daten sendet - hab ich das so richtig verstanden?
Genau.

So Gute ist, du musst nicht wirklich mehr Code schreiben, bloss ihn anders anordnen. :)

Die neue Klasse, die die Daten sendet:
import java.io.*;
import java.net.*;
import java.util.Date;

public class MyDateSender implements Runnable {
private Socket sockamServ; //Socket Object

public MyDateSender(Socket sockamServ) {
this.sockamServ = sockamServ;
}

public void run() {
OutputStream oStream;
Date date;
String outStr;
oStream = sockamServ.getOutputStream(); // Stream erstellen
while (true) {
try {
date = new Date();
oStream.write(date.toString().getBytes(), 0, date.toString().length()); // sende das Datum
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}catch (IOException e) { // brich ab im Falle eines Fehlers beim Senden
System.err.println("Warning: connection lost!");
break;
}
}
}
}

Die alte Serverklasse, jetzt mit weniger Code(tm).
import java.io.*;
import java.net.*;
import java.util.Date;

public class MyTimeServerThread2 implements Runnable {

private volatile Thread clockThread = null;

public void start() {
if (clockThread == null) {
clockThread = new Thread(this, "TimeServerThread");
clockThread.start();
}
}

public void stop() {
clockThread = null;
}

public void run() {
Thread thisThread = Thread.currentThread();
while (clockThread == thisThread) {
try {
InetAddress inetAdr = InetAddress.getByName("localhost"); // die Adresse
ServerSocket servSo = new ServerSocket(31337, 0, inetAdr);

System.out.println("Server running on: "+inetAdr.getHostAddress()+":"+servSo.getLocalPort());

while (true) {
try {
if ((sockamServ=servSo.accept()) != null) { // prüfe auf Verbindungsaufbauwunsch
Thread senderThread = new Thread(new MyDateSender(sockamServ));
senderThread.start();
}
}catch (IOException e) {
System.err.println("ERROR: IO Exception!\n" + e);
break;
}
}
}catch (IOException e) {
System.err.println("ERROR: IO Exception creating socket!\n" + e);
}
}
}
}

Wie man sieht, ist das alles aus deinem bisherigen Code zusammen gecopy-pasted. Ist also ungetested und ich übernehme daher keine Garantie, dass es läuft. Und ich entschuldige mich für die Formatierung.


Ich hoffe, die wollten mir der Aufgabenstellung nicht ausdrücken, dass _alller_ Code in #main sein muss. :eek:

Aqualon
2005-07-07, 11:11:18
Ich hoffe, die wollten mir der Aufgabenstellung nicht ausdrücken, dass _alller_ Code in #main sein muss. :eek:So klingt es aber irgendwie...

Der Sinn dahinter ist mir etwas unklar, wenn alles irgendwie in die main gepackt wird, ist das alles andere als gut lesbarer Code.

Ich musste vor kurzem dieselbe Aufgabe in C machen (ok, wir hätten fork machen sollen um die Zeit an den Client zu schicken, aber ich habs dann mit Threads gemacht ;-)), war eigentlich ganz nett.

Das wirkliche Problem bei der Aufgabe ist aber, dass die Zeit wirklich jede Sekunde übertragen wird. Bei mir war es durch sleep bedingt spätestens nach ein paar Minuten der Fall, dass eine Sekunde übersprungen wurde. Kennt jemand ne Möglichkeit das besser hinzubekommen oder muss man damit leben?

Aqua

HellHorse
2005-07-07, 12:04:48
Ich musste vor kurzem dieselbe Aufgabe in C machen (ok, wir hätten fork machen sollen um die Zeit an den Client zu schicken, aber ich habs dann mit Threads gemacht ;-)), war eigentlich ganz nett.
Ist also wirklich so, dass Java wie C unterrichtet wird. :usad:


Das wirkliche Problem bei der Aufgabe ist aber, dass die Zeit wirklich jede Sekunde übertragen wird. Bei mir war es durch sleep bedingt spätestens nach ein paar Minuten der Fall, dass eine Sekunde übersprungen wurde. Kennt jemand ne Möglichkeit das besser hinzubekommen?
Oh ja:
scheduleAtFixedRate (http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ScheduledExecutorService.html#scheduleAtFixedRate(java.lang.Runnable,%20long,%20 long,%20java.util.concurrent.TimeUnit))

Sephiroth
2005-07-07, 15:17:54
Genau.

So Gute ist, du musst nicht wirklich mehr Code schreiben, bloss ihn anders anordnen. :)

Die neue Klasse, die die Daten sendet:

...

Wie man sieht, ist das alles aus deinem bisherigen Code zusammen gecopy-pasted. Ist also ungetested und ich übernehme daher keine Garantie, dass es läuft. Und ich entschuldige mich für die Formatierung.

Danke, ich werds nacher mal probieren.


Ich hoffe, die wollten mir der Aufgabenstellung nicht ausdrücken, dass _alller_ Code in #main sein muss. :eek:
Doch, ich kann dir das nacher mal hochladen (der Client ist der selbe - im prinzip wie ich schon sagte - alles was in der run meines Servers steht in die main rein).


Das wirkliche Problem bei der Aufgabe ist aber, dass die Zeit wirklich jede Sekunde übertragen wird. Bei mir war es durch sleep bedingt spätestens nach ein paar Minuten der Fall, dass eine Sekunde übersprungen wurde. Kennt jemand ne Möglichkeit das besser hinzubekommen oder muss man damit leben?
Ich hab dann noch eine Abfrage reingebaut, die prüft ob seit dem letzten versenden mind. 1000ms vergangen sind und erst dann sendet der Server wieder.

HellHorse
2005-07-07, 16:08:23
Doch,
Wahhhhhh, die Geschichten sind also wirklich wahr :uhammer:
Es gibt also auch das Java Buch / Lehrmittel, das Klassen erst auf Seite 328 einführt.
Alan Kay hat also doch recht. Wieder mal.

ich kann dir das nacher mal hochladen
Bloss nicht, höchstens wenn du den Thread vorher auf die Spielweise schiebst.

massa
2005-07-07, 16:56:15
Die Aufgabenstellung klingt für mich weniger nach Buch sondern mehr nach FH-Studiengang ;)

Aqualon
2005-07-07, 20:03:39
Ist also wirklich so, dass Java wie C unterrichtet wird. :usad:Naja, selbst in C wurde uns gesagt, dass man nicht alles in die main() quetschen soll, für was hat man denn sonst Methoden...

Ok, aufgrund meiner ersten Programmiererfahrungen mit Java neige ich auch in C dazu sehr viele Methoden zu verwenden, was nicht unbedingt immer erwünscht ist.

Aqua

Sephiroth
2005-07-10, 13:18:09
;( ich bekomm das Umwandeln von long in byte[8] nicht hin - naja, eigentlich die andere Richtung, denn für long -> byte[] hab ich was im Netz gefunden. Ich komm nur mit den Shift Operatoren usw. nicht klar :-/

private static byte[] toByteArray(long foo, int length) {
byte[] array = new byte[length];
for (int iInd = 0; iInd < array.length; ++iInd){
array[iInd] = (byte) ((foo >> (iInd*8)) % 0xFF);
}
return array;
}

Coda
2005-07-10, 14:17:06
Naja, selbst in C wurde uns gesagt, dass man nicht alles in die main() quetschen soll, für was hat man denn sonst Methoden...Es geht darum alles in eine Klasse zu quetschen, nicht alles in eine Funktion.

Solche Lehrmethoden gehören gründlich verboten. Purer Käse imho.

HellHorse
2005-07-10, 14:52:05
;( ich bekomm das Umwandeln von long in byte[8] nicht hin
Dann lass es und benutz DataOutputStream und DataInputStream ;)

Sephiroth
2005-07-10, 19:13:41
Dann lass es und benutz DataOutputStream und DataInputStream ;)
thx, hab ich dann auch gemacht - trotzdem lässt mich das jetz nicht in Ruhe X-(

Aqualon
2005-07-10, 20:15:09
thx, hab ich dann auch gemacht - trotzdem lässt mich das jetz nicht in Ruhe X-(long l=0; int i;
for(i=1;i<foo.length;i++) {
l+=(foo[foo.length-1-i]*(16<<(4*(i-1)))); //BigEndian
l+=(foo[i]*(16<<(4*(i-1)))); //LittleEndian
}
l+=foo[i-1];
foo ist das byte-Array. Hoffe das hilft dir weiter ;)

Aqua

HellHorse
2005-07-10, 20:32:34
thx, hab ich dann auch gemacht - trotzdem lässt mich das jetz nicht in Ruhe X-(
Also ich hab's damals so gemacht (encrypt wollte nur bytes fressen :():
Zuerst Test: :)

public class UtilsTest extends TestCase {

private static final long[] TEST_VALUES = new long[] {
Long.MIN_VALUE,
-1,
0,
1,
1108384411234l,
Long.MAX_VALUE,
};

public UtilsTest(String s) {super(s);}

public void test1() {
for (int i = 0; i < TEST_VALUES.length; ++i) {
long next = TEST_VALUES[i];
byte[] encoded = Utils.encodeLong(next);
long decoded = Utils.decodeLong(encoded);
assertEquals(next, decoded);
}
}
}
dann

public class Utils {

private static final int BYTES_PER_LONG = 8;
private static final int BITS_PER_BYTE = 8;

public static byte[] encodeLong(long l) {
byte[] bytes = new byte[BYTES_PER_LONG];
for (int i = 0; i < BYTES_PER_LONG; ++i) {
bytes[i] = (byte) (l >>> i * BITS_PER_BYTE);
}
return bytes;
}

public static long decodeLong(byte[] bytes) {
if (bytes.length < BYTES_PER_LONG) {
throw new IllegalArgumentException("need eight bytes to decode a long");
}
long l = 0;
long mask = 0xFF;
for (int i = 0; i < BYTES_PER_LONG; ++i) {
l |= (long) bytes[i] << i * BITS_PER_BYTE & mask;
mask <<= BITS_PER_BYTE;
}
return l;
}
}

Sephiroth
2005-07-11, 14:53:29
Danke :)

Wenn ich Ende nächste Woche wieder mehr Zeit hab, werd ich mich mal näher damit beschäftigen.