# UDP verlorene Pakete/ socket.receive zu langsam



## _-`avaj´-_ (14. Mai 2012)

Hallo,
ich arbeite gerade an einem Programm, mit dem man Datein via UDP übertragen kann.
Dabei werden die einzelnen Pakete mit Größe und Position markiert, verschickt, in einer .part Datei gespeichert und dann wieder richtig zusammengesetzt.
Aber mein Problem ist, dass der Sender zu schnell sendet sprich bevor socket.recive(p) erneut wirksam ist gehen viele Pakete verloren.
Ich habe beim versenden mal ein Threat.sleep() eingebaut und festgestellt, dass ich ganze 50 ms zwischen den Sendungen einbauen muss, damit alles ankommt!
Mein nächster Ansatz wäre das ganze über 2 Sockets laufen zu lassen und die Pakete immer abwächselnt an einen der beiden zu schicken.
Gibt es eine Einfachere Methode oder muss ich das ganze entweder mit Threat.sleep() oder 2 Sockets machen?


----------



## SlaterB (14. Mai 2012)

UDP ist nunmal UDP, da soll man nicht jedes Paket erhalten müssen, wenn ich mich recht erinnere,
verwende doch TCP, normale Sockets? dann ist für alles in der richtigen Reihenfolge gesorgt

wobei ein solcher Verlust sicher schon ungewöhnlich ist, 
kann sein dass das selbst für die allgemeinen UDP-Kriterien nicht normal ist,


was macht der Empfänger? vielleicht sollte der die Pakete möglichst schnell bearbeiten, nur in eine Liste einfügen und wieder horchen,
ein zweiter Thread könnte sie verarbeiten,


----------



## _-`avaj´-_ (14. Mai 2012)

Ich würde gerne UDP verwenden, da ich dafür kein Portforwarding etc. brauche, sondern Daten -unabhänig davon ob die Gegenstelle im eigenem Netzwerk ist oder nicht- verschicken kann.
Der Empfänger mach eigentlich nur p.getData() und startet einen neuen Threat, der das ganze dann in die .part Datei schreibt.
Auf Verluste bin ich generell schon eingestellt (der Empfänger schreibt mit welche Pakete angekommen sind und lässt sich die fehlenden hinterherschicken) aber wenn es so viele sind ist das ganze halt umso aufwändiger...


----------



## SlaterB (14. Mai 2012)

ich bin mir wie gesagt nicht sicher, ob man das nicht auch gleich einen Fehler nennen sollte, 
also kann nicht einmal sagen ob du mit deinem Programm reagieren musst,

also weiter helfen auch nicht, hoffentlich wer anders

------

eine ganz harmlose Suche führte eben noch zu
java - How to minimize UDP packet loss - Stack Overflow
hilft vielleicht bei dir


----------



## cz3kit (14. Mai 2012)

Kurz zu UDP: UDP wird verwendet, wenn es nicht wichtig ist ob und in welcher Reihenfolge die Pakete ankommen. Dies wird zum Beispiel bei Streams oder Spielen mit Multiplayer verwendet. Warum? Weil es nicht schlimm ist, wenn mal ein Paket nicht ankommt, dann hast du im Spiel ein Lag und bei Streams merkt man es sowieso kaum.

Wenn du jedoch sichergehen willst, dass Pakete in der richtigen Reihenfolge und das sie auf jeden Fall ankommen, dann solltest du TCP verwenden. Hier solltest du dann auch nicht das Problem haben, dass der Sender es zu schnell versendet, da TCP Funktionen hat, wie das Congestion Window/Sliding Window. Der Empfänger reguliert dann die Anzahl der Pakete, die der Sender versenden darf. Außerdem sorgt TCP schon selber dafür, dass jedes Paket sein Ziel erreicht. Das heißt, du musst dich darum nicht mehr kümmern.

Was spricht den gegen TCP?


----------



## SlaterB (14. Mai 2012)

> Ich würde gerne UDP verwenden, da ich dafür kein Portforwarding etc. brauche, sondern Daten -unabhänig davon ob die Gegenstelle im eigenem Netzwerk ist oder nicht- verschicken kann.



(ob alles korrekt, nötig und sinnvoll sei dahingestellt, kann ich eh kaum beurteilen)


----------



## cz3kit (15. Mai 2012)

Kannst du mal etwas dazu sagen wie Kommuniziert wird? Also befindet sich etwas zwischen den Kommunizierenden (Firewall, Router?). Weil bei TCP wird auch nicht umbedingt Portforwarding benötigt, andersherum kann es aber auch bei UDP verwendet werden.



> Aber mein Problem ist, dass der Sender zu schnell sendet sprich bevor socket.recive(p) erneut wirksam ist gehen viele Pakete verloren.



Wenn du willst, dass der Sender nicht so schnell Sendet, dann schaut dir das Konzept der Flusssteuerung von TCP an: Flusssteuerung.

