2010-01-14 5 views
6

필자는 영구적 인 소켓 연결에서 장기 실행 작업을 포함하는 액터에 상당한 문제가 있습니다. 다음은 4 개 미만의 서버 인스턴스를 만들면 올바르게 실행되는 테스트 코드입니다. 그러나 인스턴스를 더 만들면 다른 소켓이 시간 초과되기 때문에 항상 3 개 또는 4 개의 동시 소켓 연결로 끝납니다. 그 이유는 무엇이며 내 코드에 분명히 잘못된 것이 있는지 궁금합니다.스칼라 액터 : long running 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. 나는 2.7.6 결승 스칼라를 사용하고있다.

+0

안녕하세요 최대, 오랜 시간을 보지 못했습니다! 당신이 scala에게 시험을하고 있다는 것을 알기 위해 차가워 요. –

답변

6

여기에 이상한 점이 있습니다.

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

또는도 : 예를 들어

actor {_self ! SClose} 

방법 actor 배우 공장입니다. 예를 들어 첫 번째 경우에는 다른 액터 (Server가 액터이기 때문에)를 생성하고 액터를 시작하는 액터를 만듭니다.

다시 말해 보겠습니다. actor {} 사이의 모든 것은 액터입니다. 그 배우 안에는 new Server이 있는데, 다른 배우를 만듭니다. 그리고 그것은 receive 안에 있으며, 물론 배우의 일부입니다. 따라서 액터 안에서는 액터를 만들어 액터를 만듭니다.

두 번째 예제에서는 액터를 만들어 자신에게 메시지를 보냅니다. 그건 나에게 의미가 없지만 배우와 경험이 많은 것은 아닙니다.

+0

글쎄, 나는 여기에서 배우 내의 배우에게 보낼 메시지를 포장하는 아이디어를 가지고 : http://stackoverflow.com/questions/1549251/scala-actors-worst-practices (두 번째 대답, 두 번째 지점) – maxmc

+0

만약 내가 언급 된 배우 제거 {} 문제가 남아 있습니다. 두 개 이상의 동시 서버 인스턴스가 안정적으로 작동하지 않습니다. – maxmc

+2

액터 안에 _not_ 없다면'Actor.actor'를 사용해야합니다. 내가 언급하는 상황은 배우들 내부에서 일어난다. –

관련 문제