2011-12-15 1 views
4

Je vois un problème (Java 6 sur SLES11) où un paquet UDP est renvoyé à l'ordinateur client (et tcpdump sur l'ordinateur client montre qu'il est reçu), mais le paquet n'est pas vu par Java.Pourquoi Java ne voit-il pas un paquet UDP entrant affiché par tcpdump?

Le test fonctionne comme ceci:

Le côté client génère des données aléatoires, il colle dans un paquet UDP, il envoie au serveur sur un port particulier et attend d'être renvoyée en écho (avec un SO_TIMEOUT de 180000 ms). Quand il le reçoit il imprime qu'il l'a reçu et depuis combien de temps il a été envoyé.

Le côté serveur écoute un paquet UDP sur ce port. Quand il le reçoit, il retarde un peu puis l'envoie tout de suite, inchangé. Le délai commence à une valeur initiale (3000 ms) puis double chaque fois qu'il reçoit un paquet jusqu'à ce qu'il atteigne un maximum (90000 ms) après quoi le délai reste juste au maximum.

Cela fonctionne bien jusqu'à ce que le retard frappe 48000 ms. Le tcpdump exécuté sur le client montre le paquet envoyé et montre le paquet de réponse arrivant 48000 ms plus tard. Cependant, le programme Java ne voit jamais ce paquet de réponse et après 180000 ms, le délai de réception a expiré. Même si la machine client a reçu le paquet après seulement 48000 ms comme le montre tcpdump.

Que pourrait-il se passer? Ci-dessous est la sortie des programmes et tcpdump et après cela est la source des programmes client et serveur.

Sur le client machine à la sortie du programme de test est la suivante:

rich-ova3:~/UDPTest2 # java -cp . Client 
Properties: 
-- listing properties -- 
ServerPort=20000 
Timeout=180000 
Server=rich-ova5.teak.eng 
Loops=8 
DatagramLength=333 

UDP socket timeout is 180000 ms 
Sending packet 1 of 8 
Received packet 1 of 8 
Round-trip time: 3001 ms 

Sending packet 2 of 8 
Received packet 2 of 8 
Round-trip time: 6001 ms 

Sending packet 3 of 8 
Received packet 3 of 8 
Round-trip time: 12001 ms 

Sending packet 4 of 8 
Received packet 4 of 8 
Round-trip time: 24000 ms 

Sending packet 5 of 8 
Error receiving: Receive timed out 
java.net.SocketTimeoutException: Receive timed out 
     at java.net.PlainDatagramSocketImpl.receive0(Native Method) 
     at java.net.PlainDatagramSocketImpl.receive(Unknown Source) 
     at java.net.DatagramSocket.receive(Unknown Source) 
     at Client.go(Client.java:80) 
     at Client.main(Client.java:39) 
Round-trip time: 180096 ms 

Sending packet 6 of 8 
Error receiving: Receive timed out 
java.net.SocketTimeoutException: Receive timed out 
     at java.net.PlainDatagramSocketImpl.receive0(Native Method) 
     at java.net.PlainDatagramSocketImpl.receive(Unknown Source) 
     at java.net.DatagramSocket.receive(Unknown Source) 
     at Client.go(Client.java:80) 
     at Client.main(Client.java:39) 
Round-trip time: 180087 ms 

Sending packet 7 of 8 
Error receiving: Receive timed out 
java.net.SocketTimeoutException: Receive timed out 
     at java.net.PlainDatagramSocketImpl.receive0(Native Method) 
     at java.net.PlainDatagramSocketImpl.receive(Unknown Source) 
     at java.net.DatagramSocket.receive(Unknown Source) 
     at Client.go(Client.java:80) 
     at Client.main(Client.java:39) 
Round-trip time: 180093 ms 

Sending packet 8 of 8 
Error receiving: Receive timed out 
java.net.SocketTimeoutException: Receive timed out 
     at java.net.PlainDatagramSocketImpl.receive0(Native Method) 
     at java.net.PlainDatagramSocketImpl.receive(Unknown Source) 
     at java.net.DatagramSocket.receive(Unknown Source) 
     at Client.go(Client.java:80) 
     at Client.main(Client.java:39) 
Round-trip time: 180078 ms 

Send successes: 8 
Send errors: 0 

Receive successes: 4 
Receive errors: 4 

La sortie tcpdump sur la machine cliente (avec mes commentaires interchange) est:

16:03:42.438252 IP 172.16.20.113.16362 > 172.16.20.115.20000: UDP, length 333 
16:03:45.439322 IP 172.16.20.115.20000 > 172.16.20.113.16362: UDP, length 333 
(a 3011 ms delay) 

