# UDP-Server mit Threads



## lordnikon (8. Nov 2007)

Hallo,

ich habe folgendes Problem. Ich muss als Studienarbeit ein Chatprogramm (TCP) in UDP umwandeln.

Ein UDP Server soll Nachrichten von mehreren Clients empfangen. In der TCP Implementierung wird für jeden Client der sich neu Verbindet ein Thread gestartet der die weitere Behandlung eingehender Nachrichten übernimmt.

Wie kann man dies nun bei UDP implementieren?
Ist es möglich für jeden sich neu verbindenden Client einen Thread zu starten und bestehende Verbindungen zu erkennen?

Da UDP nicht Verbindungsorientiert ist dürfte dies doch nicht möglich sein.
Wie kann man dies also behandeln?


----------



## tuxedo (8. Nov 2007)

Naja, du musst dir da ein Protokoll um UDP drum rum basteln. Bei TCP hat es ja gereicht nur die eigentliche Nachricht zum Server zu übermitteln. Den Sender hat der Server ja anhand der Verbindung erkannt. Bei UDP musst du jetzt die Pakete mit Absender etc. etwas aufblasen. 

Der Client muss ein Login-Paket senden das den Benutzernamen etc. enthält. Der Server nimmt das entgegen, erkennt das Loginpaket und generiert eine ID. Diese ID teilt er dem Client mit und merkt sich selbst (in einer Liste) "Der Client mit dem Namen ABC hat eben von mir die ID 123 bekommen".

Am Server bastelst du dir am besten eine Art Paket-Dispatcher der Pakete entgegen nimmt, rein schaut von wem sie sind und was es ist (nachricht, login, logout, ...), und wohin sie ggf. müssen, und dann leitest du die Pakete zum Empfänger.
Prinzipiell brauchst du dann auch keine Client-Threads mehr. Die Liste mit Clients reicht aus. Der Paketdispatcher pickt sich aus der Liste einfach den Empfänger raus und ruft die senden-methode auf. Fertig.




- Alex


----------



## lordnikon (8. Nov 2007)

Eine Identifikation durch einen Benutzernamen ist bereits vorhanden!

Das Problem ist das in der Zeit in der wir ein eingehendes Pakete bearbeiten nichts empfangen werden kann und somit einzelne Paket verlorgen gehen.

Falls es hilfreich ist kann ich dir auch unseren Server reinposten.


----------



## tuxedo (8. Nov 2007)

Ja, poste mal den *Abschnitt* wo die Pakete empfangen werden.

- Alex

P.S.

Du solltest die Bearbeitung eines Pakets in je einen Thread stecken. 
Also in einer Schleife Pakete empfangen und sofort nach dem Empfangen das Paket einem Thread in die Hand drücken und den machen lassen. Der Thread "stirbt" dann wenn er fertig ist mit dem bearbeiten.


----------



## lordnikon (8. Nov 2007)

Ok hier der Server

ich hoffe du kommst zurecht 




```
public static void main(String args[]) {
		try {
			serverSocket = new DatagramSocket(port);
			System.out.println("ServerCommunicator waiting for clients...");

			manager = new ChatEventManager();

			while (true) {

				DatagramPacket packet = new DatagramPacket(data, data.length);

				serverSocket.receive(packet);

				ServerCommunicator communicator = new ServerCommunicator(
						serverSocket);

				// Starten des ServerCommunicator Threads:
				communicator.start();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

		public ServerCommunicator(DatagramSocket incoming) {
		this.server = incoming;

	}

	/**
	 * run Methode zum starten des Threads.
	 * 
	 */
	public void run() {
		boolean finished = false;

		while (!finished) {

			DatagramPacket packet = new DatagramPacket(data, data.length);

			try {
				serverSocket.receive(packet);
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

			try {

					sequenceNumber = query.getSequenceNumber();

				// Adresse des Absenders für Antwort ermitteln ermitteln:
				InetAddress target = packet.getAddress();
				int port = packet.getPort();

				switch (query.getQuery()) {
				case Query.LOGIN:
					manager.login(query.getName());
					send(new Query(Query.CONFIRM, null, null), target, port);
					break;
				case Query.MESSAGE:
					manager.tell(query.getName(), query.getMessage());
					send(new Query(Query.CONFIRM, null, null), target, port);
					break;
				case Query.LOGOUT:
					manager.logout(query.getName());
					send(new Query(Query.CONFIRM, null, null), target, port);
					break;
				case Query.POLLING:
					ChatEvent evt = manager.poll(query.getName());
					if (evt != null && evt.getCommand() == ChatEvent.LOGOUT)
						finished = true;
					send(evt, target, port);

					break;

				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	} // run

	/**
	 * Sendet die Antwort an den Client
	 * 
	 * @param object
	 *            Das Antwortobjekt
	 * @param target
	 *            Der Zielhost
	 * @param port
	 *            Zielport
	 * @throws IOException
	 */
	public void send(Object query, InetAddress target, int port)
			throws IOException {

		DatagramPacket sendPacket = Settings.createSendPacket(query, target,
				port);
		server.send(sendPacket);

	}

} 
[code]
```
[/code]


----------



## lordnikon (8. Nov 2007)

Achja hab ich fast vergessen....da du vorgeschlagen hast ...hab ich schon ausprobiert.....nur leider hatten wir dann erhebliche Performance Probleme.


----------



## tuxedo (8. Nov 2007)

Zeile 13 bis 20 ist etwas ungeschickt formuliert. Du hast das Paket doch schon gelesen. D.h. du musst nicht den Socket (wie bei TCP) übergeben. Es reicht das Paket.

Und das kannst du in einer Zeile machen:


```
new PacketProcessor(packet).start();
```

In der run-methode ab Zeile 35:

* Wieso hast du da eine While-Schleife? Es reicht das Paket einmal abzuarbeiten und dann die run-Methode wieder zu verlassen, sprich den Thread zu beenden.
* Wo bricht die Schleifen denn ab? Abbruchbedingung?
* Und wieso liest du in Zeile 43 nochmal ein Packet? Du hast das Packet doch schon in der Main gelesen.

Der Rest sieht okay aus.

Wieviele Pakete gehen denn beim Server ein dass du da "Performanceprobleme" bekommst?

- Alex


----------



## lordnikon (8. Nov 2007)

Wir haben es so umgestellt das der "Haupt"thread die Pakete empfängt und dann einen Thread startet der die Bearbeitung des Threads übernimmt.

In der Zeit bei dem dieser Thread gestartet wird kann der Hauptthread keine Pakete empfangen!


----------



## tuxedo (8. Nov 2007)

Naja, "hexen" kann kein Programm. Du kannst ja schlecht das Starten des Bearbeitungsthreads auch noch in einem Thread auslagern. 

Probier mal das starten des Barbeitsungsthread so zu verkürzen wie ich's dargestellt hab. 

Die von mir aufgezählten Punkte hast du jetzt dummerweise nicht angesprochen. Wie schaut's damit aus?

Allgemein: Da UDP ja verbindungslos ist, solltest du für "wichtige" Pakete eine Eingangsbestätigung zurück an den Client schicken. Und beim client ein TimeOut einbauen: "Wenn in Zeit X keine Antwort kommt: Nochmal schicken".

- Alex


----------