Dennoch bin ich ber Meinung, dass du das Rad neu erfindest. Sag bescheid wenn ich falsch liege und du hast eine konkrete Idee die du verfolgst. Ich lerne gerne was neues dazu.


----------



## _-`avaj´-_ (15. Mai 2012)

Ich will einfach nur ein Programm schreiben mit dem man Datein verschicken kann (z.B. wie über Skype) und dabei mein Programm auf jeden PC der Welt mit Internetanschluss und Java ziehen kann und es ohne Voreinstellungen einfach läuft...
Dabei ist so gut wie immer Firewall und Router dazwischen...

Wenn mir jemand eine Möglichkeit nennt, mit der das ganze möglich ist, ohne dass ich "das Rad neu erfinde"  , dann bin ich damit auch vollkommen einverstanden, aber ich hab bis jetzt nur gehört, dass nur UDP ohne Portfreigabe etc. läuft und man damit nur Pakete verschicken kann und keine Streams nutzen kann, wenn ich mich da täusche lasse ich mich gerne erleuchetn


----------



## SlaterB (15. Mai 2012)

hast du
socket.setReceiveBufferSize(großer Wert);
aus meinem Link getestet?


----------



## cz3kit (15. Mai 2012)

Jetzt verstehe ich was du für eine Idee hast  DU hast Recht, TCP eignet sich nicht für Streams, da ist UDP besser. 
Wenn ich das jetzt richtig sehe, machst du eine Mischung aus beiden Protokollen. Wie nennst du es?


----------



## _-`avaj´-_ (15. Mai 2012)

So ich habe jetzt das ganze endlich zum laufen gebracht!
Und ich bin mir auch gar nicht mehr sicher ob das ganze daran liegt, dass .recive() zu langsam ist...
Ich habe ein Bild in 1733 Pakete geteilt (jeweils mit 1020 bytes -die übrigen 4 bytes werden für Stellenmarkierung/Größe benötigt).
Folgende Liste gibt an wie viele Pakete nach der letzten Sendung noch fehlen:

send missings: 273
send missings: 269
send missings: 266
send missings: 264
send missings: 263
send missings: 263
send missings: 263
send missings: 261
send missings: 261
send missings: 237
send missings: 225
send missings: 225
send missings: 222
send missings: 222
send missings: 219
send missings: 219
send missings: 216
send missings: 216
send missings: 214
send missings: 214
send missings: 214
send missings: 210
send missings: 210
send missings: 210
send missings: 210
send missings: 152
send missings: 116
send missings: 63
send missings: 38
send missings: 18

Auffallend sind die großen Sprünge dabei...
Ich weiss nicht warum das ganze nich einfach gleichmäßig abnimmt ??
Hat jem. ne Erklärung dafür parat?

