NSFX手册的学习(14)

网络实用工具

地址

库提供地址类模板来模拟网络地址。实际上,地址类模板表示固定长度的无符号整数值。

Address的模板参数是地址的位长度。例如,

// Defaults to 0.
Address<8>   a1;

// Assign an initial value.
Address<16>  a2(0x2dfe);

// Assign an initial value via a big-endian buffer.
const uint8_t bytes[] = { 0xc0, 0xab, 0x01, 0x64 };
Address<32>  a4(bytes, sizeof (bytes), big_endian);

// Assign an initial value via byte sequence.
Address<48>  a6(0xac, 0xb1, 0x07, 0xae, 0x31, 0xcd, big_endian);

// Assign an initial value via uint32 sequence.
Address<128> a8(0x92c3fe81, 0xeb81630b, 0x7d6bdac9, 0x35da031a, big_endian);
地址的位大小可以通过静态成员函数GetBitSize()获得,字节大小可以通过静态成员函数GetSize()获得。例如,
size_t bitSize  = Address<48>::GetBitSize();
size_t byteSize = Address<48>::GetSize();

在NSFX中,包被建模为字节序列。因此,包的主要部分是缓冲区。在模拟包和物理包之间很容易转换。
作为一种网络惯例,包是通过添加/删除头或尾来修改的。
为了提高存储效率,一个包可以由多个组件共享。当多个组件持有一个数据包时,如果该数据包被其中一个组件修改,则该修改对任何其他持有者都不可见。在内部,如果修改对其他组件可见,则为修改组件复制数据包,并且修改仅应用于新副本。例如,

// Creates an empty packet.
Packet packet;

// Add a header to the packet by 8 bytes.
PacketBuffer header = packet.AddHeader(8);

// Add a trailer to the packet by 4 bytes.
PacketBuffer trailer = packet.AddTrailer(4);
要将数据写入缓冲区,用户应使用缓冲区迭代器。例如,
// Obtain a writable buffer iterator from the header.
PacketBufferIterator it = header.begin();

uint32_t value = 0x1a23ce2d;
// Write 4 bytes in big-endian order.
WriteB(it, value);

// Write 4 bytes in little-endian order.
WriteL(it, value);

// Write the trailer.
it = trailer.begin();
WriteB(it, value);

数据包的字节大小可以通过GetSize()成员函数获得。例如,
size_t byteSize = packet.GetSize();

要读取缓冲区,用户应使用只读缓冲区迭代器。例如,
// Obtain a read-only buffer.
ConstPacketBuffer buffer = packet.GetBuffer();

// Obtain a read-only buffer iterator.
ConstPacketBufferIterator cit = buffer.begin();

uint32_t value;
// Read 4 bytes in big-endian order.
ReadB(it, &value);

// Read 4 bytes in little-endian order.
ReadL(it, &value);

// Remove 8 bytes from the start of the packet.
packet.RemoveHeader(8);

// Remove 4 bytes from the end of the packet.
packet.RemoveTrailer(4);
数据包可以被分割和重新组合。例如,

//创建数据包。
Packet Packet;Packet.AddHeader(8);//制作两个包片段。
Packet frag1=Packet.MakeFragment(0,4);Packet frag2=Packet.MakeFragment(4,Packet.GetSize());//重新组装数据包。
Packet copy1=frag1;copy1.AddTrailer(frag2);//相反,重新组装包。
数据包copy2=frag2;copy2.AddHeader(frag1);

标签

除了缓冲区,包的另一部分是标签列表。标签承载物理包中不存在的边信息。例如,发送方可以添加带有时间戳的标记,而接收方可以使用时间戳来计算包的端到端延迟。
标签有一个ID,并且与一个包的字节范围相关联。当包被分段时,如果分段包含相关的字节,则保留标记。当一个包被重新组装时,碎片中的所有标签都会被集合起来还原。

字节标记

字节标记有两种类型的标记。最常用的类型是字节标记。字节标记有一个固定大小的缓冲区(字节序列)来携带边信息。例如,

