3

There are some nice libraries like this from Apache, but that's little bit too complex for my purpose. All I need is to get the best estimate of HTTP latency (the time it takes to get connected with the server, regardless of transfer speed).

I tried the HTTP connection code from this answer:

  private void doPing() {
    //Remember time before connection
    long millis = System.currentTimeMillis();
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) {
      //We don't need any data
      reader.close();
      //Times is the array where we store our results
      times.add((int)(System.currentTimeMillis()-millis));
      //I see lot's of these in console, so it's probably working
      System.out.println("Request done...");
    }
    //If internet is dead, does it throw exception?
    catch(Exception e) {
      times.add(-1);
    }
  }

The thing is that I am not so sure what am I measuring. Looping through the values gave me this results:

Testing connection to http://www.seznam.cz
      Threads: 5
      Loops per thread: 50
Given up waiting for results.
Average time to connection: 53.8 [ms]
Failures: 0.0%

Testing connection to http://www.seznam.cz
      Threads: 5
      Loops per thread: 100
Average time to connection: 43.58 [ms]
Failures: 0.0%

Testing connection to http://www.seznam.cz
      Threads: 5
      Loops per thread: 400
Average time to connection: 30.145 [ms]
Failures: 0.0%

Testing connection to http://www.stackoverflow.com
      Threads: 5
      Loops per thread: 30
Given up waiting for results.
Average time to connection: 4006.1111111111113 [ms]
Failures: 0.0%

Testing connection to http://www.stackoverflow.com
      Threads: 5
      Loops per thread: 80
Given up waiting for results.
Average time to connection: 2098.695652173913 [ms]
Failures: 0.0%

Testing connection to http://www.stackoverflow.com
      Threads: 5
      Loops per thread: 200
Given up waiting for results.
Average time to connection: 0.0 [ms]
//Whoops, connection dropped again
Failures: 100.0%

//Some random invalid url
Testing connection to http://www.sdsfdser.tk/
      Threads: 4
      Loops per thread: 20
Average time to connection: 0.0 [ms]
Failures: 100.0%

Not only that I am not so sure if I calculated what I wanted (though it reflects my experience), I am also not sure what happes in non standard cases.

  • Does the URL handle timeouts?
  • Will it allways throw exception on timeout?

While keeping in mind that this project is supposed to be simple and lightweight, could you tell me if I'm doing it right?

Community
  • 1
  • 1
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • why threads? it unnecessarily complicates things IMO – Diego Apr 27 '15 at 21:37
  • Because it's important to see how network handles concurrent connections. It's not relevant, however, whether I used threads or not. – Tomáš Zato Apr 27 '15 at 21:38
  • Why not sending ping commnad: http://stackoverflow.com/questions/8815012/how-to-run-ping-command-and-get-ping-host-summary – Szarpul Apr 27 '15 at 21:39
  • @Szarpul Because it's more complicated and slower to create the console instance and parse results as opposed to just calculating difference between two `long`s while calling some java code. Also I plan to run this on raspberry Pi but develop it on windows. So I'd prefer something cross platform. – Tomáš Zato Apr 27 '15 at 21:42

1 Answers1

1

I think hailin suggested you create a raw Socket and connect it to the server instead of using URLConnection. I tried both, and I'm getting much higher latency with your version. I think opening a URLConnection must be doing some additional stuff in the background, though I'm not sure what.

Anyway, here's the version using a Socket (add exception handling as needed):

Socket s = new Socket();
SocketAddress a = new InetSocketAddress("www.google.com", 80);
int timeoutMillis = 2000;
long start = System.currentTimeMillis();
try {
    s.connect(a, timeoutMillis);
} catch (SocketTimeoutException e) {
    // timeout
} catch (IOException e) {
    // some other exception
}
long stop = System.currentTimeMillis();
times.add(stop - start);
try {
    s.close();
} catch (IOException e) {
    // closing failed
}

This resolves the hostname (www.google.com in the example), establishes a TCP connection on port 80 and adds the milliseconds it took to times. If you don't want the time for the DNS resolution in there, you can create an InetAddress with InetAddress.getByName("hostname") before you start the timer and pass that to the InetSocketAddress constructor.

Edit: InetSocketAddress's constructor also resolves the host name right away, so constructing it from a resolved ip address shouldn't make a difference.

dddsnn
  • 2,231
  • 1
  • 12
  • 6
  • Thanks, this makes sense. But are you sure that even if we send nothing at all, the measured time really confirms we did connect? I allways thought that it's impossible to know whether socket disconnected unless some heartbeat is implemented... – Tomáš Zato Apr 27 '15 at 23:08
  • 1
    Yes, I just had a look in wireshark. The code as I put it does a full [TCP handshake](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Connection_establishment). That's one RTT, plus the little bookkeeping overhead. And as soon as you send the last ACK you can consider yourself connected. By the way, wireshark measured 35 ms between sending the SYN and sending the ACK, while the program output 40, so there's some overhead in there (or the timer isn't accurate enough?). – dddsnn Apr 27 '15 at 23:40
  • After some testing important problem popped out. DNS resolution can take up to 5 seconds if the internet is down (or very slow). Unlike the socket connect method this cannot have a timeout set or be `interrupt`ed. After digging in the std library sources I think this is a Java general issue. Will probably solve this by resolving the addresses first. – Tomáš Zato Apr 28 '15 at 17:07