本文中提到的PeerCast特指www.peercast.org发布的开源P2P流媒体软件PeerCast,作者为Giles。
而不是斯坦福大学P2P小组H. Deshpande等人在论文《Streaming Live Media over Peers》中提出的PeerCast,通常相关研究论文里提及的都是这个PeerCast。
也不是乔治亚理工学院Jianjun Zhang等人在论文《Reliable End System Multicasting with a Heterogeneous Overlay Network》中提出的PeerCast。
请勿混淆。
PeerCast是一款免费、开源的P2P流媒体软件,作者的目标是开发一个简单易用的系统,用于研究和改进P2P流媒体技术,因此这是一个尚不成熟的、实验性质的非营利性项目。
PeerCast官方站点www.peercast.org成立于2002年4月,最新版的客户端发布于2007年12月,版本号为0.1218。源代码采用Subversion进行版本管理,完整的源代码可在SVN服务器svn://peercast.org/peercast/trunk(最新版本)和svn://peercast.org/peercast/tags(历史版本)下载。
目前PeerCast主要用于网络电台的直播,不支持点播,在线人数规模约为数百人。
l Yellow Page:简称YP,整个网络的根目录服务器,维护一份包含所有流媒体频道信息的列表。
l 广播者:流媒体频道的发布者,是频道所属网络的数据来源。
l 收听者:流媒体频道的收听者,从父节点下载数据。
l 转播者:流媒体频道的转播者,除了充当收听者之外,同时还负责向直接子节点上传数据。
l 转播数:转播者的直接子节点数量,该值相当于节点的上传带宽。
l Channel ID:简称cid,16字节的频道ID,广播者创建频道时随机生成,相当于BitTorrent网络中文件的info_hash。
l Session ID:简称sid,16字节的节点会话ID,节点每次启动时随机生成,是节点的全局唯一标识符,相当于BitTorrent网络中的peer_id。
l Broadcast ID:简称bcid,16字节的广播者ID,安装PeerCast后首次运行时随机生成,用于标识广播者。
PeerCast网络可分为四层,从高到低依次是YP、广播者、转播者和收听者,网络结构如下图所示:
可见PeerCast网络是典型的树状结构。YP处于网络最高层,是整个网络的根节点;下面几层中,传输同一个频道的所有节点构成一棵广播树,其中广播者是频道广播树的根节点,从流媒体服务器(图中未画出)获取原始数据,向上提供频道信息,以便YP进行索引,向下提供流媒体数据;转播者处于中间,它的存在体现了P2P网络的基本特征:即节点不仅接收数据,还要进行转发;收听者处于最低层,是广播树的叶子节点,只下载而不提供上传,它的在在体现了树状P2P模型的最大缺点:叶子节点的网络资源没有得到充分利用。
需要说明的是,每个PeerCast节点都可能同时扮演多个角色,如既是YP又是广播者,或既是频道A的广播者又是频道B的收听者。这也意味着在真实的PeerCast网络中,不同的频道子网络从拓扑结构上看可能发生交叉,但其数据传输是各自独立的。
本节主要讨论转播者和收听者这两种P2P节点的基本工作原理,省略了对YP和广播者的相关说明。
1.连接YP:
用户首先访问YP的Web页面,该页面中显示了一份完整的频道列表。在手动选取想要收听的频道后,YP根据请求的频道ID返回该频道的广播者信息。
2.连接广播者:
节点先向广播者发送HTTP协议格式的频道请求,收到回复后发送握手消息,握手成功后可能有两种情况:
(1) 若广播者转播数未达到设置上限,握手成功后即开始向节点连续传输流媒体数据,并将本节点加入其子节点列表;
(2) 若广播者转播数已达到设置上限,无法为本节点提供服务,则握手成功后,广播者将根据一定算法从自己的所有子节点中为本节点选取最多8个备选父节点,本节点收到这些节点信息后将其加入邻居列表,从中选取一个节点进行连接,如此循环至成功接收流媒体数据为止。
如连接的节点位于防火墙或NAT后,需要通过YP中转连接请求。
广播树中的数据传输采用纯推机制(push),即数据是由父节点主动发送的,无需子节点请求。节点加入网络后,即开始连续接收父节点发来的流媒体数据,通常接收的第一个数据包为流媒体头,随后的都是普通流数据。数据传输过程中父节点不会向本节点发送任何额外的消息。
节点将接收到的数据保存到自己的流媒体缓冲区中,该缓冲区由一个循环队列实现,当缓冲数据足够多时,节点将调用本地媒体播放器。
节点与播放器之间的通信采用HTTP 1.0协议,流媒体数据被映射为一个HTTP服务器上的文件,播放器访问该文件地址,从节点获取数据后解码播放。
可以看出节点只是完成了数据的传输,不涉及任何流媒体的编解码操作。
节点会定时向上层节点广播本机信息,广播包首先发给父节点,然后被层层向上转发,最终到达广播者。其经过途中的所有节点都将根据广播包内容更新自己的子节点列表。可以看出这属于汇聚式的消息转发(区别于泛洪式转发)。
广播包中含有两个重要的数值:
hops:表示广播包源节点与收到包的当前节点之间的跳数,初始值为0,每经过一跳加1;
ttl:表示广播包的剩余生存时间,初始值通常为11,每经过一跳减1,值减为0时广播包将不再向上转发。
通过这样的广播机制,节点可以得到比较完整的子节点列表(ttl的限制使距离过远的广播包无法到达上层)。但由于广播包中不含路由信息,因此节点只能知道所有子节点的层次关系,而无法得知其详细的拓扑结构。
节点正常退出时,先向所有直接子节点(即邻接子节点)发送退出消息,再向父节点最后广播一次本机信息,并断开频道数据流,父节点检测到流断开后将向本节点发送退出消息,最后节点断开所有连接,退出网络。
节点正常退出前没有帮助其子节点重定向到新的父节点,连接断开后,其直接子节点需要自行重新加入广播树。
节点异常退出时,其父节点及直接子节点均立即检测到数据流断开,父节点回收其占用的资源,直接子节点重新加入广播树。
需要说明的是,不管节点是正常退出还是异常退出,父节点都没有立即从其子节点列表删除本节点的信息,需要经过若干个广播包发送周期后才会删除已退出的节点信息,这可能造成后加入的节点获得无效的备用父节点信息。另外PeerCast节点之间没有维持连接的心跳包,是通过检测连接是否断开来发现有节点异常退出的。
PeerCast中没有提供平衡广播树结构的算法,可能造成节点负载不均、树深度过大,难于维护。
该PeerCast在网络结构上类似于斯坦福大学的PeerCast模型,都是树状结构,但节点加入和退出机制都不同。
PCP协议是PeerCast客户端之间的应用层通信协议,在传输层使用TCP协议。该协议定义了一套通信消息,用于控制信息及流媒体数据的传输。
一共有三类消息:
1.主消息:如helo, oleh, host, chan, info等;
2.子消息:如sid,是helo的子消息,必须附在主消息之后;
3.无主从关系的独立消息:如pcp, ok, quit。
注意:
(1)有的主消息不能独立出现,必须作为其它主消息的子消息,如主消息info必须作为chan的子消息,这称为消息的嵌套;
(2)消息最多可嵌套两次,如info作为chan的子消息,chan再作为host的子消息。
根据消息类型不同,有两种消息格式:
1.msg_id(4字节char) + sub_msg_count(4字节int)
这种格式用于主消息。其中消息id为ASCII码表示的字符串,不足4字节的用"/0"补足("pcp/n"除外,可能是作者笔误)。
sub_msg_count的低位表示后面紧接的子消息数量,其高位固定为0x80,是为了区别于第二种消息的第4-8位。
例如:
68 65 6C 6F | 05 00 00 80 (host消息,后接5个子消息)
2.msg_id(4字节char) + payload_len(4字节int) + payload
这种格式用于子消息或独立消息。payload_len表示消息负载的长度,payload数据类型可能为char/bytes/string/int/short。
例如:
69 70 00 00 | 04 00 00 00 | 3A 1E 73 CA (ip = 202.115.30.58)
71 75 69 74 | 04 00 00 00 | E8 03 00 00 (quit消息,0x03E8表示退出原因)
注意:
(1)sub_msg_count和payload_len均采用Little-Endian字节序,即低位在前,高位在后;
(2)payload为int/short时也采用Little-Endian字节序。
下表列出了主要的消息及简单的功能说明,因消息ID通常为简写,故消息后的括号内给出了其具体含义,以供参考。完整的消息列表请参见PeerCast源代码目录下的/core/common/pcp.h文件。
序号 |
主消息或独立消息 |
可能的子消息 |
功能说明 |
1 |
helo(hello) |
agnt(agent), ver(version), sid(session ID),port, ping, bcid(broadcast ID) |
发出握手连接请求,ping子消息表明请求对方测试本机是否在防火墙后或内网中 |
2 |
oleh(helo的反转) |
agnt(agent), ver(version), sid(session ID),port, bcid(broadcast ID) |
回复握手连接 |
3 |
root |
uint(update interval), url, chkv(check version), next, asci(ascii), upd(update) |
YP或广播者发送的根节点信息 |
4 |
bcst |
grp(group), hops, ttl(time to live), from, vers(version), cid(channel ID), host, chan |
向上层节点广播信息,通常用于定时广播本机信息,告知上层节点本机的存在 |
5 |
host |
cid(channel ID), id(session ID), ip, port, numl(num of listeners), numr(num of relays), uptm(up time), ver(version), flg1(flag), oldp(old media play position), newp(new media play position) |
节点信息,包含节点ID、IP、监听端口等关键信息 |
6 |
chan(channel) |
id(channel ID), bcid(broadcast ID), info, trck(track), pkt(packet) |
频道信息 |
7 |
info |
name, bitr(bitrate), gnre(genre), url, desc(description), cmnt(comment), type |
频道基本信息,必须作为chan的子消息 |
8 |
trck(track) |
titl(title), crea(creator), url, albm(album) |
频道当前播放音轨信息,必须作为chan的子消息 |
9 |
pkt(packet) |
type, pos(media play position), data |
频道数据包信息,必须作为chan的子消息,其中type标明是包头还是普通包,data子消息的负载即流媒体数据 |
10 |
pcp |
无 |
开始测试对方是否在防火墙后或内网中 |
11 |
ok |
无 |
握手连接成功,之后将是连续的频道数据包 |
12 |
quit |
无 |
标识节点准备断开连接 |