Search code examples
scalasocketsserversocketblocking

How can I set a timeout for a blocking call?


I have a multi-threaded chat server working with multiple clients (each client is being handled in a new thread).

If a client disconnects, the relevant thread on the server hangs on inputstream.readLine() which is a blocking call.

How can I set some kind of timeout so that I can close the socket and release the thread after 2000ms without a response?

class Handler(socket: Socket) extends Runnable {
  def run() : Unit = {
    val inputstream = new BufferedReader(new InputStreamReader(socket.getInputStream()))
    val outputstream = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))
    var break = false
    while(break != true){
      val input = inputstream.readLine() // blocking call
// how do i time out after 2000ms?
      input match {
        case "update" =>
          outputstream.write("message")
          outputstream.newLine()
          outputstream.flush()
        case _ => // any other input, break and close socket
          break = true
      }
    }
    socket.close()
  }
}

object ChatServer extends App {
  val server = new ServerSocket(10000)
  while(true) {
    val socket = server.accept // blocking call
    (new Thread(new Handler(socket))).start()
    // some condition{
    //   server.close()
    // }
  }
}

Solution

  • You can use BufferedReader.ready():

    True if the next read() is guaranteed not to block for input, false otherwise. Note that returning false does not guarantee that the next read will block.

    class Handler(socket: Socket) extends Runnable {
      def run(): Unit = {
        val inputstream = new BufferedReader(new InputStreamReader(socket.getInputStream))
        val outputstream = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream))
    
        var break = false
        val startTime = System.currentTimeMillis()
    
        while (!break) {
          if (inputstream.ready()) {
            val input = inputstream.readLine()
            input match {
              case "update" =>
                outputstream.write("message")
                outputstream.newLine()
                outputstream.flush()
              case _ => // any other input, break and close socket
                break = true
            }
          } else if (System.currentTimeMillis() - startTime >= 2000) {
            break = true
          }
        }
        socket.close()
      }
    }