Naja laufen tut jetzt alles und auch relativ schnell, also danke für die Hilfe (v.a. an SlaterB vlt bekomm ich ja mit deinem Link noch bessere Ergebnisse hin


----------



## bERt0r (16. Mai 2012)

Also dass TCP sich nicht für Streams eignet hör ich hier zum ersten mal. Das Stream Argument für UDP gilt nur, wenn man bei der Übertragung auf einzelne Datenpakete und richtige Reihenfolge verzichten kann. Ein FileOutputStream ist schließlich auch ein Stream, beim schreiben einer Datei ist mir die Vollständigkeit und Reihenfolge der übertragenen Bits aber sehr wohl wichtig.
Wenn du das unbedingt mit UDP lösen willst, muss dein Empfänger jedes Paket beim Sender vorher beantragen.
Das sieht dann so aus:
S -> Sende Datei mit 100 Einträgen
E -> Sende Eintrag 1-10
S -> Sendet Eintrag 1-10
E empfängt Eintrag 3,6 ,8
E -> Sende Eintrag 1,2,4,5,7,9,10

Nur so kannst du sicherstellen dass auch alles beim Empfänger ankommt. Das ist dann sehr viel mehr Arbeit als das ganze einfach per TCP zu machen. Das ist nämlich genausoschnell. Und durch eine Firewall kommst du mit UDP auch nicht durch. Wenn du jetzt auf holepunching anspielst, das bedeutet nichts anderes als durch einen UDP Trick eine TCP Verbindung aufzubauen.


----------



## _-`avaj´-_ (16. Mai 2012)

bERt0r hat gesagt.:


> Wenn du jetzt auf holepunching anspielst, das bedeutet nichts anderes als durch einen UDP Trick eine TCP Verbindung aufzubauen.



Heißt das, dass ich mit holepunching eine TCP Verbindung aufbaue?
Kann ich dann damit auch mit Streams arbeiten?

PS:
Wie ich es hinbekomme, dass alles ankommt und in richtiger Reienfolge in die Datei geschrieben wird war nicht die Frage.
Das ist mir schon klar und das habe ich auch so umgesetzt und es funktioniert auch 


So jetzt nochmal zu einer ganz anderen Frage:
Ich habe ja wie gesagt alles in eine .part Datei gespeichert... Nun ist die Frage, wie ich alles geordnet in die richtige Datei bekomme: Ich habe bis jetzt den Ansatz, dass sich ein InputStream von der .part Datei das jeweils nächste Paket sucht und in den OutputStream schreibt; Da aber in der .part Datei alles ungeordnet ist muss ich immer mal wieder Sprünge machen: mit skip(int) nach vorne ist ja kein Problem aber wie komme ich nach hinten?
Ich weiss, dass es die Methoden mark(int)/reset() gibt aber die sind nicht unterstützt sagt er mir...
Nun die Frage: Ist mark(int)/reset() für FileInputStreams generell nicht verfügbar oder leigt es daran, dass ich an einem Bild arbeite (.png) bzw. gibt es eine andere Möglichkeit das ganze zu ordnen?
Ich mache es im Moment so, dass ich den alten InputStream schließe und einen neuen aufmache, der dann wieder vorne beginnt, aber das geht natürlich bei 1700 paketen oder mehr ziemlich auf die cpu...


----------



## bERt0r (16. Mai 2012)

Peer-to-Peer Communication Across Network Address Translators

Zu deiner Frage: Du hast doch in deiner Datei sicher lauter byte Arrays von deinen Packets gespeichert. Und vorne in den Arrays steht die Reihung richtig? Dann würde ich einfach mal alle einlesen und in eine ArrayList packen.


----------



## _-`avaj´-_ (17. Mai 2012)

thx für den Link!
Und ja ich habe Arrays mit Zuordnung an den ersten beiden Stellen...
Aber wenn ich alles einlese, dann kann ich max. so große Datein einlesen, wie ich freien Arbeitsspeicher habe oder?
Und der ist nur ein paar GBytes gorß -.-


----------



## _-`avaj´-_ (17. Mai 2012)

Und noch etwas:
Der Artikel ist zwar sehr gut um zu verstehen, wie das alles funktioniert aber beinhaltet keinen Java Code...
Ich bin eig. auch erst ein Anfänger deshalb wäre ein bischen Code nicht schlecht


----------



## bERt0r (17. Mai 2012)

Als Anfänger ist Holepunching aber nicht die richtige Übungslektüre. Versuch doch erstmal ein vernünftiges Programm zu schreiben, bei dem du auf die Netzwerkarchitektur vertraust.


----------



## _-`avaj´-_ (17. Mai 2012)

Ich habe gschrieben eig. ein Anfänger 
Ich habe bereits mit TCP Netzwerkintern gearbeitet und das hat auch alles funktioniert (ist ja auch nicht sonderlich schwer)
Aber ich würde halt gerne Programme schreiben, mit denen ich übers Internet kommunizieren kann u.a. für Spiele etc.
Ich verstehe Code sehr schnell aber ich bräuchte halt mal ein Beispiel zum Verständniss...


----------



## _-`avaj´-_ (19. Mai 2012)

Hallo?
Bekomm ich keine Antworten mehr...?


----------



## HolePuncher (19. Mai 2012)

UDP Hole Punching ist eine beliebte Technik und NAT-FireWalls zu umgehen, ist aber auch mit einem gewissen Mehraufwand verbunden.

Man kann zwar auch mit TCP Hole Punching machen, dies wird aber nur von einer kleinen Anzahl bestimmter Geräte unterstützt. Davon abgesehen ist TCP Hole Punching so mit Java alleine nicht umsetzbar da es ziemliches low-level verlangt um innerhalb des TCP-Paketes auch die SYN-Nummern anzupassen damit eine NAT-FireWall dieses dann auch als "dem Stream zugehörig" anerkennt. Ohne JNI/JNA ist das aber nicht machbar.

Auch könntest du erstmal etwas schreiben bei dem du den Server nutzt. Dann kannst du erstmal beruhigt TCP nutzen. Denn einen Server brauchst du auch für UDP Hole Punching. Das ganze heißt dann STUN und ist Standard.

Der Mehraufwand bei einem solchen Vorhaben ist neben dem betreiben eines Servers zum Verbindungsaufbau und einer möglichst gut funktionierenden Implementierung von STUN auch das Problem das UDP nun mal 1) kein Stream ist sondern einzelne Datagramme 2) die Reihenfolge und Packet-Loss selbst behandelt werden muss und 3) unzuverlässig ausgelegt ist.

Warum du allerdings einen solchen Packet-Loss überhaupt hast ist schwer zu sagen.
Da du ja geschrieben hast das du schon mehrere Threads nutzt (einer der die Pakete empfängt und deren Inhalt an einen zweiten Thread (hier am besten eine Queue nutzen) der die Daten verarbeitet und einen dritten der dann die Daten auf Platte schreibt) kann die Ursache nur noch beim Netz selbst oder gewissen Einstellungen des OS liegen.

Ob man allerdings wirklich auf UDP , Hole Punching und die selbst zu implementierende Überwachung geht oder gleich den Server die verknüpfung zweier TCP-Streams erledigen lässt, darüber kann man sich sicher streiten. Fakt ist aber das es einfacher ist den Server gleich die Arbeit machen zu lassen da du so oder so überhaupt erstmal einen Server brauchst. Und ob dieser nun nur dafür da ist STUN zu ermöglichen oder gleich sinnvoll genutzt wird, ich glaube hier liegt auch ein Entscheidungskreterium wie man das Vorhaben möglichst effizient umsetzt.

Da du dich selbst eher als Anfänger bezeichnest würde ich dir erstmal die Variante TCP und Verknüpfung der Streams durch den Server vorschlagen. Es sollte sich bei guter Anbindung des Servers nicht bemerkbar in der Geschwindigkeit machen.

PS : nur weil dir mal 48h bei so einem komplexen Thema keiner antwortet heißt es nicht das man Antworten einfordern kann. Ich verfolge das Thema auch seit ein paar Tagen und bin am überlegen, hatte aber bis heute keine wirklich sinnvolle Idee WAS ich hätte posten können.


----------



## _-`avaj´-_ (19. Mai 2012)

Danke für deine Antwort!
Also TCP Hole Punching kann ich ja dann wohl vergessen (bzw. es eignet sich nicht, da ich mein Programm so schreiben möchte, dass möglichst alle Computer damit zurecht kommen).

Das ganze über den Server laufen zu lassen ist aber auch nicht möglich, da der nur php unterstützt (es sei denn es geht auch mit php?).

Zu dem Paket-Verlust:
Ich habe mal die Pakete überwacht (ich habe eine kleine Textdatei dafür genommen mit 10 Paketen a 1020 byte) und mir mal ausgeben lassen welche Pakete alle geschickt/empfangen werden:
Der Sender hat die Pakete alles in richtiger Rehenfolge und komplett abgeschickt aber der Empfänger hat zwar genau 10 Pakete erhalten aber waren die Pakete tlw. doppelt (d.h. Pakete sind angekommen: 1,2,33,555,8,99).


PS:


HolePuncher hat gesagt.:


> PS : nur weil dir mal 48h bei so einem komplexen Thema keiner antwortet heißt es nicht das man Antworten einfordern kann. Ich verfolge das Thema auch seit ein paar Tagen und bin am überlegen, hatte aber bis heute keine wirklich sinnvolle Idee WAS ich hätte posten können.



Es war nur so, dass mir immer mindestens einmal pro Tag (meistens sogar mehrmals) geantwortet wurde und auch bei anderen Themen, die ich beobachtet habe. Und da hab ich mich halt gewundert und mal nachgefragt 
Und siehe da es hat gewirkt


----------



## HolePuncher (20. Mai 2012)

Wie gesagt : TCP Hole Punching ist 1) nur mit JNI/JNA möglich und 2) wird es nur von einer geringen Anzahl von Geräten direkt unterstützt.
Wenn man z.B. mit C direkt auf der Socket-API des OS programmiert mag es sicher möglichkeiten geben um, ich nenne es mal eine kleine Abwandlung von TCP Hole Punching, sicher und hoch-kompatibel zum laufen zu bringen. Und sowas kann man ja dann auch mit JNI/JNA nachbauen, aber ich denke das halt der Aufwand den Nutzen zumindest bei diesem speziellen Problem nicht wirklich rechtfertigt.

Ob man das ganze nur über den Server laufen lassen kann wenn man dort nur Space mit PHP hat (ich vermute jetzt mal ganz stark einen Free-Hoster mit sehr "strengen" Nutzungseinschränkungen) ist fraglich und würde sich sicher schwierig gestalten. Aber mit den gegebenen "Freiheiten" seitens des Hosters ist es auf jeden Fall möglich.
Eine alternativ dazu wären z.b. Java-Servlets. Das kannst du dir in etwa so wie ein auf dem Server laufendes Applet vorstellen (gut, der Vergleich hat vielleicht Lücken und ist nicht der beste, aber es ist eine doch recht einfache Erklärung) mit dem sogar real-time Anwendungen möglich sind. Wobei ich nicht weis in wie weit hier die meisten Hoster "abriegeln". Es gibt zwar auch Free-Hoster welche Java-Servlets ermöglichen, ich kann mir aber vorstellen das diese ähnlich stark eingeschränkt sind wie vergleichbare Angebote mit PHP, da Java zumindest in meinen Augen in diesem Einsatzzweck deutlich mächtiger ist.

Und das mit den doppelten Paketen lässt sich auch erklären : UDP-Spezifikationen.
Denn laut diesen ist eigentlich so gut wie nichts garantiert : Reihenfolge, Empfang, fehlende/doppelte Pakete, etc ...
Ich würde sogar fast soweit gehen und behaupten das es noch nicht mal garantiert ist das die Pakete auch überhaupt versendet werden, also vom "Programm" das diese "sendet" auch wirklich übers NIC im Netzwerk landen. Wobei ich denke das dafür die Kapazitäten schon sehr gering sein müssen um einen solchen massiven Drop zu verursachen.