16:03:45.440315 IP 172.16.20.113.25559 > 172.16.20.115.20000: UDP, length 333 
16:03:51.441394 IP 172.16.20.115.20000 > 172.16.20.113.25559: UDP, length 333 
(a 6001 ms delay) 

16:03:51.441938 IP 172.16.20.113.30457 > 172.16.20.115.20000: UDP, length 333 
16:04:03.442564 IP 172.16.20.115.20000 > 172.16.20.113.30457: UDP, length 333 
(a 12000 ms delay) 

16:04:03.443095 IP 172.16.20.113.46143 > 172.16.20.115.20000: UDP, length 333 
16:04:27.443572 IP 172.16.20.115.20000 > 172.16.20.113.46143: UDP, length 333 
(a 24001ms delay) 

16:04:27.444109 IP 172.16.20.113.31747 > 172.16.20.115.20000: UDP, length 333 
16:05:15.444688 IP 172.16.20.115.20000 > 172.16.20.113.31747: UDP, length 333 
(a 48001 ms delay) 

16:07:27.540689 IP 172.16.20.113.38357 > 172.16.20.115.20000: UDP, length 333 
16:08:57.541312 IP 172.16.20.115.20000 > 172.16.20.113.38357: UDP, length 333 
(a 90000 ms delay) 

16:10:27.627411 IP 172.16.20.113.33915 > 172.16.20.115.20000: UDP, length 333 
16:11:57.631436 IP 172.16.20.115.20000 > 172.16.20.113.33915: UDP, length 333 
(a 90004 ms delay) 

16:13:27.720668 IP 172.16.20.113.36494 > 172.16.20.115.20000: UDP, length 333 
16:14:57.722353 IP 172.16.20.115.20000 > 172.16.20.113.36494: UDP, length 333 
(a 90001 ms delay) 

Sur le serveur machine la sortie du programme de test est:

rich-ova5:~/UDPTest2 # java -cp . Server 
Properties: 
-- listing properties -- 
MinSleepTime=1000 
DatagramLength=333 
MaxSleepTime=90000 
Port=20000 

Received packet. 
Delaying for 3000 ms 
Sending packet. 

Received packet. 
Delaying for 6000 ms 
Sending packet. 

Received packet. 
Delaying for 12000 ms 
Sending packet. 

Received packet. 
Delaying for 24000 ms 
Sending packet. 

Received packet. 
Delaying for 48000 ms 
Sending packet. 

Received packet. 
Delaying for 90000 ms 
Sending packet. 

Received packet. 
Delaying for 90000 ms 
Sending packet. 

Received packet. 
Delaying for 90000 ms 
Sending packet. 

Et voici la source client:

import java.io.FileInputStream; 
import java.io.IOException; 
import java.net.DatagramPacket; 
import java.net.DatagramSocket; 
import java.net.InetAddress; 
import java.net.UnknownHostException; 
import java.util.Properties; 
import java.util.Random; 

public class Client { 
    private static final String CONFIG_FILE = "client.conf"; 

    private int datagramLength; 
    private boolean debug; 
    private InetAddress server; 
    private int serverPort; 
    private int timeout; 
    private int loops; 

    public static void main(String[] arg) throws Exception { 
     Client c = new Client(); 
     c.go(); 
    } 

    private void go() throws Exception { 
     readClientConfig(); 
     Random rand = new Random(System.currentTimeMillis()); 

     System.out.println("UDP socket timeout is " + timeout + " ms"); 

     byte[] referenceData = new byte[datagramLength]; 
     byte[] sentData = new byte[referenceData.length]; 

     int receiveErrors = 0; 
     int receiveSuccess = 0; 
     int sendErrors = 0; 
     int sendSuccess = 0; 

     for (int i = 0; i < loops; i++) { 
      long sendTime = 0; 
      long receiveTime = 0; 

      rand.nextBytes(referenceData); 
      System.arraycopy(referenceData, 0, sentData, 0, referenceData.length); 

      DatagramSocket serverSock = new DatagramSocket(); 
      serverSock.setSoTimeout(timeout); 

      DatagramPacket datagram = new DatagramPacket(sentData, sentData.length, server, serverPort); 
      try { 
       System.out.println("Sending packet " + (i + 1) + " of " + loops); 
       serverSock.send(datagram); 
       sendSuccess++; 
      } catch (Exception e) { 
       System.out.println("Error sending: " + e.getMessage()); 
       e.printStackTrace(); 
       sendErrors++; 
       continue; 
      } 
      sendTime = System.currentTimeMillis(); 

      try { 
       serverSock.receive(datagram); 
       System.out.println("Received packet " + (i + 1) + " of " + loops); 
       receiveSuccess++; 
      } catch (Exception e) { 
       receiveErrors++; 
       System.out.println("Error receiving: " + e.getMessage()); 
       e.printStackTrace(); 
      } 
      serverSock.close(); 

      receiveTime = System.currentTimeMillis(); 
      System.out.println("Round-trip time: " + (receiveTime - sendTime) + " ms"); 

      byte[] receivedData = datagram.getData(); 
      if (receivedData.length != referenceData.length) { 
       System.out.println("Mismatched packet length."); 
       sendErrors++; 
      } 

      for (int j = 0; j < datagramLength; j++) { 
       if (referenceData[j] != receivedData[j]) { 
        System.out.println("Mismatched packet contents."); 
        sendErrors++; 
       } 
      } 

      System.out.println(); 
     } 

     System.out.println("Send successes: " + sendSuccess); 
     System.out.println("Send errors: " + sendErrors); 
     System.out.println(); 
     System.out.println("Receive successes: " + receiveSuccess); 
     System.out.println("Receive errors: " + receiveErrors); 
    } 

