Websocket 如何在两字节分隔符处拆分akka ByteString

Websocket 如何在两字节分隔符处拆分akka ByteString,websocket,akka,Websocket,Akka,我正在创建一个自己的WebSocket协议,并认为它有一个文本键/值头部分,以两个连续的换行结束,然后是一个二进制尾部 事实证明,在两个换行符处将一个ByteString一分为二是非常乏味的。例如,没有内置的.split方法。不,用于查找二进制指纹的indexOf 你用什么做这个?有没有更简单的方法来建立这样一个协议 参考资料: 阿克卡 使用AKKAHTTP10.1.0-RC1,AKKA2.5.8我想到了这个。还没有在实践中测试过 @tailrec def peelMsg(bs: ByteStr

我正在创建一个自己的WebSocket协议,并认为它有一个文本键/值头部分,以两个连续的换行结束,然后是一个二进制尾部

事实证明,在两个换行符处将一个ByteString一分为二是非常乏味的。例如,没有内置的.split方法。不,用于查找二进制指纹的indexOf

你用什么做这个?有没有更简单的方法来建立这样一个协议

参考资料:

阿克卡
使用AKKAHTTP10.1.0-RC1,AKKA2.5.8

我想到了这个。还没有在实践中测试过

@tailrec
def peelMsg(bs: ByteString, accHeaderLines: Seq[String]): Tuple2[Seq[String],ByteString] = {

  val (a: ByteString, tail: ByteString) = bs.span(_ != '\n')
  val b: ByteString = tail.drop(1)

  if (a.isEmpty) {    // end marker - empty line
    Tuple2(accHeaderLines,b)
  } else {
    val acc: Seq[String] = accHeaderLines :+ a.utf8String    // append
    peelMsg(b,acc)
  }
}

val (headerLines: Seq[String], value: ByteString) = peelMsg(bs,Seq.empty)

一种方法是首先从ByteString的indexedSeq创建滑动对,然后使用分隔符对的标识索引拆分ByteString,如下例所示:

import akka.util.ByteString

val bs = ByteString("aa\nbb\n\nxyz")
// bs: akka.util.ByteString = ByteString(97, 97, 10, 98, 98, 10, 10, 120, 121, 122)

val delimiter = 10

// Create sliding pairs from indexedSeq of the ByteString
val slidingList = bs.zipWithIndex.sliding(2).toList
// slidingList: List[scala.collection.immutable.IndexedSeq[(Byte, Int)]] = List(
//   Vector((97,0), (97,1)), Vector((97,1), (10,2)), Vector((10,2), (98,3)),
//   Vector((98,3), (98,4)), Vector((98,4), (10,5)), Vector((10,5), (10,6)),
//   Vector((10,6), (120,7)), Vector((120,7), (121,8)), Vector((121,8), (122,9))
// )

// Get indexes of the delimiter-pair
val dIndex = slidingList.filter{
  case Vector(x, y) => x._1 == delimiter && y._1 == delimiter
}.flatMap{
  case Vector(x, y) => Seq(x._2, y._2)
}

// Split the ByteString list
val (bs1, bs2) = ( bs.splitAt(dIndex(0))._1, bs.splitAt(dIndex(1))._2.tail )
// bs1: akka.util.ByteString = ByteString(97, 97, 10, 98, 98)
// bs2: akka.util.ByteString = ByteString(120, 121, 122)
我的代码,目前:

// Find the index of the (first) double-newline
//
val n: Int = {
  val bsLen: Int = bs.length

  val tmp: Int = bs.zipWithIndex.find{
    case ('\n',i) if i<bsLen-1 && bs(i+1)=='\n' => true
    case _ => false
  }.map(_._2).getOrElse{
    throw new RuntimeException("No delimiter found")
  }
  tmp
}

val (bs1: ByteString, bs2: ByteString) = bs.splitAt(n)    // headers, \n\n<binary>

受@leo-c答案的影响,但使用普通的。查找而不是滑动窗口。意识到由于ByteString允许随机访问,我可以将流式搜索与该条件结合起来。

akka http源代码中有一个内部流式LineParser,您可以复制/重用它。看,别用这个。没有必要去递归;似乎有更好的想法,但为了讨论,我把它留在这里。