Das was du beschreibst klingt schon nach einer Art STUN. Vielleicht erklärst du uns ja mal wie genau der Verbindungsaufbau zustande kommt und die eigentliche "Kommunikation" abläuft. Ich denke zwar eher weniger das man dadurch die Fehler beheben können wird, aber vielleicht hat ja jemand eine bessere Idee zur konkreten Umsetzung.

Ganz grob, wie hier auch schon geschrieben und gelinkt, läuft UDP Hole Punching in etwa folgendermaßen ab :
Gegeben seien zwei Clienten : *A*lice und *B*ob, die jeweils hinter einem NAT sitzen: NATa und NATb, ferner gibt es noch den Server S
Das Ziel ist es das A direkt Nachrichten an B senden kann und vice-versa.
Das ganze gibt es in einer standardisierten Form die STUN heißt, steht für "Session Traversal Utilities for NAT". Ursprünglich in RFC3489 als "Simple traversal of UDP through NATs" beschrieben.
Der Ablauf, gleich ob beginnend bei Alice oder Bob, sieht vor das beide "Partner" zumindest über eine "Informationsverbindung" bei einer zentralen Stelle (dem Server) gemeldet sind.
Wenn nun einer zum jeweils anderen eine Verbindung aufbauen möchte sagt dieser erstmal dem Server bescheid das er einen Verbindungsaufbau wünscht. Dabei wird ein frei gewählter Port mitgesendet an den sich später das "Ziel" verbinden kann und über den die Kommunikation läuft. Ob dies nun via UDP geschieht (wie STUN), über eine TCP-"Kontrollverbindung" oder einen WebService mit Poll ist dabei egal.
Der Server leitet nun diese Information an den gewünschten Gesprächspartner weiter. Hier kann optional über die "Steuerverbindung" ein Request implementiert werden bei dem der Server das "Ziel" erstmal fragt ob eine Verbindung hergestellt werden kann/darf.
Mit dieser Anfrage werden zusätzlich Informationen über den Initiator mitgeschickt. Dies sind dessen IP sowie ein von diesem gewählter UDP-Port.
Wenn das "Ziel" nun diese Verbindung annimmt beginnt dieser Client mit dem Schicken von UDP-Paketen zum empfangenen IP-PORT-Paar. Gleichzeitig wird dem Server mitgeteilt das die Verbindung erwünscht wird und ebenfalls ein gewählter Port mitgeteilt.
Der Server sendet diese Information und dem eigentlichen Initiator und dieser beginnt daraufhin ebenfalls mit dem Senden von UDP-Paketen zum Partner.

Und jetzt passiert etwas was mit der sog. NAT-Session-Table zu tun hat:
Die beiden NATs NATa und NATb bemerken eine ausgehenden UDP-Verbindung zu einem bestimmten Ziel. Da natürlich eine Antwort auch erfolgreich empfangen werden können soll wird diese Information in die Session-Table eingetragen. Somit wird ein Eintrag bestehend aus SourceIP:SourcePort <-> DestinationIPestinationPort gebildet. Da diese Liste natürlich nicht endlos werden kann haben ALLE Sessions ein gewisses Timeout. Für TCP-Verbindungen liegt dies in der Regel sehr hoch (bei D-Link sogar bis zu 5000 Sekunden). Für UDP hingegen meist nur zwischen 30 und 120 Sekunden.
Wenn nun also NATa sich gemerkt hat das von einem Rechner aus seinem Netz eine UDP-Verbindung zu einem bestimmten Ziel geöffnet wurde werden alle Pakete deren "Absender" auf genau diese "Regel" zutrifft auch wieder an den entsprechenden Rechner im LAN weitergeleitet.
Das gleiche passiert auch bei NATb. Nun können sich beide Programme direkt UDP-Pakete schicken.

Da das Timeout für UDP-Verbindungen sehr gering ist sollte man einen "Ping" oder besser "keep-alive" implementieren um die Verbindung offen zuhalten. Am einfachsten geht dies wenn beide Seiten alle 5 oder 10 Sekunden einfach ein Paket mit dem Inhalt "PING" zum jeweils anderen senden. Auch ist es intiligent dieses zu beantworten und auf diese Antwort zu reagieren, so dass bei Ausbleiben des PING-Reply sofort festgestellt wird das die Verbindung zusammengebrochen ist.

Wie du siehst ist hier der Server nur für ganz wenige Daten wichtig : die Übermittlung der Verbindungs-Anfrage sowie deren spezifischer Parameter IPaorta <-> IPbortb. Ist die Direkt-Verbindung erstmal hergestellt wird der Server nicht mehr benötigt. Andere Namen sind z.B. noch Peer-to-Peer / P2P oder aus dem IRC bekannt : Client-to-Client / CTC , bzw. DCC : Direct-Client-Connection. Dies aber nur als Anmerkungen wonach du bei Google noch so suchen könntest. (Wobei IRC-DCC eher ein schlechtes Beispiel ist da viele hierfür das explizite Freigeben von Ports benötigen.)


