目录
[2.3.4 CBR UDP分组产生器:UdpClient与UdpEchoClient](#2.3.4 CBR UDP分组产生器:UdpClient与UdpEchoClient)
UdpServer中需要专门配置的属性为:PacketWindowSize
~examples/udp-client-server/udp-client-server.cc
[2.3.5 Trace UDP 分组产生器:UdpTraceClient](#2.3.5 Trace UDP 分组产生器:UdpTraceClient)
[UdpTraceClient 与 UdpClient 的主要区别:](#UdpTraceClient 与 UdpClient 的主要区别:)
~examoles/udp-client-server/udp-trace-client-server.cc
2.3.4 CBR UDP分组产生器:UdpClient与UdpEchoClient
UdpClient与UdpEchoClient具有几乎相同的行为,均部署在客户端,使用UDP传输协议并以固定码率向服务器端发送分组,对应的服务器端应用分别为UdpServer与UdpEchoServer
UdpClient与UdpServer组合是单向传输;UdpEchoClient与UdpEchoServer组合是双向传输
UdpEchoServer每接收一个分组就会返回一个相同负载大小的回执(echUdpServero)
UdpServer能够计算每一个的分组的应用层单向传播延迟和丢失分组数量,在发送分组时,每一个UdpClient分组都会携带一个SeqTsHeader分组头,其中包含一个4B的时间戳和一个8B的应用层序列号。
UdpClient常用专属属性:
- PacketSize:发送分组负载大小(包括SeqTsHeader),单位是B
- Interval:分组发送间隔时间
- MaxPackets:能够发送的最大分组节数,默认值为0,即不发送分组
UdpServer中需要专门配置的属性为:PacketWindowSize
- PacketWindowSize:用于计算分组丢失数量,窗口大小必须是8的倍数
cpp
//创建UdpServer
UdpServerHelper server(9);
server.SetAttribute(
"PacketWindowSize", //分组窗口大小,单位是分组个数
UintegerValue(16));
UdpClient与UdpServer的助手类别分别是UdpClientHelper和UdpServerHelper,在创建是不需要指定下层协议,只需要指定端口
UdpEchoClient和UdpEchoServer的助手类分别是UdpEchoClientHelper和UdpEchoServerHelper,使用方法与UdpClientHelper和UdpServerHelper相同(除UdpEchoServer没有滑动窗口属性)
~examples/udp/udp-echo.cc
cpp
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* ns-3 中的 UDP 回显(echo)示例程序,模拟了一个简单的局域网中两个节点之间的 UDP 通信
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// 网络拓扑
//
// n0 n1 n2 n3
// | | | |
// =================
// LAN
//
// - UDP flows from n0 to n1 and back
// - DropTail queues
// - Tracing of queues and packet receptions to file "udp-echo.tr"
#include <fstream>
#include "ns3/core-module.h"// 核心模块
#include "ns3/csma-module.h"// CSMA模块(有线局域网)
#include "ns3/applications-module.h" // 应用层模块
#include "ns3/internet-module.h"// 网络层模块(IP协议栈)
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("UdpEchoExample");
int
main (int argc, char *argv[])
{
//
// Users may find it convenient to turn on explicit debugging
// for selected modules; the below lines suggest how to do this
//
#if 0
LogComponentEnable ("UdpEchoExample", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_ALL);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_ALL);
#endif
//
// Allow the user to override any of the defaults and the above Bind() at
// run-time, via command-line arguments
//
bool useV6 = false;//控制是否使用 IPv6(默认为 false,使用 IPv4)
Address serverAddress;
CommandLine cmd (__FILE__);
cmd.AddValue ("useIpv6", "Use Ipv6", useV6);//服务器地址,根据协议版本选择 IPv4 或 IPv6
cmd.Parse (argc, argv);
//
// Explicitly create the nodes required by the topology (shown above).
//
NS_LOG_INFO ("Create nodes.");
NodeContainer n;
n.Create (4); // 创建4个节点
InternetStackHelper internet;
internet.Install (n);// 在所有节点上安装 TCP/IP 协议栈
NS_LOG_INFO ("Create channels.");
//
// Explicitly create the channels required by the topology (shown above).
//
CsmaHelper csma;
csma.SetChannelAttribute ("DataRate", DataRateValue (DataRate (5000000)));// 5 Mbps
csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));// 2ms延迟
csma.SetDeviceAttribute ("Mtu", UintegerValue (1400));// MTU 1400字节
NetDeviceContainer d = csma.Install (n);
//
// We've got the "hardware" in place. Now we need to add IP addresses.
//
NS_LOG_INFO ("Assign IP Addresses.");
if (useV6 == false)
{
//IPv4 配置
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer i = ipv4.Assign (d);
serverAddress = Address(i.GetAddress (1));// 节点1的IPv4地址
}
else
{
//IPv6 配置
Ipv6AddressHelper ipv6;
ipv6.SetBase ("2001:0000:f00d:cafe::", Ipv6Prefix (64));
Ipv6InterfaceContainer i6 = ipv6.Assign (d);
serverAddress = Address(i6.GetAddress (1,1));// 节点1的IPv6地址
}
NS_LOG_INFO ("Create Applications.");
//
// 创建应用程序
//UDP 回显服务器(节点1)
uint16_t port = 9; // 端口9:标准的回显服务端口(RFC 862)
UdpEchoServerHelper server (port);
ApplicationContainer apps = server.Install (n.Get (1));// 安装在节点1
apps.Start (Seconds (1.0));
apps.Stop (Seconds (10.0));//服务器运行时间:1.0-10.0秒
//
// UDP 回显客户端(节点0)
//
uint32_t packetSize = 1024;
uint32_t maxPacketCount = 1;
Time interPacketInterval = Seconds (1.);
UdpEchoClientHelper client (serverAddress, port);
client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount));
client.SetAttribute ("Interval", TimeValue (interPacketInterval));
client.SetAttribute ("PacketSize", UintegerValue (packetSize));
apps = client.Install (n.Get (0));
apps.Start (Seconds (2.0));
apps.Stop (Seconds (10.0));
#if 0
//
// Users may find it convenient to initialize echo packets with actual data;
// the below lines suggest how to do this
//
client.SetFill (apps.Get (0), "Hello World");// 填充字符串
client.SetFill (apps.Get (0), 0xa5, 1024);// 填充特定字节值
uint8_t fill[] = { 0, 1, 2, 3, 4, 5, 6};// 填充字节数组
client.SetFill (apps.Get (0), fill, sizeof(fill), 1024);
#endif
AsciiTraceHelper ascii;
csma.EnableAsciiAll (ascii.CreateFileStream ("udp-echo.tr"));
csma.EnablePcapAll ("udp-echo", false);
//
// Now, do the actual simulation.
//
NS_LOG_INFO ("Run Simulation.");
Simulator::Run ();
Simulator::Destroy ();
NS_LOG_INFO ("Done.");
}
程序功能和执行逻辑
1、网络拓扑:
cpp
n0 (客户端) --- CSMA总线 --- n1 (服务器) --- CSMA总线 --- n2, n3 (空闲节点)
- 所有节点通过 CSMA 总线连接
-带宽:5 Mbps
-延迟:2ms
-MTU:1400字节
2、UDP回显协议流程:
- 标准UDP回显协议(RFC862)
客户端发送:UDP数据包到服务器端口9
服务器响应:将接收到的数据包原样返回给客户端
- 本程序的通信流程
|---------|--------------------------|
| 时间(秒) | 事件 |
| 1.0秒 | 服务器开始监听端口9 |
| 2.0秒 | 客户端发送一个1024字节的UDP数据包到服务器 |
| 2.0+2ms | 服务器受到数据包 |
| 2.0+4ms | 客户端收到服务器的回显响应 |
3、数据包结构分析:
- 发送的数据包
|------------------|---------------------------|
| 以太网帧(14字节): ||
| 目的的MAC地址 | n1的MAC地址 |
| 源MAC地址 | n0的MAC地址 |
| 类型 | 0x0800(IPv4)或0x86DD(IPv6) |
| IP数据包: ||
| IPv4头部 (或IPv6头部) | 20字节 |
| 目的IP | n1的IP地址 |
| 源IP | n0的IP地址 |
| UDP数据报: ||
| UDP头部 | 8字节 |
| 源端口 | 随机端口 |
| 长度 | 1024+8=1032字节 |
| 校验和 | 可选 |
| 应用数据: ||
| 1024字节 | 默认填充0 |
- 总数据包大小
|-------|--------|
| IPv4: | |
| 以太网 | 14字节 |
| IP头部 | 20字节 |
| UDP头部 | 8字节 |
| 应用数据 | 1024字节 |
| 总计 | 1066字节 |
| IPv6: | |
| 以太网 | 14字节 |
| IP头部 | 40字节 |
| UDP头部 | 8字节 |
| 应用数据 | 1024字节 |
| 总计 | 1086字节 |
4、执行时间线:
|----------|--------------------|
| 时间(秒) | 事件 |
| 0.0 | 模拟开始 |
| 1.0 | 服务器开始监听端口9 |
| 2.0 | 客户端发送第一个UDP数据包 |
| 2.000002 | 数据包到达服务器(考虑2ms延迟) |
| 2.000004 | 服务器响应到达客户端(又2ms延迟) |
| 10.0 | 服务器和客户端应用停止 |
| 10.0+ | 模拟结束 |
5、协议差异IPv4 VS IPv6:
|------|----------------------|-----------------------------------|
| | IPv4 | IPv6 |
| 地址分配 | 使用私有地址空间 10.1.1.0/24 | 使用全球单播地址 2001:0000:f00d:cafe::/64 |
| 头部差异 | 20字节(基本头部) | 40字节(固定长度) |
| 功能差异 | 需要 ARP 解析 MAC 地址 | 使用邻居发现协议(NDP) |
6、关键特性:
- CSMA/CD 模拟
程序模拟了带有冲突检测的载波侦听多路访问:
节点在发送前侦听信道
检测到冲突时进行退避
使用DropTail队列管理
- 协议栈完整性
完整的TCP/IP协议栈
支持IPv4和IPv6双协议栈
链路层、网络层、传输层完整模拟
- 应用层模拟
UdpEchoServer:接收数据包并原样返回
UdpEchoClient:发送数据包并等待响应
支持自定义数据包内容
~examples/udp-client-server/udp-client-server.cc
cpp
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* 模拟了一个简单的客户端-服务器 UDP 通信模型,其中客户端节点向服务器节点发送一系列 UDP 数据包,
* 服务器节点接收并统计这些数据包
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// 网络拓扑
//
// n0 n1
// | |
// =======
// LAN
//
// - UDP flows from n0 to n1
#include <fstream>
#include "ns3/core-module.h"
#include "ns3/csma-module.h"
#include "ns3/applications-module.h"
#include "ns3/internet-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("UdpClientServerExample");
int
main (int argc, char *argv[])
{
//
// 启用 UdpClient 和 UdpServer 的日志记录,方便调试和观察程序运行情况
//
LogComponentEnable ("UdpClient", LOG_LEVEL_INFO);
LogComponentEnable ("UdpServer", LOG_LEVEL_INFO);
bool useV6 = false;//控制是否使用 IPv6(默认为 false,使用 IPv4)
Address serverAddress;//存储服务器地址,根据协议版本选择 IPv4 或 IPv6 地址
CommandLine cmd (__FILE__);
cmd.AddValue ("useIpv6", "Use Ipv6", useV6);
cmd.Parse (argc, argv);
//
// Explicitly create the nodes required by the topology (shown above).
//
NS_LOG_INFO ("Create nodes.");
NodeContainer n;
n.Create (2);//// 创建2个节点
InternetStackHelper internet;
internet.Install (n);// 在所有节点上安装 TCP/IP 协议栈
NS_LOG_INFO ("Create channels.");
//
// 创建 CSMA 网络
//
CsmaHelper csma;
csma.SetChannelAttribute ("DataRate", DataRateValue (DataRate (5000000)));
// 5 Mbps
csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));// 2ms 延迟
csma.SetDeviceAttribute ("Mtu", UintegerValue (1400));// MTU 1400字节
NetDeviceContainer d = csma.Install (n);
//
// 根据命令行参数选择使用 IPv4 或 IPv6
//
NS_LOG_INFO ("Assign IP Addresses.");
if (useV6 == false)
{
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer i = ipv4.Assign (d);
serverAddress = Address (i.GetAddress (1));// 节点1(服务器)的 IPv4 地址
}
else
{
Ipv6AddressHelper ipv6;
ipv6.SetBase ("2001:0000:f00d:cafe::", Ipv6Prefix (64));
Ipv6InterfaceContainer i6 = ipv6.Assign (d);
serverAddress = Address(i6.GetAddress (1,1));// 节点1(服务器)的 IPv6 地址
}
NS_LOG_INFO ("Create Applications.");
//
// 创建应用程序
// UDP 服务器(节点1)
//
uint16_t port = 4000;// 服务器监听端口
UdpServerHelper server (port);
ApplicationContainer apps = server.Install (n.Get (1));// 安装在节点1(服务器)
apps.Start (Seconds (1.0));
apps.Stop (Seconds (10.0));//运行时间为 1.0-10.0 秒
//
// UDP 客户端(节点0)
//
uint32_t MaxPacketSize = 1024;// 数据包大小:1024字节
Time interPacketInterval = Seconds (0.05);// 数据包间隔:0.05秒(50毫秒)
uint32_t maxPacketCount = 320;// 最大数据包数量:320个
UdpClientHelper client (serverAddress, port);
client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount));
client.SetAttribute ("Interval", TimeValue (interPacketInterval));
client.SetAttribute ("PacketSize", UintegerValue (MaxPacketSize));
apps = client.Install (n.Get (0));// 安装在节点0(客户端)
apps.Start (Seconds (2.0));
apps.Stop (Seconds (10.0));
//
// Now, do the actual simulation.
//
NS_LOG_INFO ("Run Simulation.");
Simulator::Run ();
Simulator::Destroy ();
NS_LOG_INFO ("Done.");
}
程序功能和执行逻辑
1、通信流程:
|----------|--------------------------|
| 时间(秒) | 事件 |
| 1.0 | 服务器开始监听端口4000 |
| 2.0 | 客户端开始发送UDP数据包 |
| 2.0-10.0 | 客户端以50ms间隔发送数据包,服务器接收并统计 |
| 10.0 | 服务器和客户端应用停止 |
2、数据包传输分析:
- 客户端发送:
cpp
-数据包大小:1024字节(应用层数据)
-发送间隔:0.05秒(20个包/秒)
-理论发送时间:320包x0.05秒=16秒
实际模拟时间仅有8秒,因此世纪发送的数据包数量少于320个
实际发送数量:
-发送窗口时间:10.0-2.0=8.0秒
-每秒发送的数据包数:1/0.05=20个
-理论发送数量:8.0x20=160个
-实际发送数量:由于模拟时间限制,客户端只能发送约160个数据包
3、数据传输量:
cpp
单个数据包大小(应用层):1024字节
每秒数据量:20x1024=10480字节=20 KB
总数据量(8秒):160x1024=163,840字节=160KB
数据速率:20 KB/s x 8 bits/byte = 160 Kbps
4、网络带宽利用率:
cpp
网络带宽:5 Mbps = 625 KB/s
客户端数据速率:160 Kbps = 20 KB/s
利用率:160 Kbps / 5 Mbps = 3.2%
结论:网络贷款远未饱和
5、关键组件:
- UdpServer
接收UDP数据包并统计
记录接收到的数据包总数
记录接收到的总字节数
- UdpClient
以固定间隔发送UDP数据包
可以设置数据包大小、发送间隔和最大数据包数量
自动填充数据包内容
UdpServer分组丢失计算方法:
UdpServer通过SeqTsHeader中的序列号计算丢失分组数量,这个序列号表示这是UdpClient发送的第几个分组(从零开始计算)。在服务器端,UdpServer维护着一个分组滑动窗口和当前接收到的最大分组序列号,它依靠应用层序列号的接收顺序来确定一个分组是否丢失。如果收到的分组序列号小于一个阈值(最大分组序列号-PacketWindowSize+1),则表明这个分组到达时间太晚,视为丢失分组,丢失分组数+1,如果分组在传输过程中丢失,没有抵达服务器端,则同样视为丢失分组。
2.3.5 Trace UDP 分组产生器:UdpTraceClient
UdpTraceClient使用UDP向UdpServer发送包含SeqTsHeader的分组。
UdpTraceClient发送分组的时间和大小是根据一个MPEG4视频压缩格式的trace文件,MPEG4 trace包含4列数据,示例格式如下:
|-----------|-----------|------------|----------------|
| Frame No. | Frametype | Time[ms] | Length[ybte] |
| 1 | I | 0 | 392 |
| 2 | P | 120 | 505 |
| 3 | B | 40 | 36 |
| 4 | B | 80 | 67 |
| 5 | P | 240 | 208 |
| 6 | B | 160 | 34 |
| 7 | B | 200 | 33 |
| 8 | P | 360 | 242 |
| 9 | B | 280 | 47 |
| 10 | B | 320 | 39 |
四列分别代表帧序列号、帧种类、压缩器产生帧的时间(ms)和帧长度(B)。对于I和P帧,UdpTraceClient根据帧产生的时间发送分组;对于B帧,UdpTraceClient将其与之前的P帧在同一时间发送
表2-1 UdpTraceClient中IBP帧发送时间
|------|----|------|------|----|------|
| 帧序列号 | 种类 | 发送时间 | 帧序列号 | 种类 | 发送时间 |
| 1 | I | 0 | 6 | B | 240 |
| 2 | P | 120 | 7 | B | 240 |
| 3 | B | 120 | 8 | P | 360 |
| 4 | B | 120 | 9 | B | 360 |
| 5 | P | 240 | 10 | B | 360 |
UdpTraceClient的助手类是UdpTraceClientHelper
UdpTraceClient常用的专属属性:
- **MaxPacketSize:**发送分组的负载大小,单位是B
- **TraceLoop:**是否循环发送trace
UdpTraceClient 与 UdpClient 的主要区别:
|-------|-------------------|-----------------------|
| | UdpClient | UdpTraceClient |
| 流量模式 | 固定间隔、固定大小的数据包 | 基于跟踪文件的变比特率流量 |
| 数据包生成 | 简单的周期性生成 | 根据跟踪文件中的帧大小和间隔生成 |
| 应用场景 | 模拟简单的恒定比特率(CBR)流量 | 模拟真实的变比特率(VBR)流量,如视频流 |
~examoles/udp-client-server/udp-trace-client-server.cc
cpp
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* 使用 UdpTraceClient 来模拟基于实际跟踪文件(如视频流)的 UDP 流量模式
* 比固定速率的 UdpClient 更能模拟真实世界的变比特率流量
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// 拓扑结构
//
// n0 n1
// | |
// =======
// LAN
//
// - UDP flows from n0 to n1
#include <fstream>
#include "ns3/core-module.h"
#include "ns3/csma-module.h"
#include "ns3/applications-module.h"
#include "ns3/internet-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("UdpTraceClientServerExample");
int
main (int argc, char *argv[])
{
//
//启用了 UdpTraceClient 的日志记录
//
LogComponentEnable ("UdpTraceClient", LOG_LEVEL_INFO);
LogComponentEnable ("UdpServer", LOG_LEVEL_INFO);
bool useV6 = false;
Address serverAddress;
CommandLine cmd (__FILE__);
cmd.AddValue ("useIpv6", "Use Ipv6", useV6);
cmd.Parse (argc, argv);
//
// Explicitly create the nodes required by the topology (shown above).
//
NS_LOG_INFO ("Create nodes.");
NodeContainer n;
n.Create (2);
InternetStackHelper internet;
internet.Install (n);
NS_LOG_INFO ("Create channels.");
//
// Explicitly create the channels required by the topology (shown above).
//
CsmaHelper csma;
csma.SetChannelAttribute ("DataRate", DataRateValue (DataRate (5000000)));
csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
//将 MTU 设置为标准以太网的 1500 字节
csma.SetDeviceAttribute ("Mtu", UintegerValue (1500));
NetDeviceContainer d = csma.Install (n);
//
// We've got the "hardware" in place. Now we need to add IP addresses.
//
NS_LOG_INFO ("Assign IP Addresses.");
if (useV6 == false)
{
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer i = ipv4.Assign (d);
serverAddress = Address (i.GetAddress (1));
}
else
{
Ipv6AddressHelper ipv6;
ipv6.SetBase ("2001:0000:f00d:cafe::", Ipv6Prefix (64));
Ipv6InterfaceContainer i6 = ipv6.Assign (d);
serverAddress = Address(i6.GetAddress (1,1));
}
NS_LOG_INFO ("Create Applications.");
//
// Create one udpServer applications on node one.
//
uint16_t port = 4000;
UdpServerHelper server (port);
ApplicationContainer apps = server.Install (n.Get (1));
apps.Start (Seconds (1.0));
apps.Stop (Seconds (10.0));
//
// Create one UdpTraceClient application to send UDP datagrams from node zero to
// node one.
//
//数据包大小:1472 字节(1500 - 20 - 8,考虑 IP 和 UDP 头部)
uint32_t MaxPacketSize = 1472;
UdpTraceClientHelper client (serverAddress, port,"");
//第三个参数是空字符串,表示使用默认的跟踪文件
client.SetAttribute ("MaxPacketSize", UintegerValue (MaxPacketSize));
apps = client.Install (n.Get (0));
apps.Start (Seconds (2.0));
apps.Stop (Seconds (10.0));
//
// Now, do the actual simulation.
//
NS_LOG_INFO ("Run Simulation.");
Simulator::Run ();
Simulator::Destroy ();
NS_LOG_INFO ("Done.");
}
程序功能和执行逻辑
1、网络参数计算:
- 数据包大小选择
cpp
uint32_t MaxPacketSize = 1472; // Back off 20 (IP) + 8 (UDP) bytes from MTU
-以太网MTU:1500字节
-IPv4头部:20字节
-UDP头部:8字节
-可用载荷:1500-20-8=1472字节
- 有效载荷效率
cpp
-应用数据:1472字节
-UDP头部:8字节
-IP头部:20字节
-以太网头部:14字节+4字节CRC=18字节
总计:1472+8+20+18=1518字节
效率:1472/1518=97%
2、执行流程:
|----------|---------------------------------|
| 时间(秒) | 事件 |
| 1.0 | 服务器开始监听端口4000 |
| 2.0 | UdpTraceClient开始发送基于跟踪文件的UDP数据包 |
| 2.0-10.0 | 客户端根据跟踪文件模式发送变比特率流量 |
| 10.0 | 服务器和客户端应用停止 |
3、流量特性:
- 变比特率(VBR)
数据包大小随时间变化
发送间隔也可能变化
- 突发性:
可能有高数据速率的突发时段
也可能有低数据速率的安静时段
- 自相似性
真实网络流量通常具有自相似特性
跟踪文件通常捕获了这种特性