2010-01-14 6 views
6

Ich habe einige Probleme mit Schauspielern, die lange laufende Operationen enthalten, in meinem Fall dauerhafte Socket-Verbindungen. Hier ist ein Testcode, der gut läuft, wenn ich weniger als vier Server-Instanzen erstelle, aber wenn ich mehr Instanzen erstelle, habe ich immer nur drei oder manchmal vier gleichzeitige Socket-Verbindungen, weil die anderen Zeitüberschreitung haben. Ich frage mich, warum das ist und ob etwas offensichtlich falsch mit meinem Code ist.scala actors: lange läuft io operations

package test 

import actors.Actor 
import actors.Actor._ 
import java.io.{PrintStream, DataOutputStream, DataInputStream} 
import java.net.{Socket, InetAddress} 
import java.text.{SimpleDateFormat} 
import java.util.{Calendar} 

case class SInput(input: String) 
case class SOutput(output: String) 
case class SClose 
case class SRepeat 

import scala.xml._ 

class Config(xml: Node) { 
    var nick: String = (xml \ "nick").text 
    var realName: String = (xml \ "realName").text 
    var server: String = (xml \ "ip").text 
    var port: Int = (xml \ "port").text.toInt 
    var identPass: String = (xml \ "identPass").text 
    var joinChannels: List[String] = List.fromString((xml \ "join").text.trim, ' ') 
} 

object ServerStarter { 
    def main(args: Array[String]): Unit = { 
    var servers = List[Server]() 

    val a = actor { 
     loop { 
     receive { 
      case config: Config => 
      actor { 
       val server = new Server(config) 
       servers = server :: servers 
       server.start 
      } 
     } 
     } 
    } 

    val xml = XML.loadFile("config.xml") 
    (xml \ "server").elements.foreach(config => a ! new Config(config)) 
    } 
} 


class Server(config: Config) extends Actor { 
    private var auth = false 
    private val socket = new Socket(InetAddress.getByName(config.server), config.port) 
    private val out = new PrintStream(new DataOutputStream(socket.getOutputStream())) 
    private val in = new DataInputStream(socket.getInputStream()) 

    def act = { 
    val _self = this 
    _self ! SRepeat 

    while (true) { 
     receive { 
     case SRepeat => 
      try { 
      val input = in.readLine 
      if (input != null) { 
       actor {_self ! SInput(input)} 
      } else { 
       actor {_self ! SClose} 
      } 
      } catch { 
      case e: Exception => 
       println(e) 
       actor {_self ! SClose} 
      } 

     case SClose => 
      println(getDate + " closing: " + config.server + " mail: " + mailboxSize) 
      try { 
      socket.close 
      in.close 
      out.close 
      } catch { 
      case e: Exception => 
       println(e) 
      } 

     case SInput(input: String) => 
      println(getDate + " " + config.server + " IN => " + input + " mail: " + mailboxSize) 
      actor {onServerInput(_self, input)} 
      _self ! SRepeat 

     case SOutput(output: String) => 
      println(getDate + " " + config.server + " OUT => " + output + " mail: " + mailboxSize) 
      actor { 
      out.println(output) 
      out.flush() 
      } 

     case x => 
      println("unmatched: " + x + " mail: " + mailboxSize) 
     } 
    } 
    } 

    private def getDate = { 
    new SimpleDateFormat("hh:mm:ss").format(Calendar.getInstance().getTime()); 
    } 

    def onServerInput(a: Actor, input: String) = { 
    if (!auth) { 
     authenticate(a) 
    } 
    else if (input.contains("MOTD")) { 
     identify(a) 
     join(a) 
    } 
    else if (input.contains("PING")) { 
     pong(a, input) 
    } else { 
    } 
    } 

    def authenticate(a: Actor) = { 
    a ! SOutput("NICK " + config.nick) 
    a ! SOutput("USER " + config.nick + " 0 0 : " + config.realName) 
    auth = true 
    } 

    def pong(a: Actor, input: String) = { 
    a ! SOutput("PONG " + input.split(":").last) 
    } 

    def identify(a: Actor) = { 
    if (config.identPass != "") { 
     a ! SOutput("nickserv :identify " + config.nick + " " + config.identPass) 
    } 
    } 

    def join(a: Actor) = { 
    config.joinChannels.foreach(channel => a ! SOutput("JOIN " + channel)) 
    } 
} 

btw. Ich benutze scala 2.7.6 final.

+0

Hey Max, lange nicht gesehen! Cool, um zu sehen, dass Sie scala versuchen. –

Antwort

6

Es gibt seltsame Dinge hier. Zum Beispiel:

actor { 
    val server = new Server(config) 
    servers = server :: servers 
    server.start 
} 

Oder auch:

actor {_self ! SClose} 

Verfahren actor ist Schauspieler Fabrik. Im ersten Fall erstellen Sie beispielsweise einen Akteur, der einen anderen Akteur erstellt (weil Server ein Akteur ist) und startet ihn.

Lassen Sie mich das wiederholen: alles zwischen actor { und } ist ein Schauspieler. Innerhalb dieses Schauspielers machen Sie new Server, was einen anderen Akteur schafft. Und das ist innerhalb einer receive, die natürlich Teil eines Schauspielers ist. In einem Schauspieler erstellen Sie also einen Schauspieler, der einen Schauspieler schafft.

Und im zweiten Beispiel erstellen Sie einen Akteur, nur um eine Nachricht an sich selbst zu senden. Das macht für mich keinen Sinn, aber ich bin nicht so erfahren mit Schauspielern.

+0

Nun, ich habe die Idee, Nachrichten zu verpacken, die ich an Schauspieler in einem Schauspieler von hier senden möchte: http://StackOverflow.com/Questions/1549251/SCALA-ACTORS-WORST-Practices (zweite Antwort, zweiter Punkt) – maxmc

+0

wenn ich entferne den erwähnten Akteur {} das Problem bleibt bestehen. Mehr als 2 gleichzeitige Server-Instanzen funktionieren nicht zuverlässig. – maxmc

+2

Die Verwendung von 'Actor.actor' sollte erfolgen, wenn Sie _nicht_ innerhalb eines Actors sind. Die Situationen, die ich erwähne, passieren alle in Schauspielern. –