从具有范围的碎片字节流中读取数据包?

时间:2012-11-21 10:52:32

标签: d range

我相信我知道范围,但我不知道何时何地使用它们,或者如何使用它们。我没有“获得”范围。考虑这个例子:


假设我们有一个网络处理程序,我们无法控制,只要有一些新数据,就会在某个线程中调用我们的回调函数:

void receivedData(ubyte[] data)

此数据流包含具有布局

的数据包
{
    ushort size;
    ubyte[size] body;
}

但是,网络处理程序不知道这一点,因此对dataReceived()的调用可能包含一个或两个部分数据包,一个或多个完整数据包或组合。为简单起见,我们假设没有损坏的数据包,并且在连接关闭时我们会收到data.length == 0

我们现在想要的是一些漂亮的D代码,将所有这些混乱转变为对

的适当调用
void receivedPacket(ubyte[] body)

我当然可以想到“蛮力”的方法来实现这一目标。但也许这里可能出现了我的困惑:范围可以发挥作用吗?我们可以将receivedData()包装成一个很好的范围吗?怎么样?或者这不是你使用范围的问题吗?为什么不呢?

(如果使用范围更有意义,请随意重新定义该示例。)

1 个答案:

答案 0 :(得分:2)

我要做的是

ubyte[1024] buffer=void;//temp buffer set the size as needed...
ushort filledPart;//length of the part of the buffer containing partial packet
union{ushort nextPacketLength=0;ubyte[2] packetLengtharr;}//length of the next packet

void receivedData(ubyte[] data){
    if(!data.length)return;

    if(nextPacketLength){
        dataPart = min(nextPacketLength-filledPart.length,data.length);

        buffer[filledPart..nextPacketLength] = data[0..dataPart];
        filledPart += dataPart;

        if(filledPart == nextPacketLength){
            receivedPacket(buffer[0..nextPacketLength]);//the call
            filledPart=nextPacketLength=0;//reset state
            receivedData(datadataPart..$]);//recurse
        }
    } else{
        packetLengtharr[]=data[0..2];//read length of next packet

        if(nextPacketLength<data.length){//full paket in data -> avoid unnecessary copies
             receivedPacket(data[2..2+nextPacketLength]);
             receivedData(data[2+nextPacketLength..$]);//recurse
        }else
            receivedData(data[2..$]);//recurse to use the copy code above
    }
}

它有三种可能的路径递归:

  1. data为空 - &gt;没有行动

  2. nextPacketLength设置为值!= 0 - &gt;将尽可能多的数据复制到缓冲区中,如果数据包已完成,请调用回调并重置filledPartnextPacketLength,然后使用其余data

  3. 进行递归
  4. nextPacketLength == 0读取数据包长度(此处带有联合),如果完整数据包可用,则调用回调,然后使用其余数据进行递归

  5. 当数据只保留长度的第一个字节时,仍然只有一个错误,但我会让你弄明白(我现在懒得做)