繁体   English   中英

Scala-使用Akka的TCP数据包帧

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM