2010-01-14 67 views
6

我對包含長時間運行操作的演員有相當多的麻煩,在我的情況下是持久套接字連接。這裏有一些測試代碼,如果我創建少於四個服務器實例,那麼運行良好,但如果我創建更多實例,則最終只有三個或有時是四個併發套接字連接,因爲其他時間會超時。 我想知道這是爲什麼,我的代碼是否有明顯的錯誤。斯卡拉演員:長時間運行io操作

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。我正在使用scala 2.7.6 final。

+0

嘿馬克斯,很久沒有見過!很酷,看到你正在給scala一個嘗試。 – 2010-01-15 17:05:49

回答

6

這裏有奇怪的事情。例如:

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

或者也:

actor {_self ! SClose} 

方法actor是一個演員的工廠。例如,在第一種情況下,您正在創建一個將創建另一個演員的演員(因爲服務器是演員),然後啓動它。

讓我重複一遍:actor {}之間的所有內容都是演員。在那個演員裏面,你在做new Server,它創造了另一個演員。這是一個receive,當然,這是一個演員的一部分。所以,在一個演員裏面,你正在創造一個演員來創建一個演員。

而在第二個示例中,您創建的演員只是爲了向自己發送消息。這對我來說毫無意義,但我對演員的經歷並不全面。

+0

好吧,我有想法打包消息,我打算髮送給演員內的演員從這裏:http://stackoverflow.com/questions/1549251/scala-actors-worst-practices(第二個答案,第二點) – maxmc 2010-01-14 18:13:16

+0

如果我刪除提到的演員{}問題依然存在。超過2個併發服務器實例無法可靠運行。 – maxmc 2010-01-14 18:18:07

+2

如果你不在演員內部,應該使用'Actor.actor'。我提到的情況都發生在演員內部。 – 2010-01-14 18:49:14