back to topic

Nach dem du nun also eine Verbindung nach dem oben genannten Weg aufgebaut hast und darüber Daten senden willst musst du dich um die Paket-Überwachung selbst kümmern. Dazu zählen vor allem das die Pakete in der richtigen Reihenfolge verarbeitet, sowie das fehlende Pakete erneut angefordert werden. Pakete welche mehrfach eintreffen kann man entweder droppen (also einfach im NICHTS verschlucken) oder sogar teilweise für Fehlererkennung nutzen.
Das sind so Aufgaben die einem TCP abnimmt, und wegen genau diesen zusätzlichen Informationen in einem TCP-Paket ist TCP Hole Punching so viel schwerer.
Es ist bei der Verbindung eigentlich egal was und wie viele Daten zu sendest, so lange du den Status der Verbindung und der Pakete überwachst.
Bei kritischen Fällen wo alle Daten ankommen müssen kann dies natürlich auch zu lasten der Performance gehen da diese Technologie eigentlich viel mehr in Bereichen eingesetzt wird wo es eher unwichtig ist wenn Pakete verloren gehen, in der falschen Reihenfolge oder sogar mehrfach ankommen.


Ich hoffe ich konnte dir wenigstens ETWAS Einblick in das Thema "UDP Hole Punching" geben.
Zum schluss habe ich noch einen Sample-Code für dich:

NAT.java

```
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;



public class NAT {

	public static void main(String[] args) throws Exception {
		
		if (args.length < 3) {
			System.out.println("USAGE: NAT [localport] [remoteIP] [remoteport]");
			return;
		}
		
		int localPort = Integer.parseInt(args[0]);
		String ip = args[1];
		int remotePort = Integer.parseInt(args[2]);
		
		System.out.println("Testing UDPTunneler");
		UDPTunneler tunnel = null;
		try {
			tunnel = new UDPTunneler(localPort,ip,remotePort);
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("Initialized, creating tunnel.");
		
		try {
			tunnel.createTunnel();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("Yes, tunnel successfully created.");
		
	}

}
```

UDPSender.java

```
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;


public class UDPSender extends Thread {

	String str,ip;
	int port;
	long interval;
	boolean run;
	DatagramSocket socket;
	
	public DatagramSocket getSocket() {
		return socket;
	}
	
	public void setStr(String str) {
		this.str = str;
	}
	
	public void send(String s) {
		byte[] data = s.getBytes();
		InetSocketAddress address = new InetSocketAddress(ip,port);
		
		DatagramPacket packet = null;
		try {
			packet = new DatagramPacket(data,data.length,address);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		try {
			socket.send(packet);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public UDPSender(DatagramSocket sock,String ip, int port,String s, long i) {
		super();
		str = s;
		interval = i;
		run = true;
		this.socket = sock; 
		this.ip = ip;
		this.port = port;
		this.start();
	}
	
	public void terminate() {
		run = false;
	}
	
	public void run() {
		
		while(run) {
			
			this.send(str);
			
			try {
				Thread.sleep(interval);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		
	}
	
}
```

UDPTunneler.java

```
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.InetSocketAddress;
import java.net.InetAddress;


public class UDPTunneler {
	
	int lport,rport;
	String ip;
	DatagramSocket socket;
	
	public UDPTunneler(int localPort, String hostIP, int remotePort) throws Exception {
		lport = localPort;
		rport = remotePort;
		ip = hostIP;
		
		socket = new DatagramSocket(localPort);
	}
	
	public void createTunnel() throws IOException {
		UDPSender sender = new UDPSender(socket,ip,rport,"CONN",1000);
		
		while(true) {
			
			DatagramPacket p = new DatagramPacket(new byte[10], 10);
			socket.receive(p);
			
			//analyse
			String data = new String(p.getData());
			if (data.indexOf("CONN") != -1) {
				sender.send("CONN");
				sender.terminate();
				break;
			}
			
		}
	}

}
```

Ich weis, der Code ist nicht sonderlich schön, entstammt aber auch nicht meiner Feder. Ich habe diesen selbst ebenfalls nur via Google gefunden.

Zum "Test"
Du legst alle 3 Klassen auf beiden Rechnern ab.
Dann callst du das Command : [c]java NAT localPort remoteIP remotePort[/c]
Die Parameter :
localPort : ein von dir festgelegter Port welcher als lokaler "Absender" dient. Von diesem Port gehen die Daten ab und an diesem Port kommen die Daten auch wieder an.
remoteIP : die Ziel-IP des anderen Rechners. Kann logischer weise auch im selben Netzwerk stehen, würde aber den Sinn des Codes aufheben da so das UDP Hole Punching nicht getestet wird. Also ruhig mal einen Router zwischen hängen.
remotePort : der auf dem jeweils anderen Rechner als "localPort" gewählte Port.

