[英]Scala - TCP Packet frame using Akka
Akka是否有任何方法可以像使用{packet,4}的Erlang一样实现包帧? 数据包看起来像这样:
4 bytes length in big endian | body...
例如:
00 00 00 05 H E L L O 0 0 0 5 W O R L D
将是两个数据包“ HELLO”和“ WORLD”,但它们被作为一个接收。
要么
00 00 00 05 H E L L
现在,Akka接收了这8个字节,但是仍然缺少一个字节,它将在下一次调用“ receive”的操作中被接收
问题是我的演员的接收总是以部分或全部请求来调用,但是我只想在接收中获得“主体”部分,并且只有在完全接收时才能获得。
因此,所需要做的就是先读取这4个字节,然后等待直到读取其他N个字节(N = 4字节标题中的长度),然后将消息发送给我的演员。 有可能吗?
我的服务器代码:
import java.net.InetSocketAddress
import akka.actor.{Props, Actor}
import akka.io.Tcp.Bind
import akka.io.{IO, Tcp}
class Server extends Actor{
import context.system
import Tcp._
IO(Tcp) ! Bind(self, new InetSocketAddress("0.0.0.0", 1234))
def receive ={
case bound @ Bound(localAddr) =>
println("Server is bound to "+localAddr.toString())
case failed @ CommandFailed(_ : Bind) =>
context stop self
case connected @ Connected(remote, local) =>
val handler = context.actorOf(Props[ClientHandler])
val connection = sender()
println(remote.toString + "connected to "+local.toString())
connection ! Register(handler)
}
}
据我所知,Akka或Scala中没有库函数。 Akka使用ByteString进行读写操作,因此我组合了一个特征,它可以完全满足您的要求。 您将其传递给发送给演员的ByteString。 然后根据报头中的数据包长度将流分解。 它是无状态的,因此它返回一个元组,其中包含已提取数据包的列表以及来自TCP流的所有未使用数据作为ByteString。 您将新的TCP数据连接到此字节字符串中返回的流的未使用部分,如下例所示。
trait Buffering {
val MAX_PACKET_LEN: Short = 10000
/**
* Extracts complete packets of the specified length, preserving remainder
* data. If there is no complete packet, then we return an empty list. If
* there are multiple packets available, all packets are extracted, Any remaining data
* is returned to the caller for later submission
* @param data A list of the packets extracted from the raw data in order of receipt
* @return A list of ByteStrings containing extracted packets as well as any remaining buffer data not consumed
*/
def getPacket(data: ByteString): (List[ByteString], ByteString) = {
val headerSize = 2
@tailrec
def multiPacket(packets: List[ByteString], current: ByteString): (List[ByteString], ByteString) = {
if (current.length < headerSize) {
(packets.reverse, current)
} else {
val len = current.iterator.getShort
if (len > MAX_PACKET_LEN || len < 0) throw new RuntimeException(s"Invalid packet length: $len")
if (current.length < len + headerSize) {
(packets.reverse, current)
} else {
val rem = current drop headerSize // Pop off header
val (front, back) = rem.splitAt(len) // Front contains a completed packet, back contains the remaining data
// Pull of the packet and recurse to see if there is another packet available
multiPacket(front :: packets, back)
}
}
}
multiPacket(List[ByteString](), data)
}
演员的用法如下:
def receive = buffer(CompactByteString())
def buffer(buf: ByteString): Receive = {
// Messages inbound from the network
case Received(data) =>
val (pkt, remainder) = getPacket(buf ++ data)
// Do something with your packet
context become buffer(remainder)
case Other Stuff => // Etc
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.