2010-01-14 7 views
6

J'ai pas mal de problèmes avec les acteurs qui contiennent des opérations de longue durée, dans mon cas, les connexions socket persistantes. Voici un code de test qui fonctionne correctement si je crée moins de quatre instances de serveur, mais si je crée plus d'instances, je finis toujours avec seulement trois ou quatre connexions socket simultanées, car les autres expirent. Je me demande pourquoi et si quelque chose ne va pas dans mon code.scala acteurs: fonctionnement long io opérations

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. J'utilise scala 2.7.6 final.

+0

Salut Max, long temps pas vu! Cool pour voir que vous essayez Scala. –

Répondre

6

Il y a des choses étranges ici. Par exemple:

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

Ou aussi:

actor {_self ! SClose} 

La méthode actor est une usine d'acteur. Dans le premier cas, par exemple, vous créez un acteur qui va créer un autre acteur (parce que Server est un acteur), et le lancez. Permettez-moi de le répéter: tout entre actor { et } est un acteur. A l'intérieur de cet acteur, vous faites new Server, ce qui crée un autre acteur. Et c'est à l'intérieur d'un receive, qui, bien sûr, fait partie d'un acteur. Donc, à l'intérieur d'un acteur, vous créez un acteur qui va créer un acteur.

Et dans le deuxième exemple, vous créez un acteur simplement pour vous envoyer un message. Cela n'a aucun sens pour moi, mais je ne suis pas si expérimenté avec les acteurs.

+0

Eh bien, j'ai eu l'idée d'envelopper les messages que j'ai l'intention d'envoyer aux acteurs au sein d'un acteur à partir d'ici: http://stackoverflow.com/questions/1549251/scala-actors-worst-practices (deuxième réponse, deuxième point) – maxmc

+0

si je supprimer l'acteur mentionné {} le problème reste. plus de 2 instances de serveur simultanées ne fonctionnent pas de manière fiable. – maxmc

+2

L'utilisation de 'Actor.actor' devrait être faite si vous êtes _not_ dans un acteur. Les situations que je mentionne tout se passent à l'intérieur des acteurs. –