    private void readClientConfig() throws IOException { 
     boolean hasError = false; 
     String val; 
     Properties prop = new Properties(); 

     FileInputStream fis = new FileInputStream(CONFIG_FILE); 
     prop.load(fis); 

     val = prop.getProperty("Server"); 
     if (val == null) { 
      System.out.println("Error reading 'Server': missing value"); 
      hasError = true; 
     } else { 
      try { 
       server = InetAddress.getByName(val); 
      } catch (UnknownHostException e) { 
       System.out.println("Error reading 'Server': " + e.getMessage()); 
       hasError = true; 
      } 
     } 

     val = prop.getProperty("ServerPort", "3000"); 
     try { 
      serverPort = Integer.parseInt(val); 
     } catch (NumberFormatException e) { 
      System.out.println("Error reading 'ServerPort': " + e.getMessage()); 
      hasError = true; 
     } 

     val = prop.getProperty("DatagramLength", "200"); 
     try { 
      datagramLength = Integer.parseInt(val); 
     } catch (NumberFormatException e) { 
      System.out.println("Error reading 'DatagramLength': " + e.getMessage()); 
      hasError = true; 
     } 

     // Time out in milliseconds. 
     val = prop.getProperty("Timeout", "3000"); 
     try { 
      timeout = Integer.parseInt(val); 
     } catch (NumberFormatException e) { 
      System.out.println("Error reading 'Timeout' value."); 
      hasError = true; 
     } 

     // Number of loops to perform. 
     val = prop.getProperty("Loops", "1"); 
     try { 
      loops = Integer.parseInt(val); 
     } catch (NumberFormatException nfe) { 
      System.out.println("Error reading 'Loops' value."); 
      hasError = true; 
     } 
     val = prop.getProperty("EnableDebug", "yes"); 
     debug = val.equalsIgnoreCase("yes"); 

     System.out.println("Properties:"); 
     prop.list(System.out); 
     System.out.println(); 

     if (hasError) { 
      System.out.println("There are errors - fix them and restart the server."); 
      System.exit(1); 
     } 
    } 

} 

Et la source du serveur :

import java.io.FileInputStream; 
import java.io.IOException; 
import java.net.DatagramPacket; 
import java.net.DatagramSocket; 
import java.util.Properties; 

public class Server { 
    private static final String CONFIG_FILE = "server.conf"; 

    private int datagramLength; 
    private boolean debug; 
    private int port; 
    private long initialDelay; 
    private long maxDelay; 

    public static void main(String[] arg) throws Exception { 
     Server s = new Server(); 
     s.go(); 
    } 

    private void go() throws Exception { 
     readServerConfig(); 

     DatagramSocket sock = new DatagramSocket(port); 

     sock.setSoTimeout(0); 

     byte[] in = new byte[datagramLength]; 
     DatagramPacket datagram = new DatagramPacket(in, datagramLength); 

     // Loop with exponentially-increasing delays between receive 
     // and reply (up to the specified maximum delay). 
     long delay = initialDelay; 
     while (true) { 
      sock.receive(datagram); 
      System.out.println("Received packet."); 

      System.out.println("Delaying for " + delay + " ms"); 
      Thread.sleep(delay); 

      System.out.println("Sending packet."); 

      datagram.setSocketAddress(datagram.getSocketAddress()); 
      datagram.setPort(datagram.getPort()); 
      sock.send(datagram); 

      System.out.println(); 

      delay *= 2; 
      if (delay > maxDelay) { 
       delay = maxDelay; 
      } 
     } 
    } 

