NS3中网络地址有很多种,它们都基于(并非是继承)Address类。

Address

在Address类定义的接口文件中,作者这么写道:

This class is very similar in design and spirit to the BSD sockaddr structure: they are both used to hold multiple types of addresses together with the type of the address.

A new address class defined by a user needs to:

  • allocate a type id with Address::Register

  • provide a method to convert his new address to an Address instance. This method is typically a member method named ConvertTo: Address MyAddress::ConvertTo (void) const;
  • provide a method to convert an Address instance back to an instance of his new address type. This method is typically a static member method of his address class named ConvertFrom: static MyAddress MyAddress::ConvertFrom (const Address &address);
  • the ConvertFrom method is expected to check that the type of the input Address instance is compatible with its own type.

同样,作者也给出了一个自己实现一个地址类的示例:

//Typical code to create a new class type looks like:
// this class represents addresses which are 2 bytes long.
class MyAddress
{
public:
  Address ConvertTo (void) const;
  static MyAddress ConvertFrom (void);
private:
  static uint8_t GetType (void);
};

// from MyAddress to Address
Address MyAddress::ConvertTo (void) const
{
  return Address (GetType (), m_buffer, 2);
}

// from Address to MyAddress
MyAddress MyAddress::ConvertFrom (const Address &address)
{
  MyAddress ad;
  NS_ASSERT (address.CheckCompatible (GetType (), 2));
  address.CopyTo (ad.m_buffer, 2);
  return ad;
}

// note: this method is DIFFERENT with the method GetTypeId
uint8_t MyAddress::GetType (void)
{
  static uint8_t type = Address::Register ();
  return type;
}

也就是说,按照作者所说的这样定义了一个自己的地址类(不需要继承),那么就可以使用Address所提供的一些功能。

这个类包含的私有变量包括:

uint8_t m_type; //!< Type of the address
uint8_t m_len;  //!< Length of the address
uint8_t m_data[MAX_SIZE]; //!< The address value

分别表示地址类型,地址长度以及地址的值。

Address Type

需要注意的是,这里面的Address Type没什么特别的,用户自己实现的类通过调用Register函数得到一个Type值,这个函数的实现其实就是保存一个静态变量,每调用一次后自增1。

具体是怎样工作的呢?

我们可以看下GetType函数分别在Ipv4AddressInetSocketAddress中的实现,它们的实现是相同的,即:

NS_LOG_FUNCTION_NOARGS ();
static uint8_t type = Address::Register ();
return type;

在Log之后,首先调用函数Register (),由于这个变量type是静态的,所以这个保证只会调用一次而且是这个类的所有实例共享的,也就说明这些值相同的实例都是同一个类。

函数

IsInvalid

用来判断这个地址是否是可用的:

bool IsInvalid (void) const;

通过判断长度是否为0,以及类型号是否为0来判断这个地址是否是可用的(这两个值在构造函数中都初始化为了0)。

GetLength

得到地址的长度:

uint8_t GetLength (void) const;

CopyTo

把地址拷贝到一个缓冲区中:

uint32_t CopyTo (uint8_t buffer[MAX_SIZE]) const;

CopyAllTo

把整个结构体拷贝到缓冲区中:

uint32_t CopyAllTo (uint8_t *buffer, uint8_t len) const;

CopyFrom

将地址从缓冲区拷贝到当前变量中:

uint32_t CopyFrom (const uint8_t *buffer, uint8_t len);

CopyAllFrom

将整个结构体从缓冲区拷贝到当前变量中:

uint32_t CopyAllFrom (const uint8_t *buffer, uint8_t len);

CheckCompatible

这个函数用来判断一个给定的类型是否与这个变量兼容。

bool CheckCompatible (uint8_t type, uint8_t len) const;

Register

用来分配一个新的地址类型id。

static uint8_t Register (void);

GetSerializedSize

要把这个地址字符串化的话,应该使用多少字节?

应该是GetLength () + 2字节,因为除了地址长度,还有一个字节的类型数据和一个字节的长度数据。

uint32_t GetSerializedSize (void) const;

Serialize & Deserialize

转化成字节串或者从字节串转化回来。

void Serialize (TagBuffer buffer) const;
void Deserialize (TagBuffer buffer);

Ipv4Address

这个类表示一个IPv4的网络层地址。

这个类没有继承任何类。其只有一个私有变量uint32_t m_address,用来表示IP地址,掩码是通过另一个类实现的。

函数

Set & Get

Set函数可以根据一个32位无符号数或者一个字符串来设置地址。

Get函数可以将地址读出为一个32位无符号数。

