Question

I want to create a server/client system using akka remoting. At first I create a simple remote Server. (I wrote the code below for testing purposes and to clarify my concepts so it really doesn't do much.)

I want my client to send a username and a password to the server which the server can verify and reply back. At first I create a client actor. From my client object I send this actor the username and password (I use future here). The client actor then uses another future to send this username and password to the server.

The server in my code gets the username and password and prints it out. The problem is I dont get anything back from the server. Since I used a future to send the information to the server, it should reply back with a result. This is where I think I have a conceptual problem. Going through the akka documentation did not clarify this. But I an sure I am messing up something very basic. The server code is:

EDITED after suggestions from TrustNoOne and cmbaxter.

package server


import collection.mutable
import akka.actor._
import com.typesafe.config.ConfigFactory
import shared._
import shared.CaseClass._

object Server extends App { 
val system = ActorSystem("ServerSystem",
     ConfigFactory.parseString("""
   akka {
     actor {
       provider = "akka.remote.RemoteActorRefProvider"
     }
     remote {
       netty.tcp {
         hostname = 127.0.0.1
         port = 5555
       }
     }
   }
 """))
system.actorOf(Props[ServerActorClass], "ServerActor")
}

class ServerActorClass extends Actor {
  def receive = {
    case userPass: UserPass => {
      sender ! verified()
      println(userPass)
    }
    case testMsg: String => println("Got a msg"+ testMsg)
  }
}

The client code is:

package client

import ...

object Client extends App {
  val system = ActorSystem("ClientSystem",
    ConfigFactory.parseString("""
   akka {
     actor {
       provider = "akka.remote.RemoteActorRefProvider"
     }
     remote {
       netty.tcp {
         hostname = 127.0.0.1
         port = 0
       }
     }
   }
  """))
  val clientActor = system.actorOf(Props[ClientActor], name = "ClientActor") //the local actor

  implicit val timout = Timeout(50 seconds)
  val f = ask(clientActor, UserPass("a","b"))
  f.onSuccess {
    case GO => println("Got something back from Client actor") //Still doesn't happen!
  }
}


class ClientActor extends Actor {

  // create the remote actor
  val server = context.actorFor("akka.tcp://ServerSystem@127.0.0.1:5555/user/ServerActor")

  implicit val timout = Timeout(1 seconds)

  def receive = {
    case a: String => println("back" + a)
    case a: UserPass => { 
      val f: Future[Any] = (server ? a) 
      f.onSuccess {
        case response: verified => {
          println("Got something back from server") //Does happen now!
          val asker = sender()
          asker ! GO()
        }
        case response: verificationFailed => {
           val asker = sender() 
           asker ! NO()
        }
      }
    }
  } 
}

Case classes that are shared by both the client and the server:

package shared
case object CaseClass {
  case class verified //Server msg to acknowledge user and pass successful verification
  case class verificationFailed //Server msg saying user pass verification failed
  case class GO
  case class NO
  case class UserPass(user:String, pass:String)

I want to know what I am doing wrong. If someone could explain rather than just point out the problem, it would be great, since I am looking to learn.

Was it helpful?

Solution

In the server actor, you're sending the response like this:

sender ! CaseClass.verified

You are actually sending the "verified" class companion object back to the client. You should either make the verified class a case object or send back to the client a verified instance:

sender ! CaseClass.verified()

You are doing other (unrelated) errors:

  • closing over sender in the future callback (make a local alias val replyTo = sender())
  • not respecting naming conventions (capital letters, etc)
  • using deprecated "actorFor". you have to use actorSelection (see akka docs)

OTHER TIPS

You're exiting without waiting for a response.

implicit val timout = Timeout(50 seconds)
val f: Future[Any] = clientActor ? UserPass("s","a")
f.onSuccess {
  case GO => println("Got something back from Client actor") //Doesnt happen!
}

That sets up a handler for a callback, but then your program just exists.

Minimally, you could scala.concurrent.Await.result(f)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top