关于Peercast源代码的分析

Peercast收听电台的源代码流程分析 以收听JOKV-FM(TEST)为例,在YP上点击Play,则其URL地址为peercast://pls/25838B9F1EAE27079B793C9FBA0E4156?tip=222.148.187.176:7144case WM_COPYDATA:   {    COPYDATASTRUCT *pc = (COPYDATASTRUCT *)lParam;    LOG_DEBUG("UR 阅读详情
关于Peercast源代码的分析
 

1. 以.h文件为单位,每个.h文件将建立一个分析文档。大致说明各个类完成的功能。

2. 对于比较重要的部分将重点分析,例如Peercast中关于Gnutella协议的定义和实现

3. 类图及各类间的调用关系图。由于比较复杂,将在后期推出

4. Peercast执行流程

5. 欢迎大家提意见以便改进。欢迎讨论和提问问题。

期待共同进步,感谢大家的支持!

Stream.h源文件分析

Stream.h包括四个类,分别是Stream、MemoryStream、FileStream、IndirectStream。其中MemoryStream、FileStream、IndirectStream均继承自Stream类。

流涉及三个基本操作:

可以读取流。读取是从流到数据结构(如字节数组)的数据传输。
可以写入流。写入是从数据结构到流的数据传输。
流可以支持查找。查找是对流内的当前位置进行查询和修改。查找功能取决于流具有的后备存储区类型。例如,网络流没有当前位置的统一概念,因此一般不支持查找。
Stream 是所有流的抽象基类。流是字节序列的抽象概念,例如文件、输入/输出设备、内部进程通信管道或者 TCP/IP 套接字。Stream 类及其派生类提供这些不同类型的输入和输出的一般视图,使程序员不必了解操作系统和基础设备的具体细节。

对实施者的说明:  在实现 Stream 的派生类时,必须提供 Read 和 Write 方法的实现。

MemoryStream 类创建这样的流,该流以内存而不是磁盘或网络连接作为支持存储区。MemoryStream 封装以字符数组形式存储的数据,该数组在创建 MemoryStream 对象时被初始化,或者该数组可创建为空数组。可在内存中直接访问这些封装的数据。内存流可降低应用程序中对临时缓冲区和临时文件的需要。

使用 FileStream 类对文件系统上的文件进行读取、写入、打开和关闭操作

Peercast对于流的封装与.net framework对于Stream的封装类似,可参见http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/cpref/html/frlrfSystemIOStreamClassTopic.asp

common.h源文件分析

GeneralException类:其中StreamException继承自GeneralException,而SockException、EOFException、CryptException、TimeoutException均从StreamException继承

GnuID、GnuIDList、Host类

GnuID是唯一标识GnuPacket的ID号。由16位组成

主要方法有

void generate(unsigned char = 0);   //通过随机数生成Brocast ID号
void encode(class Host *, const char *,const char *,unsigned char); //通过IP地址和其他数据对ID进行重新编码

GnuIDList维护一个GnuID的链表

Host类用于对主机IP地址、端口号的处理

IniFile.h源文件分析

peercast.ini的格式如下

[Server]
serverPort = 7144
autoServe = Yes
forceIP =
isRoot = No
maxBitrateOut = 0
maxRelays = 2
maxDirect = 0

IniFile类定义三个字符串变量,currLine,nameStr,valueStr

currentLine对应INI文件中的一行,例如serverPort = 7144

nameStr对应相应的变量名,例如serverPort

valueStr对应相应的变量值,例如7144

写入INI文件时根据写入变量值的不同提供几种写入方法:

 writeSection(const char *name)写入段,例如writeSection( "Server" )则写入[Server]

writeIntValue(const char *name, int iv)写入整型变量,writeIntValue( serverPort, 7144 ),则写入

serverPort = 7144,其他类似方法还有writeStrValue,writeBoolValue,writeLine等

读取INI文件时,readNext()每次读取INI文件中的一行到currLine中,并把相应的变量名和变量值读取到nameStr和valueStr中

getName()返回变量名,根据变量值类型的不同相应有getIntValue,getStrValue,getBoolValue

这里用loadSettings的部分代码解释一下读取配置文件的过程