Isxxx

  • IsEqual:用来和另外一个Ipv4Address类型的地址比较是否相等;
  • IsAny:用来判断这个地址是不是0.0.0.0
  • IsBroadcast:用来判断这个地址是不是127.0.0.1
  • IsBroadcast:用来判断这个地址是不是255.255.255.255
  • IsMulticast:用来判断是不是多播,也就是224.0.0.0 - 239.255.255.255
  • IsLocalMulticast:用来判断是不是本地多播,也就是224.0.0.0/24

Serialize & Deserialize

转化成字节串或者从字节串转化回来。

void Serialize (uint8_t buf[4]) const;
static Ipv4Address Deserialize (const uint8_t buf[4]);

CombineMask

Combine this address with a network mask

This method returns an IPv4 address that is this address combined (bitwise and) with a network mask, yielding an IPv4 network address.

从实现上来看,其实就是将当前地址和掩码进行按位与操作,然后将结果以地址形式返回。

Ipv4Address CombineMask (Ipv4Mask const &mask) const;

Address Compatible Functions

为了与Address类兼容,其也实现了上面提到的需要实现的Address中的函数。

InetSocketAddress

这个类是一个Internet Socket Address,也就是一个互联网的Socket地址。虽然这是一个地址,但是其并没有继承类Address

源代码中的注释这么解释这个类:

This class is similar to inet_sockaddr in the BSD socket API. i.e., this class holds an Ipv4Address and a port number to form an ipv4 transport endpoint.

也就是说这是一个基于IPv4协议的传输层的地址,我们也称其为endpoint

这个类型的主要变量有三个:

Ipv4Address m_ipv4; //!< the IPv4 address
uint16_t m_port;  //!< the port
uint8_t m_tos;   //!< the ToS

除了与Address相兼容的函数,其它函数只是设置地址端口这些函数,不需赘述。

Ipv4EndPoint

这个类可以代表一个连接。

A representation of an internet endpoint/connection

This class provides an internet four-tuple (source and destination ports and addresses). These are used in the ns3::Ipv4EndPointDemux as targets of lookups. The class also has a callback for notification to higher layers that a packet from a lower layer was received. In the ns3 internet-stack, these notifications are automatically registered to be received by the corresponding socket.

严格来说,这个类不能算作一个地址,因为它包含了两个地址:一个发送方地址和一个接收方地址。这点在这个类的私有成员变量中即可看的一清二楚,也就是:

// 本机地址
Ipv4Address m_localAddr;

// 本机端口
uint16_t m_localPort;

// 对方地址
Ipv4Address m_peerAddr;

// 对方端口
uint16_t m_peerPort;

// 这个连接所绑定的本地设网卡
Ptr<NetDevice> m_boundnetdevice;

// 接收到包后的回调
Callback<void,Ptr<Packet>, Ipv4Header, uint16_t, Ptr<Ipv4Interface> > m_rxCallback;

// 接收到ICMPv6后的回调
Callback<void,Ipv4Address,uint8_t,uint8_t,uint8_t,uint32_t> m_icmpCallback;

// 摧毁时的回调
Callback<void> m_destroyCallback;

// 当前连接是否可以接受包
bool m_rxEnabled;

函数

Get & Set

比如设置本机地址,设置对方地址和端口。得到本地地址端口,得到对方地址和端口等。唯一缺的是设置本地端口

BindToNetDevice & GetBoundNetDevice

将这个连接绑定到设备/网卡上,或者得到绑定的设备。

void BindToNetDevice (Ptr<NetDevice> netdevice);
Ptr<NetDevice> GetBoundNetDevice (void);

SetxxxCallback

设置一些事件发生后的回调。

void SetRxCallback (Callback<void,Ptr<Packet>, Ipv4Header, uint16_t, Ptr<Ipv4Interface> > callback);
void SetIcmpCallback (Callback<void,Ipv4Address,uint8_t,uint8_t,uint8_t,uint32_t> callback);
void SetDestroyCallback (Callback<void> callback);

ForwardUp & ForwardIcmp

这个函数将接收到的包发送到上层,也就是传输层。它的实现是这样的:

void 
Ipv4EndPoint::ForwardUp (Ptr<Packet> p, const Ipv4Header& header, uint16_t sport,
                         Ptr<Ipv4Interface> incomingInterface)
{
  NS_LOG_FUNCTION (this << p << &header << sport << incomingInterface);
  
  if (!m_rxCallback.IsNull ())
    {
      m_rxCallback (p, header, sport, incomingInterface);
    }
}

可以看出这个函数通过调用之前绑定的当接收到包之后的回调函数来通知上层,包已经到达,上层应通过m_rxCallback函数进行处理。

同样,ForwardIcmp函数也是如此。

SetRxEnabled & IsRxEnabled

设置/得到当前连接是否能够接受包。

void SetRxEnabled (bool enabled);
bool IsRxEnabled (void);

参考文献