    private void readServerConfig() throws IOException { 
     boolean hasError = false; 
     String val; 
     Properties prop = new Properties(); 

     FileInputStream fis = new FileInputStream(CONFIG_FILE); 
     prop.load(fis); 

     val = prop.getProperty("Port", "3000"); 
     try { 
      port = Integer.parseInt(val); 
     } catch (NumberFormatException e) { 
      System.out.println("Error reading 'Port' value: " + e.getMessage()); 
      hasError = true; 
     } 

     val = prop.getProperty("DatagramLength", "200"); 
     try { 
      datagramLength = Integer.parseInt(val); 
     } catch (NumberFormatException e) { 
      System.out.println("Error reading 'DatagramLength' value: " + e.getMessage()); 
      hasError = true; 
     } 

     val = prop.getProperty("StartSleepTime", "3000"); 
     try { 
      initialDelay = Long.parseLong(val); 
     } catch (NumberFormatException e) { 
      System.out.println("Error reading 'StartSleepTime' value: " + e.getMessage()); 
      hasError = true; 
     } 

     val = prop.getProperty("MaxSleepTime", "60000"); 
     try { 
      maxDelay = Long.parseLong(val); 
     } catch (NumberFormatException e) { 
      System.out.println("Error reading 'MaxSleepTime' value: " + e.getMessage()); 
      hasError = true; 
     } 

     val = prop.getProperty("EnableDebug", "yes"); 
     debug = val.equalsIgnoreCase("yes"); 

     System.out.println("Properties:"); 
     prop.list(System.out); 
     System.out.println(); 

     if (hasError) { 
      System.out.println("There are errors - fix them and restart the server."); 
      System.exit(1); 
     } 
    } 

} 

Et client.conf:

# Server address 
Server = rich-ova5.teak.eng 

# Server port number. 
ServerPort = 20000 

# This is the length of the data to send. 
# It must match the server's size. 
DatagramLength = 333 

# Number of times to run the client. 
Loops = 8 

# Client time out period in milliseconds. 
Timeout = 180000 

Et enfin, server.conf:

# Server port number. 
Port=20000 

# Length of the datagram. 
# It must match the client's size. 
DatagramLength = 333 

# Initial sleep time. 
MinSleepTime = 1000 

# Maximu sleep time. Goes from the MinSleepTime to the 
# maximum sleep time. 
MaxSleepTime = 90000 
+0

Quel système d'exploitation utilisez-vous? –

+0

Comme je l'ai dit au début de la question, SLES11. – QuantumMechanic

+0

C'est une faute de frappe (que j'ai corrigé). Comme vous pouvez le voir à partir de la sortie du client et du serveur dans la question, les deux programmes utilisent le port 20000. – QuantumMechanic

Répondre

0

(Désolé de prendre si longtemps pour revenir. J'ai trouvé la réponse à un certain temps il y a.)

je me sens plutôt stupide. Il s'avère qu'il y avait une règle de pare-feu en place sur les paquets UDP entrants. Si le paquet "reply" arrivait "assez tôt" par rapport au paquet "request" sortant, tout allait bien. Mais une fois que le délai est devenu trop long, le pare-feu a considéré que la réponse n'était pas liée à la demande et l'a donc considérée comme un paquet entrant aléatoire et l'a donc bloqué.

0

Si possible, peut-être vérifier le TTL IPv4 (Time to Live) du paquet dans la capture de tcpdump.

Après les 48 secondes, le paquet peut être reçu mais rejeté par la pile IP du côté récepteur en raison de la durée de vie.

Voir aussi cette réponse sur la modification du TTL (qui dit qu'il est seulement pour les prises Multicast, mais a été acceptée de toute façon): Java control IP TTL?

+5

TTL se réfère au nombre de sauts, pas une valeur de temps. –

+0

Notez que [TTL] (http://en.wikipedia.org/wiki/Time_to_live) n'a pas signifié «temps» depuis des décennies - c'est vraiment un _hop count_. Le délai entre l'envoi de paquets n'influencera pas l'ensemble TTL sur le paquet lorsqu'il est initialement envoyé. La valeur par défaut dans '/ proc/sys/net/ipv4/ip_default_ttl' (' 64' sur mon système) devrait vraiment convenir à la plupart des réseaux. – sarnold

+0

Mon TTL est 64. Et ce n'est pas un problème car il n'y a pas de sauts entre les machines (sur le même sous-réseau sans routeurs entre eux). – QuantumMechanic

0

qui pourrait se produire si la prise du récepteur tampon de réception est pleine.

Un peu hors sujet, mais ceci:

datagram.setSocketAddress(datagram.getSocketAddress()); 
datagram.setPort(datagram.getPort()); 

est auto-évidemment redondante. Vous pouvez réutiliser le datagramme entrant pour la réponse en changeant simplement ses données. Il a déjà l'adresse de retour et le port, par construction.

+0

Oh, bon point. Merci d'avoir noté ça! – QuantumMechanic

Questions connexes