5

Below is the source code of a client and a server. The client just connects (concurrently) to the server and immediatly closes the connection. When all threads are done, it waits 2 minutes and connects again. I am confused about the fact that sometimes a simple connect needs around 3 seconds! Most of the time a connect needs just around 0-32ms.

Here a typical output from the client:

...
Connect 23 [ms]: 16
Connect 22 [ms]: 32
Connect 21 [ms]: 32
Connect 15 [ms]: 32
Connect 14 [ms]: 16
Connect 13 [ms]: 16
Connect 11 [ms]: 32
Connect 25 [ms]: 3016

This only seems to happen if client and server are on different hosts. Windows and linux comparable behaviour Java 1.6.23

to start the server 2 parameters are needed: [port] [thread pool size]

to start the client 3 parameters are needed: [host] [port] [thread pool size]

for the example i used 150 thread pool size for server and 25 thread pool size for the client.

Can anybody explain this behaviour?

----- server -----

package de.test.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ServerApp {

   public static void main(String[] args) throws IOException {
      System.out.println("server running...");

      final int port = Integer.parseInt(args[0]);
      final int threads = Integer.parseInt(args[1]);
      final ExecutorService executorService = Executors
            .newFixedThreadPool(threads);
      ServerSocket serverSocket = new ServerSocket(port);
      while (true) {
         final Socket clientSocket = serverSocket.accept();

         executorService.execute(new Runnable() {
            @Override
            public void run() {
               try {
                  InputStream is = clientSocket.getInputStream();
                  int read = is.read();
                  if (read != -1) {
                     System.out.println("should not happen");
                  }
               } catch (IOException e) {
                  throw new RuntimeException(e);
               } finally {
                  close(clientSocket);
                  System.out.println("connection closed");
               }
            }

            private void close(final Socket connection) {
               try {
                  connection.close();
               } catch (IOException e1) {
                  throw new RuntimeException(e1);
               }
            };
         });
      }
   }
}

----- client -----

package de.test.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

public class ConnectApp {

   public static void main(String[] args) throws InterruptedException {
      final String host = args[0];
      final int port = Integer.parseInt(args[1]);
      final int THREAD_COUNT = Integer.parseInt(args[2]);

      final ExecutorService executorService = Executors
            .newFixedThreadPool(THREAD_COUNT);
      final AtomicLong threadCounter = new AtomicLong(0);
      while (true) {
         final CountDownLatch doneSignal = new CountDownLatch(THREAD_COUNT);
         for (int i = 0; i < THREAD_COUNT; i++) {
            executorService.execute(new Runnable() {
               @Override
               public void run() {
                  Socket socket = null;
                  try {
                     long start = System.currentTimeMillis();
                     socket = new Socket();

                     socket.setTcpNoDelay(true);
                     socket.connect(new InetSocketAddress(host, port));
                     System.out.println(socket.getTcpNoDelay());
                     long stop = System.currentTimeMillis();
                     System.out.println("Connect "
                           + threadCounter.incrementAndGet() + " [ms]: "
                           + (stop - start));
                  } catch (UnknownHostException e) {
                     throw new RuntimeException(e);
                  } catch (IOException e) {
                     throw new RuntimeException(e);
                  } finally {
                     close(socket);
                     doneSignal.countDown();
                  }

               }

               private void close(Socket socket) {
                  try {
                     if (socket != null)
                        socket.close();
                  } catch (IOException e1) {
                     throw new RuntimeException(e1);
                  }
               }
            });
         }
         doneSignal.await();
         System.out.println("Waiting 2 minutes...");
         Thread.sleep(1000 * 60 * 2);
      }
   }
}
3
  • Did you check your ping times to the client? Commented Jan 25, 2011 at 20:21
  • You have a very subtle bug in your apps. On inspection I'd doubt it's causing the behaviour you're observing, but technically ExecutorService.execute(Runnable) can, at its discretion execute on the calling thread. This would turn it into a blocking call. A more correct call would probably be executorService.submit(new Runnable().... Again, I doubt it's the cause since you are using a ThreadPoolExecutor but it's probably worth fixing anyway. Commented Jan 25, 2011 at 20:54
  • 1
    Actually on more inspection it looks like this particular implementation of ExecutorService just delegates to execute anyway so that's really probably not a problem. Commented Jan 25, 2011 at 20:59

1 Answer 1

2

All your sockets are trying to connect at once. Since they are all trying to connect to the same single threaded server, one will be accept, 50 (by default) will be in the backlog and the reset may not connect or have to wait a particularly long time.

I suggest trying a 40 ms spacing between attempting to connect to see if this is the problem.

BTW: you only call threadCounter.incrementAndGet() so why is it going up and down? You could just use

for (int i = 0; i < THREAD_COUNT; i++) {
    final int threadCount = i;
Sign up to request clarification or add additional context in comments.

6 Comments

@Peter:But the server is not single threaded.Once a connection is done it passes the socket to a thread in the pool and waits for next connection.I am not sure what you mean here
The server spends most its time accepting connections, there is only one thread doing this so your test is effictively single threaded. (There is always multiple threads running in a JVM, this doesn't make all JVM apps multi-threaded IMHO)
@Peter:Not sure I follow.There is one thread that is always waiting for new connections. Once a connection comes, it passes the socket to another thread from the tread pool and the main thread waits for new connections.Isn't this classic multithreaded server programming?
@Peter: Your suggested delay helps there is no big 3s blocking intervall anymore. But Who/what is responsible for this quite big blocking interval. Is it the server, is it the client? is this a kind of DoS protection? From OS, TCP/IP ...? A colleague rewrote this client/server in C, andthe results are the same.
It is most likely the server will not take a large number of un-accepted clients without some delay. The problem is your behaviour is unrealistic and likely to confuse any performance tuning the OS does.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.