Wenn alles gut geht dann sollte sogar quer übers Netz bei beiden Rechnern der Output kommen : [c]Yes, tunnel successfully created.[/c]

Ob dir das bei deinem Problem hilft weis ich erlich gesagt nicht.
Weitere Infos :
Session Traversal Utilities for NAT ? Wikipedia
STUN - Wikipedia, the free encyclopedia
NAT traversal - Wikipedia, the free encyclopedia
UDP hole punching - Wikipedia, the free encyclopedia
Wobei ich dir persönlich die Seiten des englischsprachigen Wikipedia eher empfehlen würde.


----------



## _-`avaj´-_ (20. Mai 2012)

Danke für deine Antwort!



HolePuncher hat gesagt.:


> Ob man das ganze nur über den Server laufen lassen kann wenn man dort nur Space mit PHP hat (ich vermute jetzt mal ganz stark einen Free-Hoster mit sehr "strengen" Nutzungseinschränkungen) ist fraglich und würde sich sicher schwierig gestalten.



Nein kein Free-Hoster sondern ein NAS-Server von QNap, sprich das Ding steht bei mir zu Hause und ich kann damit alles machen, was ich will 



HolePuncher hat gesagt.:


> Eine alternativ dazu wären z.b. Java-Servlets. Das kannst du dir in etwa so wie ein auf dem Server laufendes Applet vorstellen



Dafür brauch ich aber wieder Java-Unterstützung oder?



HolePuncher hat gesagt.:


> Und das mit den doppelten Paketen lässt sich auch erklären : UDP-Spezifikationen.
> Denn laut diesen ist eigentlich so gut wie nichts garantiert : Reihenfolge, Empfang, fehlende/doppelte Pakete, etc ...



Ich hatte gedacht, dass Pakete nur fehlen können... Danke dafür, das klärt einiges 



HolePuncher hat gesagt.:


> Das was du beschreibst klingt schon nach einer Art STUN. Vielleicht erklärst du uns ja mal wie genau der Verbindungsaufbau zustande kommt und die eigentliche "Kommunikation" abläuft.



Bis jetzt (also in der Testphase) gebe ich Ziel-IP/-Port manuell an beiden Computern ein. Aber du hast Recht: Nacher soll der Verbindungsaufbau wie bei STUN über den Server laufen. Beide Rechner tragen sich dann als online beim Server an (der Server erkennt auch wenn ein Rechner inaktiv wird und löscht ihn wieder aus der Liste) und dann läuft alles so, wie du gesagt hat, also Übermittlung der Daten und Verbindungsaufbau...
Zur Serverkummunikation:
Ich lasse das ganze einfach über eine index.php laufen spricht ich wähle einfach den Server mit einer URL an (url.openStream()).

Danke für dein Beispiel aber, wie ich es schon öfters erwähnt habe: Ich bin zwar Anfänger, habe aber schon öffters mit all dem erfolgreich gearbeitet 
PS: Genau dieses Beispiel habe ich für meine ersten UDP Programme benutzt 

Also um nochmal zusammen zu fassen:
Ich habe jetzt ein Programm, dass mir via STUN eine UDP Verbindung zu einem Anderen Rechner aufbaut; Danach wird eine Datei versendet, indem sie mit meheren Paketen, die jeweils ihre Position an den ersten beiden Stellen ihres Byte-Arrays haben, verschikt wird; Am Empfänger werden die einzelnen Pakete in eine .part Datei geschrieben und ihre Nummer in einer ArrayList gespeichert (dabei werden doppelte Pakete einfach übergangen); Danach wertet der Empfänger aus, welche Pakete noch fehlen und lässt sie sich nochmals senden, bis alle Pakete da sind; Nun wird die Partdatei in die Richtige Datei umgewandelt.

Soweit läuft auch alles!
Probleme sind:
-Das senden der verlorenen Pakete dauert länger als der eigentliche Sendevorgang...
-Das umschreiben der .part Datei in die richtige Datei geht sehr auf die cpu, da ich mit meinem InputStream immer wieder an den Anfang der Datei Springen muss, indem ich den Stream schließe und neu aufmache...

Ich werde das Thema weiter verfolgen, falls jem. meine Probleme beheben kann oder jem. ein ganz anderen Ansatz hat!

Danke nochmal für euere Hilfe!


----------



## HolePuncher (20. Mai 2012)

Vielleicht hilft dir auch "Flusssteuerung" wie es TCP verwendet. Dabei wird jedes erhaltene Paket mit einem weiteren quitiert. Somit fällt die Prüfung auf den Sender der bei nicht-Erhalt des quitierungs-Paketes innerhalb eines Timeouts das entsprechende Paket erneut sendet. Das geht schneller und einfacher als wenn der Client die fehlenden Pakete explizit anfordern muss.

