1

I'm writing an old school text based telnet server that right now is a glorified chat room in Scala with Akka actor based IO. What is happening is that client the client will start to type something and then an event will happen and when it gets written, it wipes out anything that has already been typed. In the following example, Tom has started to type "say How are you?" but Fred arrives after he has only typed "say How ar" and this input is wiped out:

Tom > say How ar
Fred has arrived.
Tom > 

Is there any way to get telnet to redisplay it's output buffer it hasn't flushed yet?

Here is the server:

class TcpServer(port: Int) extends Actor {
  import TcpServer._
  import context.system

  val log = Logging(system, this)
  var connectionNum: Int = 1

  log.info(STARTING_SERVER)

  IO(Tcp) ! Bind(self, new InetSocketAddress("0.0.0.0", port))

  def receive = {
    case b @ Bound(localAddress) =>
      log.info(PORT_BOUND, localAddress)
    case c @ Connected(remote, local) =>
      log.info(CONNECTION_ACCEPTED)
      context.actorOf(ConnectionHandler.connectionProps(sender()), s"conn$connectionNum")
      connectionNum += 1
    case CommandFailed(_: Bind) =>
      log.error(BINDING_FAILED)
      context stop self
  }
}

Here is the ConnectionHandler, it's companion object, and the message case classes it uses:

class ConnectionHandler(connection: ActorRef) extends Actor {
  import context._

  val log = Logging(context.system, this)

  connection ! Register(self)

  var delegate = actorOf(Props[LoginHandler], "login")
  watch(delegate)

  def receive = {
    case Received(data) =>
      val dataString = data.utf8String.trim
      log.info("Received data from connection: {}", dataString)
      delegate ! Input(dataString)

    case Output(content) =>
      connection ! Write(ByteString(content))

    case LoggedIn(user) =>
      unwatch(delegate)
      delegate ! PoisonPill

      delegate = actorOf(UserHandler.connectionProps(user), user.name.toLowerCase)
      watch(delegate)

    case Terminated =>
      log.warning("User delegate died unexpectedly.")
      connection ! ConfirmedClose

    case CloseConnection(message) =>
      connection ! Write(ByteString(message + "\n"))
      connection ! ConfirmedClose
      log.info("User quit.")

    case ConfirmedClosed =>
      log.info("Connection closed.")
      stop(self)

    case PeerClosed =>
      log.info("Connection closed by client.")
      stop(self)
  }
}

object ConnectionHandler {
  def connectionProps(connection: ActorRef): Props = Props(new ConnectionHandler(connection))
}

case class Input(input: String)
case class Output(output: String)
case class LoggedIn(user: User)
case class CloseConnection(message: String)
kapunga
  • 436
  • 3
  • 7

1 Answers1

0

Okay, after finally phrasing my google queries correctly, I found what I needed here: Force telnet client into character mode

The basic solution is that I forced the client into character at a time mode and echo'd back the characters I care about. The bonus to this is that now I can do tab completion, command history, and make the passwords not show up.

Here is the relevant code snippet:

val controlString = ByteString('\u00ff','\u00fb','\u0001','\u00ff','\u00fb','\u0003','\u00ff','\u00fc','\u0022')

connection ! Write(controlString)
Community
  • 1
  • 1
kapunga
  • 436
  • 3
  • 7