2011-12-15 2 views
4

Я вижу проблему (Java 6 на SLES11), где UDP-пакет отправляется обратно на клиентскую машину (и tcpdump на клиентской машине показывает, что он получен), но пакет не просматривается Java.Почему Java не видит входящий пакет UDP, который показывает tcpdump?

тест работает следующим образом:

сторона клиент генерирует случайные данные, вставляет его в UDP-пакета, посылает его на сервер на определенный порт и ждет, чтобы он был эхом (с SO_TIMEOUT от 180000 мс). Когда он получает его обратно, он распечатывает, что он получил его и как долго он был отправлен.

Серверная сторона прослушивает пакет UDP на этом порту. Когда он получает его, он задерживает бит, а затем отправляет его обратно, без изменений. Задержка начинается с некоторого начального значения (3000 мс), а затем удваивается каждый раз, когда он получает пакет, пока не достигнет максимума (90000 мс), после чего задержка просто останется максимальной.

Он отлично работает до тех пор, пока задержка не достигнет 48000 мс. Находящийся на клиенте tcpdump показывает отправленный пакет и показывает ответный пакет, поступивший через 48000 мс позже. Тем не менее, Java-программа никогда не видит этот ответный пакет и после 180000 мс утверждает, что время ожидания приема. Несмотря на то, что клиентская машина получила пакет после 48000 мс, как показывает tcpdump.

Что может случиться?

Ниже приведены результаты программ и tcpdump, после чего это источник клиентских и серверных программ.

На клиента машины на выходе тестовой программы является:

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 

tcpdump выход на клиента машины (с interlined мои комментарии) является:

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) 

На сервер машина выход тестовой программы:

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. 

А вот клиент источник:

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); 
     } 
    } 

} 

И сервер источник:

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); 
     } 
    } 

} 

И 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 

И, наконец, 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

Какая ОС вы используете? –

+0

Как я уже говорил в начале вопроса, SLES11. – QuantumMechanic

+0

Это опечатка (которую я исправил). Как вы можете видеть из фактического вывода клиента и сервера в вопросе, обе программы используют порт 20000. – QuantumMechanic

ответ

0

(Извините, так долго, чтобы вернуться к этому. Я нашел ответ из некоторое время назад.)

Я чувствую себя довольно глупо. Оказывается, на входящих UDP-пакетах было создано правило брандмауэра. Если пакет «ответ» пришел «достаточно скоро» в отношении исходящего пакета «запрос», все было хорошо. Но как только задержка зашла слишком долго, брандмауэр считал, что ответ не связан с запросом, и поэтому рассматривал его как некоторый случайный входящий пакет и, таким образом, блокировал его.

0

Если возможно, возможно проверить IPv4 TTL (время жизни) пакета в захвате TCPDUMP.

После 48 секунд пакет может быть принят, но он упал с помощью IP-стека на стороне приемника из-за TTL.

Также см этот ответ об изменении TTL (который говорит, что это только для многоадресной рассылки сокетов, но был принят в любом случае): Java control IP TTL?

+5

TTL относится к числу переходов, а не к значению времени. –

+0

Обратите внимание, что [TTL] (http://en.wikipedia.org/wiki/Time_to_live) не означало «время» в течение десятилетий - это действительно кол-во _hop. Задержка между отправкой пакетов не будет влиять на TTL, установленную в пакете при его первоначальном отправке. Значение по умолчанию в '/ proc/sys/net/ipv4/ip_default_ttl' (' 64' на моей системе) действительно должно быть подходящим для большинства сетей. – sarnold

+0

Мой TTL - 64. И это не проблема, поскольку между машинами нет перелетов (в одной подсети без маршрутизаторов между ними). – QuantumMechanic

0

Это может произойти, если гнездо приемника Приемный буфер заполнен.

Немного не по теме, но это:

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

является самоочевидной излишним. Вы можете повторно использовать входящую дейтаграмму для ответа, просто изменив его данные. Он уже имеет обратный адрес и порт, по строительству.

+0

О, хорошая точка. Спасибо, что заметили это! – QuantumMechanic

Смежные вопросы