Zu deinem "Stream auf/zu/auf/zu" : nutze doch ein [japi]RandomAccessFile[/japi]. In diesem kannst du frei navigieren ohne jedes mal wieder von vorne anfangen zu müssen.
Wenn du die einzelnen Chunks natürlich in jeweils eigene Dateien schreibst dann musst du logischer weise immer wieder Streams öffnen.
Hier bin ich aber der Meinung das man bis zu einer gewissen Dateigröße alles locker im RAM machen kann und dann nur am Ende einmal die fertige Datei schreibt. Natürlich dann das aufräumen des RAM nicht vergessen.


----------



## _-`avaj´-_ (20. Mai 2012)

Flusssteuerung ist vlt. einfacher (vor allem sind die Pakete dann auch gleich in der richtigen Reihenfolge) aber doch auf keinen Fall schneller?!
Wenn ich für jedes gesendete Paket erstmal auf eine Antwort warten muss bis ich weitersenden kann, dann ist das doch ziemlich zeitaufwändig...? Und was passiert wenn die Bestätigung verloren geht? -Dann wartet der Sender bis zum Timeout und das heisst nochmal Zeitverlust...

Aber der Tipp mit dem RandomAccessFile ist echt seeeehr gut!!!
Danke dafür, das macht alles um einiges einfacher *freu*


----------



## HolePuncher (20. Mai 2012)

Ich hab nie gesagt das es schneller geht. Auch hast du die Flusssteuerung etwas missverstanden.
Du wartest beim Sender nicht erst auf die Antwort bevor du das nächste Paket sendest sondern sendest das Paket, merkst dir welcher Nummer dieses hatte und packst es zusammen mit einem Timestamp in eine Liste. Dann sendest du sofort das nächste. Nun hast du einen zweiten Thread der auf die Bestätigungen wartet und bei erhalt den entsprechenden Eintrag aus der Liste entfernt. Ein dritter Thread geht alle paar Intervalle durch und guckt welches Paket beim erreichen des Timeouts noch drin steht und sendet es erneut.
Das ganze kann man auch mit Timer und TimerTask bauen da man dem TimerTask genau sagen kann wann dieser loszulegen hat. So kann dieser selbstständig beim start prüfen ob seine ID noch in der Liste steht und falls ja sein Paket neu senden. Andernfalls returnt er einfach und wirft sich selbst aus der Timer-Liste.

Etwas "ähnliches" läuft auch bei TCP im Hintergrund ab, jedoch deutlich professioneller und performanter. Aber so ungefähr könnte man es z.B. selbst implementieren.
Ein Problem was mir dabei noch einfällt wäre das man das Timeout zu klein wählt, also das gilt : Timeut<Ping. Also sollte man diesen vorher "ausmessen". Einfach ein paar Pakete mit Timestamp zum gegenüber schicken, dieser sendet diese einfach zurück, Timestamp lesen und Zeit berechnen. so gute 10 bis 20 Pakete sollten einen guten Mittelwert geben.

Das ganze Thema ist halt, wie du siehst, doch ein Stück weit komplexer als du vermutlich am Anfang gedacht hast.
Spontan würde ich mal versuchen mich mit einem "der Großen" in verbindung zu setzen und zu erfragen wie "die" es gelöst haben. Denke aber nicht das man da viel Antworten bekommen wird. Denke das hier eher "3rd-party-multi-messanger" deutlich auskunftsfreudiger sein sollten. Ein Beispiel wäre QIP, ist aber leider russisch. Denke also das du da selbst mit Englisch nicht sehr weit kommen wirst. Aber vielleicht ist es ja ein Anfang.


----------



## bERt0r (20. Mai 2012)

Ich hab mir jetzt nicht alles durchgelesen, aber eine simple googlesuche nach "tcp holepunching java" liefert dies hier: UCE RMI - TCP Hole Punching Sockets


----------



## _-`avaj´-_ (20. Mai 2012)

@HolePuncher:
Ja, ich habe die Flusssteuerung falsch verstanden (mein Fehler 
Mein Ansatz ist jetzt erstmal alle Pakete auf einmal zu senden und dann zu gugen was noch fehlt und dann das Senden der fehlenden Pakete mit der Flusssteuerung zu machen...
Wenn die Flusssteuerung effizient ist kann ich auch mal vergleichen und dann ggf. gleich alles auf die Weise senden...

Mich an "die Großen" zu wenden ist mir glaube ich zu aufwändig... Mein Programm ist eh nicht von größter Wichtigkeit ich fand es nur ein interressantes Thema und dazu auch noch praktisch und da wollt ich mal schaun , ob ich das hinbekomm...

Mein Ansatz, wie gesagt, funkt aber halt noch langsam.
Flusssteuerung ist halt jetzt ein ganz anderer Ansatz aber deshalb habe ich ja das Thema verfasst: Damit halt auch andere Vorschläge kommen, die vlt. auch besser sind und ich finde das einen guten Ansatz und ich werd mich Morgen (wenn Zeit) auch gleich dransetzten 


Danke nochmal für solche Ideen!


----------

