/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl <filius@c.fameibl.de> ** and Stefan Freischlad ** Filius is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 2 of the License, or ** (at your option) version 3. ** ** Filius is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ** PURPOSE. See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with Filius. If not, see <http://www.gnu.org/licenses/>. */ package filius.software.transportschicht; import java.util.LinkedList; import filius.Main; import filius.exception.SocketException; import filius.exception.VerbindungsException; import filius.software.system.InternetKnotenBetriebssystem; import filius.software.vermittlungsschicht.IpPaket; /** * Der UDP-Socket stellt die Schnittstelle zum Versenden von Segmenten zur * Verfuegung, ohne eine Verbindung herzustellen. Ausserdem wird jede Nachricht * in genau einem Segment uebertragen. */ public class UDPSocket extends Socket { /** Liste eingehender Segmente */ private LinkedList<UdpSegment> puffer = new LinkedList<UdpSegment>(); /** * Ob der Socket verbunden ist, d. h. Aufruf von verbinden() war erfolgreich * und Socket wurde noch nicht durch schliessen() geschlossen. */ private boolean verbunden = false; /** * Konstruktor zur Initialisierung des Sockets. Dazu wird das mit dem Socket * verbundene Transport-Protokoll initialisiert und ein beliebiger Port wird * reserviert. * * @param betriebssystem * @param zielAdresse * @param zielPort * @throws VerbindungsException */ public UDPSocket(InternetKnotenBetriebssystem betriebssystem, String zielAdresse, int zielPort) throws VerbindungsException { super(betriebssystem, zielAdresse, zielPort, IpPaket.UDP); Main.debug.println("INVOKED-2 ("+this.hashCode()+") "+getClass()+" (UDPSocket), constr: UDPSocket("+betriebssystem+","+zielAdresse+","+zielPort+")"); } /** * Konstruktor zur Initialisierung des Sockets. Dazu wird das mit dem Socket * verbundene Transport-Protokoll initialisiert und ein bestimmter Port wird * reserviert. * * @param betriebssystem * @param zielAdresse * @param zielPort * @param lokalerPort * ein bestimmter lokaler Port, der beim Betriebssystem * reserviert werden soll. Dieser Parameter wird nur dann * verwendet, wenn der Wert groesser 0 ist. * @throws VerbindungsException */ public UDPSocket(InternetKnotenBetriebssystem betriebssystem, String zielAdresse, int zielPort, int lokalerPort) throws VerbindungsException { super(betriebssystem, zielAdresse, zielPort, IpPaket.UDP, lokalerPort); Main.debug.println("INVOKED-2 ("+this.hashCode()+") "+getClass()+" (UDPSocket), constr: UDPSocket("+betriebssystem+","+zielAdresse+","+zielPort+","+lokalerPort+")"); } /** * Konstruktor zur Initialisierung eines Sockets mit Belegung eines * bestimmten lokalen Ports aber ohne eine feste Ziel-IP-Adresse. Diese wird * beim Empfang eines Segments bestimmt. * * @param betriebssystem * @param lokalerPort * @param zielAdresse * @param zielPort * @throws VerbindungsException */ public UDPSocket(InternetKnotenBetriebssystem betriebssystem, int lokalerPort) throws VerbindungsException { super(betriebssystem, lokalerPort, IpPaket.UDP); Main.debug.println("INVOKED-2 ("+this.hashCode()+") "+getClass()+" (UDPSocket), constr: UDPSocket("+betriebssystem+","+lokalerPort+")"); } /** * Pakete fuer diesen Socket werden ueber diese Funktion in den Puffer * geschrieben. * * @author carsten */ public void hinzufuegen(String startIp, int startPort, Object segment) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), hinzufuegen("+startIp+","+startPort+","+segment+")"); zielIp = startIp; zielPort = startPort; //Main.debug.println(getClass().toString() //+ "\n\thinzufuegen() wurde aufgerufen" //+ "\n\tAbsender-Adresse: " + startIp + ":" + startPort //+ "\n\tDaten: " + segment.toString()); synchronized (puffer) { puffer.add((UdpSegment) segment); puffer.notifyAll(); } } /** * Methode zum Empfang einer Nachricht (entspricht in dieser Implementierung * genau einem UDP-Segment) mit einem Timeout. Wenn der Timeout vor * eintreffen eines Datagramms auftritt, ist der Rueckgabewert 'null'. * * @return gibt den empfangenen Datenstring zurueck. */ public synchronized String empfangen(long millis) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), empfangen("+millis+")"); UdpSegment segment; synchronized (puffer) { if (puffer.size() < 1) { try { puffer.wait(millis); } catch (InterruptedException e) { e.printStackTrace(Main.debug); } } if (puffer.size() >= 1) { segment = (UdpSegment) puffer.removeFirst(); return segment.getDaten(); } else { return null; } } } /** * Methode zum Empfang einer Nachricht (entspricht in dieser Implementierung * genau einem UDP-Segment) * * @return gibt den empfangenen Datenstring zurueck. */ public synchronized String empfangen() { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), empfangen()"); return empfangen(0); } /** Methode zum Senden einer Nachricht ueber UDP */ public synchronized void senden(String nachricht) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), senden("+nachricht+")"); UdpSegment segment; segment = new UdpSegment(); segment.setDaten(nachricht); segment.setQuellPort(lokalerPort); segment.setZielPort(zielPort); super.sende(segment); } /** Methode zum senden eines Broadcast Datagramms mit UDP */ public synchronized void sendeBroadcast(String nachricht) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), sendeBroadcast("+nachricht+")"); UdpSegment segment; segment = new UdpSegment(); segment.setDaten(nachricht); segment.setQuellPort(lokalerPort); segment.setZielPort(zielPort); protokoll.senden("255.255.255.255", segment); } /** * Methode zum Schliessen eines Sockets. Der Port wird wieder freigegeben! */ public void schliessen() { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), schliessen()"); synchronized (puffer) { puffer.notifyAll(); } austragenPort(); verbunden = false; } /** * Methode zum unterbrechen eines Methodenaufrufs zum empfangen eines * Datagramms. Diese Methode wird beim Wechsel vom Aktions- zum * Entwurfsmodus aufgerufen. * */ public void beenden() { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), beenden()"); synchronized (puffer) { puffer.notifyAll(); } } public void verbinden() { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), verbinden()"); if (!verbunden) { if (modus == PASSIV) { synchronized (puffer) { try { puffer.wait(); } catch (InterruptedException e) { } } } try { eintragenPort(); verbunden = true; } catch (SocketException e) { Main.debug.println("EXCEPTION ("+this.hashCode()+"): verbinden() NICHT erfolgreich"); e.printStackTrace(Main.debug); } } } public boolean istVerbunden() { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (UDPSocket), istVerbunden()"); return verbunden; } public String getStateAsString() { if (this.istVerbunden()) { return "CONNECTED"; } else { return "DISCONNECTED"; } } }