Witajcie,
Pracuję nad komunikatorem, który będzie przesyłał wiadomości bez pośrednictwa serwera (połączenie klient-klient). Doczytałem już, że problemem będzie obejście NAT. I rzeczywiście. Po paru próbach, bez otwarcia portów ani rusz. Więc próbuję ogarnąć (tak na szybko - dlatego kod robiony trochę metodą Ctrl+C - Ctrl+V) o co mniej więcej chodzi z metodą hole punchingu. Niby zrozumiałem, aczkolwiek uzyskuję różne wyniki swoich prób. Mianowicie, po otrzymaniu przez klienta wiadomości od serwera z adresem IP oraz portem drugiego klienta, wysyłam do niego testowe 30 wiadomości w postaci ("Witam 0", "Witam 1", itd...). Po czym oczekuję na wiadomości. Jak się okazuje jeden z klientów nie ma problemu z ich odbieraniem, natomiast drugi z nich ma problem z odebraniem kilku pierwszych wiadomości (zazwyczaj od 3 do 10). Próbowałem dać sekundowe opóźnienie czasowe, pomiędzy wysyłaniem wiadomości, a odbieraniem pakietów z powitaniem, lecz to nie pomaga. Czy to jest całkowicie normalne? Myślałem, że nie powinien dojść tylko pierwszy pakiet (wiadomość). Od czego to zależy? Dodam, że testy wykonuję tylko na swoim komputerze, lecz łączę się wszędzie poprzez globalne IP. Dodaję przykładowe wyniki dla klientów oraz kody. Kody pisane na szybko, za co przepraszam :)

Pozdrawiam,
jpowie01

Przykładowe wyniki:

Drugi klient = XXX.XXX.XXX.XXX:59825
Witam 0
Połączenie ustabilizowane.
Drugi klient = XXX.XXX.XXX.XXX:59826
Witam 9
Połączenie ustabilizowane.

Klient:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class Klient {
	static DatagramSocket gniazdo;
	
	public static void main(String[] args) {
		// Dane serwera
		String adresIPSerwera = "XXX.XXX.XXX.XXX"; // Tu oczywiście mój adres IP
		int portSerwera = 12345; // Oraz port, który jest odblokowany na routerze
		
		// Utworzenie gniazda
		try {
			gniazdo = new DatagramSocket();
		} catch (SocketException e) {
			e.printStackTrace();
		}
		
		// Wysłanie danych o kliencie do serwera
		byte[] daneDoSerwera = new byte[4096];
		String daneDoSerweraString = "UDP";
		daneDoSerwera = daneDoSerweraString.getBytes();
		DatagramPacket pakietDoSerwera = null;
		try {
			pakietDoSerwera = new DatagramPacket(daneDoSerwera, daneDoSerwera.length, InetAddress.getByName(adresIPSerwera), portSerwera);
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
		}
		try {
			gniazdo.send(pakietDoSerwera);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// Odbieranie IP i portu drugiego klienta
		byte[] daneOdSerwera = new byte[4096];
		DatagramPacket pakietOdSerwera = new DatagramPacket(daneOdSerwera, daneOdSerwera.length);
		try {
			gniazdo.receive(pakietOdSerwera);
		} catch (IOException e) {
			e.printStackTrace();
		}
		String daneOdSerweraString = new String(pakietOdSerwera.getData());
		String daneOKliencie[] = daneOdSerweraString.split(":", 2);
		String adresIP = daneOKliencie[0].substring(1);
		for (int i = 0; i < daneOKliencie[1].length(); i++) {
			if (daneOKliencie[1].charAt(i) == 0) {
				daneOKliencie[1] = daneOKliencie[1].substring(0, i);
				break;
			}
		}
		int port = Integer.parseInt(daneOKliencie[1]);
		
		System.out.println("Klient = " + adresIP + ":" + port);
		
		// Wysyłanie pakietu do drugiego klienta
		for (int i = 0; i < 30; i++) {
			byte[] daneDoDrugiegoKlienta = new byte[4096];
			String daneDoDrugiegoKlientaString = "Witam " + i;
			daneDoDrugiegoKlienta = daneDoDrugiegoKlientaString.getBytes();
			DatagramPacket pakietDoDrugiegoKlienta = null;
			try {
				pakietDoDrugiegoKlienta = new DatagramPacket(daneDoDrugiegoKlienta, daneDoDrugiegoKlienta.length, InetAddress.getByName(adresIP), port);
			} catch (UnknownHostException e1) {
				e1.printStackTrace();
			}
			try {
				gniazdo.send(pakietDoDrugiegoKlienta);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		// Próba odebrania pakietu od drugiego klienta
		byte[] daneOdDrugiegoKlienta = new byte[4096];
		DatagramPacket pakietOdDrugiegoKlienta = new DatagramPacket(daneOdDrugiegoKlienta, daneOdDrugiegoKlienta.length);
		try {
			gniazdo.receive(pakietOdDrugiegoKlienta);
		} catch (IOException e) {
			e.printStackTrace();
		}
		String daneOdDrugiegoKlientaString = new String(pakietOdDrugiegoKlienta.getData());
		System.out.println(daneOdDrugiegoKlientaString);
		
		// Połączenie ustabilizowane
		System.out.println("Połączenie ustabilizowane.");
	}
}

Serwer:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;


public class Serwer {
	static DatagramSocket gniazdo;

	public static void main(String[] args) {
		// Utworzenie gniazda
		try {
			gniazdo = new DatagramSocket(12345); // Port, który jest odblokowany na routerze
		} catch (SocketException e) {
			e.printStackTrace();
		}
		
		// IP i Porty
		InetAddress IP1 = null;
		int port1 = 0;
		InetAddress IP2 = null;
		int port2 = 0;
		
		// Odbieranie danych od dwóch klientów
		for (int i = 0; i < 2; i++) {
			byte[] odebraneDane = new byte[4096];
			DatagramPacket pakiet = new DatagramPacket(odebraneDane, odebraneDane.length);
			try {
				gniazdo.receive(pakiet);
			} catch (IOException e) {
				e.printStackTrace();
			}
			String str = new String(pakiet.getData());
			if (i == 0) {
				IP1 = pakiet.getAddress();
				port1 = pakiet.getPort();
			} else {
				IP2 = pakiet.getAddress();
				port2 = pakiet.getPort();
			}
		}
		
		System.out.println("Klient 1 = " + IP1 + ":" + port1);
		System.out.println("Klient 2 = " + IP2 + ":" + port2);
		
		// Wysyłanie danych o klientach
		for (int i = 0; i < 2; i++) {
			byte[] daneDoKlienta = new byte[4096];
			String daneDoKlientaString = null;
			if (i == 0)
				daneDoKlientaString = IP2 + ":" + String.valueOf(port2);
			if (i == 1)
				daneDoKlientaString = IP1 + ":" + String.valueOf(port1);
			daneDoKlienta = daneDoKlientaString.getBytes();
			DatagramPacket pakietDoKlienta = null;
			if (i == 0)
				pakietDoKlienta = new DatagramPacket(daneDoKlienta, daneDoKlienta.length, IP1, port1);
			if (i == 1)
				pakietDoKlienta = new DatagramPacket(daneDoKlienta, daneDoKlienta.length, IP2, port2);
			try {
				gniazdo.send(pakietDoKlienta);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}