///////////////sender///////////////创建数据包。
Packet Packet;Packet.AddHeader(4);//创建一个标记缓冲区来携带时间戳。
tag buffer buffer(TimePoint::GetSize());//获取一个标记缓冲迭代器。
TagBufferIterator it=buffer.begin();//写入标记缓冲区。
TimePoint t0=clock->Now();WriteB(it,t0);//向包中添加字节标记。
//字节标记与当前头相关联,即字节0、1、2和3。
enum{TAGID_TIMESTAMP=0};packet.AddByteTag(TAGID_TIMESTAMP,buffer,0,4);//向数据包添加另一个头。
//字节标记仍然与内部头相关联,即字节4、5、6和7。
packet.AddHeader(4);///////////////////接收器/////////////////移除外部头。
packet.RemoveHeader(4);//获取指定偏移量处与字节关联的字节标记的缓冲区。
size_t offset=0;//由于字节标记与字节0、1、2和3关联,//offset可以是0、1、2或3。
constagbuffer buffer=packet.GetByteTag(TAGID_TIMESTAMP,offset);//获取只读标记缓冲迭代器。
ConstTagBufferIterator it=buffer.cbegin();//读取时间戳。
TimePoint t0;ReadB(it,&t0);//计算端到端延迟。
持续时间eteDelay=clock->Now()-t0;

标签ID

标签应用于对等协议实体,以便与数据包一起传输侧信息。实体只能操作与其自身协议头相关联的标记。因此,标记ID只在同一类型的实体中具有本地含义,并且标记ID可以被不同类型的实体重用,因为它们不会操作其他协议头上的标记。例如,

////////////////////////////上层协议///////////////////////////////////////创建数据包。
Packet Packet;Packet.AddHeader(4);//创建一个标记缓冲区来携带时间戳。
tag buffer buffer(TimePoint::GetSize());//获取一个标记缓冲迭代器。
TagBufferIterator it=buffer.begin();//写入标记缓冲区。
TimePoint t0=clock->Now();WriteB(it,t0);//向包中添加字节标记。
//字节标记与当前头相关联,即字节0、1、2和3。
枚举{TAGID_TIMESTAMP=0};packet.AddByteTag(TAGID_TIMESTAMP,buffer,0,4);/////////////////////////////////////////////在较低层的协议///////////////////////////////////向数据包添加另一个头。
packet.AddHeader(4);//创建一个标记缓冲区来携带一个4字节的值。
tag buffer buffer(4);//获取一个标记缓冲迭代器。
TagBufferIterator it=buffer.begin();//写入标记缓冲区。
uint32_t value=0x1a23ce2d WriteB(it,value);//向包中添加字节标记。
//标记ID也是0。
枚举{TAGID_VALUE=0};packet.AddByteTag(TAGID_VALUE,缓冲区,0,4);

不建议使用标记跨协议层传输接口控制信息(ICI)。因为这些协议必须同意标签id。它会导致协议之间更紧密的耦合。

包标记

另一种类型的标记是包标记。数据包标签携带数据包,而不是字节序列。有时,发送者需要对包进行编码,并发送编码的字节。接收器需要解码字节并恢复数据包。然而,编码和解码通常是复杂的操作。在模拟中,可能不需要执行编码或解码。在这种情况下,原始分组可以由分组标签携带。例如,

///////////////sender///////////Packet Packet;Packet.AddHeader(8);//假设编码的内容应该有16个字节。
Packet encoded;encoded.AddHeader(16);//通过标记携带包来简化编码过程。
枚举{TAGID_RAW_PACKET=0};编码的.AddPacketTag(TAGID_RAW_PACKET,PACKET,0,16);///////////////////接收器/////////////////////////通过从标记中恢复包来简化解码过程。
Packet decoded=encoded.GetPacketTag(TAGID_RAW_Packet,0);

学习资源自:https://github.com/Fuzzier/nsfx/blob/a9281bd89be3da9c694f0613f0e910e73f8d1689/tutorials.md

你可能感兴趣的:(NSFX手册的学习(14))