The cases in the act method of the main actor are never matched in this code, so my wrapUp method is never called.
import java.net.Socket
import scala.actors._
import Actor._
import scala.collection.mutable.ArrayBuffer
object LowPortScanner {
var lastPort = 0
var openPorts = ArrayBuffer[Int]()
var longestRunTime = 00.00
var results = List[Tuple3[Int, Range, Long]]()
val host = "localhost"
val numProcs = 1 to Runtime.getRuntime().availableProcessors()
val portsPerProc = 1024 / numProcs.size
val caller = self
val procPortRanges = numProcs.foldLeft(List[Tuple2[Int, Range]]()) { (portRanges, proc) =>
val tuple2 = (proc.toInt, (lastPort + 1) to (lastPort + portsPerProc))
lastPort += portsPerProc
tuple2 :: portRanges
}
def main(args: Array[String]): Unit = {
//spawn an actor for each processor that scans a given port range
procPortRanges.foreach { proc =>
actor {
caller ! scan(proc._1, proc._2)
} //end inner actors
} //end numProcs.foreach
//catch results from the processor actors above
def act {
loop {
reactWithin(100) {
//update the list of results returned from scan
case scanResult: Tuple3[Int, Range, Long] =>
println("Processor " + scanResult._1 + " completed scan of ports " + scanResult._2.first + " through " + scanResult._2.last)
results = results ::: List(scanResult)
//check if results have been returned for each actor
case TIMEOUT =>
println("Main actor timed out")
if (results.size == numProcs.size) wrapUp
case _ =>
println("got back something weird from one of the port scan actors!")
wrapUp
}
}
}
//Attempt to open a socket on each port in the given range
//returns a Tuple3[procID: Int, ports: Range, time: Long
def scan(proc: Int, ports: Range) = {
val startTime = System.nanoTime()
ports.foreach { n =>
try {
//println("Processor " + proc + " is checking port " + n)
print(".")
val socket = new Socket(host, n)
//println("Found open port: " + n)
openPorts += n
socket.close
} catch {
case e: Exception =>
//println("While scanning port " + n + " caught Exception: " + e)
}
}
(proc, ports, startTime - System.nanoTime())
}
//output results and kill the main actor
def wrapUp {
println("These are the open ports in the range 1-1024:")
openPorts.foreach { port => println(port) }
results.foreach { result => if (result._3 > longestRunTime) { longestRunTime = result._3 } }
println("Time to scan ports 1 through 1024 is: %3.3f".format(longestRunTime / 1000))
caller ! exit
}
}
}
The code that spawns off the actors for each processor works fine, the scan method is invoked correctly for each range of ports, and I have confirmed that exactly 1024 ports are scanned.
My expectation is that the line :
caller ! scan(proc._1, proc._2)
should send a message containing (Int, Range, Long) back to the main actor. The main actor’s act method should then catch that message and execute:
case scanResult: Tuple3[Int, Range, Long] =>
println("Processor " + scanResult._1 + " completed scan of ports " + scanResult._2.first + " through " + scanResult._2.last)
results = results ::: List(scanResult)
This is not happening, however. In fact, as far as I can tell, none of my messages are coming back to the main actor. I’m not clear on what I’m missing or doing wrong.
The problem as you note is that the message being sent are not being received. This is because the actor created with
actor {caller ! scan(proc._1, proc._2)}does not have anactdefinition associated with it. Thedef act{...}method has nothing to do with the actor created since it is a method onLowPortScannerand not an actor.Maintaining the spirit of your code you can define the body to be executed by the actor within the
actor{...}and assign it to a value and send it messages.Another way to do it is to extend the Actor trait. This way the
def act{...}will handle messages received by theLowPortScanneractor. I also did some minor refactoring including usingthisinstead ofself. The Actor also had to be started withthis.start().Here are the results from a run: