I have a service, which starts Thread to perform some operations on socket. The code looks like:
public class ServerRunnable implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(ProtocolConstants.USB_SERVER_PORT));
while (true) {
Socket client = serverSocket.accept();
// some code
} catch (Exception e) {
Log.e("Exception while listening on socket.", e);
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
Log.e(e);
}
}
}
When I start service first time, everything is ok, but then I have to stop it using stopService method. When I start it one more time it returns following exception:
java.net.BindException: bind failed: EADDRINUSE (Address already in use)
In addition I added ServerSocket closing in onDestroy method of service but it did not help.
The setReuseAddress is performed before bind, so why it is not working?
解决方案
As far as I can guess it gets stuck on serverSocket.accept(), waiting for a connection to come in. Hence the Runnable does never finish even after closing the app. Calling the app again gives you this error (because the port is still blocked by the Runnable that was created before that). There are multiple ways of handling this:
You can call and stop your Runnable like this:
ThreadPoolExecutor threadPoolExecutor = Executors.newSingleThreadExecutor();
Runnable longRunningTask = new Runnable();
Future longRunningTaskFuture = threadPoolExecutor.submit(longRunningTask);
longRunningTaskFuture.cancel(true); //this might not trigger right away
You can stop the serverSocket.accept() by calling serversocket.close() (which will throw a SocketException that needs to be handled)
I solved this (might not be the correct way to handle this) by sending data (a string) to my serverSocket from the onClose() method in my Activity:
Socket socket = new Socket();
socket.setReuseAddress(true);
socket.connect((new InetSocketAddress(YOURSETTINGS, HERE);
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new String("TIMEOUT"));
oos.close();
os.close();
socket.close();
You can optionally catch this in your server like this:
ObjectInputStream objectInputStream = new objectInputStream(serverSocket.getInputStream());
Object object = objectInputStream.readObject();
if (object.getClass().equals(String.class) && ((String) object).equals("TIMEOUT"))
{
//Here you can do something with the Runnable before it is gone
}