void ServMgr::loadSettings(const char *fn)
{
 IniFile iniFile;

 if (!iniFile.openReadOnly(fn))
  saveSettings(fn);

 if (iniFile.openReadOnly(fn))
 {
  while (iniFile.readNext())
  {
   // server settings
   if (iniFile.isName("serverPort"))
    servMgr->serverHost.port = iniFile.getIntValue();
   else if (iniFile.isName("autoServe"))
    servMgr->autoServe = iniFile.getBoolValue();
   else if (iniFile.isName("autoConnect"))
    servMgr->autoConnect = iniFile.getBoolValue();
   else if (iniFile.isName("icyPassword"))  // depreciated
    strcpy(servMgr->password,iniFile.getStrValue());
   else if (iniFile.isName("forceIP"))
    servMgr->forceIP = iniFile.getStrValue();

也就是用readNext()逐行读取并用IF ELSE语句判断是否是要读的变量,直至读到文件末尾为止

Channel.h源代码分析

ChanInfo类:保存频道信息

::String name;
 GnuID id,bcID;
 int  bitrate;
 TYPE contentType;
 PROTOCOL srcProtocol;
 unsigned int lastPlayStart,lastPlayEnd;
 unsigned int numSkips;
 unsigned int createdTime;

 STATUS  status;

 TrackInfo track;
 ::String desc,genre,url,comment;

Channel类:管理具体的频道操作

THREAD_PROC Channel::stream(ThreadInfo *thread)

ChannelMgr类,它完成频道创建、管理、寻找、停止等操作

Channel *ChanMgr::createChannel(ChanInfo &info, const char *mount)

void ChanMgr::findAndPlayChannel(ChanInfo &info, bool keep)

ChannelSource负责具体的流传输工作,其中定义一个虚方法stream由子类PeercastSource、ICYSource、URLSource实现

virtual void stream(Channel *) = 0;

ChannelHit:维持一份与你收听同一个频道的节点的信息

void ChanHit::pickNearestIP(Host &h)

void ChanHit::initLocal(int numl,int numr,int,int uptm,bool connected,unsigned int oldp,unsigned int newp)

ChannelHitList:维持一份ChannelHit列表

ChanHit *ChanHitList::addHit(ChanHit &h)

http.h源文件分析

定义HTTP消息:

static const char *HTTP_SC_OK   = "HTTP/1.0 200 OK";
static const char *HTTP_SC_NOTFOUND  = "HTTP/1.0 404 Not Found";
static const char *HTTP_SC_UNAVAILABLE = "HTTP/1.0 503 Service Unavailable";

static const char *HTTP_HS_SERVER  = "Server:";
static const char *HTTP_HS_AGENT  = "User-Agent:";
static const char *HTTP_HS_CONTENT  = "Content-Type:";
static const char *HTTP_HS_HOST   = "Host:";
static const char *HTTP_HS_ACCEPT  = "Accept:";
static const char *HTTP_HS_LENGTH  = "Content-Length:";

static const char *MIME_MP3   = "audio/mpeg";
static const char *MIME_OGG   = "application/ogg";

HTTP类

class HTTP : public IndirectStream
{
public:
 HTTP(Stream &s)
 {
  init(&s);
 }

 void initRequest(const char *r)
 {
  strcpy(cmdLine,r);
 }
 void readRequest();
 bool isRequest(const char *);

 int  readResponse();
 bool checkResponse(int);

 bool nextHeader();
 bool isHeader(const char *);
 char *getArgStr();
 int  getArgInt();

 void getAuthUserPass(char *, char *);

 char cmdLine[8192],*arg;

};

wsocket.h源文件分析

WSAClientSocket继承自ClientSocket,完成对基本WinSock函数的封装。

ClientSocket只是提供一个接口,具体实现由其继承类WSAClientSocket(WINDOWS)和UClientSocket (UNIX)实现

这里先介绍一下Host类:

unsigned int ip; //主机IP

unsigned short port; //主机端口号
 unsigned int value;

下面介绍一下WSAClientSocket的具体实现

//初始化,每个Winsock应用都必须加载合适的WinSock DLL版本.加载库是通过调用WSAStartup函数实现的

void WSAClientSocket::init()
{
 WORD wVersionRequested;
 WSADATA wsaData;
 int err;
   
 wVersionRequested = MAKEWORD( 2, 0 );
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 )
  throw SockException("Unable to init sockets");

}

//建立套接字,通过调用socket函数来实现

void WSAClientSocket::open(Host &rh)
{
 sockNum = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);

 if (sockNum == INVALID_SOCKET)
  throw SockException("Can`t open socket");

 setBlocking(false);
#ifdef DISABLE_NAGLE
 setNagle(false);
#endif

 host = rh;

 memset(&remoteAddr,0,sizeof(remoteAddr));

 remoteAddr.sin_family = AF_INET;
 remoteAddr.sin_port = htons(host.port);
 remoteAddr.sin_addr.S_un.S_addr = htonl(host.ip);

}

//服务器绑定。一旦为某种协议创建了套接字,就必须将套接字绑定到一个已知地址上。使用bind函数

void WSAClientSocket::bind(Host &h)
{
 struct sockaddr_in localAddr;

 if ((sockNum = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
  throw SockException("Can`t open socket");

 setBlocking(false);
 setReuse(true);

 memset(&localAddr,0,sizeof(localAddr));
 localAddr.sin_family = AF_INET;
 localAddr.sin_port = htons(h.port);
 localAddr.sin_addr.s_addr = INADDR_ANY;

 if( ::bind (sockNum, (sockaddr *)&localAddr, sizeof(localAddr)) == -1)
  throw SockException("Can`t bind socket");

//接下来要做的,是将套接字置入监听模式。bind函数的作用只是将套接字和指定的地址关联在一起。指示套接字等待连接传入的API是listen

 if (::listen(sockNum,SOMAXCONN))
  throw SockException("Can`t listen",WSAGetLastError());

 host = h;
}

 //现在我们已做好了接受客户机连接的准备,通过ACCEPT函数来完成

ClientSocket *WSAClientSocket::accept()
{

 int fromSize = sizeof(sockaddr_in);
 sockaddr_in from;

 int conSock = ::accept(sockNum,(sockaddr *)&from,&fromSize);

 if (conSock ==  INVALID_SOCKET)
  return NULL;

    WSAClientSocket *cs = new WSAClientSocket();
 cs->sockNum = conSock;

 cs->host.port = from.sin_port;
 cs->host.ip = from.sin_addr.S_un.S_un_b.s_b1<<24 |
      from.sin_addr.S_un.S_un_b.s_b2<<16 |
      from.sin_addr.S_un.S_un_b.s_b3<<8 |
      from.sin_addr.S_un.S_un_b.s_b4;

 cs->setBlocking(false);
#ifdef DISABLE_NAGLE
 cs->setNagle(false);
#endif

 return cs;
}

 //关闭套接字

void WSAClientSocket::close()
{
 if (sockNum)
 {
  shutdown(sockNum,SD_SEND);

  setReadTimeout(2000);
  try
  {
   //char c;
   //while (readUpto(&c,1)!=0);
   //readUpto(&c,1);
  }catch(StreamException &) {}

  if (closesocket(sockNum))
   LOG_ERROR("closesocket() error");

  sockNum=0;
 }
}

//客户端连接

 void WSAClientSocket::connect()
{
 if (::connect(sockNum,(struct sockaddr *)&remoteAddr,sizeof(remoteAddr)) == SOCKET_ERROR)
  checkTimeout(false,true);

}

//发送数据

void WSAClientSocket::write(const void *p, int l)
{
 while (l)
 {
  int r = send(sockNum, (char *)p, l, 0);
  if (r == SOCKET_ERROR)
  {
   checkTimeout(false,true);
  }
  else if (r == 0)
  {
   throw SockException("Closed on write");
  }
  else
  if (r > 0)
  {
   stats.add(Stats::BYTESOUT,r);
   if (host.localIP())
    stats.add(Stats::LOCALBYTESOUT,r);

   updateTotals(0,r);
   l -= r;
   p = (char *)p+r;
  }
 }
}

//接收数据

int WSAClientSocket::read(void *p, int l)
{
 int bytesRead=0;
 while (l)
 {
  int r = recv(sockNum, (char *)p, l, 0);
  if (r == SOCKET_ERROR)
  {
   // non-blocking sockets always fall through to here
   checkTimeout(true,false);

  }else if (r == 0)
  {
   throw EOFException("Closed on read");

  }else
  {
   stats.add(Stats::BYTESIN,r);
   if (host.localIP())
    stats.add(Stats::LOCALBYTESIN,r);
   updateTotals(r,0);
   bytesRead += r;
   l -= r;
   p = (char *)p+r;
  }
 }
 return bytesRead;
}

Sys.h源代码分析

String类:完成字符串的一些定义和操作

Random类:可调用next方法返回随机数

Sys类:提供一些系统功能,如线程操作、返回随机数、返回时

WEvent类:

WLock类:对临界区操作的封装,用于线程同步

ThreadInfo类:线程信息

 
开源 P2P 直播 视频会议 一个P2P点播直播开源项目:P2PCenter 最近跟着公司的项目走,我也研究了不少东西,尤其是在P2P方面,广泛涉猎各种开源项目,尤其是国外的开源项目,意外的发现了一个国内的项目,做的还不错,推荐一下。 --------------------- 使用平台: 客户端:Windows 平台 服务器:Windows & Linux 框架图例: 项目简介:  P2P点播直播 阅读详情

相关推荐

协方差矩阵自适应进化策略(CMA-ES)详解:从基础原理到优化算法

CMA-ES是一种先进的无导数优化算法,基于生物进化原理解决连续域非线性优化问题。该算法通过自适应调整多元正态分布的均值、协方差矩阵和步长参数,自动学习搜索空间结构,无需梯度信息即可高效优化。核心创新包括协方差矩阵的秩1和秩μ更新策略,以及基于进化路径的步长控制机制。CMA-ES具有旋转不变性、标度不变性和全局探索能力,特别适合3-300维的黑盒优化问题,在复杂系统仿真、超参数调优等领域表现优异。算法实现中关键参数如种群大小、学习率等均有自适应设置规则,使其成为连续优化领域的基准算法之一。

qq_42212808的博客 486

Peercast源代码分析

Peercast分为两个目录:    core目录主要完成核心的操作    ui目录实现界面其中ui下面又可分为html目录和win32目录    html目录主要是一些网页,用来完成Peercast的相关配置,其中en为英文版本。    以下就en下的一些文件做一些介绍:    win32目录下的文件:      Simple.cpp 负责窗口界面的生成及一些相关启动管理      gu

guanyhu的专栏 966

Kylin操作系统下OpenLDAP安装

Kylin操作系统下OpenLDAP安装

跟着大数据和AI去旅行 1578

Peercast源代码文件结构分析

Peercast分为两个目录:    core目录主要完成核心的操作    ui目录实现界面其中ui下面又可分为html目录和win32目录    html目录主要是一些网页,用来完成Peercast的相关配置,其中en为英文版本。    以下就en下的一些文件做一些介绍:    win32目录下的文件:      Simple.cpp 负责窗口界面的生成及一些相关启动管理      gu

bbisonic的专栏 2152

Peercast简介、分析及常见问题处理 (一)

一。简介      PeerCast.org成立于2002年四月,它是一个非盈利性的站点,提供免费的P2P电台软件。这个项目的目标是创建一个容易使用、简单的和可靠的软件客户端,从而使任何人都可以广播流媒体,而不必耗费昂贵的服务器或者带宽。     PeerCast为广播者提供相当可观的节省,因为他们不必提供带宽给所有的接听者。一个单独的56K调制解调器即可用来为整个网络广播一个电台。

4063

特此声明

最近技术文章似乎少了些。因为这几天是周末,所以似乎比较懒。其实按说我已经没有周末的概念了,但还是想偷个懒儿。人都有惰性,这点咱就不否认了。最近觉得文章有点乱,主要是有些技术性文章和非技术性文章混在一起了。关心P2P流媒体的朋友,请在左边的文章类别中选择你感兴趣的内容。包括P2P相关知识和基于P2P的流媒体研究。关心Peercast的朋友,请在左边的文章内容中选择Peercast和Gnu

bbisonic的专栏 1177

分析PeerCast的博客集

收一个分析PeerCast的Blog:bbisonic的专栏 一:Peercast整体架构分析 原文http://blog.csdn.net/bbisonic/archive/2006/04/18/667974.aspx 现在的P2P流媒体主要有两种架构: 1. 基于树的架构。这是由流媒体的多播演化而来的,也就是播放同一频道的节点组成一棵树,提供广播的源节点为这棵树的根。每个节点可以为下层

Jingle的专栏 1226

关于Peercast一些观点的更新

Peercast原本是基于Gnutella协议的。但发展到现在,已经跟Gnutella没有多大关系了。相关的地方只是在广播机制上采用的仍然是泛洪机制。Gnutella协议是基于图的协议。 而现在的Peercast是基于树的转发机制,类似于IP组播。所以严格来说Peercast并不能称之为P2P流媒体,因为只从一个源获得数据,无法体现P2P中加入人数越多越流畅的优势。 P

bbisonic的专栏 2552

关于Peercast注释版的最后声明

Peercast注释版是我在做本科毕业设计时分析Peercast的一些成果,当时阅读代码的时候做了注释,然后写了一些分析文章。本来想发到自己的博客上保留一份记录的,不想后来竟成了很多人参考Peercast的资料。这点上我是有些惭愧的,因为很多不成熟的想法和不正确的观点可能会误导大家,也希望大家能够分辨吧。本来想形成一份比较完整的注释版,但后来由于毕业以及转变研究方向等因素已经没有继续做下去了。不过

bbisonic的专栏 5064

peercast 代码分析(1)——框架分析

peercast是一个开放源代码的p2p软件1. 根据我的分析,目前版本的p2p网络是一个树装结构。节目源同时也是p2p树的维护者,新加入的节点需要向树的根(节目源)发出请求,树的根会告诉他一个叶子列表,然后新加入的节点会选择一个叶子加入。2.在测试时,不要用10.0;192.168;172. 等网络。peercast hardcode了一些值。3.wmv部分,充分利用了microsoft

tedyxuan的专栏 1626

Peercast: P2P流媒体传输系统的深入剖析与源码实践

Gnutella协议是最早的P2P文件共享协议之一,它在2000年由Nullsoft公司开发,最初被用作音乐文件分享平台Napster的竞争者。由于其非中心化的特点,Gnutella迅速获得了大量用户的青睐。协议经历了数次迭代,虽然早期版本存在效率不高的问题,但随着技术的改进,Gnutella成为了研究P2P网络架构的重要对象。Gnutella协议的核心在于不依赖中央服务器,每个节点既是客户端也是服务器,节点之间通过广播和多播来交换信息,共同维护网络的运行。

weixin_27645199的博客 877

基于P2P的流媒体开源系统介绍----peercast,myseelite,p2pcenter

现在有很多人架设视频网站,要缓解视频网站服务器带宽的压力,最好的办法是使用p2p的方式。从头写一个p2p系统,要花费太多功夫,不如直接用开源的。本文介绍3款开源的p2p点播/直播系统。只是简单介绍,并给出一些有用信息,并不做源码分析。     1.peercast     这是比较古老的一个直播系统,性能上有不少缺陷,现在应该只有学习价值,没有应用价值了。下载地址:http://dow

暂时为空 2180

修剪草坪

猛然间发现博客已经三天没有更新了,唉呀呀,这怎么得了!所以说最近好象有些懒,也难怪嘛,据说前天是周六,昨天是周日的,别告诉我不是? 最近打台球打得不亦乐乎,嗯,这东西还真是蛮好玩的。不过老天似乎跟我过不去,因为每次去打台球都不是那么顺利。我们打台球的地方在珠江绿洲后面,要经过一个桥还要走那么一小段路,哈哈,珠江绿洲就是所谓的“水钢琴”了,可惜通惠河就是不争气呀,看起来像小水沟般

bbisonic的专栏 1394

开源 视频会议 收藏

一个P2P点播直播开源项目:P2PCenter(我转过来的时候发现已经都打不开了。。。) ---------------------使用平台:客户端:Windows 平台服务器:Windows & Linux框架图例: 项目简介:P2P点播直播开源项目是一个由Kevin发起,多人参与开发并不断更新,符合GPL开源协议的开源项目。 项目成员:系统架构及初...

weixin_30664615的博客 224

归并排序:从排序到逆序对统计

关于归并排序及其在四道leetcode题目中的应用。 需要提取核心要点。归并排序的增值点在于合并有序区间时可同步统计信息。文章覆盖四道题:普通排序、逆序对、右侧更小元素、翻转对。前三题递进,最后一题展示了在归并前用双指针统计特殊关系。还需提到核心思想是分治、合并时统计、有序性带来的一次性统计优势。 用户指明“根据以下内容”,所以摘要必须源自文本,不能添加外部信息。

2302_80962717的博客 590

408 数据结构|红黑树插入:只记两大类

父红叔红和父红叔黑。

2301_82049134的博客 23

基于STM32的温湿度检测和无线的传输(1).doc

基于STM32的温湿度检测和无线的传输(1).doc

上一篇: 两个深刻的结论
iPub
博客等级 码龄21年 0粉丝 2原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值