如何终止因 Socket.accept() 而被阻塞太久的线程?

huangapple go评论89阅读模式
英文:

How to terminate a thread that has been blocked for too long due to Socket.accept()?

问题

public class Slave implements Runnable {
   public ServerSocket slaveSocket;

   public Slave(ServerSocket sk) {
       socket = sk;
   }

   @Override
   public void run() {
      try {
          slaveSocket.setSoTimeout(30000); // Set a timeout of 30 seconds
          Socket client = slaveSocket.accept(); // slave will wait to serve a client
          // more code...
          
          Socket clientPart2 = slaveSocket.accept();
          // more code...
      } catch (SocketTimeoutException e) {
          System.out.println("Slave thread timed out after 30 seconds.");
      } catch (IOException e) {
          // Handle other IO exceptions here
      }
   }
}

public class Server {
   public static void main(String[] args) {
       try {
           ServerSocket serverSocket = new ServerSocket(0); // a client connects to a random port
           Slave slave = new Slave(serverSocket);
           new Thread(slave).start(); // slave serves the current client, server waits for new client

           // send new slave's port to the client ...
       } catch (IOException e) {
           // Handle IO exceptions here
       }
   }
}

Please note that the code above includes exception handling and sets a timeout for the slaveSocket.accept() method. This will cause the accept() method to throw a SocketTimeoutException after 30 seconds if no client connects, allowing the slave thread to exit gracefully.

英文:
public class Slave implements Runnable {
   public ServerSocket slaveSocket;
   
   public Slave(ServerSocket sk) {socket = sk;}

   @Override
   public void run() {
      Socket client = slaveSocket.accept(); // slave will wait to serve a client
      // more code...
      
      Socket clientPart2 = slaveSocket.accept();
      // more code...
   }
}

public class Server {
   public static void main(String[] args) {
       // for example only, incomplete code
       ServerSocket serverSocket = new ServerSocket(0); // a client connect to 8088
       Slave slave = new Slave(serverSocket);
       new Thread(slave).start(); // slave serve the current client, the server wait for new client
       
       // send new slave's port to client ... 
   }
}

So I have a server that serves multiple clients at once. Whenever a client connects, the server will create a new Slave, send the IP/port of that slave to the client, then the client will work with the slave.

However, if the client receives the slave's address then do nothing (or quit) (Edit: it means the client and server are connected but the client do nothing, because for example the user goes for lunch) slaveSocket.accept() causes that slave Thread to run forever, which is wasteful.

I want the slave thread to exit after 30 second of waiting for slaveSocket.accept(). Since slaveSocket.accept() is blocking, I cannot do that from inside the void run().

What is the correct, clean way to solve this problem? Thank you.

Edit 1: a ServerSocket is passed to the slave because the client can have multiple processes that will connect to that slave. So it doesn't just perform one function.

答案1

得分: 3

如果您使用setSoTimeout设置了超时,并且没有客户端连接,ServerSocket.accept将抛出一个异常。您可以捕获此异常。

要设置30秒的超时,请使用:

serverSocket.setSoTimeout(30000);
英文:

If you set a timeout with setSoTimeout and no client connects, ServerSocket.accept will throw an exception. You can catch this exception.

To set a timeout of 30 seconds, use:

serverSocket.setSoTimeout(30000)

答案2

得分: 2

非阻塞 I/O:

看一下 AsynchronousServerSocketChannel 的 accept 方法,它返回一个 Future。然后这个 Future 有一个 带有超时的 getter,可以实现你所要求的功能。

注意:你可以阅读一个相关教程

然后这个 getter 会返回一个 AsynchronousSocketChannel,可以通过相应的 Channels.newInputStreamChannels.newOutputStream 方法将其转换回阻塞,以便在工作线程中使用阻塞方法。

阻塞 I/O:

我认为你实际上是想要实现一个按顺序接受客户端并在并行中为其提供服务的服务器,采用阻塞 I/O。如果是这种情况,你可以看一下以下示例:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Objects;

public class Main {

    public static class Worker implements Runnable {
        private final Socket sck;
        private OutputStream os;
        private InputStream is;

        public Worker(final Socket sck) {
            this.sck = Objects.requireNonNull(sck);
        }

        @Override
        public void run() {
            try {
                os = sck.getOutputStream();
                is = sck.getInputStream();

                // 所有与客户端的工作都在这里完成,除非你需要与客户端建立多个连接。
            }
            catch (final IOException iox) {
                System.err.println(iox);
            }
            finally {
                try { is.close(); } catch (final IOException | RuntimeException x) {}
                try { os.close(); } catch (final IOException | RuntimeException x) {}
                try { sck.close(); } catch (final IOException | RuntimeException x) {}
            }
        }
    }

    public static void main(final String[] args) {
        ServerSocket srv = null;
        try {
            srv = new ServerSocket(8088);
            while (true)
                new Thread(new Worker(srv.accept())).start();
        }
        catch (final IOException iox) {
            System.err.println(iox);
        }
        finally {
            try { srv.close(); } catch (final IOException | RuntimeException x) {}
        }
    }
}
英文:

Non-blocking I/O:

Take a look at AsynchronousServerSocketChannel's accept method which returns a Future. Then the Future has a getter with timeout which can do what you are asking.

Note: you may read a related tutorial.

Then the getter will return an AsynchronousSocketChannel which can be converted back to blocking via the corresponding Channels.newInputStream and Channels.newOutputStream methods to be used with the blocking approach in the worker threads.

Blocking I/O:

I think you actually meant on how to implement a server which accepts clients sequentially and serves them in parallel, with blocking I/O. If that is the case, then you may take a look at the following example:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Objects;
public class Main {
public static class Worker implements Runnable {
private final Socket sck;
private OutputStream os;
private InputStream is;
public Worker(final Socket sck) {
this.sck = Objects.requireNonNull(sck);
}
@Override
public void run() {
try {
os = sck.getOutputStream();
is = sck.getInputStream();
//ALL the work with the client goes here, unless you need more than one connections with him.
}
catch (final IOException iox) {
System.err.println(iox);
}
finally {
try { is.close(); } catch (final IOException | RuntimeException x) {}
try { os.close(); } catch (final IOException | RuntimeException x) {}
try { sck.close(); } catch (final IOException | RuntimeException x) {}
}
}
}
public static void main(final String[] args) {
ServerSocket srv = null;
try {
srv = new ServerSocket(8088);
while (true)
new Thread(new Worker(srv.accept())).start();
}
catch (final IOException iox) {
System.err.println(iox);
}
finally {
try { srv.close(); } catch (final IOException | RuntimeException x) {}
}
}
}

huangapple
  • 本文由 发表于 2020年4月3日 23:45:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/61015564.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定