live/groupsock/include/NetAddress.hh
1.概述
group是组/群的意思,socket是网络接口的代名词了。这个部分很庞大,主要是与网络相关的。而live555的网络模块很多都涉及到组播的概念。
使用Socket进行的网络连接,网络地址一般由地址(IP)和端口(port)组成。
live555中定义了一些数据类型,表明了目前所支持的网络地址类型。
typedef u_int32_t netAddressBits;
typedef u_int16_t portNumBits;
1.1. NetAddress
NetAddress是一个用于保存网络地址的类,它不是对struct sockaddr的封装。其内部定义了两个数据成员,分别是用于保存地址数据的u_int8_t* fData和用于指示地址长度的unsigned fLength。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCsUspap-1595303656936)(https://note.youdao.com/yws/public/resource/b1e07c64a1c583bbabdcf3b6a8269212/xmlnote/8D5BCF80D9354016B30313208D911CA3/4710)]
class NetAddress {
public:
//调用assign来分配空间
NetAddress(u_int8_t const* data,
unsigned length = 4 /* default: 32 bits */);
NetAddress(unsigned length = 4); // sets address data to all-zeros
NetAddress(NetAddress const& orig);
NetAddress& operator=(NetAddress const& rightSide);
//析构就是对clean的调用。
virtual ~NetAddress();
unsigned length() const { return fLength; }
u_int8_t const* data() const // always in network byte order
{ return fData; }
private:
void assign(u_int8_t const* data, unsigned length);
void clean();
unsigned fLength;
//clean方法用于将fData指向的内存空间进行释放。
u_int8_t* fData;
};
- assign
先说这个而不是构造函数,是因为这个方法是一个关键方法。构造函数也要用到它。
assign为fData成员动态分配内存空间和拷贝数据。通过参数length来确定分配空间的大小,而参数data用于作为数据源拷贝到申请的新空间。要注意的是这个方法的权限是private的,所以没有检查data==NULL也是可以的。
//为fDate申请length字节内存空间,并将data指向内容拷贝到新空间
void NetAddress::assign(u_int8_t const* data, unsigned length) {
fData = new u_int8_t[length];
if (fData == NULL) {
fLength = 0;
return;
}
for (unsigned i = 0; i < length; ++i) fData[i] = data[i];
fLength = length;
}
1.2. NetAddressList
网络地址列表是用于保存一系列网络地址的类。它与NetAddress无直接联系。
NetAddressList类内部定义了一个二级指针NetAddress** fAddressArray,在使用的时候给它动态申请一个元素个数为unsigned fNumAddresses的指针(NetAddress*)数组。指针数组的每一个元素又指向一个动态申请的NetAddress对象。
class NetAddressList {
public:
// 构造函数hostname可以是一个点分十进制的IP地址,也可以是主机域名
NetAddressList(char const* hostname);
NetAddressList(NetAddressList const& orig);
NetAddressList& operator=(NetAddressList const& rightSide);
virtual ~NetAddressList();
//获取地址表中元素个数
unsigned numAddresses() const { return fNumAddresses; }
NetAddress const* firstAddress() const;
// Used to iterate through the addresses in a list:
// 迭代器,用于遍历列表中的地址:
class Iterator {
public:
Iterator(NetAddressList const& addressList);
NetAddress const* nextAddress(); // NULL iff none
private:
NetAddressList const& fAddressList;//必须绑定一个地址表
unsigned fNextIndex;//下一个地址的索引
};
private:
//assign方法为地址表动态申请内存来保存地址元素。
void assign(netAddressBits numAddresses, NetAddress** addressArray);
//它的作用是将地址表和表中所有的地址元素都释放了。之前assign分配空间,在这里对应的释放。
void clean();
friend class Iterator;
unsigned fNumAddresses;
NetAddress** fAddressArray;
};
typedef u_int16_t portNumBits;
class Port {
public:
Port(portNumBits num /* in host byte order */);
portNumBits num() const { return fPortNum; } // in network byte order
private:
portNumBits fPortNum; // stored in network byte order
#ifdef IRIX
portNumBits filler; // hack to overcome a bug in IRIX C++ compiler
#endif
};
- assign
void NetAddressList::assign(unsigned numAddresses, NetAddress** addressArray) {
//为地址表分配内存空间
fAddressArray = new NetAddress*[numAddresses];
if (fAddressArray == NULL) {
fNumAddresses = 0;
return;
}
//为地址表每个地址分配内存空间
for (unsigned i = 0; i < numAddresses; ++i) {
fAddressArray[i] = new NetAddress(*addressArray[i]);
}
fNumAddresses = numAddresses;
}
- NetAddressList的构造
NetAddressList(char const* hostname)构造函数很长,内容不多,但是涉及到一些网络编程的基础知识.
首先参数hostname,是一个C风格的字符串,如果它保存的是一个点分十进制的IP地址(例如:”192.168.1.128”),那么只会给这个地址表申请一个元素的空间来保存地址。注意,保存的地址在一个NetAddress对象中,对象里面保存的是整型数形式的地址.
这里有一句netAddressBits addr = our_inet_addr((char*)hostname);这个函数的作用是把点分十进制的IP地址转换为整型数形式的地址。参数不是点分十进制的IP地址字符串,那么函数会返回错误码INADDR_NONE 。our_inet_addr实质上是调用的inet_addr(socket库函数),其定义在live555sourcecontrol\groupsock\inet.c文件中。
那如果参数hostname不是一个IP地址,那么它就应该是主机名(通常指域名,如live555.com)。一个域名可能对应不止一个IP地址(windows下可以使用nslookup命令查看,linux/unix下可以用dig命令)。这里使用了gethostbyname函数来获取它的所有地址。然后分配空间拷贝保存了这些地址。
NetAddressList::NetAddressList(char const* hostname)
: fNumAddresses(0), fAddressArray(NULL) {
// First, check whether "hostname" is an IP address string:
netAddressBits addr = our_inet_addr((char*)hostname);
if (addr != INADDR_NONE) {
// Yes, it was an IP address string. Return a 1-element list with this address:
//它是一个IP地址字符串,那么这个地址表只需要1个元素
fNumAddresses = 1;
fAddressArray = new NetAddress*[fNumAddresses];
if (fAddressArray == NULL) return;
//申请空间,保存这个地址。注意保存的是整数地址而不是字符串
fAddressArray[0] = new NetAddress((u_int8_t*)&addr, sizeof (netAddressBits));
return;
}
// "hostname" is not an IP address string; try resolving it as a real host name instead:
// 当它不是一个IP地址字符串,尝试解析hostname真实的地址来代替
#if defined(USE_GETHOSTBYNAME) || defined(VXWORKS)
struct hostent* host;
#if defined(VXWORKS)
char hostentBuf[512];
host = (struct hostent*)resolvGetHostByName((char*)hostname, (char*)&hostentBuf, sizeof hostentBuf);
#else
//gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针(不要试图delete这个返回的地址)
host = gethostbyname((char*)hostname);
#endif
if (host == NULL || host->h_length != 4 || host->h_addr_list == NULL) return; // no luck
u_int8_t const** const hAddrPtr = (u_int8_t const**)host->h_addr_list;
// First, count the number of addresses:
// First, count the number of addresses:取得地址个数
u_int8_t const** hAddrPtr1 = hAddrPtr;
while (*hAddrPtr1 != NULL) {
++fNumAddresses;
++hAddrPtr1;
}
// Next, set up the list:
// Next, set up the list: 给地址表分配内存
fAddressArray = new NetAddress*[fNumAddresses];
if (fAddressArray == NULL) return;
//逐个拷贝地址到地址表
for (unsigned i = 0; i < fNumAddresses; ++i) {
fAddressArray[i] = new NetAddress(hAddrPtr[i], host->h_length);
}
#else
// Use "getaddrinfo()" (rather than the older, deprecated "gethostbyname()"):
struct addrinfo addrinfoHints;
memset(&addrinfoHints, 0, sizeof addrinfoHints);
addrinfoHints.ai_family = AF_INET; // For now, we're interested in IPv4 addresses only
struct addrinfo* addrinfoResultPtr = NULL;
int result = getaddrinfo(hostname, NULL, &addrinfoHints, &addrinfoResultPtr);
if (result != 0 || addrinfoResultPtr == NULL) return; // no luck
// First, count the number of addresses:
const struct addrinfo* p = addrinfoResultPtr;
while (p != NULL) {
if (p->ai_addrlen < 4) continue; // sanity check: skip over addresses that are too small
++fNumAddresses;
p = p->ai_next;
}
// Next, set up the list:
fAddressArray = new NetAddress*[fNumAddresses];
if (fAddressArray == NULL) return;
unsigned i = 0;
p = addrinfoResultPtr;
while (p != NULL) {
if (p->ai_addrlen < 4) continue;
fAddressArray[i++] = new NetAddress((u_int8_t const*)&(((struct sockaddr_in*)p->ai_addr)->sin_addr.s_addr), 4);
p = p->ai_next;
}
// Finally, free the data that we had allocated by calling "getaddrinfo()":
freeaddrinfo(addrinfoResultPtr);
#endif
}
1.3. AddressPortLookupTable
AddressPortLookupTable类内部定义了一个HashTable* fTable用于保存哈希表的地址。在构造函数中动态创建了一个哈希表对象给它。AddressPortLookupTable使用了两个地址和一个端口号组合作为一个key,value是Add方法的时候确定的。
AddressPortLookupTable类只提供了增删查三种操作,没有提供修改表项的操作。
使用哈希表的优点在于可以快速的查找key对应的value。
class AddressPortLookupTable {
public:
// 为内部哈希表fTable创建对象,哈希表的key是3个元素的unsigned int数组
AddressPortLookupTable();
// 释放内部哈希表fTable
virtual ~AddressPortLookupTable();
// 使用address1、address2、port组成key,value为值添加到哈希表
// 如果对应key的条目已经存在,返回旧的value,否则返回NULL
void* Add(netAddressBits address1, netAddressBits address2, Port port, void* value);
// Returns the old value if different, otherwise 0
//从哈希表中移除key对应的条目,对应条目存在返回true
Boolean Remove(netAddressBits address1, netAddressBits address2, Port port);
// 从哈希表中查找key对应的value,没找到返回NULL
void* Lookup(netAddressBits address1, netAddressBits address2, Port port);
// Returns 0 if not found
void* RemoveNext() { return fTable->RemoveNext(); }
// Used to iterate through the entries in the table
// 迭代器,用于遍历在表中的条目
class Iterator {
public:
Iterator(AddressPortLookupTable& table);
virtual ~Iterator();
void* next(); // NULL iff none
private:
HashTable::Iterator* fIter;
};
private:
friend class Iterator;
HashTable* fTable;
};