2017-12-29 34 views
0

自分自身のWebSocketプロトコルを作成していて、テキストキー/値ヘッダー部分が2つの連続する改行で終わり、その後にバイナリテールが続くと考えています。2バイトの区切り文字でakka ByteStringを分割する方法

2つの改行でByteStringを半分に分割することは本当に面倒です。 1つのための組み込みの.splitメソッドはありません。バイナリフィンガープリントを見つけるためのものは.indexOfではありません。

あなたはこれに何を使用しますか?このようなプロトコルを作るのに簡単な方法はありますか?

参考文献:

  • アッカ-HTTP 10.1.0-RC1を使用して

ByteStringアッカ、アッカ2.5.8

+0

コピー/再利用できるakka-httpソースコードに(内部の)ストリーミングLineParserがあります。 https://github.com/akka/akka-http/blob/86c56ab41c595b739f36b30bbf3135cdb7e45bba/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/sse/LineParser.scala#L25 – jrudolph

答えて

0

私はこの思い付きました。実際にはまだそれをテストしていない。

@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) 
+0

を参照してください。再帰的に移動する必要はありません。より良いアイデアは周りにあるようですが、私はここで議論のためにそれを残しています。 – akauppi

1

一つのアプローチは、第一延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) 
0

私の今のコード、:@レオ-Cの答えに影響を受け

// 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> 

が、スライディングウィンドウの代わりに、通常の.findを使用。 ByteStringはランダムアクセスが可能なので、ストリーミング検索とその条件を組み合わせることができます。

関連する問題