2013-02-05 15:48:29 +01:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
|
|
|
|
*/
|
|
|
|
|
package akka.io
|
|
|
|
|
|
2013-02-07 17:54:42 +01:00
|
|
|
import java.nio.ByteBuffer
|
2013-02-05 15:48:29 +01:00
|
|
|
import java.nio.channels.DatagramChannel
|
|
|
|
|
import java.nio.channels.SelectionKey._
|
2013-02-06 11:38:42 +01:00
|
|
|
import scala.annotation.tailrec
|
2013-02-07 17:54:42 +01:00
|
|
|
import scala.util.control.NonFatal
|
2013-05-06 17:01:01 +02:00
|
|
|
import akka.actor.{ Actor, ActorLogging, ActorRef }
|
2013-04-26 12:18:01 +02:00
|
|
|
import akka.dispatch.{ UnboundedMessageQueueSemantics, RequiresMessageQueue }
|
2013-05-06 17:01:01 +02:00
|
|
|
import akka.util.ByteString
|
|
|
|
|
import akka.io.SelectionHandler._
|
|
|
|
|
import akka.io.UdpConnected._
|
2013-02-05 15:48:29 +01:00
|
|
|
|
2013-02-15 11:59:01 +01:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
2013-05-06 17:01:01 +02:00
|
|
|
private[io] class UdpConnection(udpConn: UdpConnectedExt,
|
|
|
|
|
channelRegistry: ChannelRegistry,
|
|
|
|
|
commander: ActorRef,
|
|
|
|
|
connect: Connect)
|
2013-04-26 12:18:01 +02:00
|
|
|
extends Actor with ActorLogging with RequiresMessageQueue[UnboundedMessageQueueSemantics] {
|
2013-02-05 15:48:29 +01:00
|
|
|
|
2013-02-06 12:17:52 +01:00
|
|
|
import connect._
|
2013-02-07 17:54:42 +01:00
|
|
|
import udpConn._
|
2013-02-05 15:48:29 +01:00
|
|
|
import udpConn.settings._
|
|
|
|
|
|
|
|
|
|
var pendingSend: (Send, ActorRef) = null
|
|
|
|
|
def writePending = pendingSend ne null
|
|
|
|
|
|
|
|
|
|
context.watch(handler) // sign death pact
|
|
|
|
|
val channel = {
|
|
|
|
|
val datagramChannel = DatagramChannel.open
|
|
|
|
|
datagramChannel.configureBlocking(false)
|
|
|
|
|
val socket = datagramChannel.socket
|
|
|
|
|
options.foreach(_.beforeDatagramBind(socket))
|
2013-02-06 12:17:52 +01:00
|
|
|
try {
|
2013-02-10 13:52:52 +01:00
|
|
|
localAddress foreach socket.bind
|
2013-02-06 12:17:52 +01:00
|
|
|
datagramChannel.connect(remoteAddress)
|
|
|
|
|
} catch {
|
|
|
|
|
case NonFatal(e) ⇒
|
2013-05-31 09:52:51 +02:00
|
|
|
log.debug("Failure while connecting UDP channel to remote address [{}] local address [{}]: {}",
|
|
|
|
|
remoteAddress, localAddress.getOrElse("undefined"), e)
|
2013-02-06 12:17:52 +01:00
|
|
|
commander ! CommandFailed(connect)
|
|
|
|
|
context.stop(self)
|
|
|
|
|
}
|
2013-02-05 15:48:29 +01:00
|
|
|
datagramChannel
|
|
|
|
|
}
|
2013-05-06 17:01:01 +02:00
|
|
|
channelRegistry.register(channel, OP_READ)
|
2013-02-10 13:52:52 +01:00
|
|
|
log.debug("Successfully connected to [{}]", remoteAddress)
|
2013-02-05 15:48:29 +01:00
|
|
|
|
|
|
|
|
def receive = {
|
2013-05-06 17:01:01 +02:00
|
|
|
case registration: ChannelRegistration ⇒
|
2013-02-06 12:17:52 +01:00
|
|
|
commander ! Connected
|
2013-05-06 17:01:01 +02:00
|
|
|
context.become(connected(registration), discardOld = true)
|
2013-02-05 15:48:29 +01:00
|
|
|
}
|
|
|
|
|
|
2013-05-06 17:01:01 +02:00
|
|
|
def connected(registration: ChannelRegistration): Receive = {
|
2013-05-26 18:29:23 +02:00
|
|
|
case SuspendReading ⇒ registration.disableInterest(OP_READ)
|
2013-05-06 17:01:01 +02:00
|
|
|
case ResumeReading ⇒ registration.enableInterest(OP_READ)
|
|
|
|
|
case ChannelReadable ⇒ doRead(registration, handler)
|
2013-02-05 15:48:29 +01:00
|
|
|
|
2013-05-26 18:29:23 +02:00
|
|
|
case Disconnect ⇒
|
2013-02-10 13:52:52 +01:00
|
|
|
log.debug("Closing UDP connection to [{}]", remoteAddress)
|
2013-02-05 15:48:29 +01:00
|
|
|
channel.close()
|
|
|
|
|
sender ! Disconnected
|
2013-02-10 13:52:52 +01:00
|
|
|
log.debug("Connection closed to [{}], stopping listener", remoteAddress)
|
2013-02-05 15:48:29 +01:00
|
|
|
context.stop(self)
|
|
|
|
|
|
|
|
|
|
case send: Send if writePending ⇒
|
|
|
|
|
if (TraceLogging) log.debug("Dropping write because queue is full")
|
|
|
|
|
sender ! CommandFailed(send)
|
|
|
|
|
|
|
|
|
|
case send: Send if send.payload.isEmpty ⇒
|
|
|
|
|
if (send.wantsAck)
|
|
|
|
|
sender ! send.ack
|
|
|
|
|
|
|
|
|
|
case send: Send ⇒
|
|
|
|
|
pendingSend = (send, sender)
|
2013-05-06 17:01:01 +02:00
|
|
|
registration.enableInterest(OP_WRITE)
|
2013-02-05 15:48:29 +01:00
|
|
|
|
|
|
|
|
case ChannelWritable ⇒ doWrite()
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 17:01:01 +02:00
|
|
|
def doRead(registration: ChannelRegistration, handler: ActorRef): Unit = {
|
2013-02-06 11:38:42 +01:00
|
|
|
@tailrec def innerRead(readsLeft: Int, buffer: ByteBuffer): Unit = {
|
2013-02-05 15:48:29 +01:00
|
|
|
buffer.clear()
|
|
|
|
|
buffer.limit(DirectBufferSize)
|
|
|
|
|
|
2013-02-06 11:38:42 +01:00
|
|
|
if (channel.read(buffer) > 0) {
|
2013-02-07 17:54:42 +01:00
|
|
|
buffer.flip()
|
2013-02-06 11:38:42 +01:00
|
|
|
handler ! Received(ByteString(buffer))
|
|
|
|
|
innerRead(readsLeft - 1, buffer)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
val buffer = bufferPool.acquire()
|
|
|
|
|
try innerRead(BatchReceiveLimit, buffer) finally {
|
2013-05-06 17:01:01 +02:00
|
|
|
registration.enableInterest(OP_READ)
|
2013-02-05 15:48:29 +01:00
|
|
|
bufferPool.release(buffer)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final def doWrite(): Unit = {
|
|
|
|
|
val buffer = udpConn.bufferPool.acquire()
|
|
|
|
|
try {
|
|
|
|
|
val (send, commander) = pendingSend
|
|
|
|
|
buffer.clear()
|
|
|
|
|
send.payload.copyToBuffer(buffer)
|
|
|
|
|
buffer.flip()
|
|
|
|
|
val writtenBytes = channel.write(buffer)
|
2013-02-10 13:52:52 +01:00
|
|
|
if (TraceLogging) log.debug("Wrote [{}] bytes to channel", writtenBytes)
|
2013-02-05 15:48:29 +01:00
|
|
|
|
|
|
|
|
// Datagram channel either sends the whole message, or nothing
|
|
|
|
|
if (writtenBytes == 0) commander ! CommandFailed(send)
|
|
|
|
|
else if (send.wantsAck) commander ! send.ack
|
|
|
|
|
} finally {
|
|
|
|
|
udpConn.bufferPool.release(buffer)
|
|
|
|
|
pendingSend = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 17:01:01 +02:00
|
|
|
override def postStop(): Unit =
|
2013-02-05 15:48:29 +01:00
|
|
|
if (channel.isOpen) {
|
|
|
|
|
log.debug("Closing DatagramChannel after being stopped")
|
|
|
|
|
try channel.close()
|
|
|
|
|
catch {
|
2013-05-31 09:52:51 +02:00
|
|
|
case NonFatal(e) ⇒ log.debug("Error closing DatagramChannel: {}", e)
|
2013-02-05 15:48:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|