2

I have a child activity that creates an asynchronous task that opens a Java ServerSocket and calls accept() on that serverPort.

Now, in this Activity's onStop() method, I have a call to cancel() my asynchronous task. In my asynchronous class, in both the onPostExecute() and onCancelled() methods, I close the serverSocket (as well as any other dataStreams or Sockets that may have been opened).

All my code runs properly. Through debugging, I know for a fact that if I:

  1. Run the application linearly up to the point where the serverSocket starts to accept() connections.
  2. Hit the back button, closing the child activity and calling its onStop() method which then cancels the asynchronous task in question and calls close() on the serverSocket.
  3. Run the child activity again, creating the new Asynchronous thread, and the code runs up to the point where I create a serverSocket on the same port as the first one. However, it throws an IO exception and says that the port is already in use.
  4. No matter how long I wait restarting this child Activity will just shoot off IO exceptions claiming that the port is already taken. However, doing things like:

    Restarting the device

    Reinstalling the Application

    Completely Closing the Application (instead of just relaunching the child activity from its parent activity)

Seem to free up the port.

How exactly does Android refresh/free up ports that you've used? Is there any way I could call a refresh on those ports programatically?

And before the obvious questions arise, I'm using P2P and java sockets, so yes, I do actually want to use direct connection to sockets and direct accept on static local ports and the such.

EDIT: formatting

EDIT2: After changing

serverSocket = new ServerSocket(portNumber);
socket = serverSocket.accept();

To:

serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress("192.168.49.1", 8000));
socket = serverSocket.accept();

I am now getting a java.net.BindException: bind failed: EADDNOTAVAIL (Cannot assign requested address) exception instead. (This only occurs after the first set of conditions. The first runthrough of the application still runs properly as it did before).

EDIT 3: If I ever find a way to properly do this, I'll post it in an edit, but for now (debugging) I'm just going to go the quick and dirty route and completely kill the app whenever the user presses the back button and restart the application to the main activity. This seems to clear up the port bind.

user1519665
  • 511
  • 5
  • 16
  • Code fragment would be helpful. What is your "asynchronous task" - AsyncTask? Are you sure you are closing the socket, what does happen to accept() after that? – msh Apr 18 '15 at 19:25

1 Answers1

2

After a few minutes, so that requests still in the network don't sent information on the same quad (source ip, source port, dest ip, dest port). This is a common feature of TCP sockets across all OSes (its in Windows, Linux, *BSD, etc). You can turn this off by specifying the SO_REUSEADDR option on the socket which will tell the OS not to keep the port reserved after closing it.

Here's a good explanation of what SO_REUSEADDR is for: What is the meaning of SO_REUSEADDR (setsockopt option) - Linux?

Community
  • 1
  • 1
Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • I've called serverSocket.setReuseAddress(false) on the serverSocket. However, the port remains in use and I still get the IOException from the port already being in use. – user1519665 Apr 18 '15 at 19:15
  • 1
    You want to call it with true. And obviously you'll have to do it on both the first and second versions of the socket. Non-reuse is the default. – Gabe Sechan Apr 18 '15 at 19:17
  • So far, I haven't gotten it to work with either serverSocket.setReuseAddress(true) or serverSocket.setReuseAddress(false). Is there a need to call it before serverSocket = new ServerSocket(portNumber), or can I call it after that? – user1519665 Apr 18 '15 at 19:20
  • 2
    You need to call it before the socket is bound. So don't use the constructor that takes a port number. Use the no-parameter constructor, call that, then call socket.bind(address, port). – Gabe Sechan Apr 18 '15 at 19:23
  • 1
    It should be noted that 'after a few minutes' apples to any ports that were *connected* via an accepte socket. The LISTENING port itself is freed immediately. If there were no connections there is no 'after a few minutes' either. – user207421 Apr 19 '15 at 00:09