以下内容为我学习live555源码的一个记录,学习时间会相对漫长,统计了一下源码大约一万三千行,并且第一行我就没看懂。代码没有固定顺序,会一边学习一遍画组织结构图,下面开始啦。
目录
UsageEnvironment
首先看UsageEnvironment这个项目
Boolean.hh
首先看到了Boolean.hh这个头文件,第一行是:
#if defined(__BORLANDC__) || (!defined(USE_LIVE555_BOOLEAN) && defined(_MSC_VER) && _MSC_VER >= 1400)
这个说没看懂主要是不知道这里面这些大写的标识是什么意思,百度一下。
首先_MSC_VER是微软的预编译控制,_MSC_VER可以分解为:MS:Microsoft的简写。C:MSC就是Microsoft的C编译器。VER:Version的简写。_MSC_VER的意思就是:Microsoft的C编译器的版本。
微软不同时期,编译器有不同的版本:
MS VC++10.0 _MSC_VER=1600
MS VC++9.0 _MSC_VER=1500
MS VC++8.0 _MSC_VER=1400
其中MS VC++10.0就是Visual C++ 2010,MS VC++9.0就是Visual C++2008,MS VC++8.0就是Visual C++2005
在程序中加入_MSC_VER宏可以根据编译器版本让不同版本的编译器选择性地编译一段程序。
然后__BORLANDC__意思就是如果是Borland C++编译器的话,那么去掉8027这个警告。
所以这句话的意思就是如果当前是Borland C++编译器或者定义了USE_LIVE555_BOOLEAN并且定义了_MSC_VER并且MSVC是c++8.0以上的那么就执行以下代码:
// Use the "bool" type defined by the Borland compiler, and MSVC++ 8.0, Visual Studio 2005 and higher
typedef bool Boolean;
#define False false
#define True true
否则就执行以下代码:
typedef unsigned char Boolean;
#ifndef __MSHTML_LIBRARY_DEFINED__
#ifndef False
const Boolean False = 0;
#endif
#ifndef True
const Boolean True = 1;
#endif
#endif
这里应该是有一些版本没有专门布尔型的定义,而是使用的无符号整型,所以这里要单独处理。
这里就是Boolean这个头文件了。
strDup.hh
下面我们接着来看一下strDup.hh这个头文件。
#include <string.h>
char* strDup(char const* str);
// Note: strDup(NULL) returns NULL
char* strDupSize(char const* str);
// Like "strDup()", except that it *doesn't* copy the original.
// (Instead, it just allocates a string of the same size as the original.)
char* strDupSize(char const* str, size_t& resultBufSize);
// An alternative form of "strDupSize()" that also returns the size of the allocated buffer.
strDup.cpp
这里只是定义了三个接口,我们看下他们的实现,是在strDup.cpp中。
#include "strDup.hh"
char* strDup(char const* str) {
if (str == NULL)
{
return NULL;
}
//计算的字符串的长度 +1这里推测应该是要预留\0的位置
size_t len = strlen(str) + 1;
//开辟一个相同大小的内存
char* copy = new char[len];
if (copy != NULL)
{
//将字符串内存复制到新开辟的内存中
memcpy(copy, str, len);
}
return copy;
}
char* strDupSize(char const* str) {
size_t dummy;
return strDupSize(str, dummy);
}
char* strDupSize(char const* str, size_t& resultBufSize)
{
//这里传递来一个引用
if (str == NULL)
{
resultBufSize = 0;
return NULL;
}
//计算str大小并预留\0的位置
resultBufSize = strlen(str) + 1;
//开辟一个同样的大小的内存并返回
char* copy = new char[resultBufSize];
return copy;
}
第一个接口用于将传入的字符串拷贝的新开辟的内存中并返回最新的内存地址,第二个接口用于给传入的字符串开辟内存并返回新开辟的内存。
哈希桶
接下来的内容要上升一个梯度了,我们来看下HashTable.hh这个头文件。
在之前我们要先理解一下哈希桶这个概念,不然直接看这个源码会没办法真正理解这个数据结构的意义。
通常大家所说的哈希函数也可以称为散列函数,哈希函数的功能只是将你的目标key通过一种映射方法,也可以说是一种函数运算f,最后得到你目标的hashValue = f(key),这里的函数f就称为哈希函数/散列函数。
可以看到哈希函数的选择是一个很关键的步骤。为了引进哈希桶算法,必然要介绍一下哈希冲突,因为哈希桶就是为了解决哈希冲突的。举个例子,有一组序列为[1,2,3,4,5,6,7,8,9],使用的哈希函数为f(key) = key mod 5,那么依次得到的hasvalue就是[1,2,3,4,0,1,2,3,4],显然在key值为1,6的时候得到的哈希值都为1,如下所示:
这个时候就产生了冲突了,也就是不同的key通过映射后得到了同样值的hashvalue。而所谓的哈希桶算法其实就是链地址解决冲突的方法,如上面的例子所示,就可以设置桶的个数为5,也就是f(key)集合的个数,而这样的话,hashvalue就可以作为桶的索引,将1,2,3,4,5分别通过f(key)得到1,2,3,4,0,则可将这几个key放入桶1,2,3,4,0的首地址所指的内存中,然后处理值为6的key,得到hashvalue值为1,这个时候需要放入桶1中,但桶1的首地址已经有了元素1,怎么办?那么就可以为每个桶开辟一片内存,内存中存放所有hashvalue相同的key,冲突的key之间用单向链表进行存储,这样就解决了哈希冲突,在查找对应key的时候,只需要通过key索引到对应的桶,然后从桶的首地址对应的节点开始查找,就是链表顺序找到,对比key的值,直到找到对应key的信息,所以,在冲突的时候,特别是冲突率比较高的时候,桶内的链表就会很长,使得查找效率比较低,而在最坏的情况下,所有的key都对应同一个hashvalue,当然这种情况不会出现,这样的哈希函数选取的也没有意义了,假设这种情况出现,那么哈希表就退化成了单链表,其他桶内存浪费,且将查找效率从O(1)直接降到了O(N),所以上面才说,哈希函数的选择也是很关键的。
在搞清楚哈希桶之后在看下面的代码:
HashTable.hh
首先
#ifndef _BOOLEAN_HH
#include "Boolean.hh"
#endif
意思就是如果没有这个宏定义,就包含以下我们自定义的boolean。
#ifndef _HASH_TABLE_HH
#define _HASH_TABLE_HH
#ifndef _BOOLEAN_HH
#include "Boolean.hh"
#endif
class HashTable {
public:
//定义析构函数
virtual ~HashTable();
// The following must be implemented by a particular
// implementation (subclass):
//创建一个表
static HashTable* create(int keyType);
//添加一个节点
virtual void* Add(char const* key, void* value) = 0;
// Returns the old value if different, otherwise 0
virtual Boolean Remove(char const* key) = 0;
//根据键值去查找一个元素
//const表示这个成员函数不会修改类的成员变量,也就是说这个成员函数的隐藏this参数是const的
//=0说明它是纯虚函数.只能让它的后代去实现这个函数的作用,自己不能实现。而且,是必须被后代实例化的。要是不加0,后代可以实现,也可以不实现
virtual void* Lookup(char const* key) const = 0;
// Returns 0 if not found
//元素数量
virtual unsigned numEntries() const = 0;
Boolean IsEmpty() const
{
return numEntries() == 0;
}
// Used to iterate through the members of the table:
class Iterator {
public:
// The following must be implemented by a particular
// implementation (subclass):
//给表创建一个迭代器
static Iterator* create(HashTable const& hashTable);
virtual ~Iterator();
//移动到下一个 参数里的key是什么意思要去实现里面看下
virtual void* next(char const*& key) = 0; // returns 0 if none
protected:
Iterator(); // abstract base class
};
// A shortcut that can be used to successively remove each of
// the entries in the table (e.g., so that their values can be
// deleted, if they happen to be pointers to allocated memory).
void* RemoveNext();
// Returns the first entry in the table.
// (This is useful for deleting each entry in the table, if the entry's destructor also removes itself from the table.)
//获取头元素这个会便于删除整个表的操作
void* getFirst();
protected:
HashTable(); // abstract base class
};
// Warning: The following are deliberately the same as in
// Tcl's hash table implementation
int const STRING_HASH_KEYS = 0;
int const ONE_WORD_HASH_KEYS = 1;
#endif
HashTable.cpp
接着我们去看这个表的实现:
void* HashTable::RemoveNext()
{
Iterator* iter = Iterator::create(*this);
char const* key;
void* removedValue = iter->next(key);
if (removedValue != 0)
{
Remove(key);
}
delete iter;
return removedValue;
}
void* HashTable::getFirst()
{
Iterator* iter = Iterator::create(*this);
char const* key;
void* firstValue = iter->next(key);
delete iter;
return firstValue;
}
BasicUsageEnvironment
打开发现cpp里面只实现了两个方法就是删除下一个元素和得到第一个元素。这里面还有一些实现我们要去找一找,发现在BasicUsageEnvironment项目下有子类继承了刚刚的HashTable。
BasicHashTable.hh
我们先来看下这个BasicHashTable类的头文件BasicHashTable.hh,先看一下表中节点的定义:
class TableEntry {
public:
TableEntry* fNext;
char const* key;
void* value;
};
然后在BasicHashtable中定义了一个继承HashTable中的iterator类的一个迭代类:
class Iterator : public HashTable::Iterator {
public:
Iterator(BasicHashTable const& table);
private:
// implementation of inherited pure virtual functions
void* next(char const*& key); // returns 0 if none
private:
BasicHashTable const& fTable;
unsigned fNextIndex; // index of next bucket to be enumerated after this
TableEntry* fNextEntry; // next entry in the current bucket
};
这个类中主要还是定义了构造函数,指向下一个节点的方法,还有三个成员变量,分别是表,下一个元素的下标,下一个元素的对象。接下来就是继承父类的方法。
virtual void* Add(char const* key, void* value);
// Returns the old value if different, otherwise 0
virtual Boolean Remove(char const* key);
virtual void* Lookup(char const* key) const;
// Returns 0 if not found
virtual unsigned numEntries() const;
接下来定义了自己的一些方法:
// returns entry matching "key", or NULL if none
//返回和下标对应的key
TableEntry* lookupKey(char const* key, unsigned& index) const;
// used to implement "lookupKey()"
// 两个key是否相等
Boolean keyMatches(char const* key1, char const* key2) const;
// creates a new entry, and inserts it in the table
//创建一个新的节点并插入到表中
TableEntry* insertNewEntry(unsigned index, char const* key);
// used to implement "insertNewEntry()"
void assignKey(TableEntry* entry, char const* key);
//删除节点
void deleteEntry(unsigned index, TableEntry* entry);
// used to implement "deleteEntry()"
void deleteKey(TableEntry* entry);
// rebuilds the table as its size increases
//当元素增长的时候要重构这个表
void rebuild();
// used to implement many of the routines above
unsigned hashIndexFromKey(char const* key) const;
unsigned randomIndex(uintptr_t i) const
{
//1103515245是个好数字,使通过hashCode散列出的0-15的数字的概率是相等的
return (unsigned)(((i * 1103515245) >> fDownShift) & fMask);
}
然后又定义了一些成员变量:
// pointer to bucket array
TableEntry** fBuckets;
// used for small tables
TableEntry* fStaticBuckets[SMALL_HASH_TABLE_SIZE];
unsigned fNumBuckets, fNumEntries, fRebuildSize, fDownShift, fMask;
int fKeyType;
目前我们还不知道这些方法和成员变量具体的作用,我们要通过cpp来看。其中unsigned 直接加变量名的写法是省略了int,编译器默认 是unsigned int。
BasicHashTable.cpp
接下来我们来看实现:
BasicHashTable::BasicHashTable(int keyType)
: fBuckets(fStaticBuckets),
fNumBuckets(SMALL_HASH_TABLE_SIZE),
fNumEntries(0),
fRebuildSize(SMALL_HASH_TABLE_SIZE*REBUILD_MULTIPLIER),
fDownShift(28),
fMask(0x3),
fKeyType(keyType)
{
for (unsigned i = 0; i < SMALL_HASH_TABLE_SIZE; ++i)
{
fStaticBuckets[i] = NULL;
}
}
BasicHashTable 用 TableEntry 指针的数组保存所有的键-值对。在该类对象创建时,一个小的TableEntry 指针数组fStaticBuckets 会随着对象的创建而创建,在 BasicHashTable 中元素比较少时,直接在这个数组中保存键值对,以此来优化当元素比较少的性能,降低内存分配的开销。fBuckets BasicHashTable 用 TableEntry 指针的数组保存所有的键-值对。在该类对象创建时,一个小的 TableEntry 指针数组 fStaticBuckets 会随着对象的创建而创建,在 BasicHashTable 中元素比较少时,直接在这个数组中保存键值对,以此来优化当元素比较少的性能,降低内存分配的开销。fBuckets 指向保存键-值对的 TableEntry 指针数组,在对象创建初期,它指向 fStaticBuckets,而在哈希桶扩容时,它指向新分配的 TableEntry 指针数组。对于容器中元素的访问,都通过
fBuckets 来完成。fNumBuckets 用于保存 TableEntry 指针数组的长度。fNumEntries 用于保存容器中键-值对的个数。fRebuildSize 为哈希桶扩容的阈值,即当 BasicHashTable 中保存的键值对超过该值时,哈希桶需要扩容。fDownShift 和 fMask 用于计算哈希值,并把哈希值映射到哈希桶容量范围内。
void* BasicHashTable::Add(char const* key, void* value) {
void* oldValue;
unsigned index;
TableEntry* entry = lookupKey(key, index);
if (entry != NULL)
{
// There's already an item with this key
oldValue = entry->value;
}
else
{
// There's no existing entry; create a new one:
entry = insertNewEntry(index, key);
oldValue = NULL;
}
entry->value = value;
// If the table has become too large, rebuild it with more buckets:
if (fNumEntries >= fRebuildSize)
{
rebuild();
}
return oldValue;
}
1.查找 BasicHashTable 中与要插入的键-值对的键匹配的元素 TableEntry。
2.若找到,把该元素的旧的值保存在 oldValue 中。
3.若没有找到,则通过 insertNewEntry(index, key) 创建一个 TableEntry 并加入到哈希桶中,oldValue 被赋值为 NULL。
4.把要插入的键-值对的值保存进新创建或找到的 TableEntry 中。
5.如果 BasicHashTable 中的元素个数超出 fRebuildSize 的大小,则对哈希桶扩容。
6.返回元素的旧的值。
unsigned BasicHashTable::hashIndexFromKey(char const* key) const
{
//将会生成一个唯一并且稳定的下标
unsigned result = 0;
if (fKeyType == STRING_HASH_KEYS)
{
while (1)
{
char c = *key++;
if (c == 0)
{
break;
}
result += (result << 3) + (unsigned)c;
}
//这个最终只会产生4个结果,因为是与0x3按位与运算 二进制0011 最终能产生的四个结果就是 0000 0001 0010 0011 分别是1234
result &= fMask;
}
else if (fKeyType == ONE_WORD_HASH_KEYS)
{
result = randomIndex((uintptr_t)key);
}
else
{
unsigned* k = (unsigned*)key;
uintptr_t sum = 0;
for (int i = 0; i < fKeyType; ++i)
{
sum += k[i];
}
result = randomIndex(sum);
}
return result;
}
这个函数是通过三种不同的key的存储方式来形成哈希值的,哈希值就是前面讲过的哈希桶中的索引。
Boolean BasicHashTable::keyMatches(char const* key1, char const* key2) const
{
// The way we check the keys for a match depends upon their type:
if (fKeyType == STRING_HASH_KEYS)
{
//第一种type的对两个字符串指针进行比较
// strcmp把 str1 所指向的字符串和 str2 所指向的字符串进行比较。
return (strcmp(key1, key2) == 0);
}
else if (fKeyType == ONE_WORD_HASH_KEYS)
{
//直接比较指针
return (key1 == key2);
}
else
{
unsigned* k1 = (unsigned*)key1;
unsigned* k2 = (unsigned*)key2;
for (int i = 0; i < fKeyType; ++i)
{
//对每一位进行比较
if (k1[i] != k2[i])
{
// keys differ
return False;
}
}
return True;
}
}
这个函数主要是针对三种不同的key的存储方式来进行分别比较的方法。
void* BasicHashTable::Lookup(char const* key) const
{
//实现继承父类的方法
unsigned index;
TableEntry* entry = lookupKey(key, index);
if (entry == NULL)
{
return NULL;
}
return entry->value;
}
BasicHashTable::TableEntry* BasicHashTable::lookupKey(char const* key, unsigned& index) const
{
TableEntry* entry;
//通过key的算法找到index
index = hashIndexFromKey(key);
for (entry = fBuckets[index]; entry != NULL; entry = entry->fNext)
{
if (keyMatches(key, entry->key))
{
break;
}
}
return entry;
}
这个查找的方法首先根据key找到对应的哈希值也就是index,找到index后,找到对应的首元素的地址,通过fnext,遍历拥有相同哈希index的每一个节点。一开始就是这里没有看明白,所以才去了解了哈希桶的概念。知道哈希桶的概念后这里也就能迎刃而解了。
BasicHashTable::TableEntry* BasicHashTable::insertNewEntry(unsigned index, char const* key)
{
//创建一个新的节点
TableEntry* entry = new TableEntry();
//entry的fNext指针指向原先的哈希对应的第一个元素
entry->fNext = fBuckets[index];
//赋值插入,将新的entry设为该哈希的第一个数据
fBuckets[index] = entry;
//数量加1
++fNumEntries;
//为这个节点设置key
assignKey(entry, key);
//返回这个节点
return entry;
}
void BasicHashTable::assignKey(TableEntry* entry, char const* key) {
// The way we assign the key depends upon its type:
if (fKeyType == STRING_HASH_KEYS)
{
//这个是含有\0的新建的内存
entry->key = strDup(key);
}
else if (fKeyType == ONE_WORD_HASH_KEYS)
{
//直接就是字符串指针
entry->key = key;
}
else if (fKeyType > 0)
{
//比1还大,例如2
//找到指针
unsigned* keyFrom = (unsigned*)key;
//创建一个无符号数组大小为2
unsigned* keyTo = new unsigned[fKeyType];
//遍历这个无符号的数组
for (int i = 0; i < fKeyType; ++i)
{
//赋值
keyTo[i] = keyFrom[i];
}
entry->key = (char const*)keyTo;
}
}
然后就是插入元素,找到对应的哈希也就是index,将新节点的fnext指针指向首元素,然后将新建的节点作为首元素。assignKey主要是为entry节点根据不同的key的方式设置键值。
下面来看一下删除的操作:
Boolean BasicHashTable::Remove(char const* key)
{
//继承父类的删除节点
unsigned index;
TableEntry* entry = lookupKey(key, index);
if (entry == NULL)
{
return False;
}
deleteEntry(index, entry);
return True;
}
void BasicHashTable::deleteEntry(unsigned index, TableEntry* entry)
{
//fBuckets[index]的值为地址
//ep为指向fBuckets[index]的地址的地址
TableEntry** ep = &fBuckets[index];
Boolean foundIt = False;
//*ep为ep取值,也就是fBuckets[index]的值他也是个地址
while (*ep != NULL)
{
if (*ep == entry)
{
foundIt = True;
//找到了就要把ep的值改为要删除元素指向的下一个元素的地址
*ep = entry->fNext;
break;
}
//否则就继续到下一个元素
ep = &((*ep)->fNext);
}
if (!foundIt)
{
// shouldn't happen
#ifdef DEBUG
fprintf(stderr, "BasicHashTable[%p]::deleteEntry(%d,%p): internal error - not found (first entry %p", this, index, entry, fBuckets[index]);
if (fBuckets[index] != NULL) fprintf(stderr, ", next entry %p", fBuckets[index]->fNext);
fprintf(stderr, ")\n");
#endif
}
//相应的减少数量
--fNumEntries;
deleteKey(entry);
delete entry;
}
void BasicHashTable::deleteKey(TableEntry* entry)
{
// The way we delete the key depends upon its type:
if (fKeyType == ONE_WORD_HASH_KEYS)
{
entry->key = NULL;
}
else
{
delete[](char*)entry->key;
entry->key = NULL;
}
}
删除元素同样是先找到对应的哈希也就是index,根据首地址和fnext,从开始位置进行遍历找到相同的entry,进行删除,根据不同ket的类型删除的方式也不相同。其中应用的2级指针对链表进行遍历删除是一种很好的操作。
接下来,当:
if (fNumEntries >= fRebuildSize)
{
rebuild();
}
add的时候节点数量超过设定的阈值时要进行扩容操作。
void BasicHashTable::rebuild()
{
// Remember the existing table size:
//记录原先的节点数量
unsigned oldSize = fNumBuckets;
//记录节点的首地址
TableEntry** oldBuckets = fBuckets;
//扩充4倍的大小
// Create the new sized table:
fNumBuckets *= 4;
//分配新的内存这应该是c++一种初始化方法
fBuckets = new TableEntry*[fNumBuckets];
for (unsigned i = 0; i < fNumBuckets; ++i)
{
//所有元素线全部初始化为0
fBuckets[i] = NULL;
}
fRebuildSize *= 4;
fDownShift -= 2;
//每次所能确定的范围增加4倍
fMask = (fMask << 2) | 0x3;
// Rehash the existing entries into the new table:
for (TableEntry** oldChainPtr = oldBuckets; oldSize > 0; --oldSize, ++oldChainPtr)
{
for (TableEntry* hPtr = *oldChainPtr; hPtr != NULL; hPtr = *oldChainPtr)
{
*oldChainPtr = hPtr->fNext;
unsigned index = hashIndexFromKey(hPtr->key);
hPtr->fNext = fBuckets[index];
fBuckets[index] = hPtr;
}
}
// Free the old bucket array, if it was dynamically allocated:
if (oldBuckets != fStaticBuckets) delete[] oldBuckets;
}
这也是我认为是这个源文件中最难理解的一个方法了,一定要先理解哈希桶之后再来看,那时候就很简单了,对于最外层循环,可以看下面这张图。
对于内层循环可以看下面这张图,其中h代表hptr,n代表null,图中列举了当外层循环进行第一次循环的时候,内存循环的遍历,内层循环中,所有的entry都有相同的哈希值也就是通过key计算得到的index都是相同的,对扩容前的哈希桶来说,每一个index存储了一条链表的首地址,这里只要链表元素的下一个元素不是null,就进行运算,更新old的位置,使hptr的下一个元素指向当前的首地址,然后使hptr成为新的首地址,也就是说,旧的元素被从首部开始被添加。
接下来我们继续看一下类中对iterator的实现:
BasicHashTable::Iterator::Iterator(BasicHashTable const& table)
: fTable(table), fNextIndex(0), fNextEntry(NULL) {
}
void* BasicHashTable::Iterator::next(char const*& key) {
while (fNextEntry == NULL) {
if (fNextIndex >= fTable.fNumBuckets) return NULL;
fNextEntry = fTable.fBuckets[fNextIndex++];
}
BasicHashTable::TableEntry* entry = fNextEntry;
fNextEntry = entry->fNext;
key = entry->key;
return entry->value;
}
. . . . . .
HashTable::Iterator* HashTable::Iterator::create(HashTable const& hashTable) {
// "hashTable" is assumed to be a BasicHashTable
return new BasicHashTable::Iterator((BasicHashTable const&)hashTable);
}
BasicHashTable 的 fBuckets 中的每个元素都保存一个 TableEntry 的链表。在这里会逐个链表地遍历。
以上就是HashTable的内容了,他是一个live555自己实现的哈希桶,这个数据结构我也是第一次见到,可以学习的地方还是很多的。接下来我们就要进入下一个源文件的学习了。
UsageEnvironment.hh
我们来看一下UsageEnvironment这个类的头文件:
//reclaim 再利用回收
Boolean reclaim();
// returns True if we were actually able to delete our object
// task scheduler:
//返回引用
TaskScheduler& taskScheduler() const
{
return fScheduler;
}
先是声明两个方法,然后:
// result message handling:
typedef char const* MsgString;
virtual MsgString getResultMsg() const = 0;
virtual void setResultMsg(MsgString msg) = 0;
virtual void setResultMsg(MsgString msg1, MsgString msg2) = 0;
virtual void setResultMsg(MsgString msg1, MsgString msg2, MsgString msg3) = 0;
virtual void setResultErrMsg(MsgString msg, int err = 0) = 0;
// like setResultMsg(), except that an 'errno' message is appended. (If "err == 0", the "getErrno()" code is used instead.)
virtual void appendToResultMsg(MsgString msg) = 0;
virtual void reportBackgroundError() = 0;
// used to report a (previously set) error message within
// a background event
virtual void internalError(); // used to 'handle' a 'should not occur'-type error condition within the library.
// 'errno'
virtual int getErrno() const = 0;
定义了一个类型,然后根据这个类型定义了一系列虚函数,这些虚函数都是和输出日志打印有关系的,然后重载了<<运算符,这个具体的作用我们稍后在实现中去看,
// 'console' output:
virtual UsageEnvironment& operator<<(char const* str) = 0;
virtual UsageEnvironment& operator<<(int i) = 0;
virtual UsageEnvironment& operator<<(unsigned u) = 0;
virtual UsageEnvironment& operator<<(double d) = 0;
virtual UsageEnvironment& operator<<(void* p) = 0;
然后定义了两个成员变量
// a pointer to additional, optional, client-specific state
void* liveMediaPriv;
void* groupsockPriv;
这两个成员变量具体是做什么的我们也在实现中去看,接着:
protected:
UsageEnvironment(TaskScheduler& scheduler); // abstract base class
virtual ~UsageEnvironment(); // we are deleted only by reclaim()
private:
TaskScheduler& fScheduler;
这里定义了类的构造函数,需要传入一个TaskScheduler的类,并且声明了一个该类型的引用,接着在头文件中定义了:
typedef void TaskFunc(void* clientData);
typedef void* TaskToken;
typedef u_int32_t EventTriggerId;
定义一个函数模版参数为void*型
定义一个TaskToken为void*型
定义一个无符号的int32型
接下来,UsageEnvironment.hh还声明了一个TaskScheduler的类,在该类中,
virtual ~TaskScheduler();
virtual TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc, void* clientData) = 0;
// Schedules a task to occur (after a delay) when we next
// reach a scheduling point.
// (Does not delay if "microseconds" <= 0)
// Returns a token that can be used in a subsequent call to
// unscheduleDelayedTask() or rescheduleDelayedTask()
// (but only if the task has not yet occurred).
virtual void unscheduleDelayedTask(TaskToken& prevTask) = 0;
// (Has no effect if "prevTask" == NULL)
// Sets "prevTask" to NULL afterwards.
// Note: This MUST NOT be called if the scheduled task has already occurred.
virtual void rescheduleDelayedTask(TaskToken& task, int64_t microseconds, TaskFunc* proc, void* clientData);
// Combines "unscheduleDelayedTask()" with "scheduleDelayedTask()"
// (setting "task" to the new task token).
// Note: This MUST NOT be called if the scheduled task has already occurred.
// For handling socket operations in the background (from the event loop):
typedef void BackgroundHandlerProc(void* clientData, int mask);
// Possible bits to set in "mask". (These are deliberately defined
// the same as those in Tcl, to make a Tcl-based subclass easy.)
定义了析构函数和四个方法,scheduleDelayedTask返回一个函数指针,unscheduleDelayedTask为取消计划任务,rescheduleDelayedTask字面意思为重新启用计划任务。
// 0010 0100 1000
#define SOCKET_READABLE (1<<1)
#define SOCKET_WRITABLE (1<<2)
#define SOCKET_EXCEPTION (1<<3)
预定义了三个别名,他们的值也在注释中标注了。
virtual void setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) = 0;
virtual void moveSocketHandling(int oldSocketNum, int newSocketNum) = 0;
void disableBackgroundHandling(int socketNum)
{
setBackgroundHandling(socketNum, 0, NULL, NULL);
}
这边定义了三个后台任务相关的接口,设置,修改,关闭,具体的实现到cpp中再去看。
virtual void doEventLoop(char volatile* watchVariable = NULL) = 0;
// Causes further execution to take place within the event loop.
// Delayed tasks, background I/O handling, and other events are handled, sequentially (as a single thread of control).
// (If "watchVariable" is not NULL, then we return from this routine when *watchVariable != 0)
这一个方法之所以要单独写,是因为他真的很重要哦。
virtual EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc) = 0;
// Creates a 'trigger' for an event, which - if it occurs - will be handled (from the event loop) using "eventHandlerProc".
// (Returns 0 iff no such trigger can be created (e.g., because of implementation limits on the number of triggers).)
virtual void deleteEventTrigger(EventTriggerId eventTriggerId) = 0;
virtual void triggerEvent(EventTriggerId eventTriggerId, void* clientData = NULL) = 0;
// Causes the (previously-registered) handler function for the specified event to be handled (from the event loop).
// The handler function is called with "clientData" as parameter.
// Note: This function (unlike other library functions) may be called from an external thread
// - to signal an external event. (However, "triggerEvent()" should not be called with the
// same 'event trigger id' from different threads.)
这里定义了三个事件触发的函数,分别是设置,删除,触发。
void turnOnBackgroundReadHandling(int socketNum, BackgroundHandlerProc* handlerProc, void* clientData)
{
setBackgroundHandling(socketNum, SOCKET_READABLE, handlerProc, clientData);
}
void turnOffBackgroundReadHandling(int socketNum)
{
disableBackgroundHandling(socketNum);
}
virtual void internalError(); // used to 'handle' a 'should not occur'-type error condition within the library.
protected:
TaskScheduler(); // abstract base class
UsageEnvironment.cpp
下面我们来看看UsageEnvironment.cpp中为我们实现了上述方法的哪些。
Boolean UsageEnvironment::reclaim() {
// We delete ourselves only if we have no remainining state:
if (liveMediaPriv == NULL && groupsockPriv == NULL)
{
delete this;
return True;
}
return False;
}
UsageEnvironment::UsageEnvironment(TaskScheduler& scheduler)
: liveMediaPriv(NULL), groupsockPriv(NULL), fScheduler(scheduler) {
}
UsageEnvironment::~UsageEnvironment() {
}
我们看到了类的构造函数析构函数还有回收内存的函数。其中构造函数中,UsageEnvironment初始了liveMediaPriv和groupsockPriv为空,Scheduler为构造时传入的参数。
// By default, we handle 'should not occur'-type library errors by calling abort(). Subclasses can redefine this, if desired.
// (If your runtime library doesn't define the "abort()" function, then define your own (e.g., that does nothing).)
void UsageEnvironment::internalError() {
//终止程序的运行,程序的异常退出
abort();
}
// By default, we handle 'should not occur'-type library errors by calling abort(). Subclasses can redefine this, if desired.
void TaskScheduler::internalError()
{
//终止程序的运行,程序的异常退出
abort();
}
两个错误的基类实现。
void TaskScheduler::rescheduleDelayedTask(TaskToken& task, int64_t microseconds, TaskFunc* proc, void* clientData)
{
unscheduleDelayedTask(task);
task = scheduleDelayedTask(microseconds, proc, clientData);
}
rescheduleDelayedTask的实现,首先是取消然后新建一个计划任务。
UsageEnvironment.cpp里面的内容大致就这么多,可以发现还有很多hh中的方法没有被实现,我们可以继续找一下这个类有没有其他的继承。
BasicUsageEnvironment0.hh
我们在BasicUsageEnvironment项目下找到了BasicUsageEnvironment0.hh,这是一个继承自UsageEnvironment的类,同样的我们先看看他的头文件。
// An abstract base class, useful for subclassing
// (e.g., to redefine the implementation of "operator<<")
class BasicUsageEnvironment0 : public UsageEnvironment {
public:
// redefined virtual functions:
virtual MsgString getResultMsg() const;
virtual void setResultMsg(MsgString msg);
virtual void setResultMsg(MsgString msg1, MsgString msg2);
virtual void setResultMsg(MsgString msg1, MsgString msg2, MsgString msg3);
virtual void setResultErrMsg(MsgString msg, int err = 0);
virtual void appendToResultMsg(MsgString msg);
virtual void reportBackgroundError();
protected:
BasicUsageEnvironment0(TaskScheduler& taskScheduler);
virtual ~BasicUsageEnvironment0();
private:
void reset();
char fResultMsgBuffer[RESULT_MSG_BUFFER_MAX];
unsigned fCurBufferSize;
unsigned fBufferMaxSize;
};
就是重新定义了基类的虚函数,然后声明了一个reset方法,定义了一个信息的缓存,大小是RESULT_MSG_BUFFER_MAX。
下面又定义了BasicTaskScheduler0这个类,这个类正好继承自TaskScheduler
#define MAX_NUM_EVENT_TRIGGERS 32
// An abstract base class, useful for subclassing
// (e.g., to redefine the implementation of socket event handling)
class BasicTaskScheduler0 : public TaskScheduler {
public:
virtual ~BasicTaskScheduler0();
virtual void SingleStep(unsigned maxDelayTime = 0) = 0;
// "maxDelayTime" is in microseconds. It allows a subclass to impose a limit
// on how long "select()" can delay, in case it wants to also do polling.
// 0 (the default value) means: There's no maximum; just look at the delay queue
public:
// Redefined virtual functions:
//基类函数重定义
virtual TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc, void* clientData);
virtual void unscheduleDelayedTask(TaskToken& prevTask);
virtual void doEventLoop(char volatile* watchVariable);
virtual EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc);
virtual void deleteEventTrigger(EventTriggerId eventTriggerId);
virtual void triggerEvent(EventTriggerId eventTriggerId, void* clientData = NULL);
protected:
//构造函数
BasicTaskScheduler0();
protected:
// To implement delayed operations:
//这里应该是队列
DelayQueue fDelayQueue;
// To implement background reads:
HandlerSet* fHandlers;
int fLastHandledSocketNum;
// To implement event triggers:
EventTriggerId volatile fTriggersAwaitingHandling; // implemented as a 32-bit bitmap
EventTriggerId fLastUsedTriggerMask; // implemented as a 32-bit bitmap
TaskFunc* fTriggeredEventHandlers[MAX_NUM_EVENT_TRIGGERS];
void* fTriggeredEventClientDatas[MAX_NUM_EVENT_TRIGGERS];
unsigned fLastUsedTriggerNum; // in the range [0,MAX_NUM_EVENT_TRIGGERS)
};
这个类中首先是析构函数,然后声明了一个方法SingleStep,然后重定义了基类的虚函数,然后定义了构造函数,定义了一个延时队列,这里还只是猜测因为还没有看DelayQueue的定义,HandlerSet也是接下里要看的,这里主要是看一下,EventTriggerId这几行,首先定义了一个无符号32位整型fTriggersAwaitingHandling,然后定义了无符号32位整型fLastUsedTriggerMask,然后定义了一个模版函数的指针,指向MAX_NUM_EVENT_TRIGGERS大小的一个数组,然后定义了一个void*行的同样大小的数组用来存放用户数据,最后定义了一个无符号整型的fLastUsedTriggerNum,这些变量具体的意义我们在cpp中去寻找。
BasicTaskScheduler0.cpp
接下来我们就去看一下BasicTaskScheduler0.cpp。
class AlarmHandler : public DelayQueueEntry {
public:
AlarmHandler(TaskFunc* proc, void* clientData, DelayInterval timeToDelay)
: DelayQueueEntry(timeToDelay), fProc(proc), fClientData(clientData)
{
}
private: // redefined virtual functions
virtual void handleTimeout()
{
(*fProc)(fClientData);
DelayQueueEntry::handleTimeout();
}
private:
TaskFunc* fProc;
void* fClientData;
};
这里我们发现第一个函数是一个继承自DelayQueueEntry的类。
DelayQueue.hh
这里我们不得不插播一个头文件和源文件,就是DelayQueue,我们先来看看他的头文件DelayQueue.hh。因为这个里面定义太多的接下来要用的类。
#ifdef TIME_BASE
typedef TIME_BASE time_base_seconds;
#else
typedef long time_base_seconds;
#endif
显示定义了两个类型,time_base_seconds为64位的整型。
首先是一个自定义的时间类:
class Timeval {
public:
time_base_seconds seconds() const
{
//返回秒
return fTv.tv_sec;
}
time_base_seconds seconds()
{
//返回秒
return fTv.tv_sec;
}
time_base_seconds useconds() const
{
//返回微秒
return fTv.tv_usec;
}
time_base_seconds useconds()
{
//返回微秒
return fTv.tv_usec;
}
//重载比较运算符
int operator>=(Timeval const& arg2) const;
int operator<=(Timeval const& arg2) const
{
return arg2 >= *this;
}
int operator<(Timeval const& arg2) const
{
return !(*this >= arg2);
}
int operator>(Timeval const& arg2) const
{
return arg2 < *this;
}
int operator==(Timeval const& arg2) const
{
return *this >= arg2 && arg2 >= *this;
}
int operator!=(Timeval const& arg2) const
{
return !(*this == arg2);
}
void operator+=(class DelayInterval const& arg2);
void operator-=(class DelayInterval const& arg2);
// returns ZERO iff arg2 >= arg1
protected:
//构造函数
Timeval(time_base_seconds seconds, time_base_seconds useconds)
{
fTv.tv_sec = seconds; fTv.tv_usec = useconds;
}
private:
time_base_seconds& secs()
{
return (time_base_seconds&)fTv.tv_sec;
}
time_base_seconds& usecs()
{
return (time_base_seconds&)fTv.tv_usec;
}
//该结构体是Linux系统中定义,其中tv_sec为秒的部分,tv_usec为微妙的部分
struct timeval fTv;
};
我标注了一些注释,主要就是对c结构体timeval进行了一次再封装。
#ifndef max
inline Timeval max(Timeval const& arg1, Timeval const& arg2) {
return arg1 >= arg2 ? arg1 : arg2;
}
#endif
#ifndef min
inline Timeval min(Timeval const& arg1, Timeval const& arg2) {
return arg1 <= arg2 ? arg1 : arg2;
}
#endif
然后定义两个内联函数,取两个Timeval中较大的和较小的。
class DelayInterval : public Timeval {
public:
DelayInterval(time_base_seconds seconds, time_base_seconds useconds)
: Timeval(seconds, useconds) {}
};
定义了一个DelayInterval类,这个类应该是用于表示时间差,时间跨度。
extern DelayInterval const DELAY_ZERO;
extern DelayInterval const DELAY_SECOND;
extern DelayInterval const DELAY_MINUTE;
extern DelayInterval const DELAY_HOUR;
extern DelayInterval const DELAY_DAY;
定义了5个DelayInterval的常量。
class _EventTime : public Timeval {
public:
_EventTime(unsigned secondsSinceEpoch = 0, unsigned usecondsSinceEpoch = 0)
// We use the Unix standard epoch: January 1, 1970
: Timeval(secondsSinceEpoch, usecondsSinceEpoch) {}
};
_EventTime TimeNow();
extern _EventTime const THE_END_OF_TIME;
定义了一个事件时间的类。
/ DelayQueueEntry /
class DelayQueueEntry {
public:
virtual ~DelayQueueEntry();
intptr_t token()
{
//返回一个token值
return fToken;
}
protected:
//传入一个时间延迟
DelayQueueEntry(DelayInterval delay);
//处理函数
virtual void handleTimeout();
private:
friend class DelayQueue;
//下一个节点
DelayQueueEntry* fNext;
//上一个节点
DelayQueueEntry* fPrev;
//剩余的时间
DelayInterval fDeltaTimeRemaining;
//token值
intptr_t fToken;
static intptr_t tokenCounter;
};
这是一个延迟队列的一个节点。
/ DelayQueue /
class DelayQueue : public DelayQueueEntry {
public:
//构造函数
DelayQueue();
virtual ~DelayQueue();
// returns a token for the entry
//添加任务节点
void addEntry(DelayQueueEntry* newEntry);
//更新任务节点,传入一个新的时间
void updateEntry(DelayQueueEntry* entry, DelayInterval newDelay);
//更新任务节点,根据token找到节点传入一个新的时间
void updateEntry(intptr_t tokenToFind, DelayInterval newDelay);
//移除任务节点
// but doesn't delete it
void removeEntry(DelayQueueEntry* entry);
// but doesn't delete it
//移除任务节点,根据token找到节点
DelayQueueEntry* removeEntry(intptr_t tokenToFind);
//字面意思下一个任务的时间,在cpp中看详细的
DelayInterval const& timeToNextAlarm();
//处理
void handleAlarm();
private:
//返回头结点
DelayQueueEntry* head() { return fNext; }
//通过token查找节点
DelayQueueEntry* findEntryByToken(intptr_t token);
// bring the 'time remaining' fields up-to-date
void synchronize();
_EventTime fLastSyncTime;
};
这个类就是延迟任务队列,解释已经写在了注释上面。
DelayQueue.cpp
下面我们去看这个DelayQueue.cpp
static const int MILLION = 1000000;
/ Timeval /
int Timeval::operator>=(const Timeval& arg2) const
{
return seconds() > arg2.seconds() || (seconds() == arg2.seconds() && useconds() >= arg2.useconds());
}
void Timeval::operator+=(const DelayInterval& arg2)
{
secs() += arg2.seconds();
usecs() += arg2.useconds();
if (useconds() >= MILLION)
{
usecs() -= MILLION;
++secs();
}
}
void Timeval::operator-=(const DelayInterval& arg2)
{
secs() -= arg2.seconds();
usecs() -= arg2.useconds();
if ((int)useconds() < 0)
{
usecs() += MILLION;
--secs();
}
if ((int)seconds() < 0)
{
secs() = usecs() = 0;
}
}
DelayInterval operator-(const Timeval& arg1, const Timeval& arg2)
{
time_base_seconds secs = arg1.seconds() - arg2.seconds();
time_base_seconds usecs = arg1.useconds() - arg2.useconds();
if ((int)usecs < 0)
{
usecs += MILLION;
--secs;
}
if ((int)secs < 0)
{
return DELAY_ZERO;
}
else
{
return DelayInterval(secs, usecs);
}
}
/ DelayInterval /
DelayInterval operator*(short arg1, const DelayInterval& arg2)
{
time_base_seconds result_seconds = arg1 * arg2.seconds();
time_base_seconds result_useconds = arg1 * arg2.useconds();
time_base_seconds carry = result_useconds / MILLION;
result_useconds -= carry * MILLION;
result_seconds += carry;
return DelayInterval(result_seconds, result_useconds);
}
上来先是对头文件中的运算符重载进行了实现。
const DelayInterval DELAY_ZERO(0, 0);
const DelayInterval DELAY_SECOND(1, 0);
const DelayInterval DELAY_MINUTE = 60 * DELAY_SECOND;
const DelayInterval DELAY_HOUR = 60 * DELAY_MINUTE;
const DelayInterval DELAY_DAY = 24 * DELAY_HOUR;
const DelayInterval ETERNITY(INT_MAX, MILLION - 1);
// used internally to make the implementation work
然后对头文件中的DelayInterval类型的常量进行了赋值。
/ DelayQueueEntry /
intptr_t DelayQueueEntry::tokenCounter = 0;
//构造函数,传入一个延时变量
DelayQueueEntry::DelayQueueEntry(DelayInterval delay)
: fDeltaTimeRemaining(delay)
{
//初始的上一个节点下一个几点都是自己
fNext = fPrev = this;
//+1
fToken = ++tokenCounter;
}
DelayQueueEntry::~DelayQueueEntry() {
}
//这里应该是超时的任务的处理逻辑
void DelayQueueEntry::handleTimeout() {
delete this;
}
这里定义了DelayQueueEntry的构造函数析构函数还有处理超时函数。
下面我们来看DelayQueue部分实现。
DelayQueue::DelayQueue()
: DelayQueueEntry(ETERNITY)
{
//构造时记录了当前时间
fLastSyncTime = TimeNow();
}
/ _EventTime /
_EventTime TimeNow()
{
struct timeval tvNow;
gettimeofday(&tvNow, NULL);
return _EventTime(tvNow.tv_sec, tvNow.tv_usec);
}
const _EventTime THE_END_OF_TIME(INT_MAX);
DelayQueue::~DelayQueue()
{
while (fNext != this)
{
DelayQueueEntry* entryToRemove = fNext;
removeEntry(entryToRemove);
delete entryToRemove;
}
}
构造函数和析构函数。
void DelayQueue::addEntry(DelayQueueEntry* newEntry)
{
//先检查一下所有节点的时间
synchronize();
//找到当前节点下一个节点
DelayQueueEntry* cur = head();
//newEntry的剩余时间是否大于等于当前节点的剩余时间
while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining)
{
//使newEntry的剩余时间等于他减去当前节点的剩余时间,这里看得到每个节点的剩余时间都是相对上一个几点的剩余时间
newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining;
//移动到下一个节点
cur = cur->fNext;
}
//否则当前节点的剩余时间减去新节点的剩余时间 是要放在当前节点的前面
cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining;
// Add "newEntry" to the queue, just before "cur":
//放在cur前面
//更新指针的指向,新节点的下一节点指针指向当前节点
newEntry->fNext = cur;
//新节点的上一节点指针指向当前节点上一节点指针
newEntry->fPrev = cur->fPrev;
//当前节点的上一节点指针和新节点的上一节点的下一节点指针指向新节点
cur->fPrev = newEntry->fPrev->fNext = newEntry;
}
void DelayQueue::synchronize()
{
// First, figure out how much time has elapsed since the last sync:
_EventTime timeNow = TimeNow();
if (timeNow < fLastSyncTime)
{
// The system clock has apparently gone back in time; reset our sync time and return:
fLastSyncTime = timeNow;
return;
}
//自上次记录时间以来经过的时间
DelayInterval timeSinceLastSync = timeNow - fLastSyncTime;
//重置为当前时间
fLastSyncTime = timeNow;
// Then, adjust the delay queue for any entries whose time is up:
DelayQueueEntry* curEntry = head();
while (timeSinceLastSync >= curEntry->fDeltaTimeRemaining)
{
//自上次经过的时间大于了该节点剩余的时间
//timeSinceLastSync减去当前任务的剩余时间后,因为每个任务节点的时间是相对于上一个的
timeSinceLastSync -= curEntry->fDeltaTimeRemaining;
//更新任务节点的fDeltaTimeRemaining为0
curEntry->fDeltaTimeRemaining = DELAY_ZERO;
//移动到下一个节点
curEntry = curEntry->fNext;
}
//否则的话减去timeSinceLastSync
curEntry->fDeltaTimeRemaining -= timeSinceLastSync;
}
这个是添加加点操作,主要就是插入到剩余执行时间合适的位置,找到合适的位置然后插入,add之前也要循环检查一遍所有的节点的运行时间。注释写的很清楚可以看看。
void DelayQueue::updateEntry(DelayQueueEntry* entry, DelayInterval newDelay)
{
if (entry == NULL)
{
return;
}
//先删除
removeEntry(entry);
entry->fDeltaTimeRemaining = newDelay;
//再添加
addEntry(entry);
}
void DelayQueue::updateEntry(intptr_t tokenToFind, DelayInterval newDelay)
{
DelayQueueEntry* entry = findEntryByToken(tokenToFind);
updateEntry(entry, newDelay);
}
这是更新节点的剩余执行时间。
void DelayQueue::removeEntry(DelayQueueEntry* entry)
{
if (entry == NULL || entry->fNext == NULL)
{
return;
}
//将该节点的下一个节点剩余时间加上该节点的剩余时间,所以这个剩余时间是不是相对于上一个节点的时间
entry->fNext->fDeltaTimeRemaining += entry->fDeltaTimeRemaining;
//将该节点上一个节点的下一个节点指针指向该节点的下一个节点
entry->fPrev->fNext = entry->fNext;
//将该节点的下一个节点的上一个节点指针指向该节点的上一个节点指针
entry->fNext->fPrev = entry->fPrev;
//将该节点的下一个节点指针和上一个节点指针都置为空
entry->fNext = entry->fPrev = NULL;
// in case we should try to remove it again
}
DelayQueueEntry* DelayQueue::removeEntry(intptr_t tokenToFind)
{
DelayQueueEntry* entry = findEntryByToken(tokenToFind);
removeEntry(entry);
return entry;
}
DelayQueueEntry* DelayQueue::findEntryByToken(intptr_t tokenToFind)
{
DelayQueueEntry* cur = head();
while (cur != this)
{
if (cur->token() == tokenToFind)
{
return cur;
}
cur = cur->fNext;
}
return NULL;
}
这是删除节点的相关操作,注释写的已经很完整了吗这里就不在对代码进行说明了,值得注意的是head()这个函数,在头文件中,
//返回头结点
DelayQueueEntry* head() { return fNext; }
他返回的是下一个节点的指针,和我们想象的不太一样,我们觉得这个应该是返回头结点指针,然后我们忽略了之前的一个定义,
DelayQueueEntry::DelayQueueEntry(DelayInterval delay)
: fDeltaTimeRemaining(delay)
{
//初始的上一个节点下一个几点都是自己
fNext = fPrev = this;
//+1
fToken = ++tokenCounter;
}
在初始化的时候fNext = fPrev = this ,上一节点指针和下一节点指针都是指向自己的,这说明我们的DelayQueue数据结构是一个循环链表,所以head返回的就是下一个节点。
我们接下来来看HandlerSet.hh这个文件。这个文件也定义了一些类和成员变量。
class HandlerDescriptor {
//构造函数
HandlerDescriptor(HandlerDescriptor* nextHandler);
virtual ~HandlerDescriptor();
public:
int socketNum;
int conditionSet;
//处理逻辑
TaskScheduler::BackgroundHandlerProc* handlerProc;
//用户数据,也是处理逻辑所需要的参数
void* clientData;
private:
// Descriptors are linked together in a doubly-linked list:
friend class HandlerSet;
friend class HandlerIterator;
//下一个指针
HandlerDescriptor* fNextHandler;
//上一个指针
HandlerDescriptor* fPrevHandler;
};
首先是一个HandlerDescriptor,他看起来像是一个处理单元。
class HandlerSet {
public:
HandlerSet();
virtual ~HandlerSet();
void assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData);
void clearHandler(int socketNum);
void moveHandler(int oldSocketNum, int newSocketNum);
private:
HandlerDescriptor* lookupHandler(int socketNum);
private:
friend class HandlerIterator;
HandlerDescriptor fHandlers;
};
接着就是上一个处理单元他的容器了,他提供了一些接口的声明。
class HandlerIterator {
public:
HandlerIterator(HandlerSet& handlerSet);
virtual ~HandlerIterator();
HandlerDescriptor* next(); // returns NULL if none
void reset();
private:
HandlerSet& fOurSet;
HandlerDescriptor* fNextPtr;
};
接着就是定义这个容器的迭代器。看完了头文件,我们去找一下他们的实现在哪里。
我们看到这些实现都是在BasicTaskScheduler0中,那么我们现在回到BasicTaskScheduler0.cpp中
AlarmHandler这个类在之前提到了,我们重新看一下,他继承自上边的DelayQueueEntry,是一个延时任务节点,构造函数时需要传入一个模版函数,还有函数所需要的参数,还有计划时间。重写了基类的handleTimeout接口,执行模版函数后然后执行基类的handleTimeout。定义了两个成员变量,分别是模版函数指针还有函数所需要的参数。
HandlerSet.hh
接着我们看看HandlerSet头文件。
HandlerDescriptor::HandlerDescriptor(HandlerDescriptor* nextHandler)
: conditionSet(0), handlerProc(NULL)
{
// Link this descriptor into a doubly-linked list:
if (nextHandler == this)
{
// initialization
fNextHandler = fPrevHandler = this;
}
else
{
//构建双向链表
fNextHandler = nextHandler;
fPrevHandler = nextHandler->fPrevHandler;
nextHandler->fPrevHandler = this;
fPrevHandler->fNextHandler = this;
}
}
HandlerDescriptor::~HandlerDescriptor() {
// Unlink this descriptor from a doubly-linked list:
//把这个节点解除掉
fNextHandler->fPrevHandler = fPrevHandler;
fPrevHandler->fNextHandler = fNextHandler;
}
这个是HandlerDescriptor的构造函数和析构函数分别是将节点添加进双向链表和将节点移除出双向链表,可以看我这张图来理解。
序号分别对应指针操作的行数。
HandlerSet.cpp
接下来我们来看HandlerSet的实现。
HandlerSet::HandlerSet()
: fHandlers(&fHandlers) {
fHandlers.socketNum = -1; // shouldn't ever get looked at, but in case...
}
HandlerSet::~HandlerSet()
{
// Delete each handler descriptor:
while (fHandlers.fNextHandler != &fHandlers)
{
//这里会执行HandlerDescriptor的析构函数fHandlers->fNextHandler会自动指向下一个节点
delete fHandlers.fNextHandler; // changes fHandlers->fNextHandler
}
}
构造函数和析构函数,构造函数默认socketNum=-1,fHandlers为自身的引用,其中析构函数会从fHandlers.fNextHandler开始一个一个删除节点,每次删除完一个节点,会执行节点的析构函数更新fHandlers.fNextHandler的指向。
void HandlerSet::assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData)
{
// First, see if there's already a handler for this socket:
HandlerDescriptor* handler = lookupHandler(socketNum);
if (handler == NULL)
{
// No existing handler, so create a new descr:
//这里会向fHandlers和fHandlers.fNextHandler之间插入新节点,所以fHandlers总是第一个节点
handler = new HandlerDescriptor(fHandlers.fNextHandler);
handler->socketNum = socketNum;
}
handler->conditionSet = conditionSet;
handler->handlerProc = handlerProc;
handler->clientData = clientData;
}
这里为给socketNum对应的HandlerDescriptor赋值,如果没有找到对应的HandlerDescriptor,就创建一个新的,这里可以看到每次新建都是在fHandlers.fNextHandler之前,所以新加入的节点就是处在fHandlers和fHandlers.fNextHandler之间,所以fHandlers总是为第一个节点,这也就能解释析构的时候从fHandlers开始,这个fHandlers相当于数据结构的一个头结点。
void HandlerSet::clearHandler(int socketNum)
{
HandlerDescriptor* handler = lookupHandler(socketNum);
//这里会执行HandlerDescriptor的析构函数fHandlers->fNextHandler会自动指向下一个节点
delete handler;
}
void HandlerSet::moveHandler(int oldSocketNum, int newSocketNum)
{
HandlerDescriptor* handler = lookupHandler(oldSocketNum);
if (handler != NULL)
{
//修改新值
handler->socketNum = newSocketNum;
}
}
这两个方法就是清除对应socketNum的HandlerDescriptor,还有修改对应的HandlerDescriptor的newSocketNum。
HandlerDescriptor* HandlerSet::lookupHandler(int socketNum)
{
HandlerDescriptor* handler;
HandlerIterator iter(*this);
while ((handler = iter.next()) != NULL)
{
//移动指针直到找到了对应的socketNum的节点
if (handler->socketNum == socketNum)
{
break;
}
}
return handler;
}
这里为根据socketNum寻找对应的HandlerDescriptor。
HandlerIterator::HandlerIterator(HandlerSet& handlerSet)
: fOurSet(handlerSet)
{
reset();
}
HandlerIterator::~HandlerIterator() {
}
void HandlerIterator::reset()
{
//重新指向fOurSet.fHandlers的下一个节点
fNextPtr = fOurSet.fHandlers.fNextHandler;
}
HandlerDescriptor* HandlerIterator::next()
{
HandlerDescriptor* result = fNextPtr;
if (result == &fOurSet.fHandlers)
{
// no more
//如果fNextPtr指向fOurSet.fHandlers,说明fOurSet.fHandlers.fNextHandler为空
result = NULL;
}
else
{
//更新fNextPtr指向下一个节点,移动指针
fNextPtr = fNextPtr->fNextHandler;
}
return result;
}
这个类是HandlerSet中的迭代器,初始化的时候传入容器handlerSet,并且初始化fNextPtr指向fOurSet.fHandlers.fNextHandler。
HandlerSet头文件中对应的实现我们这里就看完了,BasicTaskScheduler0还有一些BasicUsageEnvironment0中BasicTaskScheduler0的实现,我们现在去看看。
BasicTaskScheduler0::BasicTaskScheduler0()
: fLastHandledSocketNum(-1), fTriggersAwaitingHandling(0), fLastUsedTriggerMask(1), fLastUsedTriggerNum(MAX_NUM_EVENT_TRIGGERS - 1)
{
fHandlers = new HandlerSet;
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i)
{
fTriggeredEventHandlers[i] = NULL;
fTriggeredEventClientDatas[i] = NULL;
}
}
BasicTaskScheduler0::~BasicTaskScheduler0() {
delete fHandlers;
}
BasicTaskScheduler0的构造函数,做一些初始化工作。
TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds, TaskFunc* proc, void* clientData)
{
if (microseconds < 0)
{
microseconds = 0;
}
DelayInterval timeToDelay((long)(microseconds / 1000000), (long)(microseconds % 1000000));
AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay);
fDelayQueue.addEntry(alarmHandler);
return (void*)(alarmHandler->token());
}
void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask)
{
DelayQueueEntry* alarmHandler = fDelayQueue.removeEntry((intptr_t)prevTask);
prevTask = NULL;
delete alarmHandler;
}
添加和移除计划的延时任务,添加时新建一个延时对象,新建一个AlarmHandler对象,然后将AlarmHandler添加进fDelayQueue队列中。返回这个AlarmHandler对象创建时产生的token。移除时根据token移除延时任务。
void BasicTaskScheduler0::doEventLoop(char volatile* watchVariable) {
// Repeatedly loop, handling readble sockets and timed events:
while (1)
{
if (watchVariable != NULL && *watchVariable != 0)
{
break;
}
SingleStep();
}
}
这个是循环执行任务的核心代码,SingleStep的逻辑会在子类中介绍。这里就是开启一个循环,不断的执行。其中watchVariable这个变量为空或者watchVariable的值为0的时候,SingleStep才会执行。watchVariable这个变量我们在后面调用的时候去看看具体是什么。
BasicUsageEnvironment0.cpp
接下来接着去看BasicUsageEnvironment0的实现。
// BasicUsageEnvironment //
BasicUsageEnvironment0::BasicUsageEnvironment0(TaskScheduler& taskScheduler)
: UsageEnvironment(taskScheduler),
//初始化传入一个TaskScheduler,设置fBufferMaxSize
fBufferMaxSize(RESULT_MSG_BUFFER_MAX) {
//初始化fResultMsgBuffer
reset();
}
BasicUsageEnvironment0::~BasicUsageEnvironment0() {
}
void BasicUsageEnvironment0::reset() {
//reset就是在第一个位置接一个结束符号
fCurBufferSize = 0;
fResultMsgBuffer[fCurBufferSize] = '\0';
}
// Implementation of virtual functions:
char const* BasicUsageEnvironment0::getResultMsg() const {
//返回fResultMsgBuffer
return fResultMsgBuffer;
}
void BasicUsageEnvironment0::setResultMsg(MsgString msg) {
//先重置然后塞数据
reset();
appendToResultMsg(msg);
}
void BasicUsageEnvironment0::setResultMsg(MsgString msg1, MsgString msg2) {
//上一个函数的拓展
setResultMsg(msg1);
appendToResultMsg(msg2);
}
void BasicUsageEnvironment0::setResultMsg(MsgString msg1, MsgString msg2, MsgString msg3) {
//上一个函数的拓展
setResultMsg(msg1, msg2);
appendToResultMsg(msg3);
}
void BasicUsageEnvironment0::setResultErrMsg(MsgString msg, int err) {
setResultMsg(msg);
if (err == 0)
{
//获取errornumber
err = getErrno();
}
#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE)
//win
#ifndef _UNICODE
//errMsg的最后以为变为结束符
char errMsg[RESULT_MSG_BUFFER_MAX] = "\0";
//FormatMessage是一个Windows API函数。它的功能就是将GetLastError函数得到的错误信息(这个错误信息是数字代号)转化成字符串信息的函数
if (0 != FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, errMsg, sizeof(errMsg) / sizeof(errMsg[0]), NULL))
{
// Remove all trailing '\r', '\n' and '.'
//声明一个指针p 其中errMsg为首地址,使p执行errMsg的末尾;当指针p没有指向首地址并且指向换行结束符号的时候;每一次操作将p向前移动一位
for (char* p = errMsg + strlen(errMsg); p != errMsg && (*p == '\r' || *p == '\n' || *p == '.' || *p == '\0'); --p)
{
//将这些值置为结束符,主要是为了替换末尾的这些换行回车符号
*p = '\0';
}
}
else
{
//如果错误代号==0输出错误代号和错误内容
snprintf(errMsg, sizeof(errMsg) / sizeof(errMsg[0]), "error %d", err);
}
//塞进数组中
appendToResultMsg(errMsg);
#endif
#else
//win之外的平台直接进行塞入
appendToResultMsg(strerror(err));
#endif
}
void BasicUsageEnvironment0::appendToResultMsg(MsgString msg)
{
char* curPtr = &fResultMsgBuffer[fCurBufferSize];
unsigned spaceAvailable = fBufferMaxSize - fCurBufferSize;
unsigned msgLength = strlen(msg);
// Copy only enough of "msg" as will fit:
//空间不够就复制空间大小的内容,其余就舍弃了
if (msgLength > spaceAvailable - 1)
{
msgLength = spaceAvailable - 1;
}
memmove(curPtr, (char*)msg, msgLength);
fCurBufferSize += msgLength;
fResultMsgBuffer[fCurBufferSize] = '\0';
}
void BasicUsageEnvironment0::reportBackgroundError()
{
//C 库函数 int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符。
//int fputs(const char *str, FILE *stream)
//str -- 这是一个数组,包含了要写入的以空字符终止的字符序列。
//stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流。
//标准输出(设备)文件,对应终端的屏幕。进程将从标准输入文件中得到输入数据,将正常输出数据输出到标准输出文件,而将错误信息送到标准错误文件中。在C中,程序执行时,一直处于开启状态。
fputs(getResultMsg(), stderr);
}
BasicUsageEnvironment.hh
接下来看看BasicUsageEnvironment的头文件
class BasicUsageEnvironment : public BasicUsageEnvironment0 {
public:
static BasicUsageEnvironment* createNew(TaskScheduler& taskScheduler);
// redefined virtual functions:
virtual int getErrno() const;
virtual UsageEnvironment& operator<<(char const* str);
virtual UsageEnvironment& operator<<(int i);
virtual UsageEnvironment& operator<<(unsigned u);
virtual UsageEnvironment& operator<<(double d);
virtual UsageEnvironment& operator<<(void* p);
protected:
BasicUsageEnvironment(TaskScheduler& taskScheduler);
// called only by "createNew()" (or subclass constructors)
virtual ~BasicUsageEnvironment();
};
class BasicTaskScheduler : public BasicTaskScheduler0 {
public:
//静态创建函数,调用构造函数
static BasicTaskScheduler* createNew(unsigned maxSchedulerGranularity = 10000/*microseconds*/);
// "maxSchedulerGranularity" (default value: 10 ms) specifies the maximum time that we wait (in "select()") before
// returning to the event loop to handle non-socket or non-timer-based events, such as 'triggered events'.
// You can change this is you wish (but only if you know what you're doing!), or set it to 0, to specify no such maximum time.
// (You should set it to 0 only if you know that you will not be using 'event triggers'.)
//析构函数
virtual ~BasicTaskScheduler();
protected:
//析构函数
BasicTaskScheduler(unsigned maxSchedulerGranularity);
// called only by "createNew()"
//以下这两个是新的方法
static void schedulerTickTask(void* clientData);
void schedulerTickTask();
protected:
// Redefined virtual functions:
//重写父类方法
virtual void SingleStep(unsigned maxDelayTime);
virtual void setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData);
virtual void moveSocketHandling(int oldSocketNum, int newSocketNum);
protected:
//定义任务最大间隔
unsigned fMaxSchedulerGranularity;
// To implement background operations:
//最大socket数量
int fMaxNumSockets;
//select()机制中提供一fd_set的数据结构,实际上是一long类型的数组,
//每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,
//建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,
//由此来通知执行了select()的进程哪一socket或文件发生了可读或可写事件。
fd_set fReadSet;
fd_set fWriteSet;
fd_set fExceptionSet;
private:
#if defined(__WIN32__) || defined(_WIN32)
// Hack to work around a bug in Windows' "select()" implementation:
int fDummySocketNum;
#endif
};
#endif
BasicUsageEnvironment.cpp
还有BasicUsageEnvironment的实现。
// BasicUsageEnvironment //
#if defined(__WIN32__) || defined(_WIN32)
extern "C" int initializeWinsockIfNecessary();
#endif
BasicUsageEnvironment::BasicUsageEnvironment(TaskScheduler& taskScheduler)
: BasicUsageEnvironment0(taskScheduler) {
#if defined(__WIN32__) || defined(_WIN32)
if (!initializeWinsockIfNecessary()) {
setResultErrMsg("Failed to initialize 'winsock': ");
reportBackgroundError();
internalError();
}
#endif
}
BasicUsageEnvironment::~BasicUsageEnvironment() {
}
BasicUsageEnvironment* BasicUsageEnvironment::createNew(TaskScheduler& taskScheduler) {
return new BasicUsageEnvironment(taskScheduler);
}
int BasicUsageEnvironment::getErrno() const {
#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE)
return WSAGetLastError();
#else
return errno;
#endif
}
UsageEnvironment& BasicUsageEnvironment::operator<<(char const* str) {
if (str == NULL) str = "(NULL)"; // sanity check
fprintf(stderr, "%s", str);
return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(int i) {
fprintf(stderr, "%d", i);
return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(unsigned u) {
fprintf(stderr, "%u", u);
return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(double d) {
fprintf(stderr, "%f", d);
return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(void* p) {
fprintf(stderr, "%p", p);
return *this;
}
主要是重载了一些操作符。
BasicTaskScheduler.cpp
最后就是BasicTaskScheduler的实现。他声明在BasicUsageEnvironment头文件中。
// BasicTaskScheduler //
BasicTaskScheduler* BasicTaskScheduler::createNew(unsigned maxSchedulerGranularity)
{
return new BasicTaskScheduler(maxSchedulerGranularity);
}
BasicTaskScheduler::BasicTaskScheduler(unsigned maxSchedulerGranularity)
: fMaxSchedulerGranularity(maxSchedulerGranularity),
fMaxNumSockets(0)
#if defined(__WIN32__) || defined(_WIN32)
, fDummySocketNum(-1)
#endif
{
//初始化变量fMaxSchedulerGranularity,fMaxNumSockets ,fDummySocketNum
//初始化变量fReadSet, fWriteSet, fExceptionSet
FD_ZERO(&fReadSet);
FD_ZERO(&fWriteSet);
FD_ZERO(&fExceptionSet);
if (maxSchedulerGranularity > 0)
{
// ensures that we handle events frequently
//确定处理事件的频率
schedulerTickTask();
}
}
BasicTaskScheduler::~BasicTaskScheduler()
{
#if defined(__WIN32__) || defined(_WIN32)
if (fDummySocketNum >= 0)
{
//关闭套接字
closeSocket(fDummySocketNum);
}
#endif
}
//当AlarmHandler执行handleTimeout的时候会执行此方法
//此方法再次调用schedulerTickTask方法继续构建AlarmHandler
void BasicTaskScheduler::schedulerTickTask(void* clientData)
{
((BasicTaskScheduler*)clientData)->schedulerTickTask();
}
void BasicTaskScheduler::schedulerTickTask()
{
//父类方法添加一个计划延时任务
//这里给AlarmHandler赋值,传递的方法为schedulerTickTask
scheduleDelayedTask(fMaxSchedulerGranularity, schedulerTickTask, this);
}
#ifndef MILLION
#define MILLION 1000000
#endif
void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
// make a copy for this select() call
//下面三行做一个变量拷贝
fd_set readSet = fReadSet;
fd_set writeSet = fWriteSet; // ditto
fd_set exceptionSet = fExceptionSet; // ditto
//找到最近要执行的任务的剩余时间
DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm();
//声明一个timeval
struct timeval tv_timeToDelay;
//赋值
tv_timeToDelay.tv_sec = timeToDelay.seconds();
tv_timeToDelay.tv_usec = timeToDelay.useconds();
// Very large "tv_sec" values cause select() to fail.
// Don't make it any larger than 1 million seconds (11.5 days)
//设定一个最大的时间
const long MAX_TV_SEC = MILLION;
if (tv_timeToDelay.tv_sec > MAX_TV_SEC)
{
//如果大于这个最大了就设置为相等
tv_timeToDelay.tv_sec = MAX_TV_SEC;
}
// Also check our "maxDelayTime" parameter (if it's > 0):
if (maxDelayTime > 0 &&
(tv_timeToDelay.tv_sec > (long)maxDelayTime / MILLION ||
(tv_timeToDelay.tv_sec == (long)maxDelayTime / MILLION &&
tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) {
tv_timeToDelay.tv_sec = maxDelayTime / MILLION;
tv_timeToDelay.tv_usec = maxDelayTime % MILLION;
}
int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
if (selectResult < 0)
{
#if defined(__WIN32__) || defined(_WIN32)
int err = WSAGetLastError();
// For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if
// it was called with no entries set in "readSet". If this happens, ignore it:
if (err == WSAEINVAL && readSet.fd_count == 0)
{
//出错的情况的处理
err = EINTR;
// To stop this from happening again, create a dummy socket:
if (fDummySocketNum >= 0)
{
closeSocket(fDummySocketNum);
}
fDummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
FD_SET((unsigned)fDummySocketNum, &fReadSet);
}
if (err != EINTR)
{
#else
if (errno != EINTR && errno != EAGAIN) {
#endif
// Unexpected error - treat this as fatal:
#if !defined(_WIN32_WCE)
perror("BasicTaskScheduler::SingleStep(): select() fails");
// Because this failure is often "Bad file descriptor" - which is caused by an invalid socket number (i.e., a socket number
// that had already been closed) being used in "select()" - we print out the sockets that were being used in "select()",
// to assist in debugging:
fprintf(stderr, "socket numbers used in the select() call:");
for (int i = 0; i < 10000; ++i)
{
if (FD_ISSET(i, &fReadSet) || FD_ISSET(i, &fWriteSet) || FD_ISSET(i, &fExceptionSet))
{
fprintf(stderr, " %d(", i);
if (FD_ISSET(i, &fReadSet)) fprintf(stderr, "r");
if (FD_ISSET(i, &fWriteSet)) fprintf(stderr, "w");
if (FD_ISSET(i, &fExceptionSet)) fprintf(stderr, "e");
fprintf(stderr, ")");
}
}
fprintf(stderr, "\n");
#endif
internalError();
}
}
//没出错的情况下
// Call the handler function for one readable socket:
//声明一个迭代器
HandlerIterator iter(*fHandlers);
//声明一个节点
HandlerDescriptor* handler;
// To ensure forward progress through the handlers, begin past the last
// socket number that we handled:
//为了确保处理程序向前推进,从我们处理的最后一个套接字编号开始:
//如果最后一个执行的socket序号大于0说明前面有执行过
if (fLastHandledSocketNum >= 0)
{
//只要下一个元素不为空,为空时退出
while ((handler = iter.next()) != NULL)
{
//如果等于最后一个执行的socket任务序号,退出
if (handler->socketNum == fLastHandledSocketNum)
{
//退出循环
break;
}
}
//为空说明handler->socketNum != fLastHandledSocketNum,反之socketNum == fLastHandledSocketNum
//从最后一个处理的socket开始重新初始化
if (handler == NULL)
{
fLastHandledSocketNum = -1;
// start from the beginning instead
iter.reset();
}
}
while ((handler = iter.next()) != NULL)
{
int sock = handler->socketNum; // alias
int resultConditionSet = 0;
//FD_ISSET 用于测试指定的文件描述符是否在该集合中
//下面三个if给resultConditionSet叠加值 分别是可读 可写 有异常
if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
//(resultConditionSet&handler->conditionSet) != 0 说明resultConditionSet == handler->conditionSet
if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL)
{
fLastHandledSocketNum = sock;
// Note: we set "fLastHandledSocketNum" before calling the handler,
// in case the handler calls "doEventLoop()" reentrantly.
(*handler->handlerProc)(handler->clientData, resultConditionSet);
break;
}
}
if (handler == NULL && fLastHandledSocketNum >= 0)
{
// We didn't call a handler, but we didn't get to check all of them,
// so try again from the beginning:
iter.reset();
while ((handler = iter.next()) != NULL)
{
int sock = handler->socketNum; // alias
int resultConditionSet = 0;
if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL)
{
fLastHandledSocketNum = sock;
// Note: we set "fLastHandledSocketNum" before calling the handler,
// in case the handler calls "doEventLoop()" reentrantly.
(*handler->handlerProc)(handler->clientData, resultConditionSet);
break;
}
}
if (handler == NULL)
{
//because we didn't call a handler
fLastHandledSocketNum = -1;
}
}
// Also handle any newly-triggered event (Note that we do this *after* calling a socket handler,
// in case the triggered event handler modifies The set of readable sockets.)
//这里处理事件
//如果等待执行的事件大于0
if (fTriggersAwaitingHandling != 0)
{
//如果当前要执行的事件id等于上一次执行的事件id
if (fTriggersAwaitingHandling == fLastUsedTriggerMask)
{
// Common-case optimization for a single event trigger:
//&= ~这个操作如果fTriggersAwaitingHandling==fLastUsedTriggerMask 那么结果就是0否则结果等于fTriggersAwaitingHandling
fTriggersAwaitingHandling &= ~fLastUsedTriggerMask;
if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL)
{
//找到fLastUsedTriggerMask对应的事件然后执行
(*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);
}
}
else
{
//如果不相等
// Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers):
unsigned i = fLastUsedTriggerNum;
EventTriggerId mask = fLastUsedTriggerMask;
do {
i = (i + 1) % MAX_NUM_EVENT_TRIGGERS;
mask >>= 1;
if (mask == 0) mask = 0x80000000;
//(fTriggersAwaitingHandling&mask) != 0说明fTriggersAwaitingHandling == mask
if ((fTriggersAwaitingHandling&mask) != 0)
{
//这里因为fTriggersAwaitingHandling == mask 所以 &= ~操作后fTriggersAwaitingHandling = 0
fTriggersAwaitingHandling &= ~mask;
if (fTriggeredEventHandlers[i] != NULL)
{
//执行
(*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);
}
//更新最后执行的值
fLastUsedTriggerMask = mask;
fLastUsedTriggerNum = i;
break;
}
} while (i != fLastUsedTriggerNum);
}
}
// Also handle any delayed event that may have come due.
//这里检查alarm的任务循环
fDelayQueue.handleAlarm();
}
void BasicTaskScheduler::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
//必要的检查
if (socketNum < 0)
{
return;
}
#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
if (socketNum >= (int)(FD_SETSIZE))
{
return;
}
#endif
//清楚原先的
FD_CLR((unsigned)socketNum, &fReadSet);
FD_CLR((unsigned)socketNum, &fWriteSet);
FD_CLR((unsigned)socketNum, &fExceptionSet);
//如果等于0
if (conditionSet == 0)
{
//fHandlers会被清空
fHandlers->clearHandler(socketNum);
if (socketNum + 1 == fMaxNumSockets)
{
//更新fMaxNumSockets
--fMaxNumSockets;
}
}
else
{
//conditionSet != 0的时候,设置对应socketNum的值
fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
if (socketNum + 1 > fMaxNumSockets)
{
//更新fMaxNumSockets
fMaxNumSockets = socketNum + 1;
}
//设置select参数
if (conditionSet&SOCKET_READABLE)
{
FD_SET((unsigned)socketNum, &fReadSet);
}
if (conditionSet&SOCKET_WRITABLE)
{
FD_SET((unsigned)socketNum, &fWriteSet);
}
if (conditionSet&SOCKET_EXCEPTION)
{
FD_SET((unsigned)socketNum, &fExceptionSet);
}
}
}
void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum)
{
//必要的检查
if (oldSocketNum < 0 || newSocketNum < 0)
{
return;
}
#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
if (oldSocketNum >= (int)(FD_SETSIZE) || newSocketNum >= (int)(FD_SETSIZE))
{
return;
}
#endif
//修改对应的FD_SET
if (FD_ISSET(oldSocketNum, &fReadSet))
{
FD_CLR((unsigned)oldSocketNum, &fReadSet);
FD_SET((unsigned)newSocketNum, &fReadSet);
}
if (FD_ISSET(oldSocketNum, &fWriteSet))
{
FD_CLR((unsigned)oldSocketNum, &fWriteSet);
FD_SET((unsigned)newSocketNum, &fWriteSet);
}
if (FD_ISSET(oldSocketNum, &fExceptionSet))
{
FD_CLR((unsigned)oldSocketNum, &fExceptionSet);
FD_SET((unsigned)newSocketNum, &fExceptionSet);
}
fHandlers->moveHandler(oldSocketNum, newSocketNum);
//设置select第一个参数的大小
if (oldSocketNum + 1 == fMaxNumSockets)
{
--fMaxNumSockets;
}
if (newSocketNum + 1 > fMaxNumSockets)
{
fMaxNumSockets = newSocketNum + 1;
}
}
groupsock
下面我们要开启新的工程了,这次我们来看看live555中主要负责网络通讯的这一个项目:groupsock
我们还是从最简单的入手来看,首先看一下NetAddress的头文件:
NetAddress.hh
typedef u_int32_t netAddressBits;
首先定义一个无符号的32位整型,定义为netAddressBits。见字如面以后你就是无符号32位整型了。
下面是NetAddress这个类的声明。
class NetAddress {
public:
//构造函数1 地址值指针,地址长度
NetAddress(u_int8_t const* data, unsigned length = 4);
// sets address data to all-zeros
//构造函数2 只有长度信息
NetAddress(unsigned length = 4);
//复制构造函数传一个对象
NetAddress(NetAddress const& orig);
//重载等号运算符
//这里应该是用于ip地址的比较吧
NetAddress& operator=(NetAddress const& rightSide);
//析构函数
virtual ~NetAddress();
//返回地址的长度信息
unsigned length() const { return fLength; }
// always in network byte order
//返回的是网络的ip信息
//这里永远是网络字节序
u_int8_t const* data() const
{
return fData;
}
private:
//这里是设置操作么,往后看cpp吧
void assign(u_int8_t const* data, unsigned length);
//清空
void clean();
//两个成员变量
unsigned fLength;
u_int8_t* fData;
};
下面是装载NetAddress的数据结构,容器。
class NetAddressList {
public:
//构造函数1 传入一个主机名称
NetAddressList(char const* hostname);
//构造函数2 传入一个同类型的对象 拷贝构造函数
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:
//成员变量1 数据结构本身
NetAddressList const& fAddressList;
//成员变量2 下一个元素下标值
unsigned fNextIndex;
};
private:
//这里应该是赋值构造操作
void assign(netAddressBits numAddresses, NetAddress** addressArray);
//清空数据结构
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
};
重载左移运算符
UsageEnvironment& operator<<(UsageEnvironment& s, const Port& p);
接下来是一个表,仅从第一看不知道是不是前面介绍的哈希桶,我们会在cpp中确认。
// A generic table for looking up objects by (address1, address2, port)
class AddressPortLookupTable {
public:
//构造函数
AddressPortLookupTable();
virtual ~AddressPortLookupTable();
//添加节点 地址1,地址2, 节点 值
void* Add(netAddressBits address1, netAddressBits address2, Port port, void* value);
// Returns the old value if different, otherwise 0
//删除节点
Boolean Remove(netAddressBits address1, netAddressBits address2, Port port);
//寻找节点
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?
HashTable* fTable;
};
接下来这个类AddressString还不清楚是干什么的。
class AddressString {
public:
//构造函数
AddressString(struct sockaddr_in const& addr);
AddressString(struct in_addr const& addr);
AddressString(netAddressBits addr); // "addr" is assumed to be in host byte order here
virtual ~AddressString();
//访问成员变量的开放接口
char const* val() const { return fVal; }
private:
//初始化方法
void init(netAddressBits addr); // used to implement each of the constructors
private:
char* fVal; // The result ASCII string: allocated by the constructor; deleted by the destructor
};
现在我们去源文件中看一下。
NetAddress.cpp
首先看一下 NetAddress的实现
// NetAddress //
NetAddress::NetAddress(u_int8_t const* data, unsigned length)
{
//构造函数交给assign去赋值
assign(data, length);
}
NetAddress::NetAddress(unsigned length)
{
//申请空间
fData = new u_int8_t[length];
if (fData == NULL)
{
fLength = 0;
return;
}
for (unsigned i = 0; i < length; ++i)
{
//全部赋值0
fData[i] = 0;
}
//成员变量赋值
fLength = length;
}
NetAddress::NetAddress(NetAddress const& orig)
{
//赋值构造函数取值交给assign进行赋值
assign(orig.data(), orig.length());
}
NetAddress& NetAddress::operator=(NetAddress const& rightSide)
{
//重载赋值运算符
//如果不等于
if (&rightSide != this)
{
//先清理原先的数据
clean();
//赋值
assign(rightSide.data(), rightSide.length());
}
return *this;
}
NetAddress::~NetAddress() {
clean();
}
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;
}
//清理
void NetAddress::clean()
{
delete[] fData;
fData = NULL;
fLength = 0;
}
然后是NetAddressList ,首先来看构造函数。
// NetAddressList //
NetAddressList::NetAddressList(char const* hostname)
: fNumAddresses(0),
fAddressArray(NULL)
{
// First, check whether "hostname" is an IP address string:
//检查是不是ip样式
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;
//二级指针 存储的是NetAddress对象地址的首地址 构造大小
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:
//将host转换为ip样式
#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
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:
u_int8_t const** hAddrPtr1 = hAddrPtr;
while (*hAddrPtr1 != NULL)
{
++fNumAddresses;
++hAddrPtr1;
}
// 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
}
接下来看看其他的构造函数
//构造函数传入同类型对象
NetAddressList::NetAddressList(NetAddressList const& orig)
{
assign(orig.numAddresses(), orig.fAddressArray);
}
直接传入对象进行构造,接下来是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::operator=(NetAddressList const& rightSide)
{
if (&rightSide != this)
{
clean();
assign(rightSide.numAddresses(), rightSide.fAddressArray);
}
return *this;
}
返回首地址就是返回第一个元素:
NetAddress const* NetAddressList::firstAddress() const
{
if (fNumAddresses == 0)
{
return NULL;
}
//返回首地址
return fAddressArray[0];
}
下面来看看NetAddressList里面的迭代器的实现
// NetAddressList::Iterator //
NetAddressList::Iterator::Iterator(NetAddressList const& addressList)
: fAddressList(addressList), fNextIndex(0) {}
NetAddress const* NetAddressList::Iterator::nextAddress()
{
//越界了
if (fNextIndex >= fAddressList.numAddresses())
{
return NULL; // no more
}
//返回下一个元素 然后++
return fAddressList.fAddressArray[fNextIndex++];
}
下面看Port这个类
// Port //
Port::Port(portNumBits num /* in host byte order */)
{
//主机字节序转网络字节序
fPortNum = htons(num);
}
UsageEnvironment& operator<<(UsageEnvironment& s, const Port& p)
{
//这里是输出到流中打印
return s << ntohs(p.num());
}
下面是AddressPortLookupTable,其中他的成员变量fTable是一个HashTable也就是哈希桶,基本上其中的add,remove,lookup都是调用哈希桶中相应的方法。
// AddressPortLookupTable //
AddressPortLookupTable::AddressPortLookupTable()
: fTable(HashTable::create(3))
{
//数据结构为哈希桶,键值类型为3
// three-word keys are used
}
AddressPortLookupTable::~AddressPortLookupTable() {
delete fTable;
}
void* AddressPortLookupTable::Add(netAddressBits address1, netAddressBits address2, Port port, void* value)
{
int key[3];
key[0] = (int)address1;
key[1] = (int)address2;
key[2] = (int)port.num();
return fTable->Add((char*)key, value);
}
void* AddressPortLookupTable::Lookup(netAddressBits address1, netAddressBits address2, Port port)
{
int key[3];
key[0] = (int)address1;
key[1] = (int)address2;
key[2] = (int)port.num();
return fTable->Lookup((char*)key);
}
Boolean AddressPortLookupTable::Remove(netAddressBits address1, netAddressBits address2, Port port)
{
int key[3];
key[0] = (int)address1;
key[1] = (int)address2;
key[2] = (int)port.num();
return fTable->Remove((char*)key);
}
AddressPortLookupTable::Iterator::Iterator(AddressPortLookupTable& table)
: fIter(HashTable::Iterator::create(*(table.fTable))) {
}
AddressPortLookupTable::Iterator::~Iterator() {
delete fIter;
}
void* AddressPortLookupTable::Iterator::next() {
char const* key; // dummy
return fIter->next(key);
}
下面是一个过滤广播地址的方法:
// isMulticastAddress() implementation //
Boolean IsMulticastAddress(netAddressBits address) {
// Note: We return False for addresses in the range 224.0.0.0
// through 224.0.0.255, because these are non-routable
// Note: IPv4-specific #####
//限定ip地址范围,去掉不表示网络的地址
netAddressBits addressInNetworkOrder = htonl(address);
return addressInNetworkOrder > 0xE00000FF &&
addressInNetworkOrder <= 0xEFFFFFFF;
下面是将ip转为字符型的方法:
// AddressString implementation //
AddressString::AddressString(struct sockaddr_in const& addr)
{
init(addr.sin_addr.s_addr);
}
AddressString::AddressString(struct in_addr const& addr)
{
init(addr.s_addr);
}
AddressString::AddressString(netAddressBits addr)
{
init(addr);
}
void AddressString::init(netAddressBits addr)
{
// large enough for "abc.def.ghi.jkl"
fVal = new char[16];
// make sure we have a value in a known byte order: big endian
netAddressBits addrNBO = htonl(addr);
sprintf(fVal, "%u.%u.%u.%u", (addrNBO >> 24) & 0xFF, (addrNBO >> 16) & 0xFF, (addrNBO >> 8) & 0xFF, addrNBO & 0xFF);
}
AddressString::~AddressString()
{
delete[] fVal;
}
NetInterface.hh
接下来我们来看NetInterface的头文件
首先是NetInterface类
class NetInterface {
public:
//析构函数
virtual ~NetInterface();
//静态的工具类
static UsageEnvironment* DefaultUsageEnvironment;
// if non-NULL, used for each new interfaces
protected:
//构造函数
NetInterface(); // virtual base class
};
然后是他的一个子类
class DirectedNetInterface : public NetInterface {
public:
//析构函数
virtual ~DirectedNetInterface();
//纯虚函数写函数参数为 数据 和数据大小
virtual Boolean write(unsigned char* data, unsigned numBytes) = 0;
//纯虚函数 源地址播放回放
virtual Boolean SourceAddrOKForRelaying(UsageEnvironment& env, unsigned addr) = 0;
protected:
//构造函数
DirectedNetInterface(); // virtual base class
};
然后是这个类的一个容器
//DirectedNetInterface的容器
class DirectedNetInterfaceSet {
public:
DirectedNetInterfaceSet();
virtual ~DirectedNetInterfaceSet();
//添加
DirectedNetInterface* Add(DirectedNetInterface const* interf);
// Returns the old value if different, otherwise 0
//移除
Boolean Remove(DirectedNetInterface const* interf);
//判断是否为空
Boolean IsEmpty() { return fTable->IsEmpty(); }
// Used to iterate through the interfaces in the set
//自己的迭代器
class Iterator {
public:
Iterator(DirectedNetInterfaceSet& interfaces);
virtual ~Iterator();
DirectedNetInterface* next(); // NULL iff none
private:
HashTable::Iterator* fIter;
};
private:
friend class Iterator;
//同样是一个哈希桶数据结构
HashTable* fTable;
};
然后是socket类同样继承自NetInterface
class Socket : public NetInterface {
public:
virtual ~Socket();
// closes the socket, and sets "fSocketNum" to -1
void reset();
virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize, unsigned& bytesRead, struct sockaddr_in& fromAddress) = 0;
// Returns False on error; resultData == NULL if data ignored
int socketNum() const { return fSocketNum; }
//返回port
Port port() const {
return fPort;
}
//工具类
UsageEnvironment& env() const { return fEnv; }
static int DebugLevel;
protected:
// virtual base class
Socket(UsageEnvironment& env, Port port);
// will also cause socketNum() to change
Boolean changePort(Port newPort);
private:
//这里应该是socket的id 不是数量
int fSocketNum;
//成员变量工具类引用
UsageEnvironment& fEnv;
//端口
Port fPort;
};
这里面的内容就丰富很多了,有socket的构造函数,重置函数,端口号,工具类,socketid。
// UsageEnvironment中的<<操作符重载
UsageEnvironment& operator<<(UsageEnvironment& s, const Socket& sock);
要重载工具类的左移运算符。
class SocketLookupTable {
public:
virtual ~SocketLookupTable();
// Creates a new Socket if none already exists
//取得socket
Socket* Fetch(UsageEnvironment& env, Port port, Boolean& isNew);
//移除
Boolean Remove(Socket const* sock);
protected:
SocketLookupTable(); // abstract base class
virtual Socket* CreateNew(UsageEnvironment& env, Port port) = 0;
private:
//哈希桶
HashTable* fTable;
};
这是一个用于寻找socket的类,也可以移除是一个哈希桶数据结构。
//交通状态
class NetInterfaceTrafficStats {
public:
//构造函数
NetInterfaceTrafficStats();
//包计数
void countPacket(unsigned packetSize);
//包总数
float totNumPackets() const { return fTotNumPackets; }
//总大小
float totNumBytes() const { return fTotNumBytes; }
//是否看到堵塞?
Boolean haveSeenTraffic() const;
private:
float fTotNumPackets;
float fTotNumBytes;
};
这是一个用于网络阻塞的类么,这里真的不太确定,由于对socket的了解也只是应用。
NetInterface.cpp
所以这里去看看NetInterface的源文件,看看以上这些声明的具体实现。
// NetInterface //
UsageEnvironment* NetInterface::DefaultUsageEnvironment = NULL;
NetInterface::NetInterface() {
}
NetInterface::~NetInterface() {
}
// NetInterface //
DirectedNetInterface::DirectedNetInterface() {
}
DirectedNetInterface::~DirectedNetInterface() {
}
这些构造函数以及析构函数就不说啦。
// DirectedNetInterfaceSet //
DirectedNetInterfaceSet::DirectedNetInterfaceSet()
: fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
}
DirectedNetInterfaceSet::~DirectedNetInterfaceSet() {
delete fTable;
}
容器的构造和析构主要是对fTable哈希桶的创建和销毁。
DirectedNetInterface* DirectedNetInterfaceSet::Add(DirectedNetInterface const* interf)
{
return (DirectedNetInterface*)fTable->Add((char*)interf, (void*)interf);
}
Boolean DirectedNetInterfaceSet::Remove(DirectedNetInterface const* interf)
{
return fTable->Remove((char*)interf);
}
调用哈希桶进行操作。
DirectedNetInterfaceSet::Iterator::Iterator(DirectedNetInterfaceSet& interfaces)
: fIter(HashTable::Iterator::create(*(interfaces.fTable)))
{
}
DirectedNetInterfaceSet::Iterator::~Iterator() {
delete fIter;
}
DirectedNetInterface* DirectedNetInterfaceSet::Iterator::next()
{
char const* key; // dummy
return (DirectedNetInterface*)fIter->next(key);
};
迭代器的创建也是之前说过很多啦。
// Socket //
int Socket::DebugLevel = 1; // default value
Socket::Socket(UsageEnvironment& env, Port port)
: fEnv(DefaultUsageEnvironment != NULL ? *DefaultUsageEnvironment : env),
fPort(port)
{
//设置socket返回socketid
fSocketNum = setupDatagramSocket(fEnv, port);
}
socket类的创建传入一个环境上下文引用,一个端口号,对环境成员变量的初始化就是看默认的环境是否为空,用参数给port成员变量赋值,最后设置socket,setupDatagramSocket会在后面看到,这里返回socket的编号。
void Socket::reset()
{
if (fSocketNum >= 0)
{
closeSocket(fSocketNum);
}
fSocketNum = -1;
}
Socket::~Socket()
{
reset();
}
析构和清理方法。
Boolean Socket::changePort(Port newPort)
{
//旧的socket的id值
int oldSocketNum = fSocketNum;
//获取旧的socket的接收缓冲区大小
unsigned oldReceiveBufferSize = getReceiveBufferSize(fEnv, fSocketNum);
//获取旧的socket发送缓冲区的大小
unsigned oldSendBufferSize = getSendBufferSize(fEnv, fSocketNum);
//关闭socket
closeSocket(fSocketNum);
fSocketNum = setupDatagramSocket(fEnv, newPort);
if (fSocketNum < 0)
{
//不可用关闭循环事件
fEnv.taskScheduler().turnOffBackgroundReadHandling(oldSocketNum);
return False;
}
//设置新的
setReceiveBufferTo(fEnv, fSocketNum, oldReceiveBufferSize);
setSendBufferTo(fEnv, fSocketNum, oldSendBufferSize);
//如果两个socket不相等的话。就进行下面的内容
if (fSocketNum != oldSocketNum)
{
// the socket number has changed, so move any event handling for it:
fEnv.taskScheduler().moveSocketHandling(oldSocketNum, fSocketNum);
}
return True;
}
先保存旧的socket的参数,然后关闭旧的socket,然后使用新的socket创建连接,返回新的id,如果小于0说明创建失败,则关闭计划任务循环,如果可用,设置接收发送缓冲区大小,如果新旧两个id是不相等的,修改循环事件为新的socket。
//重写的内容
UsageEnvironment& operator<<(UsageEnvironment& s, const Socket& sock)
{
return s << timestampString() << " Socket(" << sock.socketNum() << ")";
}
重写左移操作符。输出自定义内容。
// SocketLookupTable //
SocketLookupTable::SocketLookupTable()
: fTable(HashTable::create(ONE_WORD_HASH_KEYS))
{
}
SocketLookupTable::~SocketLookupTable()
{
delete fTable;
}
这里是构造函数和析构函数。主体还是哈希桶。
Socket* SocketLookupTable::Fetch(UsageEnvironment& env, Port port, Boolean& isNew)
{
isNew = False;
Socket* sock;
do
{
//调用哈希桶
sock = (Socket*)fTable->Lookup((char*)(long)(port.num()));
if (sock == NULL)
{
// we need to create one:
sock = CreateNew(env, port);
if (sock == NULL || sock->socketNum() < 0)
{
break;
}
//调用哈希桶添加一个新的
fTable->Add((char*)(long)(port.num()), (void*)sock);
isNew = True;
}
return sock;
} while (0);
delete sock;
return NULL;
}
这个方法为了得到一个socket,首先根据端口号查找,如果没有找到就创建一个并且添加进这个数据结构。
Boolean SocketLookupTable::Remove(Socket const* sock)
{
//调用哈希桶
return fTable->Remove((char*)(long)(sock->port().num()));
}
移除为调用哈希桶移除的方法。
// NetInterfaceTrafficStats //
NetInterfaceTrafficStats::NetInterfaceTrafficStats()
{
//初始化都为0
fTotNumPackets = fTotNumBytes = 0.0;
}
void NetInterfaceTrafficStats::countPacket(unsigned packetSize)
{
//计数+1
fTotNumPackets += 1.0;
//字节+size
fTotNumBytes += packetSize;
}
Boolean NetInterfaceTrafficStats::haveSeenTraffic() const
{
//fTotNumPackets不为0说明有堵塞
return fTotNumPackets != 0.0;
}
这个阻塞的监控类。可以看注释来理解。
IOHandlers.hh
接下来我们来看IOHandlers的头文件,只有一个函数。
#ifndef _IO_HANDLERS_HH
#define _IO_HANDLERS_HH
#ifndef _NET_INTERFACE_HH
#include "NetInterface.hh"
#endif
// Handles incoming data on sockets:
void socketReadHandler(Socket* sock, int mask);
#endif
IOHandlers.cpp
再来看看实现
#include "IOHandlers.hh"
#include "TunnelEncaps.hh"
//TEMP: Use a single buffer, sized for UDP tunnels:
//This assumes that the I/O handlers are non-reentrant
// This is usually overkill, because UDP packets are usually no larger
// than the typical Ethernet MTU (1500 bytes). However, I've seen
//reports of Windows Media Servers sending UDP packets as large as
//27 kBytes. These will probably undego lots of IP-level
//fragmentation, but that occurs below us. We just have to hope that
//fragments don't get lost.
//使用一个缓冲区,为udp通道量身打造
//这里假设io处理器是不可重入的
// 这通常是过度的因为udp通常不会大于以太网mtu
//但是我曾经看到27kb的udp包,可能是很多ip碎片造成的,我们只是希望随便不会丢失
// bytes
//最大的缓冲区大小
static unsigned const maxPacketLength = 50 * 1024;
//最大缓冲区大小加上一个目前还不清楚的大小
static unsigned const ioBufferSize = maxPacketLength + TunnelEncapsulationTrailerMaxSize;
//声明这个缓冲区
static unsigned char ioBuffer[ioBufferSize];
void socketReadHandler(Socket* sock, int /*mask*/)
{
//读取的自己大小
unsigned bytesRead;
//数据来源
struct sockaddr_in fromAddress;
//环境上下文
UsageEnvironment& saveEnv = sock->env();
// because handleRead(), if it fails, may delete "sock"
//执行读取操作
if (!sock->handleRead(ioBuffer, ioBufferSize, bytesRead, fromAddress))
{
//打印错误
saveEnv.reportBackgroundError();
}
}
这个函数主要是开辟一个udp的io缓冲区,执行读取数据操作,其中一些变量还不能确定他们大小和作用,后面会看到的。
TunnelEncaps.hh
接下来我们看一下TunnelEncaps的头文件。
typedef u_int16_t Cookie;
class TunnelEncapsulationTrailer {
// The trailer is layed out as follows:
// bytes 0-1: source 'cookie'
// bytes 2-3: destination 'cookie'
// bytes 4-7: address
// bytes 8-9: port
// byte 10: ttl
// byte 11: command
// Optionally, there may also be a 4-byte 'auxilliary address'
// (e.g., for 'source-specific multicast' preceding this)
// bytes -4 through -1: auxilliary address
public:
Cookie& srcCookie()
{
return *(Cookie*)byteOffset(0);
}
Cookie& dstCookie()
{
return *(Cookie*)byteOffset(2);
}
u_int32_t& address()
{
return *(u_int32_t*)byteOffset(4);
}
Port& port()
{
return *(Port*)byteOffset(8);
}
u_int8_t& ttl()
{
return *(u_int8_t*)byteOffset(10);
}
u_int8_t& command()
{
return *(u_int8_t*)byteOffset(11);
}
u_int32_t& auxAddress()
{
return *(u_int32_t*)byteOffset(-4);
}
private:
inline char* byteOffset(int charIndex)
{
return ((char*)this) + charIndex;
}
};
这是一个字节数组一样的数据结构,大概12+4个字节,每个字节存储不同的数据,公有的方法列表都是按照数据所占字节数来取得对应的数据。其中:
inline char* byteOffset(int charIndex)
{
return ((char*)this) + charIndex;
}
用于从头指针移动到对应下标返回所指向的地址。由:
Cookie& srcCookie()
{
return *(Cookie*)byteOffset(0);
}
对应的取值方法进行内容的提取。
接下来定义常量;
const unsigned TunnelEncapsulationTrailerSize = 12; // bytes
const unsigned TunnelEncapsulationTrailerAuxSize = 4; // bytes
const unsigned TunnelEncapsulationTrailerMaxSize = TunnelEncapsulationTrailerSize + TunnelEncapsulationTrailerAuxSize;
这里是字节数组的常规数据大小加上辅助数据大小和一个总大小。
// Command codes:
// 0: unused
const u_int8_t TunnelDataCmd = 1;
const u_int8_t TunnelJoinGroupCmd = 2;
const u_int8_t TunnelLeaveGroupCmd = 3;
const u_int8_t TunnelTearDownCmd = 4;
const u_int8_t TunnelProbeCmd = 5;
const u_int8_t TunnelProbeAckCmd = 6;
const u_int8_t TunnelProbeNackCmd = 7;
const u_int8_t TunnelJoinRTPGroupCmd = 8;
const u_int8_t TunnelLeaveRTPGroupCmd = 9;
// 0x0A through 0x10: currently unused.
const u_int8_t TunnelExtensionFlag = 0x80; // a flag, not a cmd code
const u_int8_t TunnelDataAuxCmd = (TunnelExtensionFlag | TunnelDataCmd);
const u_int8_t TunnelJoinGroupAuxCmd = (TunnelExtensionFlag | TunnelJoinGroupCmd);
const u_int8_t TunnelLeaveGroupAuxCmd = (TunnelExtensionFlag | TunnelLeaveGroupCmd);
// Note: the TearDown, Probe, ProbeAck, ProbeNack cmds have no Aux version
// 0x84 through 0x87: currently unused.
const u_int8_t TunnelJoinRTPGroupAuxCmd = (TunnelExtensionFlag | TunnelJoinRTPGroupCmd);
const u_int8_t TunnelLeaveRTPGroupAuxCmd = (TunnelExtensionFlag | TunnelLeaveRTPGroupCmd);
// 0x8A through 0xFF: currently unused
这里定义了第11个字节的cmd的所有取值。其中
inline Boolean TunnelIsAuxCmd(u_int8_t cmd)
{
return (cmd&TunnelExtensionFlag) != 0;
}
用来判断参数的cmd和TunnelExtensionFlag标志位是不是相等,如果相等返回true。而
const u_int8_t TunnelDataAuxCmd = (TunnelExtensionFlag | TunnelDataCmd);
这些其实就是相加。
GroupEId.hh
接下来我们看一下GroupEId的头文件:
class GroupEId {
public:
// used for a 'source-independent multicast' group
//用于多播
GroupEId(struct in_addr const& groupAddr, portNumBits portNum, u_int8_t ttl);
// used for a 'source-specific multicast' group
GroupEId(struct in_addr const& groupAddr, struct in_addr const& sourceFilterAddr, portNumBits portNum);
//组地址
struct in_addr const& groupAddress() const { return fGroupAddress; }
//源过滤地址
struct in_addr const& sourceFilterAddress() const { return fSourceFilterAddress; }
//是否是ssm
Boolean isSSM() const;
//端口号
portNumBits portNum() const { return fPortNum; }
//TTL是 Time To Live的缩写,该字段指定IP包被路由器丢弃之前允许通过的最大网段数量。TTL是IPv4包头的一个8 bit字段。
u_int8_t ttl() const { return fTTL; }
private:
void init(struct in_addr const& groupAddr, struct in_addr const& sourceFilterAddr, portNumBits portNum, u_int8_t ttl);
private:
//成员变量 组地址
struct in_addr fGroupAddress;
//成员变量 源过滤地址
struct in_addr fSourceFilterAddress;
// in network byte order
//成员变量端口号
portNumBits fPortNum;
//生存时间
u_int8_t fTTL;
};
声明了两个构造函数,一个初始化函数,四个成员变量分别是组地址,源过滤地址,端口,生存时间。关于ttl我们做一个小的了解。
TTL的作用是限制IP数据包在计算机网络中的存在的时间。TTL的最大值是255,TTL的一个推荐值是64。
虽然TTL从字面上翻译,是可以存活的时间,但实际上TTL是IP数据包在计算机网络中可以转发的最大跳数。TTL字段由IP数据包的发送者设置,在IP数据包从源到目的的整个转发路径上,每经过一个路由器,路由器都会修改这个TTL字段值,具体的做法是把该TTL的值减1,然后再将IP包转发出去。如果在IP包到达目的IP之前,TTL减少为0,路由器将会丢弃收到的TTL=0的IP包并向IP包的发送者发送 ICMP time exceeded消息。
TTL的主要作用是避免IP包在网络中的无限循环和收发,节省了网络资源,并能使IP包的发送者能收到告警消息。
TTL 是由发送主机设置的,以防止数据包不断在IP互联网络上永不终止地循环。转发IP数据包时,要求路由器至少将 TTL 减小 1。
GroupEId.cpp
接下来看他的实现
#include "GroupEId.hh"
GroupEId::GroupEId(struct in_addr const& groupAddr, portNumBits portNum, u_int8_t ttl)
{
//定义一个sourceFilterAddr
struct in_addr sourceFilterAddr;
// indicates no source filter
//初始化sourceFilterAddr的值为1
sourceFilterAddr.s_addr = ~0;
//进行初始化
init(groupAddr, sourceFilterAddr, portNum, ttl);
}
GroupEId::GroupEId(struct in_addr const& groupAddr, struct in_addr const& sourceFilterAddr, portNumBits portNum)
{
init(groupAddr, sourceFilterAddr, portNum, 255);
}
Boolean GroupEId::isSSM() const
{
//如果fSourceFilterAddress.s_addr为0则为ssm,这里GroupEId肯定就不是ssm了
return fSourceFilterAddress.s_addr != netAddressBits(~0);
}
void GroupEId::init(struct in_addr const& groupAddr, struct in_addr const& sourceFilterAddr, portNumBits portNum, u_int8_t ttl)
{
fGroupAddress = groupAddr;
fSourceFilterAddress = sourceFilterAddr;
fPortNum = portNum;
fTTL = ttl;
}
GroupsockHelper.hh
让我们接下来看看GroupsockHelper的头文件
#ifndef _GROUPSOCK_HELPER_HH
#define _GROUPSOCK_HELPER_HH
#ifndef _NET_ADDRESS_HH
#include "NetAddress.hh"
#endif
//设置udp
int setupDatagramSocket(UsageEnvironment& env, Port port);
//设置stream
int setupStreamSocket(UsageEnvironment& env, Port port, Boolean makeNonBlocking = True, Boolean setKeepAlive = False);
//读取socket内容
int readSocket(UsageEnvironment& env, int socket, unsigned char* buffer, unsigned bufferSize, struct sockaddr_in& fromAddress);
//写socket
//环境上下文
//socket
//目的地址
//端口号
//ttl
//发送缓冲区
//缓冲区大小
Boolean writeSocket(
UsageEnvironment& env,
int socket,
struct in_addr address,
portNumBits portNum/*network byte order*/,
u_int8_t ttlArg,
unsigned char* buffer,
unsigned bufferSize);
//不带有ttl的
Boolean writeSocket(
UsageEnvironment& env,
int socket,
struct in_addr address,
portNumBits portNum/*network byte order*/,
unsigned char* buffer,
unsigned bufferSize);
// An optimized version of "writeSocket" that omits the "setsockopt()" call to set the TTL.
//忽略SigPipe信号 SigPipe下面会讲
void ignoreSigPipeOnSocket(int socketNum);
//获取发送数据缓冲区大小
unsigned getSendBufferSize(UsageEnvironment& env, int socket);
//获取接收数据缓冲区大小
unsigned getReceiveBufferSize(UsageEnvironment& env, int socket);
//设置缓冲区大小为requestedSize
unsigned setSendBufferTo(UsageEnvironment& env, int socket, unsigned requestedSize);
unsigned setReceiveBufferTo(UsageEnvironment& env, int socket, unsigned requestedSize);
//增加缓冲区大小为requestedSize
unsigned increaseSendBufferTo(UsageEnvironment& env, int socket, unsigned requestedSize);
unsigned increaseReceiveBufferTo(UsageEnvironment& env, int socket, unsigned requestedSize);
//让socket不阻塞
Boolean makeSocketNonBlocking(int sock);
//让socket阻塞设置超时时间
// A "writeTimeoutInMilliseconds" value of 0 means: Don't timeout
Boolean makeSocketBlocking(int sock, unsigned writeTimeoutInMilliseconds = 0);
//设置socket的保持活动
Boolean setSocketKeepAlive(int sock);
// source-specific multicast join/leave
//设置socket加入组
Boolean socketJoinGroup(UsageEnvironment& env, int socket, netAddressBits groupAddress);
//设置socket离开组
Boolean socketLeaveGroup(UsageEnvironment&, int socket, netAddressBits groupAddress);
//ssm相关
Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket, netAddressBits groupAddress, netAddressBits sourceFilterAddr);
Boolean socketLeaveGroupSSM(UsageEnvironment&, int socket, netAddressBits groupAddress, netAddressBits sourceFilterAddr);
//获取源端口
Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port);
// in network order
netAddressBits ourIPAddress(UsageEnvironment& env);
// IP addresses of our sending and receiving interfaces. (By default, these
// are INADDR_ANY (i.e., 0), specifying the default interface.)
//发送接口地址
extern netAddressBits SendingInterfaceAddr;
//接收接口地址
extern netAddressBits ReceivingInterfaceAddr;
// Allocates a randomly-chosen IPv4 SSM (multicast) address:
//随机分配一个ipv4地址
netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env);
// Returns a simple "hh:mm:ss" string, for use in debugging output (e.g.)
char const* timestampString();
#ifdef HAVE_SOCKADDR_LEN
#define SET_SOCKADDR_SIN_LEN(var) var.sin_len = sizeof var
#else
#define SET_SOCKADDR_SIN_LEN(var)
#endif
#define MAKE_SOCKADDR_IN(var,adr,prt) /*adr,prt must be in network order*/\
struct sockaddr_in var;\
var.sin_family = AF_INET;\
var.sin_addr.s_addr = (adr);\
var.sin_port = (prt);\
SET_SOCKADDR_SIN_LEN(var);
// By default, we create sockets with the SO_REUSE_* flag set.
// If, instead, you want to create sockets without the SO_REUSE_* flags,
// Then enclose the creation code with:
// {
// NoReuse dummy;
// ...
// }
class NoReuse {
public:
NoReuse(UsageEnvironment& env);
~NoReuse();
private:
UsageEnvironment& fEnv;
};
// Define the "UsageEnvironment"-specific "groupsockPriv" structure:
struct _groupsockPriv {
// There should be only one of these allocated
HashTable* socketTable;
int reuseFlag;
};
_groupsockPriv* groupsockPriv(UsageEnvironment& env); // allocates it if necessary
void reclaimGroupsockPriv(UsageEnvironment& env);
#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
// For Windoze, we need to implement our own gettimeofday()
//windows端必须实现gettimeofday的方法,因为这个方法是linux系统sys/time.h中包含的
extern int gettimeofday(struct timeval*, int*);
#else
#include <sys/time.h>
#endif
// The following are implemented in inet.c:
extern "C" netAddressBits our_inet_addr(char const*);
extern "C" void our_srandom(int x);
extern "C" long our_random();
// because "our_random()" returns a 31-bit number
extern "C" u_int32_t our_random32();
#endif
声明了很多接口,等下会在cpp中看到,这里先解释两个地方,一个是extern关键字,对于熟练使用c++的朋友来说,这里就不用看了。
extern可以置于变量或者函数前,以标识变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的。
第二,当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或其他模块中使用。记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。
//发送接口地址
extern netAddressBits SendingInterfaceAddr;
//接收接口地址
extern netAddressBits ReceivingInterfaceAddr;
这里的意思就是要引用全局变量,要声明,如果不使用extern,就是netAddressBits SendingInterfaceAddr;这里就变成了你在头文件中定义了变量,而SendingInterfaceAddr变量在其他头文件中可能已经定义了,你在自己的头文件中如果不使用extern就是重复定义。而使用extern就代表你声明了这里要引用一个全局变量。这个全局变量在别处已经定义过了。而在定义的时候extern是可以被省略的。
但是读到这里有个迷惑,这东西和头文件是不是有点冲突,头文件能做到这些啊。可以看看这篇文章:
https://blog.youkuaiyun.com/yuyantai1234/article/details/7245412
接下来我们看看,sigpipe。
当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。又或者当一个进程向某个已经收到RST的socket执行写操作是,内核向该进程发送一个SIGPIPE信号。该信号的缺省学位是终止进程,因此进程必须捕获它以免不情愿的被终止。根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。若不想客户端退出可以把 SIGPIPE设为SIG_IGN。
GroupsockHelper.cpp
接下来我们来看实现。实现主要是网络方面的。对于只是会简单使用socket进行通信的我来说还是很有难度的,这里直接贴上代码,代码中有部分注释,可以帮助理解一下。
/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2019 Live Networks, Inc. All rights reserved.
// Helper routines to implement 'group sockets'
// Implementation
#include "GroupsockHelper.hh"
#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
//windows平台
#include <time.h>
extern "C" int initializeWinsockIfNecessary();
#else
//linux平台
#include <stdarg.h>
#include <time.h>
#include <sys/time.h>
#if !defined(_WIN32)
#include <netinet/tcp.h>
#ifdef __ANDROID_NDK__
#include <android/ndk-version.h>
#define ANDROID_OLD_NDK __NDK_MAJOR__ < 17
#endif
#endif
#include <fcntl.h>
#define initializeWinsockIfNecessary() 1
#endif
#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
#else
#include <signal.h>
#define USE_SIGNALS 1
#endif
#include <stdio.h>
// By default, use INADDR_ANY for the sending and receiving interfaces:
//发送接口地址
netAddressBits SendingInterfaceAddr = INADDR_ANY;
//接受接口地址
netAddressBits ReceivingInterfaceAddr = INADDR_ANY;
static void socketErr(UsageEnvironment& env, char const* errorMsg)
{
//打印错误日志
env.setResultErrMsg(errorMsg);
}
NoReuse::NoReuse(UsageEnvironment& env)
: fEnv(env)
{
//设置重用标识为0
groupsockPriv(fEnv)->reuseFlag = 0;
}
NoReuse::~NoReuse()
{
//NoReuse类的析构函数
//设置重用标识为1
groupsockPriv(fEnv)->reuseFlag = 1;
//回收
reclaimGroupsockPriv(fEnv);
}
_groupsockPriv* groupsockPriv(UsageEnvironment& env)
{
if (env.groupsockPriv == NULL)
{
// We need to create it
//_groupsockPriv是一个哈希桶
_groupsockPriv* result = new _groupsockPriv;
result->socketTable = NULL;
// default value => allow reuse of socket numbers
result->reuseFlag = 1;
//这里把这个哈希桶设置为环境上下文的groupsockPriv变量
env.groupsockPriv = result;
}
//返回该哈希桶的指针
return (_groupsockPriv*)(env.groupsockPriv);
}
void reclaimGroupsockPriv(UsageEnvironment& env)
{
//获取哈希桶,通过环境上下文中保存的
_groupsockPriv* priv = (_groupsockPriv*)(env.groupsockPriv);
if (priv->socketTable == NULL && priv->reuseFlag == 1/*default value*/)
{
// We can delete the structure (to save space); it will get created again, if needed:
delete priv;
//重置
env.groupsockPriv = NULL;
}
}
static int createSocket(int type)
{
// Call "socket()" to create a (IPv4) socket of the specified type.
// But also set it to have the 'close on exec' property (if we can)
int sock;
#ifdef SOCK_CLOEXEC
sock = socket(AF_INET, type | SOCK_CLOEXEC, 0);
if (sock != -1 || errno != EINVAL)
{
return sock;
}
// An "errno" of EINVAL likely means that the system wasn't happy with the SOCK_CLOEXEC; fall through and try again without it:
#endif
sock = socket(AF_INET, type, 0);
#ifdef FD_CLOEXEC
if (sock != -1)
{
fcntl(sock, F_SETFD, FD_CLOEXEC);
}
#endif
return sock;
}
int setupDatagramSocket(UsageEnvironment& env, Port port)
{
if (!initializeWinsockIfNecessary())
{
//没有初始化成功
socketErr(env, "Failed to initialize 'winsock': ");
return -1;
}
//创建socket
int newSocket = createSocket(SOCK_DGRAM);
if (newSocket < 0)
{
//创建失败
socketErr(env, "unable to create datagram socket: ");
return newSocket;
}
//获取重用标识
int reuseFlag = groupsockPriv(env)->reuseFlag;
//清理环境中旧的groupsockPriv
reclaimGroupsockPriv(env);
//设置newSocket重用
if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseFlag, sizeof reuseFlag) < 0)
{
socketErr(env, "setsockopt(SO_REUSEADDR) error: ");
closeSocket(newSocket);
return -1;
}
#if defined(__WIN32__) || defined(_WIN32)
// Windoze doesn't properly handle SO_REUSEPORT or IP_MULTICAST_LOOP
#else
#ifdef SO_REUSEPORT
//设置port重用 windows没有这个东西
if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,(const char*)&reuseFlag, sizeof reuseFlag) < 0)
{
socketErr(env, "setsockopt(SO_REUSEPORT) error: ");
closeSocket(newSocket);
return -1;
}
#endif
#ifdef IP_MULTICAST_LOOP
const u_int8_t loop = 1;
//设置ip循环
if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loop, sizeof loop) < 0)
{
socketErr(env, "setsockopt(IP_MULTICAST_LOOP) error: ");
closeSocket(newSocket);
return -1;
}
#endif
#endif
// Note: Windoze requires binding, even if the port number is 0
netAddressBits addr = INADDR_ANY;
#if defined(__WIN32__) || defined(_WIN32)
#else
//对于除windows系统 如果port不等于0 ip不等于0
if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {
#endif
if (port.num() == 0)
{
addr = ReceivingInterfaceAddr;
}
MAKE_SOCKADDR_IN(name, addr, port.num());
//绑定
if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0)
{
//失败的情况
char tmpBuffer[100];
sprintf(tmpBuffer, "bind() error (port number: %d): ", ntohs(port.num()));
socketErr(env, tmpBuffer);
closeSocket(newSocket);
return -1;
}
#if defined(__WIN32__) || defined(_WIN32)
#else
}
#endif
// Set the sending interface for multicasts, if it's not the default:
if (SendingInterfaceAddr != INADDR_ANY)
{
//如果SendingInterfaceAddrip地址不是0
struct in_addr addr;
addr.s_addr = SendingInterfaceAddr;
//设置socket的发送地址
if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&addr, sizeof addr) < 0)
{
socketErr(env, "error setting outgoing multicast interface: ");
closeSocket(newSocket);
return -1;
}
}
return newSocket;
}
//设置非阻塞模式
Boolean makeSocketNonBlocking(int sock)
{
#if defined(__WIN32__) || defined(_WIN32)
unsigned long arg = 1;
return ioctlsocket(sock, FIONBIO, &arg) == 0;
#elif defined(VXWORKS)
int arg = 1;
return ioctl(sock, FIONBIO, (int)&arg) == 0;
#else
int curFlags = fcntl(sock, F_GETFL, 0);
return fcntl(sock, F_SETFL, curFlags | O_NONBLOCK) >= 0;
#endif
}
//设置阻塞模式
Boolean makeSocketBlocking(int sock, unsigned writeTimeoutInMilliseconds)
{
Boolean result;
#if defined(__WIN32__) || defined(_WIN32)
unsigned long arg = 0;
result = ioctlsocket(sock, FIONBIO, &arg) == 0;
#elif defined(VXWORKS)
int arg = 0;
result = ioctl(sock, FIONBIO, (int)&arg) == 0;
#else
int curFlags = fcntl(sock, F_GETFL, 0);
result = fcntl(sock, F_SETFL, curFlags&(~O_NONBLOCK)) >= 0;
#endif
if (writeTimeoutInMilliseconds > 0)
{
#ifdef SO_SNDTIMEO
#if defined(__WIN32__) || defined(_WIN32)
DWORD msto = (DWORD)writeTimeoutInMilliseconds;
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&msto, sizeof(msto));
#else
struct timeval tv;
tv.tv_sec = writeTimeoutInMilliseconds / 1000;
tv.tv_usec = (writeTimeoutInMilliseconds % 1000) * 1000;
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof tv);
#endif
#endif
}
return result;
}
Boolean setSocketKeepAlive(int sock)
{
#if defined(__WIN32__) || defined(_WIN32)
// How do we do this in Windows? For now, just make this a no-op in Windows:
#else
int const keepalive_enabled = 1;
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepalive_enabled, sizeof keepalive_enabled) < 0)
{
return False;
}
#ifdef TCP_KEEPIDLE
int const keepalive_time = 180;
if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepalive_time, sizeof keepalive_time) < 0) {
return False;
}
#endif
int const keepalive_count = 5;
if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepalive_count, sizeof keepalive_count) < 0) {
return False;
}
int const keepalive_interval = 20;
if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepalive_interval, sizeof keepalive_interval) < 0) {
return False;
}
#endif
return True;
}
int setupStreamSocket(UsageEnvironment& env, Port port, Boolean makeNonBlocking, Boolean setKeepAlive)
{
//初始化socket
if (!initializeWinsockIfNecessary())
{
socketErr(env, "Failed to initialize 'winsock': ");
return -1;
}
//创建socket
int newSocket = createSocket(SOCK_STREAM);
if (newSocket < 0)
{
socketErr(env, "unable to create stream socket: ");
return newSocket;
}
//获取重用标识
int reuseFlag = groupsockPriv(env)->reuseFlag;
//清理
reclaimGroupsockPriv(env);
//设置socket
if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseFlag, sizeof reuseFlag) < 0)
{
socketErr(env, "setsockopt(SO_REUSEADDR) error: ");
closeSocket(newSocket);
return -1;
}
// SO_REUSEPORT doesn't really make sense for TCP sockets, so we
// normally don't set them. However, if you really want to do this
// #define REUSE_FOR_TCP
#ifdef REUSE_FOR_TCP
#if defined(__WIN32__) || defined(_WIN32)
// Windoze doesn't properly handle SO_REUSEPORT
#else
#ifdef SO_REUSEPORT
if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,
(const char*)&reuseFlag, sizeof reuseFlag) < 0) {
socketErr(env, "setsockopt(SO_REUSEPORT) error: ");
closeSocket(newSocket);
return -1;
}
#endif
#endif
#endif
// Note: Windoze requires binding, even if the port number is 0
#if defined(__WIN32__) || defined(_WIN32)
#else
if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {
#endif
MAKE_SOCKADDR_IN(name, ReceivingInterfaceAddr, port.num());
//绑定
if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0)
{
char tmpBuffer[100];
sprintf(tmpBuffer, "bind() error (port number: %d): ", ntohs(port.num()));
socketErr(env, tmpBuffer);
closeSocket(newSocket);
return -1;
}
#if defined(__WIN32__) || defined(_WIN32)
#else
}
#endif
if (makeNonBlocking)
{
//设置为不阻塞
if (!makeSocketNonBlocking(newSocket))
{
socketErr(env, "failed to make non-blocking: ");
closeSocket(newSocket);
return -1;
}
}
// Set the keep alive mechanism for the TCP socket, to avoid "ghost sockets"
// that remain after an interrupted communication.
if (setKeepAlive)
{
if (!setSocketKeepAlive(newSocket))
{
socketErr(env, "failed to set keep alive: ");
closeSocket(newSocket);
return -1;
}
}
return newSocket;
}
int readSocket(UsageEnvironment& env, int socket, unsigned char* buffer, unsigned bufferSize, struct sockaddr_in& fromAddress)
{
//发送方地址长度信息
SOCKLEN_T addressSize = sizeof fromAddress;
//接受消息返回消息长度
int bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0, (struct sockaddr*)&fromAddress, &addressSize);
//如果长度小于0
if (bytesRead < 0)
{
//##### HACK to work around bugs in Linux and Windows:
int err = env.getErrno();
if (err == 111 /*ECONNREFUSED (Linux)*/
#if defined(__WIN32__) || defined(_WIN32)
// What a piece of crap Windows is. Sometimes
// recvfrom() returns -1, but with an 'errno' of 0.
// This appears not to be a real error; just treat
// it as if it were a read of zero bytes, and hope
// we don't have to do anything else to 'reset'
// this alleged error:
|| err == 0 || err == EWOULDBLOCK
#else
|| err == EAGAIN
#endif
|| err == 113 /*EHOSTUNREACH (Linux)*/) { // Why does Linux return this for datagram sock?
fromAddress.sin_addr.s_addr = 0;
return 0;
}
//##### END HACK
socketErr(env, "recvfrom() error: ");
}
//如果等于0
else if (bytesRead == 0)
{
// "recvfrom()" on a stream socket can return 0 if the remote end has closed the connection. Treat this as an error:
return -1;
}
return bytesRead;
}
Boolean writeSocket(UsageEnvironment& env, int socket, struct in_addr address, portNumBits portNum, u_int8_t ttlArg, unsigned char* buffer, unsigned bufferSize)
{
// Before sending, set the socket's TTL:
#if defined(__WIN32__) || defined(_WIN32)
#define TTL_TYPE int
#else
#define TTL_TYPE u_int8_t
#endif
TTL_TYPE ttl = (TTL_TYPE)ttlArg;
if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof ttl) < 0)
{
socketErr(env, "setsockopt(IP_MULTICAST_TTL) error: ");
return False;
}
return writeSocket(env, socket, address, portNum, buffer, bufferSize);
}
Boolean writeSocket(UsageEnvironment& env, int socket, struct in_addr address, portNumBits portNum, unsigned char* buffer, unsigned bufferSize) {
do
{
//设置发送目的地
MAKE_SOCKADDR_IN(dest, address.s_addr, portNum);
//发送
int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0, (struct sockaddr*)&dest, sizeof dest);
//没发送完啊
if (bytesSent != (int)bufferSize)
{
char tmpBuf[100];
sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize);
socketErr(env, tmpBuf);
break;
}
return True;
} while (0);
return False;
}
//设置sigpipe参数
void ignoreSigPipeOnSocket(int socketNum)
{
#ifdef USE_SIGNALS
#ifdef SO_NOSIGPIPE
int set_option = 1;
setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
#else
signal(SIGPIPE, SIG_IGN);
#endif
#endif
}
//获取socket参数 这里是获取缓冲区大小
static unsigned getBufferSize(UsageEnvironment& env, int bufOptName, int socket)
{
unsigned curSize;
SOCKLEN_T sizeSize = sizeof curSize;
if (getsockopt(socket, SOL_SOCKET, bufOptName, (char*)&curSize, &sizeSize) < 0)
{
socketErr(env, "getBufferSize() error: ");
return 0;
}
return curSize;
}
//获取发送缓冲区大小
unsigned getSendBufferSize(UsageEnvironment& env, int socket)
{
return getBufferSize(env, SO_SNDBUF, socket);
}
//获取接收缓冲区大小
unsigned getReceiveBufferSize(UsageEnvironment& env, int socket)
{
return getBufferSize(env, SO_RCVBUF, socket);
}
//设置socket参数 这里是指缓冲区大小
static unsigned setBufferTo(UsageEnvironment& env, int bufOptName, int socket, unsigned requestedSize)
{
SOCKLEN_T sizeSize = sizeof requestedSize;
setsockopt(socket, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize);
// Get and return the actual, resulting buffer size:
return getBufferSize(env, bufOptName, socket);
}
//设置发送缓冲区大小
unsigned setSendBufferTo(UsageEnvironment& env, int socket, unsigned requestedSize)
{
return setBufferTo(env, SO_SNDBUF, socket, requestedSize);
}
//设置接收缓冲区大小
unsigned setReceiveBufferTo(UsageEnvironment& env, int socket, unsigned requestedSize)
{
return setBufferTo(env, SO_RCVBUF, socket, requestedSize);
}
//增加缓冲区到指点大小以上
static unsigned increaseBufferTo(UsageEnvironment& env, int bufOptName, int socket, unsigned requestedSize)
{
// First, get the current buffer size. If it's already at least
// as big as what we're requesting, do nothing.
unsigned curSize = getBufferSize(env, bufOptName, socket);
// Next, try to increase the buffer to the requested size,
// or to some smaller size, if that's not possible:
while (requestedSize > curSize)
{
SOCKLEN_T sizeSize = sizeof requestedSize;
if (setsockopt(socket, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize) >= 0)
{
// success
return requestedSize;
}
requestedSize = (requestedSize + curSize) / 2;
}
return getBufferSize(env, bufOptName, socket);
}
//增加发送缓冲区大小
unsigned increaseSendBufferTo(UsageEnvironment& env, int socket, unsigned requestedSize)
{
return increaseBufferTo(env, SO_SNDBUF, socket, requestedSize);
}
//增加接受缓冲区大小
unsigned increaseReceiveBufferTo(UsageEnvironment& env, int socket, unsigned requestedSize)
{
return increaseBufferTo(env, SO_RCVBUF, socket, requestedSize);
}
static void clearMulticastAllSocketOption(int socket)
{
#ifdef IP_MULTICAST_ALL
// This option is defined in modern versions of Linux to overcome a bug in the Linux kernel's default behavior.
// When set to 0, it ensures that we receive only packets that were sent to the specified IP multicast address,
// even if some other process on the same system has joined a different multicast group with the same port number.
int multicastAll = 0;
(void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll);
// Ignore the call's result. Should it fail, we'll still receive packets (just perhaps more than intended)
#endif
}
//socket加入组
Boolean socketJoinGroup(UsageEnvironment& env, int socket, netAddressBits groupAddress)
{
if (!IsMulticastAddress(groupAddress))
{
return True; // ignore this case
}
struct ip_mreq imr;
imr.imr_multiaddr.s_addr = groupAddress;
imr.imr_interface.s_addr = ReceivingInterfaceAddr;
if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&imr, sizeof(struct ip_mreq)) < 0)
{
#if defined(__WIN32__) || defined(_WIN32)
if (env.getErrno() != 0)
{
// That piece-of-shit toy operating system (Windows) sometimes lies
// about setsockopt() failing!
#endif
socketErr(env, "setsockopt(IP_ADD_MEMBERSHIP) error: ");
return False;
#if defined(__WIN32__) || defined(_WIN32)
}
#endif
}
clearMulticastAllSocketOption(socket);
return True;
}
Boolean socketLeaveGroup(UsageEnvironment&, int socket, netAddressBits groupAddress) {
if (!IsMulticastAddress(groupAddress))
{
return True; // ignore this case
}
struct ip_mreq imr;
imr.imr_multiaddr.s_addr = groupAddress;
imr.imr_interface.s_addr = ReceivingInterfaceAddr;
if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*)&imr, sizeof(struct ip_mreq)) < 0)
{
return False;
}
return True;
}
// The source-specific join/leave operations require special setsockopt()
// commands, and a special structure (ip_mreq_source). If the include files
// didn't define these, we do so here:
#if !defined(IP_ADD_SOURCE_MEMBERSHIP)
struct ip_mreq_source {
struct in_addr imr_multiaddr; /* IP multicast address of group */
struct in_addr imr_sourceaddr; /* IP address of source */
struct in_addr imr_interface; /* local IP address of interface */
};
#endif
#ifndef IP_ADD_SOURCE_MEMBERSHIP
#ifdef LINUX
#define IP_ADD_SOURCE_MEMBERSHIP 39
#define IP_DROP_SOURCE_MEMBERSHIP 40
#else
#define IP_ADD_SOURCE_MEMBERSHIP 25
#define IP_DROP_SOURCE_MEMBERSHIP 26
#endif
#endif
Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket, netAddressBits groupAddress, netAddressBits sourceFilterAddr)
{
if (!IsMulticastAddress(groupAddress))
{
return True; // ignore this case
}
struct ip_mreq_source imr;
#if ANDROID_OLD_NDK
imr.imr_multiaddr = groupAddress;
imr.imr_sourceaddr = sourceFilterAddr;
imr.imr_interface = ReceivingInterfaceAddr;
#else
imr.imr_multiaddr.s_addr = groupAddress;
imr.imr_sourceaddr.s_addr = sourceFilterAddr;
imr.imr_interface.s_addr = ReceivingInterfaceAddr;
#endif
if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (const char*)&imr, sizeof(struct ip_mreq_source)) < 0)
{
socketErr(env, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP) error: ");
return False;
}
clearMulticastAllSocketOption(socket);
return True;
}
Boolean socketLeaveGroupSSM(UsageEnvironment& /*env*/, int socket,
netAddressBits groupAddress,
netAddressBits sourceFilterAddr) {
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
struct ip_mreq_source imr;
#if ANDROID_OLD_NDK
imr.imr_multiaddr = groupAddress;
imr.imr_sourceaddr = sourceFilterAddr;
imr.imr_interface = ReceivingInterfaceAddr;
#else
imr.imr_multiaddr.s_addr = groupAddress;
imr.imr_sourceaddr.s_addr = sourceFilterAddr;
imr.imr_interface.s_addr = ReceivingInterfaceAddr;
#endif
if (setsockopt(socket, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP,
(const char*)&imr, sizeof(struct ip_mreq_source)) < 0) {
return False;
}
return True;
}
//获取源端口 网络序
static Boolean getSourcePort0(int socket, portNumBits& resultPortNum/*host order*/)
{
sockaddr_in test; test.sin_port = 0;
SOCKLEN_T len = sizeof test;
if (getsockname(socket, (struct sockaddr*)&test, &len) < 0)
{
return False;
}
resultPortNum = ntohs(test.sin_port);
return True;
}
//获取源端口
Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port)
{
portNumBits portNum = 0;
if (!getSourcePort0(socket, portNum) || portNum == 0)
{
// Hack - call bind(), then try again:
MAKE_SOCKADDR_IN(name, INADDR_ANY, 0);
bind(socket, (struct sockaddr*)&name, sizeof name);
if (!getSourcePort0(socket, portNum) || portNum == 0)
{
socketErr(env, "getsockname() error: ");
return False;
}
}
port = Port(portNum);
return True;
}
//是否是不好的ip地址
static Boolean badAddressForUs(netAddressBits addr)
{
// Check for some possible erroneous addresses:
netAddressBits nAddr = htonl(addr);
return (nAddr == 0x7F000001 /* 127.0.0.1 */ || nAddr == 0 || nAddr == (netAddressBits)(~0));
}
Boolean loopbackWorks = 1;
//我们的ip地址
netAddressBits ourIPAddress(UsageEnvironment& env)
{
//初始化
static netAddressBits ourAddress = 0;
int sock = -1;
struct in_addr testAddr;
//如果接受端地址不是any
if (ReceivingInterfaceAddr != INADDR_ANY)
{
// Hack: If we were told to receive on a specific interface address, then
// define this to be our ip address:
ourAddress = ReceivingInterfaceAddr;
}
if (ourAddress == 0)
{
// We need to find our source address
struct sockaddr_in fromAddr;
fromAddr.sin_addr.s_addr = 0;
// Get our address by sending a (0-TTL) multicast packet,
// receiving it, and looking at the source address used.
// (This is kinda bogus, but it provides the best guarantee
// that other nodes will think our address is the same as we do.)
do
{
loopbackWorks = 0; // until we learn otherwise
#ifndef DISABLE_LOOPBACK_IP_ADDRESS_CHECK
testAddr.s_addr = our_inet_addr("228.67.43.91"); // arbitrary
Port testPort(15947); // ditto
sock = setupDatagramSocket(env, testPort);
if (sock < 0)
{
break;
}
if (!socketJoinGroup(env, sock, testAddr.s_addr))
{
break;
}
unsigned char testString[] = "hostIdTest";
unsigned testStringLength = sizeof testString;
if (!writeSocket(env, sock, testAddr, testPort.num(), 0, testString, testStringLength))
{
break;
}
// Block until the socket is readable (with a 5-second timeout):
fd_set rd_set;
FD_ZERO(&rd_set);
FD_SET((unsigned)sock, &rd_set);
const unsigned numFds = sock + 1;
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
int result = select(numFds, &rd_set, NULL, NULL, &timeout);
if (result <= 0)
{
break;
}
unsigned char readBuffer[20];
int bytesRead = readSocket(env, sock, readBuffer, sizeof readBuffer, fromAddr);
if (bytesRead != (int)testStringLength || strncmp((char*)readBuffer, (char*)testString, testStringLength) != 0)
{
break;
}
// We use this packet's source address, if it's good:
loopbackWorks = !badAddressForUs(fromAddr.sin_addr.s_addr);
#endif
} while (0);
if (sock >= 0)
{
socketLeaveGroup(env, sock, testAddr.s_addr);
closeSocket(sock);
}
if (!loopbackWorks) do
{
// We couldn't find our address using multicast loopback,
// so try instead to look it up directly - by first getting our host name, and then resolving this host name
char hostname[100];
hostname[0] = '\0';
int result = gethostname(hostname, sizeof hostname);
if (result != 0 || hostname[0] == '\0')
{
env.setResultErrMsg("initial gethostname() failed");
break;
}
// Try to resolve "hostname" to an IP address:
NetAddressList addresses(hostname);
NetAddressList::Iterator iter(addresses);
NetAddress const* address;
// Take the first address that's not bad:
netAddressBits addr = 0;
while ((address = iter.nextAddress()) != NULL)
{
netAddressBits a = *(netAddressBits*)(address->data());
if (!badAddressForUs(a))
{
addr = a;
break;
}
}
// Assign the address that we found to "fromAddr" (as if the 'loopback' method had worked), to simplify the code below:
fromAddr.sin_addr.s_addr = addr;
} while (0);
// Make sure we have a good address:
netAddressBits from = fromAddr.sin_addr.s_addr;
if (badAddressForUs(from))
{
char tmp[100];
sprintf(tmp, "This computer has an invalid IP address: %s", AddressString(from).val());
env.setResultMsg(tmp);
from = 0;
}
ourAddress = from;
// Use our newly-discovered IP address, and the current time,
// to initialize the random number generator's seed:
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
unsigned seed = ourAddress ^ timeNow.tv_sec^timeNow.tv_usec;
our_srandom(seed);
}
return ourAddress;
}
netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env)
{
// First, a hack to ensure that our random number generator is seeded:
(void)ourIPAddress(env);
// Choose a random address in the range [232.0.1.0, 232.255.255.255)
// i.e., [0xE8000100, 0xE8FFFFFF)
netAddressBits const first = 0xE8000100, lastPlus1 = 0xE8FFFFFF;
netAddressBits const range = lastPlus1 - first;
return ntohl(first + ((netAddressBits)our_random()) % range);
}
char const* timestampString()
{
struct timeval tvNow;
gettimeofday(&tvNow, NULL);
#if !defined(_WIN32_WCE)
static char timeString[9]; // holds hh:mm:ss plus trailing '\0'
time_t tvNow_t = tvNow.tv_sec;
char const* ctimeResult = ctime(&tvNow_t);
if (ctimeResult == NULL)
{
sprintf(timeString, "??:??:??");
}
else
{
char const* from = &ctimeResult[11];
int i;
for (i = 0; i < 8; ++i)
{
timeString[i] = from[i];
}
timeString[i] = '\0';
}
#else
// WinCE apparently doesn't have "ctime()", so instead, construct
// a timestamp string just using the integer and fractional parts
// of "tvNow":
static char timeString[50];
sprintf(timeString, "%lu.%06ld", tvNow.tv_sec, tvNow.tv_usec);
#endif
return (char const*)&timeString;
}
#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
// For Windoze, we need to implement our own gettimeofday()
// used to make sure that static variables in gettimeofday() aren't initialized simultaneously by multiple threads
static LONG initializeLock_gettimeofday = 0;
#if !defined(_WIN32_WCE)
#include <sys/timeb.h>
#endif
int gettimeofday(struct timeval* tp, int* /*tz*/)
{
static LARGE_INTEGER tickFrequency, epochOffset;
static Boolean isInitialized = False;
LARGE_INTEGER tickNow;
#if !defined(_WIN32_WCE)
QueryPerformanceCounter(&tickNow);
#else
tickNow.QuadPart = GetTickCount();
#endif
if (!isInitialized) {
if (1 == InterlockedIncrement(&initializeLock_gettimeofday)) {
#if !defined(_WIN32_WCE)
// For our first call, use "ftime()", so that we get a time with a proper epoch.
// For subsequent calls, use "QueryPerformanceCount()", because it's more fine-grain.
struct timeb tb;
ftime(&tb);
tp->tv_sec = tb.time;
tp->tv_usec = 1000 * tb.millitm;
// Also get our counter frequency:
QueryPerformanceFrequency(&tickFrequency);
#else
/* FILETIME of Jan 1 1970 00:00:00. */
const LONGLONG epoch = 116444736000000000LL;
FILETIME fileTime;
LARGE_INTEGER time;
GetSystemTimeAsFileTime(&fileTime);
time.HighPart = fileTime.dwHighDateTime;
time.LowPart = fileTime.dwLowDateTime;
// convert to from 100ns time to unix timestamp in seconds, 1000*1000*10
tp->tv_sec = (long)((time.QuadPart - epoch) / 10000000L);
/*
GetSystemTimeAsFileTime has just a seconds resolution,
thats why wince-version of gettimeofday is not 100% accurate, usec accuracy would be calculated like this:
// convert 100 nanoseconds to usec
tp->tv_usec= (long)((time.QuadPart - epoch)%10000000L) / 10L;
*/
tp->tv_usec = 0;
// resolution of GetTickCounter() is always milliseconds
tickFrequency.QuadPart = 1000;
#endif
// compute an offset to add to subsequent counter times, so we get a proper epoch:
epochOffset.QuadPart
= tp->tv_sec * tickFrequency.QuadPart + (tp->tv_usec * tickFrequency.QuadPart) / 1000000L - tickNow.QuadPart;
// next caller can use ticks for time calculation
isInitialized = True;
return 0;
}
else {
InterlockedDecrement(&initializeLock_gettimeofday);
// wait until first caller has initialized static values
while (!isInitialized) {
Sleep(1);
}
}
}
// adjust our tick count so that we get a proper epoch:
tickNow.QuadPart += epochOffset.QuadPart;
tp->tv_sec = (long)(tickNow.QuadPart / tickFrequency.QuadPart);
tp->tv_usec = (long)(((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);
return 0;
}
#endif
#undef ANDROID_OLD_NDK
groupsock.hh
现在还有一个难啃的骨头就是本项目的同名类,groupsock,对然对socket的理解不够深入,但是这个类对于后面的理解有很重要的作用,我们还是要一句一句的攻克。我们从头文件开始看。
#ifndef _GROUPSOCK_HH
#define _GROUPSOCK_HH
#ifndef _GROUPSOCK_VERSION_HH
#include "groupsock_version.hh"
#endif
#ifndef _NET_INTERFACE_HH
#include "NetInterface.hh"
#endif
#ifndef _GROUPEID_HH
#include "GroupEId.hh"
#endif
// An "OutputSocket" is (by default) used only to send packets.
// No packets are received on it (unless a subclass arranges this)
class OutputSocket : public Socket {
//输出socket
public:
//构造与析构函数
OutputSocket(UsageEnvironment& env);
virtual ~OutputSocket();
//写
virtual Boolean write(netAddressBits address, portNumBits portNum/*in network order*/, u_int8_t ttl, unsigned char* buffer, unsigned bufferSize);
Boolean write(struct sockaddr_in& addressAndPort, u_int8_t ttl, unsigned char* buffer, unsigned bufferSize)
{
return write(addressAndPort.sin_addr.s_addr, addressAndPort.sin_port, ttl, buffer, bufferSize);
}
protected:
OutputSocket(UsageEnvironment& env, Port port);
//源端口号
portNumBits sourcePortNum() const { return fSourcePort.num(); }
private: // redefined virtual function
//处理读
virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize, unsigned& bytesRead, struct sockaddr_in& fromAddressAndPort);
private:
Port fSourcePort;
unsigned fLastSentTTL;
};
class destRecord
{
public:
//目标记录集构造函数 地址 端口 id 下一个
destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId, destRecord* next);
virtual ~destRecord();
public:
destRecord* fNext;
GroupEId fGroupEId;
unsigned fSessionId;
};
//Groupsock是用于发送和接受的packets数据包
// A "Groupsock" is used to both send and receive packets.
//从他的名字来理解他被设计与发送和接受多播但是也可以用于单播
// As the name suggests, it was originally designed to send/receive
// multicast, but it can send/receive unicast as well.
//继承自OutputSocket OutputSocket中既有发送也有处理读数据 还有两个成员变量fSourcePort端口 和ttl生命周期
class Groupsock : public OutputSocket {
public:
//构造函数1 传入环境变量上下文 地址 端口号 生命周期
// used for a 'source-independent multicast' group
Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr, Port port, u_int8_t ttl);
// used for a 'source-specific multicast' group
//构造函数2 传入环境变量上下文 地址 源过滤器地址 端口号
Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr, struct in_addr const& sourceFilterAddr, Port port);
//析构函数
virtual ~Groupsock();
//创建一个目标记录集
// Can be redefined by subclasses that also subclass "destRecord"
virtual destRecord* createNewDestRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId, destRecord* next);
// By default, the destination address, port and ttl for
//默认的 对于发出去的包 目标地址 端口号 生命周期
// outgoing packets are those that were specified in
// 是在构造函数中指定的
// the constructor. This works OK for multicast sockets,
//这对于multicast是可以的,
// but for unicast we usually want the destination port
//但是通常对于unicast我们想要知道目标端口
// number, at least, to be different from the source port.
//至少对于来源端口是不同的
// (If a parameter is 0 (or ~0 for ttl), then no change is made to that parameter.)
//如果 ttl是0 那么对该参数不做任何更改
// (If no existing "destRecord" exists with this "sessionId", then we add a new "destRecord".)
//如果不存在对应id的destRecord,那么添加一个新的
//通过newDestAddr找到对应的socket修改参数?
void changeDestinationParameters(struct in_addr const& newDestAddr, Port newDestPort, int newDestTTL, unsigned sessionId = 0);
// returns 0 if not found
//通过destAddrAndPort找到SessionId
unsigned lookupSessionIdFromDestination(struct sockaddr_in const& destAddrAndPort) const;
// As a special case, we also allow multiple destinations (addresses & ports)
//作为特例 我们同样允许多重地址
// (This can be used to implement multi-unicast.)
//这可以用于定义单播
//添加一个目的地址
virtual void addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId);
//移除一个目的地址
virtual void removeDestination(unsigned sessionId);
//移除所有的目的地址
void removeAllDestinations();
//是否拥有多重的地址
Boolean hasMultipleDestinations() const
{
return fDests != NULL && fDests->fNext != NULL;
}
//组地址
struct in_addr const& groupAddress() const
{
return fIncomingGroupEId.groupAddress();
}
//组过滤器地址
struct in_addr const& sourceFilterAddress() const
{
return fIncomingGroupEId.sourceFilterAddress();
}
//是否是ssm
Boolean isSSM() const
{
return fIncomingGroupEId.isSSM();
}
//获取生命周期函数
u_int8_t ttl() const
{
return fIncomingGroupEId.ttl();
}
//像多个地址发送但是不接受
// send, but don't receive any multicast packets
void multicastSendOnly();
//输出 参数 环境上下文 缓冲区 缓冲区大小 DirectedNetInterface
virtual Boolean output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize, DirectedNetInterface* interfaceNotToFwdBackTo = NULL);
//获取DirectedNetInterface的集合
DirectedNetInterfaceSet& members() { return fMembers; }
//是否没有成员的时候删除
Boolean deleteIfNoMembers;
// for tunneling
Boolean isSlave;
//进入管道交通状态
static NetInterfaceTrafficStats statsIncoming;
//输出管道交通状态
static NetInterfaceTrafficStats statsOutgoing;
//转发进入管道交通状态
static NetInterfaceTrafficStats statsRelayedIncoming;
//转发输出管道交通状态
static NetInterfaceTrafficStats statsRelayedOutgoing;
//一下是非静态变量
NetInterfaceTrafficStats statsGroupIncoming; // *not* static
NetInterfaceTrafficStats statsGroupOutgoing; // *not* static
NetInterfaceTrafficStats statsGroupRelayedIncoming; // *not* static
NetInterfaceTrafficStats statsGroupRelayedOutgoing; // *not* static
Boolean wasLoopedBackFromUs(UsageEnvironment& env, struct sockaddr_in& fromAddressAndPort);
public:
// redefined virtual functions
//处理读数据
virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize, unsigned& bytesRead, struct sockaddr_in& fromAddressAndPort);
protected:
//通过destAddrAndPort查找destRecord
destRecord* lookupDestRecordFromDestination(struct sockaddr_in const& destAddrAndPort) const;
private:
//删除目标地址
void removeDestinationFrom(destRecord*& dests, unsigned sessionId);
// used to implement (the public) "removeDestination()", and "changeDestinationParameters()"
//输出到除了某个地址之外的所有地址
int outputToAllMembersExcept(DirectedNetInterface* exceptInterface, u_int8_t ttlToFwd, unsigned char* data, unsigned size, netAddressBits sourceAddr);
protected:
//目标记录集
destRecord* fDests;
private:
//输入的GroupEId
GroupEId fIncomingGroupEId;
//DirectedNetInterface集合
DirectedNetInterfaceSet fMembers;
};
//重写左移操作符
UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g);
// A data structure for looking up a 'groupsock'
// by (multicast address, port), or by socket number
// 一个groupsock的查找类
class GroupsockLookupTable {
public:
// Creates a new Groupsock if none already exists
Groupsock* Fetch(UsageEnvironment& env, netAddressBits groupAddress, Port port, u_int8_t ttl, Boolean& isNew);
// Creates a new Groupsock if none already exists
Groupsock* Fetch(UsageEnvironment& env, netAddressBits groupAddress, netAddressBits sourceFilterAddr, Port port, Boolean& isNew);
// Returns NULL if none already exists
Groupsock* Lookup(netAddressBits groupAddress, Port port);
// Returns NULL if none already exists
Groupsock* Lookup(netAddressBits groupAddress, netAddressBits sourceFilterAddr, Port port);
// Returns NULL if none already exists
Groupsock* Lookup(UsageEnvironment& env, int sock);
//移除
Boolean Remove(Groupsock const* groupsock);
// Used to iterate through the groupsocks in the table
//groupsocks 表内的迭代器
class Iterator {
public:
Iterator(GroupsockLookupTable& groupsocks);
Groupsock* next(); // NULL iff none
private:
AddressPortLookupTable::Iterator fIter;
};
private:
//添加一个新的
Groupsock* AddNew(UsageEnvironment& env, netAddressBits groupAddress, netAddressBits sourceFilterAddress, Port port, u_int8_t ttl);
private:
friend class Iterator;
AddressPortLookupTable fTable;
};
#endif
头文件中每一行的注释都写上去了,仔细看的话应该可以理解的。很多方法我们要通过去实现里面看。
多播和广播
在介绍cpp之前先搞清楚一个概念就是socket的多播和广播。
首先多播,有这么一种情况,网络电台可能需要同时向成千上万的用户传输相同的数据,如果用我们以前讲过的传输形式,每个用户都传输一次,这样肯定是不合理的。因此,就引入了多播技术来解决这个问题,它可以同时向大量用户发送相同数据。其基本原理是这样的:有个多播组,只要加入这个组里的所有客服端,服务端发送的数据它们都能收到,具体传输到多播组里的每个客户是由路由完成的(如果路由器不支持多播或网络堵塞,实现多播也会使用隧道技术)
多播的数据传输特点如下:
1,多播服务器端针对特定多播组,只需发送1次数据,该组内的所有所有客服端都能接收数据。
2,多播组数可在IP地址范围内任意增加。
设置生存时间和加入多播组的方法
1,设置生存时间:只指服务端发送的数据包最远能传递的距离,用整数表示,并且每经过1个路由器就减1,当为0时,该数据包无法再被传递,只能销毁。因此,这个值设置过大将影响网络流量。当然,设置过小也会无法传递到目标(通过套接字可选项设置,示例代码中有使用方法)。
2,加入多播组:也是通过套接字可选项设置,示例代码中有使用方法,这里只介绍多播组的结构体ip_mreq。
struct ip_mreq
{
struct in_addr imr_multiaddr; //多播组的IP地址
struct in_addr imr_interface; //加入的客服端主机IP地址
}
然后是广播,广播在功能上和多播是一样的,都是同时可以向大量客户传递数据。但他们在网络范围上有区别,多播可以跨越不同的网络,只要加入了多播组就能接收数据。但广播只能向同一网络中的主机传输数据。
广播分为:直接广播与本地广播,直接广播sender的IP地址只需指定网络地址,主机地址全部填255。这样处在这个网络地址里的所有主机就可以接收数据了。而本地广播sender的IP地址写255.255.255.255,这样本地网络所有主机就可以接收数据了。
多播:“多播”也可以称为“组播”,在网络技术的应用并不是很多,网上视频会议、网上视频点播特别适合采用多播方式。因为如果采用单播方式,逐个节点传输,有多少个目标节点,就会有多少次传送过程,这种方式显然效率极低,是不可取的;如果采用不区分目标、全部发送的广播方式,虽然一次可以传送完数据,但是显然达不到区分特定数据接收对象的目的。采用多播方式,既可以实现一次传送所有目标节点的数据,也可以达到只对特定对象传送数据的目的。 IP网络的多播一般通过多播IP地址来实现。多播IP地址就是D类IP地址,即224.0.0.0至239.255.255.255之间的IP地址。Windows 2000中的DHCP管理器支持多播IP地址的自动分配。
知道了这些以后,我们现在要区分source-independent multicast还有source-specific multicast后者也就是会一直见到的ssm。
source-specific multicast,源特定组播,连接者连接时指定了源地址,发送者发送到接受者的包就是接受者指定源地址的包。通过这样的需求,减少了网络的的需求,更加安全,要求连接者必须要指定源地址。相对于ASM any-source multicast,发送者需要发送所有的源地址到接受者那里。
source-independent multicast,独立源组播,ism
groupsock.cpp
#include "Groupsock.hh"
#include "GroupsockHelper.hh"
//##### Eventually fix the following #include; we shouldn't know about tunnels
#include "TunnelEncaps.hh"
#ifndef NO_SSTREAM
#include <sstream>
#endif
#include <stdio.h>
/ OutputSocket //
//构造函数1 不指定端口号 由kernel来选择 设定数据源端口号为0 设定生命周期的256
OutputSocket::OutputSocket(UsageEnvironment& env)
: Socket(env, 0 /* let kernel choose port */),
fSourcePort(0),
fLastSentTTL(256/*hack: a deliberately invalid value*/)
{
}
//构造函数1 指定端口号 由kernel来选择 设定数据源端口号为0 设定生命周期的256
OutputSocket::OutputSocket(UsageEnvironment& env, Port port)
: Socket(env, port),
fSourcePort(0),
fLastSentTTL(256/*hack: a deliberately invalid value*/)
{
}
//析构函数
OutputSocket::~OutputSocket()
{
}
//写函数
//目标地址 目标端口 ttl 发送缓冲区地址 地址大小
Boolean OutputSocket::write(netAddressBits address, portNumBits portNum, u_int8_t ttl, unsigned char* buffer, unsigned bufferSize)
{
//设置目的地址
struct in_addr destAddr;
destAddr.s_addr = address;
//执行的时候第一次不会走到这里
if ((unsigned)ttl == fLastSentTTL)
{
// Optimization: Don't do a 'set TTL' system call again
//如果生命周期等于上一次设置的生命周期就不在设置ttl
if (!writeSocket(env(), socketNum(), destAddr, portNum, buffer, bufferSize))
{
return False;
}
}
else
{
//第一次会走到这里设置ttl生命周期
if (!writeSocket(env(), socketNum(), destAddr, portNum, ttl, buffer, bufferSize))
{
return False;
}
//将生命周期ttl设置给fLastSentTTL变量
fLastSentTTL = (unsigned)ttl;
}
if (sourcePortNum() == 0)
{
// Now that we've sent a packet, we can find out what the
// kernel chose as our ephemeral source port number:
//现在我们已经发送了包,我们可以找到是什么核被选择做为我们临时的源端口
//通过socket找到端口号
if (!getSourcePort(env(), socketNum(), fSourcePort))
{
if (DebugLevel >= 1)
{
env() << *this << ": failed to get source port: " << env().getResultMsg() << "\n";
}
return False;
}
}
return True;
}
// By default, we don't do reads:
//通常不做读操作 OutputSocket的handleRead
Boolean OutputSocket::handleRead(unsigned char* /*buffer*/, unsigned /*bufferMaxSize*/, unsigned& /*bytesRead*/, struct sockaddr_in& /*fromAddressAndPort*/)
{
return True;
}
/ destRecord //
//destRecord构造函数 参数目的地址 目的端口 ttl sessionid 下一个指向的destRecord
destRecord::destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId, destRecord* next)
: fNext(next),
fGroupEId(addr, port.num(), ttl),
fSessionId(sessionId)
{
}
//destRecord析构函数
destRecord::~destRecord()
{
delete fNext;
}
/ Groupsock //
NetInterfaceTrafficStats Groupsock::statsIncoming;
NetInterfaceTrafficStats Groupsock::statsOutgoing;
NetInterfaceTrafficStats Groupsock::statsRelayedIncoming;
NetInterfaceTrafficStats Groupsock::statsRelayedOutgoing;
// Constructor for a source-independent multicast group
//多播构造函数 source-independent 独立源 sim
Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr, Port port, u_int8_t ttl)
: OutputSocket(env, port),
deleteIfNoMembers(False),
isSlave(False),
fDests(new destRecord(groupAddr, port, ttl, 0, NULL)),
fIncomingGroupEId(groupAddr, port.num(), ttl)
{
//socket加入到组中
if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr))
{
//加入失败
if (DebugLevel >= 1)
{
env << *this << ": failed to join group: " << env.getResultMsg() << "\n";
}
}
// Make sure we can get our source address:
if (ourIPAddress(env) == 0)
{
if (DebugLevel >= 0)
{
// this is a fatal error
env << "Unable to determine our source address: " << env.getResultMsg() << "\n";
}
}
if (DebugLevel >= 2)
{
env << *this << ": created\n";
}
}
// Constructor for a source-specific multicast group
//特定源组播构造函数 要求传入环境上下文 组播地址 特定源 端口号
//sourceFilterAddr 这里弄明白了就是连接者连接到组中是要指定的源地址
Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr, struct in_addr const& sourceFilterAddr, Port port)
: OutputSocket(env, port),
deleteIfNoMembers(False),
isSlave(False),
fDests(new destRecord(groupAddr, port, 255, 0, NULL)),
fIncomingGroupEId(groupAddr, sourceFilterAddr, port.num())
{
// First try a SSM join. If that fails, try a regular join:
//尝试ssm方式加入
if (!socketJoinGroupSSM(env, socketNum(), groupAddr.s_addr, sourceFilterAddr.s_addr))
{
if (DebugLevel >= 3)
{
env << *this << ": SSM join failed: " << env.getResultMsg();
env << " - trying regular join instead\n";
}
//尝试普通加入
if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr))
{
if (DebugLevel >= 1)
{
env << *this << ": failed to join group: " << env.getResultMsg() << "\n";
}
}
}
//加入成功
if (DebugLevel >= 2)
{
env << *this << ": created\n";
}
}
//析构函数
Groupsock::~Groupsock()
{
//如果是ssm
if (isSSM())
{
//退出ssm组播
if (!socketLeaveGroupSSM(env(), socketNum(), groupAddress().s_addr, sourceFilterAddress().s_addr))
{
//退出失败的话退出普通
socketLeaveGroup(env(), socketNum(), groupAddress().s_addr);
}
}
else
{
//退出普通
socketLeaveGroup(env(), socketNum(), groupAddress().s_addr);
}
delete fDests;
//析构成功
if (DebugLevel >= 2)
{
env() << *this << ": deleting\n";
}
}
//创建一个新的目的地址组播成员
//地址 端口 ttl sessinid 下一个目的地址
destRecord* Groupsock::createNewDestRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId, destRecord* next)
{
// Default implementation:
return new destRecord(addr, port, ttl, sessionId, next);
}
void Groupsock::changeDestinationParameters(struct in_addr const& newDestAddr, Port newDestPort, int newDestTTL, unsigned sessionId)
{
destRecord* dest;
//找到对应的destRecord,找一个dest->fSessionId = sessionId 找不到就会一直循环
for (dest = fDests; dest != NULL && dest->fSessionId != sessionId; dest = dest->fNext) {}
if (dest == NULL)
{
//没找到
// There's no existing 'destRecord' for this "sessionId"; add a new one:
fDests = createNewDestRecord(newDestAddr, newDestPort, newDestTTL, sessionId, fDests);
return;
}
// "dest" is an existing 'destRecord' for this "sessionId"; change its values to the new ones:
// 获取到groupAddress
struct in_addr destAddr = dest->fGroupEId.groupAddress();
if (newDestAddr.s_addr != 0)
{
//首先传入的newDestAddr.s_addr不能等于0
if (newDestAddr.s_addr != destAddr.s_addr && IsMulticastAddress(newDestAddr.s_addr))
{
// 其次传入的newDestAddr.s_addr不能等于旧的destAddr.s_addr 并且newDestAddr.s_addr要是一个组播地址
// If the new destination is a multicast address, then we assume that
// we want to join it also. (If this is not in fact the case, then
// call "multicastSendOnly()" afterwards.)
//将旧的destAddr.s_addr退出组播
socketLeaveGroup(env(), socketNum(), destAddr.s_addr);
//新的newDestAddr.s_addr加入组播
socketJoinGroup(env(), socketNum(), newDestAddr.s_addr);
}
//更新值
destAddr.s_addr = newDestAddr.s_addr;
}
//获取旧的端口
portNumBits destPortNum = dest->fGroupEId.portNum();
if (newDestPort.num() != 0)
{
//端口也要改变
if (newDestPort.num() != destPortNum && IsMulticastAddress(destAddr.s_addr))
{
//端口新旧不相同,则改变端口
// Also bind to the new port number:
changePort(newDestPort);
// And rejoin the multicast group:
socketJoinGroup(env(), socketNum(), destAddr.s_addr);
}
destPortNum = newDestPort.num();
}
//修改ttl
u_int8_t destTTL = ttl();
if (newDestTTL != ~0)
{
destTTL = (u_int8_t)newDestTTL;
}
dest->fGroupEId = GroupEId(destAddr, destPortNum, destTTL);
// Finally, remove any other 'destRecord's that might also have this "sessionId":
//修改完成后删除其他还拥有次sessionId的destRecord
removeDestinationFrom(dest->fNext, sessionId);
}
//获取fSessionId
unsigned Groupsock::lookupSessionIdFromDestination(struct sockaddr_in const& destAddrAndPort) const
{
destRecord* dest = lookupDestRecordFromDestination(destAddrAndPort);
if (dest == NULL)
{
return 0;
}
return dest->fSessionId;
}
//添加DestRecord
void Groupsock::addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId) {
// Default implementation:
// If there's no existing 'destRecord' with the same "addr", "port", and "sessionId", add a new one:
for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext)
{
if (sessionId == dest->fSessionId && addr.s_addr == dest->fGroupEId.groupAddress().s_addr && port.num() == dest->fGroupEId.portNum())
{
//如果找到了一模一样的就返回
return;
}
}
//创建
fDests = createNewDestRecord(addr, port, 255, sessionId, fDests);
}
//删除DestRecord
void Groupsock::removeDestination(unsigned sessionId)
{
// Default implementation:
removeDestinationFrom(fDests, sessionId);
}
//删除所有DestRecord
void Groupsock::removeAllDestinations()
{
delete fDests;
fDests = NULL;
}
void Groupsock::multicastSendOnly()
{
// We disable this code for now, because - on some systems - leaving the multicast group seems to cause sent packets
// to not be received by other applications (at least, on the same host).
#if 0
socketLeaveGroup(env(), socketNum(), fIncomingGroupEId.groupAddress().s_addr);
for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
socketLeaveGroup(env(), socketNum(), dests->fGroupEId.groupAddress().s_addr);
}
#endif
}
//输出
Boolean Groupsock::output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize, DirectedNetInterface* interfaceNotToFwdBackTo) {
do
{
// First, do the datagram send, to each destination:
//设置写成功变量
Boolean writeSuccess = True;
for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext)
{
//对于每一个dests进行write
if (!write(dests->fGroupEId.groupAddress().s_addr, dests->fGroupEId.portNum(), dests->fGroupEId.ttl(), buffer, bufferSize))
{
//失败
writeSuccess = False;
break;
}
}
if (!writeSuccess)
{
//只要有一个失败就退出
break;
}
//计算传输中的大小
statsOutgoing.countPacket(bufferSize);
statsGroupOutgoing.countPacket(bufferSize);
// Then, forward to our members:
int numMembers = 0;
if (!members().IsEmpty())
{
numMembers = outputToAllMembersExcept(interfaceNotToFwdBackTo, ttl(), buffer, bufferSize, ourIPAddress(env));
if (numMembers < 0)
{
break;
}
}
if (DebugLevel >= 3)
{
env << *this << ": wrote " << bufferSize << " bytes, ttl " << (unsigned)ttl();
if (numMembers > 0)
{
env << "; relayed to " << numMembers << " members";
}
env << "\n";
}
return True;
} while (0);
if (DebugLevel >= 0)
{
// this is a fatal error
UsageEnvironment::MsgString msg = strDup(env.getResultMsg());
env.setResultMsg("Groupsock write failed: ", msg);
delete[](char*)msg;
}
return False;
}
Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize, unsigned& bytesRead, struct sockaddr_in& fromAddressAndPort)
{
// Read data from the socket, and relay it across any attached tunnels
//##### later make this code more general - independent of tunnels
bytesRead = 0;
//最大读取字节数
int maxBytesToRead = bufferMaxSize - TunnelEncapsulationTrailerMaxSize;
//读取字节数
int numBytes = readSocket(env(), socketNum(), buffer, maxBytesToRead, fromAddressAndPort);
if (numBytes < 0)
{
//小于0失败
if (DebugLevel >= 0)
{
// this is a fatal error
UsageEnvironment::MsgString msg = strDup(env().getResultMsg());
env().setResultMsg("Groupsock read failed: ", msg);
delete[](char*)msg;
}
return False;
}
// If we're a SSM group, make sure the source address matches:
//如果是特定源组播要确保源地址一致性
if (isSSM() && fromAddressAndPort.sin_addr.s_addr != sourceFilterAddress().s_addr)
{
return True;
}
// We'll handle this data.
// Also write it (with the encapsulation trailer) to each member,
// unless the packet was originally sent by us to begin with.
//我们将处理这些数据并将这些数据写给组播的成员
bytesRead = numBytes;
int numMembers = 0;
if (!wasLoopedBackFromUs(env(), fromAddressAndPort))
{
statsIncoming.countPacket(numBytes);
statsGroupIncoming.countPacket(numBytes);
numMembers = outputToAllMembersExcept(NULL, ttl(), buffer, bytesRead, fromAddressAndPort.sin_addr.s_addr);
if (numMembers > 0)
{
statsRelayedIncoming.countPacket(numBytes);
statsGroupRelayedIncoming.countPacket(numBytes);
}
}
if (DebugLevel >= 3)
{
env() << *this << ": read " << bytesRead << " bytes from " << AddressString(fromAddressAndPort).val() << ", port " << ntohs(fromAddressAndPort.sin_port);
if (numMembers > 0)
{
env() << "; relayed to " << numMembers << " members";
}
env() << "\n";
}
return True;
}
Boolean Groupsock::wasLoopedBackFromUs(UsageEnvironment& env, struct sockaddr_in& fromAddressAndPort)
{
if (fromAddressAndPort.sin_addr.s_addr == ourIPAddress(env) || fromAddressAndPort.sin_addr.s_addr == 0x7F000001/*127.0.0.1*/)
{
if (fromAddressAndPort.sin_port == sourcePortNum())
{
#ifdef DEBUG_LOOPBACK_CHECKING
if (DebugLevel >= 3)
{
env() << *this << ": got looped-back packet\n";
}
#endif
return True;
}
}
return False;
}
//通过destAddrAndPort寻找destRecord
destRecord* Groupsock::lookupDestRecordFromDestination(struct sockaddr_in const& destAddrAndPort) const
{
for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext)
{
if (destAddrAndPort.sin_addr.s_addr == dest->fGroupEId.groupAddress().s_addr && destAddrAndPort.sin_port == dest->fGroupEId.portNum())
{
return dest;
}
}
return NULL;
}
//删除
void Groupsock::removeDestinationFrom(destRecord*& dests, unsigned sessionId)
{
destRecord** destsPtr = &dests;
while (*destsPtr != NULL)
{
if (sessionId == (*destsPtr)->fSessionId)
{
// Remove the record pointed to by *destsPtr :
destRecord* next = (*destsPtr)->fNext;
(*destsPtr)->fNext = NULL;
delete (*destsPtr);
*destsPtr = next;
}
else
{
//相应替换
destsPtr = &((*destsPtr)->fNext);
}
}
}
//向所有组员发送组播数据除了指定成员
int Groupsock::outputToAllMembersExcept(DirectedNetInterface* exceptInterface, u_int8_t ttlToFwd, unsigned char* data, unsigned size, netAddressBits sourceAddr)
{
// Don't forward TTL-0 packets
//如果ttlToFwd已经为0了 不发送了
if (ttlToFwd == 0)
{
return 0;
}
DirectedNetInterfaceSet::Iterator iter(members());
unsigned numMembers = 0;
DirectedNetInterface* interf;
while ((interf = iter.next()) != NULL)
{
// Check whether we've asked to exclude this interface:
//如果找到了这个要排除的成员
if (interf == exceptInterface)
{
//就直接返回
continue;
}
// Check that the packet's source address makes it OK to
// be relayed across this interface:
UsageEnvironment& saveEnv = env();
// because the following call may delete "this"
if (!interf->SourceAddrOKForRelaying(saveEnv, sourceAddr))
{
if (strcmp(saveEnv.getResultMsg(), "") != 0)
{
// Treat this as a fatal error
return -1;
}
else
{
continue;
}
}
if (numMembers == 0)
{
// We know that we're going to forward to at least one
// member, so fill in the tunnel encapsulation trailer.
// (Note: Allow for it not being 4-byte-aligned.)
//数据
TunnelEncapsulationTrailer* trailerInPacket = (TunnelEncapsulationTrailer*)&data[size];
TunnelEncapsulationTrailer* trailer;
Boolean misaligned = ((uintptr_t)trailerInPacket & 3) != 0;
unsigned trailerOffset;
u_int8_t tunnelCmd;
if (isSSM())
{
// add an 'auxilliary address' before the trailer
//ssm要做相应操作
trailerOffset = TunnelEncapsulationTrailerAuxSize;
tunnelCmd = TunnelDataAuxCmd;
}
else
{
trailerOffset = 0;
tunnelCmd = TunnelDataCmd;
}
unsigned trailerSize = TunnelEncapsulationTrailerSize + trailerOffset;
unsigned tmpTr[TunnelEncapsulationTrailerMaxSize];
if (misaligned)
{
trailer = (TunnelEncapsulationTrailer*)&tmpTr;
}
else
{
trailer = trailerInPacket;
}
trailer += trailerOffset;
if (fDests != NULL)
{
trailer->address() = fDests->fGroupEId.groupAddress().s_addr;
Port destPort(ntohs(fDests->fGroupEId.portNum()));
trailer->port() = destPort; // structure copy
}
trailer->ttl() = ttlToFwd;
trailer->command() = tunnelCmd;
if (isSSM())
{
trailer->auxAddress() = sourceFilterAddress().s_addr;
}
if (misaligned)
{
memmove(trailerInPacket, trailer - trailerOffset, trailerSize);
}
size += trailerSize;
}
//写入
interf->write(data, size);
++numMembers;
}
return numMembers;
}
UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g) {
UsageEnvironment& s1 = s << timestampString() << " Groupsock("
<< g.socketNum() << ": "
<< AddressString(g.groupAddress()).val()
<< ", " << g.port() << ", ";
if (g.isSSM()) {
return s1 << "SSM source: "
<< AddressString(g.sourceFilterAddress()).val() << ")";
}
else
{
return s1 << (unsigned)(g.ttl()) << ")";
}
}
// GroupsockLookupTable //
// A hash table used to index Groupsocks by socket number.
//创建一个哈希桶
static HashTable*& getSocketTable(UsageEnvironment& env)
{
_groupsockPriv* priv = groupsockPriv(env);
if (priv->socketTable == NULL)
{
// We need to create it
//本质上为哈希桶
priv->socketTable = HashTable::create(ONE_WORD_HASH_KEYS);
}
return priv->socketTable;
}
//注销一个groupsock
static Boolean unsetGroupsockBySocket(Groupsock const* groupsock) {
do
{
if (groupsock == NULL)
{
break;
}
//获取groupsock的sock
int sock = groupsock->socketNum();
// Make sure "sock" is in bounds:
if (sock < 0)
{
break;
}
//找到这个group组
HashTable*& sockets = getSocketTable(groupsock->env());
//查找对应的socket
Groupsock* gs = (Groupsock*)sockets->Lookup((char*)(long)sock);
if (gs == NULL || gs != groupsock)
{
break;
}
//删除
sockets->Remove((char*)(long)sock);
//如果空了就销毁哈希桶
if (sockets->IsEmpty())
{
// We can also delete the table (to reclaim space):
delete sockets; sockets = NULL;
reclaimGroupsockPriv(gs->env());
}
return True;
} while (0);
return False;
}
//add
static Boolean setGroupsockBySocket(UsageEnvironment& env, int sock, Groupsock* groupsock)
{
do
{
// Make sure the "sock" parameter is in bounds:
if (sock < 0)
{
char buf[100];
sprintf(buf, "trying to use bad socket (%d)", sock);
env.setResultMsg(buf);
break;
}
HashTable* sockets = getSocketTable(env);
// Make sure we're not replacing an existing Groupsock (although that shouldn't happen)
Boolean alreadyExists = (sockets->Lookup((char*)(long)sock) != 0);
if (alreadyExists)
{
char buf[100];
sprintf(buf, "Attempting to replace an existing socket (%d)", sock);
env.setResultMsg(buf);
break;
}
sockets->Add((char*)(long)sock, groupsock);
return True;
} while (0);
return False;
}
//获取Groupsock
static Groupsock* getGroupsockBySocket(UsageEnvironment& env, int sock)
{
do
{
// Make sure the "sock" parameter is in bounds:
if (sock < 0)
{
break;
}
HashTable* sockets = getSocketTable(env);
return (Groupsock*)sockets->Lookup((char*)(long)sock);
} while (0);
return NULL;
}
//也是获取没有就添加
Groupsock* GroupsockLookupTable::Fetch(UsageEnvironment& env, netAddressBits groupAddress, Port port, u_int8_t ttl, Boolean& isNew)
{
isNew = False;
Groupsock* groupsock;
do
{
groupsock = (Groupsock*)fTable.Lookup(groupAddress, (~0), port);
if (groupsock == NULL)
{
// we need to create one:
groupsock = AddNew(env, groupAddress, (~0), port, ttl);
if (groupsock == NULL) break;
isNew = True;
}
} while (0);
return groupsock;
}
// ssm的fetch
Groupsock* GroupsockLookupTable::Fetch(UsageEnvironment& env, netAddressBits groupAddress, netAddressBits sourceFilterAddr, Port port, Boolean& isNew)
{
isNew = False;
Groupsock* groupsock;
do
{
groupsock = (Groupsock*)fTable.Lookup(groupAddress, sourceFilterAddr, port);
if (groupsock == NULL)
{
// we need to create one:
groupsock = AddNew(env, groupAddress, sourceFilterAddr, port, 0);
if (groupsock == NULL) break;
isNew = True;
}
} while (0);
return groupsock;
}
Groupsock* GroupsockLookupTable::Lookup(netAddressBits groupAddress, Port port)
{
return (Groupsock*)fTable.Lookup(groupAddress, (~0), port);
}
Groupsock* GroupsockLookupTable::Lookup(netAddressBits groupAddress, netAddressBits sourceFilterAddr, Port port)
{
return (Groupsock*)fTable.Lookup(groupAddress, sourceFilterAddr, port);
}
Groupsock* GroupsockLookupTable::Lookup(UsageEnvironment& env, int sock)
{
return getGroupsockBySocket(env, sock);
}
Boolean GroupsockLookupTable::Remove(Groupsock const* groupsock)
{
unsetGroupsockBySocket(groupsock);
return fTable.Remove(groupsock->groupAddress().s_addr, groupsock->sourceFilterAddress().s_addr, groupsock->port());
}
//添加
Groupsock* GroupsockLookupTable::AddNew(UsageEnvironment& env, netAddressBits groupAddress, netAddressBits sourceFilterAddress, Port port, u_int8_t ttl)
{
Groupsock* groupsock;
do
{
struct in_addr groupAddr; groupAddr.s_addr = groupAddress;
if (sourceFilterAddress == netAddressBits(~0))
{
// regular, ISM groupsock
groupsock = new Groupsock(env, groupAddr, port, ttl);
}
else
{
// SSM groupsock
struct in_addr sourceFilterAddr;
sourceFilterAddr.s_addr = sourceFilterAddress;
groupsock = new Groupsock(env, groupAddr, sourceFilterAddr, port);
}
if (groupsock == NULL || groupsock->socketNum() < 0) break;
if (!setGroupsockBySocket(env, groupsock->socketNum(), groupsock)) break;
fTable.Add(groupAddress, sourceFilterAddress, port, (void*)groupsock);
} while (0);
return groupsock;
}
GroupsockLookupTable::Iterator::Iterator(GroupsockLookupTable& groupsocks)
: fIter(AddressPortLookupTable::Iterator(groupsocks.fTable)) {
}
Groupsock* GroupsockLookupTable::Iterator::next() {
return (Groupsock*)fIter.next();
};
代码好长好长啊,但是还是请大家看着注释结合自己的理解读一遍吧,我这里也是一知半解的读完的,因为数据结构有点多自己也有点懵了,这些就是live555源码中的工具篇了,我们会在接下来进入框架篇的学习。