2008年04月24日

一:硬架构

1:机房的选择:
在选择机房的时候,根据网站用户的地域分布,可以选择网通或电信机房,但更多时候,可能双线机房才是合适的。越大的城市,机房价格越贵,从成本的角 度看可以在一些中小城市托管服务器,比如说北京的公司可以考虑把服务器托管在天津,廊坊等地,不是特别远,但是价格会便宜很多。
2:带宽的大小:
通常老板花钱请我们架构网站的时候,会给我们提出一些目标,诸如网站每天要能承受100万PV的访问量等等。这时我们要预算一下大概需要多大的带宽,计算带宽大小主要涉及两个指标(峰值流量和页面大小),我们不妨在计算前先做出必要的假设:
第一:假设峰值流量是平均流量的5倍。
第二:假设每次访问平均的页面大小是100K字节左右。
如果100万PV的访问量在一天内平均分布的话,折合到每秒大约12次访问,如果按平均每次访问页面的大小是100K字节左右计算的话,这12次访 问总计大约就是1200K字节,字节的单位是Byte,而带宽的单位是bit,它们之间的关系是1Byte = 8bit,所以1200K Byte大致就相当于9600K bit,也就是9Mbps的样子,实际情况中,我们的网站必须能在峰值流量时保持正常访问,所以按照假设的峰值流量算,真实带宽的需求应该在45Mbps 左右。
当然,这个结论是建立在前面提到的两点假设的基础上,如果你的实际情况和这两点假设有出入,那么结果也会有差别。
3:服务器的划分:
先看我们都需要哪些服务器:图片服务器,页面服务器,数据库服务器,应用服务器,日志服务器等等。
对于访问量大点的网站而言,分离单独的图片服务器和页面服务器相当必要,我们可以用lighttpd来跑图片服务器,用apache来跑页面服务 器,当然也可以选择别的,甚至,我们可以扩展成很多台图片服务器和很多台页面服务器,并设置相关域名,如img.domain.com和 www.domain.com,页面里的图片路径都使用绝对路径,如<img src="http://img.domain.com/abc.gif" />,然后设置DNS轮循,达到最初级的负载均衡。当然,服务器多了就不可避免的涉及一个同步的问题,这个可以使用rsync软件来搞定。
数据库服务器是重中之重,因为网站的瓶颈问题十有八九是出在数据库身上。现在一般的中小网站多使用MySQL数据库,不过它的集群功能似乎还没有达 到stable的阶段,所以这里不做评价。一般而言,使用MySQL数据库的时候,我们应该搞一个主从(一主多从)结构,主数据库服务器使用innodb 表结构,从数据服务器使用myisam表结构,充分发挥它们各自的优势,而且这样的主从结构分离了读写操作,降低了读操作的压力,甚至我们还可以设定一个 专门的从服务器做备份服务器,方便备份。不然如果你只有一台主服务器,在大数据量的情况下,mysqldump基本就没戏了,直接拷贝数据文件的话,还得 先停止数据库服务再拷贝,否则备份文件会出错。但对于很多网站而言,即使数据库服务仅停止了一秒也是不可接受的。如果你有了一台从数据库服务器,在备份数 据的时候,可以先停止服务(slave stop)再备份,再启动服务(slave start)后从服务器会自动从主服务器同步数据,一切都没有影响。但是主从结构也是有致命缺点的,那就是主从结构只是降低了读操作的压力,却不能降低写 操作的压力。为了适应更大的规模,可能只剩下最后这招了:横向/纵向分割数据库。所谓横向分割数据库,就是把不同的表保存到不同的数据库服务器上,比如说 用户表保存在A数据库服务器上,文章表保存在B数据库服务器上,当然这样的分割是有代价的,最基本的就是你没法进行LEFT JOIN之类的操作了。所谓纵向分割数据库,一般是指按照用户标识(user_id)等来划分数据存储的服务器,比如说:我们有5台数据库服务器,那么 “user_id % 5 + 1”等于1的就保存到1号服务器,等于2的就保存到2好服务器,以此类推,纵向分隔的原则有很多种,可以视情况选择。不过和横向分割数据库一样,纵向分割 数据库也是有代价的,最基本的就是我们在进行如COUNT, SUM等汇总操作的时候会麻烦很多。综上所述,数据库服务器的解决方案一般视情况往往是一个混合的方案,以其发挥各种方案的优势,有时候还需要借助 memcached之类的第三方软件,以便适应更大访问量的要求。
如果有专门的应用服务器来跑PHP脚本是最合适不过的了,那样我们的页面服务器只保存静态页面就可以了,可以给应用服务器设置一些诸如 app.domain.com之类的域名来和页面服务器加以区别。对于应用服务器,我还是更倾向于使用prefork模式的apache,配上必要的 xcache之类的PHP缓存软件,加载模块要越少越好,除了mod_rewrite等必要的模块,不必要的东西统统舍弃,尽量减少httpd进程的内存 消耗,而那些图片服务器,页面服务器等静态内容就可以使用lighttpd或者tux来搞,充分发挥各种服务器的特点。
如果条件允许,独立的日志服务器也是必要的,一般小网站的做法都是把页面服务器和日志服务器合二为一了,在凌晨访问量不大的时候cron运行前一天 的日志计算,不过如果你使用awstats之类的日志分析软件,对于百万级访问量而言,即使按天归档,也会消耗很多时间和服务器资源去计算,所以分离单独 的日志服务器还是有好处的,这样不会影响正式服务器的工作状态。

二:软架构
1:框架的选择:
现在的PHP框架有很多选择,比如:CakePHP,Symfony,Zend Framework等等,至于应该使用哪一个并没有唯一的答案,要根据Team里团队成员对各个框架的了解程度而定。很多时候,即使没有使用框架,一样能 写出好的程序来,比如Flickr据说就是用Pear+Smarty这样的类库写出来的,所以,是否用框架,用什么框架,一般不是最重要的,重要的是我们 的编程思想里要有框架的意识。
2:逻辑的分层:
网站规模到了一定的程度之后,代码里各种逻辑纠缠在一起,会给维护和扩展带来巨大的障碍,这时我们的解决方式其实很简单,那就是重构,将逻辑进行分层。通常,自上而下可以分为表现层,应用层,领域层,持久层。
所谓表现层,并不仅仅就指模板,它的范围要更广一些,所有和表现相关的逻辑都应该被纳入表现层的范畴。比如说某处的字体要显示为红色,某处的开头要 空两格,这些都属于表现层。很多时候,我们容易犯的错误就是把本属于表现层的逻辑放到了其他层面去完成,这里说一个很常见的例子:我们在列表页显示文章标 题的时候,都会设定一个最大字数,一旦标题长度超过了这个限制,就截断,并在后面显示“..”,这就是最典型的表现层逻辑,但是实际情况,有很多程序员都 是在非表现层代码里完成数据的获取和截断,然后赋值给表现层模板,这样的代码最直接的缺点就是同样一段数据,在这个页面我可能想显示前10个字,再另一个 页面我可能想显示前15个字,而一旦我们在程序里固化了这个字数,也就丧失了可移植性。正确的做法是应该做一个视图助手之类的程序来专门处理此类逻辑,比 如说:Smarty里的truncate就属于这样的视图助手(不过它那个实现不适合中文)。
所谓应用层,它的主要作用是定义用户可以做什么,并把操作结果反馈给表现层。至于如何做,通常不是它的职责范围(而是领域层的职责范围),它会通过 委派把如何做的工作交给领域层去处理。在使用MVC架构的网站中,我们可以看到类似下面这样的URL: domain.com/articles/view/123,其内部编码实现,一般就是一个Articles控制器类,里面有一个view方法,这就是一 个典型的应用层操作,因为它定义了用户可以做一个查看的动作。在MVC架构中,有一个准则是这么说的:Rich Model Is Good。言外之意,就是Controller要保持“瘦”一些比较好,进而说明应用层要尽量简单,不要包括涉及领域内容的逻辑。
所谓领域层,最直接的解释就是包含领域逻辑的层。它是一个软件的灵魂所在。先来看看什么叫领域逻辑,简单的说,具有明确的领域概念的逻辑就是领域逻 辑,比如我们在ATM机上取钱,过程大致是这样的:插入银联卡,输入密码,输入取款金额,确定,拿钱,然后ATM吐出一个交易凭条。在这个过程中,银联卡 在ATM机器里完成钱从帐户上划拨的过程就是一个领域逻辑,因为取钱在银行中是一个明确的领域概念,而ATM机吐出一个交易凭条则不是领域逻辑,而仅是一 个应用逻辑,因为吐出交易凭条并不是银行中一个明确的领域概念,只是一种技术手段,对应的,我们取钱后不吐交易凭条,而发送一条提醒短信也是可能的,但并 不是一定如此,如果在实际情况中,我们要求取款后必须吐出交易凭条,也就是说吐出交易凭条已经和取款紧密结合,那么你也可以把吐出交易凭条看作是领域逻辑 的一部分,一切都以问题的具体情况而定。在Eric那本经典的领域驱动设计中,把领域层分为了五种基本元素:实体,值对象,服务,工厂,仓储。具体可以参 阅书中的介绍。领域层最常犯的错误就是把本应属于领域层的逻辑泄露到了其他层次,比如说在一个CMS系统,对热门文章的定义是这样的:每天被浏览的次数多 于1000次,被评论的次数多于100次,这样的文章就是热门文章。对于一个CMS来说,热门文章这个词无疑是一个重要的领域概念,那么我们如何实现这个 逻辑的设计的?你可能会给出类似下面的代码:“SELECT … FROM … WHERE 浏览 > 1000 AND 评论 > 100”,没错,这是最简单的实现方式,但是这里需要注意的是“每天被浏览的次数多于1000次,被评论的次数多于100次”这个重要的领域逻辑被隐藏到 了SQL语句中,SQL语句显然不属于领域层的范畴,也就是说,我们的领域逻辑泄露了。
所谓持久层,就是指把我们的领域模型保存到数据库中。因为我们的程序代码是面向对象风格的,而数据库一般是关系型的数据库,所以我们需要把领域模型 碾平,才能保存到数据库中,但是在PHP里,直到目前还没有非常好的ORM出现,所以这方面的解决方案不是特别多,参考Martin的企业应用架构模式一 书,大致可以使用的方法有行数据入口(Row Data Gateway)或者表数据入口(Table Data Gateway),或者把领域层和持久层合二为一变成活动记录(Active Record)的方式。

2008年04月17日

转自http://blog.163.com/hello-_-world/blog/static/231894442007101995827755/

Linux与Windows 共享文件Samba

作者:北南南北,正在增加中… … 需要您的参与;
来自:LinuxSir.Org
简介:本文只是讲一讲我们最常用的Linux与Windows共享文件,主要是为新手指一指路。如何建立最简单的Samba服务器,并讲述遇到问题应该从何处寻找解决方案;正在更新之中,希望您的参加 … … 谢谢;

目录

0、架设Samba 服务器的前提;
0.1 查看文件内容和编辑文件的工具;
0.2 关于文件和目录相关;
03 用户和用户组相关;
04 进程管理;
1 Samba 简介
2 Samba 功能和应用范围
3 Samba 两个服务器相关启动程序、客户端及服务器配置文件等;
3.1 Samba 有两个服务器,一个是smbd,另一个是nmbd;
3.2 查看Samba 服务器的端口及防火墙;
3.3 查看Samba 服务器的配置文件;
3.4 Samba 在Linux 中的一些工具(服务器端和客户端);
3.5 在Linux 中的常用工具,Windows查看Linux共享的方法 ;
3.5.1 在Linux系统中查看网络中Windows共享文件及Linux中的Samba共享文件;
3.5.2 在Windows中访问Linux Samba服务器共享文件的办法;
3.5.3 smbfs文件系统的挂载;
4 由最简单的一个例子说起,匿名用户可读可写的实现;

第一步: 更改smb.conf
第二步:建立相应目录并授权;
第三步:启动smbd和nmbd服务器;
第四步:查看smbd进程,确认Samba 服务器是否运行起来了;
第五步:访问Samba 服务器的共享;
5、复杂一点的用户共享模型(适合10人左右的小型企业);

5.1 共享权限设计实现的功能;
5.2 在服务器上创建相应的目录;
5.3 添加用户用户组,设置相应目录家目录的权限;
5.3.1 添加用户组;
5.3.2 添加用户;
5.3.3 添加samba用户,并设置密码;
5.3.4 配置相关目录的权限和归属;
5.4 修改Samba配置文件;
5.5 关于客户端访问;
5.5.1 Windows客户端访问;

++++++++++++++++++++++++++++++++++++++++++++++++
正文
++++++++++++++++++++++++++++++++++++++++++++++++

0、架设Samba 服务器的前提;

Linux是一个多用户的操作系统,对任何服务器的架设与都用户、用户组及权限相关,这是操作的基础。Samba服务器也不例外,对这些知识的掌握也是极为重要的。在Windows系统上虽然也能架共享文件服务器,但它的权限控制实在令人不敢恭维。如果我们用Windows系统来架网络共享文件系统,就是点鼠标也能把我们点迷糊了。但在Linux中,我们可以轻松的改一改配置文件,不到几分钟就能建好自己的Samba服务器。哪个更容易,只有你知道;

对于Samba服务器的架设,有的弟兄简单的认为,只要把改一下配置文件,创建好相应的目录就行了。其实并不是这样的,还要深入的工作,比如目录的权限和归属,也就是说能让哪个用户和用户组有读写权。只有把配置文件和共享目录的权限结合起来,才能架好Samba服务器;

下面是常用的基础知识,我们在本文中所涉及的内容都可以在下面的列表中找到相应的解释;

0.1 查看文件内容和编辑文件的工具;

《Linux 文件内容查看工具介绍》
《文件编辑器 vi》

0.2 关于文件和目录相关;

《Linux 文件类型 及文件的扩展名》
《Linux 文件和目录管理之列出、删除、复制、移动及改名》
《Linux 文件和目录的属性》
《简述Linux文件搜索》

03 用户和用户组相关;

《Linux 用户(user)和用户组(group)管理概述》
《用户(User)和用户组(Group)配置文件详解》
《Linux 用户管理工具介绍》
《Linux 用户(User)查询篇》

04 进程管理;

《Linux 进程管理》

1 Samba 简介

Samba(SMB是其缩写) 是一个网络服务器,用于Linux和Windows共享文件之用;Samba 即可以用于Windows和Linux之间的共享文件,也一样用于Linux和Linux之间的共享文件;不过对于Linux和Linux之间共享文件有更好的网络文件系统NFS,NFS也是需要架设服务器的;

大家知道在Windows 网络中的每台机器即可以是文件共享的服务器,也可以同是客户机;Samba 也一样能行,比如一台Linux的机器,如果架了Samba Server 后,它能充当共享服务器,同时也能做为客户机来访问其它网络中的Windows共享文件系统,或其它Linux的Sabmba 服务器;

我们在Windows网络中,看到共享文件功能知道,我们直接就可以把共享文件夹当做本地硬盘来使用。在Linux的中,就是通过Samba的向网络中的机器提供共享文件系统,也可以把网络中其它机器的共享挂载在本地机上使用;这在一定意义上说和FTP是不一样的。

Samba 用的netbios协议,如果您用Samba 不成功,

Linux与Windows 、Linux 和 Linux

2 Samba 功能和应用范围

Samba 应该范围主要是Windows和Linux 系统共存的网络中使用;如果一个网络环境都是Linux或Unix类的系统,没有必要用Samba,应该用NFS更好一点;

那Samba 能为我们提供点什么服务呢?主要是共享文件和共享打印机;

3 Samba 两个服务器相关启动程序、客户端及服务器配置文件等;

3.1 Samba 有两个服务器,一个是smb,另一个是nmb;

smb 是Samba 的主要启动服务器,让其它机器能知道此机器共享了什么;如果不打开nmb服务器的话,只能通过IP来访问,比如在Windows的IE浏览器上打入下面的一条来访问;

\\192.168.1.5\共享目录
\\192.168.1.5\opt

而nmb是解析用的,解析了什么呢?就是把这台Linux机器所共享的工作组及在此工作组下的netbios name解析出来;
一般的情况下,在RPM包的系统,如果是用RPM包安装的Samba ,一般可以通过如下的方式来启动Samba服务器;

[root@localhost ~]# /etc/init.d/smb start
启动 SMB 服务: [ 确定 ]
启动 NMB 服务: [ 确定 ]

如果停止呢?就在smb后面加stop ;重启就是restart

[root@localhost ~]# /etc/init.d/smb stop
[root@localhost ~]# /etc/init.d/smb restart

对于所有系统来说,通用的办法就是直接运行smb 和nmb;当然您要知道smb和nmb所在的目录才行;如果是自己编译的Samba ,您应该知道您把Samba放在哪里了;

[root@localhost ~]# /usr/sbin/smbd
[root@localhost ~]# /usr/sbin/nmbd

查看服务器是否运行起来了,则用下面的命令;
[root@localhost ~]# pgrep smbd
[root@localhost ~]# pgrep nmbd

关掉Samba服务器,也可以用下面的办法,大多是通用的;要root权限来执行;

[root@localhost ~]# pkill smbd
[root@localhost ~]# pkill nmbd

3.2 查看Samba 服务器的端口及防火墙;

查看这个有何用呢?有时你的防火墙可能会把smbd服务器的端口封掉,所以我们应该smbd服务器所占用的端口;下面查看中,我们知道smbd所占用的端口是139和445 ;

[root@localhost ~]# netstat -tlnp |grep smb
tcp 0 0 0.0.0.0:139 0.0.0.0:* LISTEN 10639/smbd
tcp 0 0 0.0.0.0:445 0.0.0.0:* LISTEN 10639/smbd
如果您有防火墙,一定要把这两个端口打开。如果不知道怎么打开。可能你和我一样是新手,还是把防火墙规则清掉也行;

[root@localhost ~]# iptables -F

[root@localhost ~]# /sbin/iptables -F
3.3、查看Samba 服务器的配置文件;

如果我们是用Linux发行版自带的Samba软件包,一般情况下Samba服务器的配置文件都位于/etc/samba目录中,服务器的主配置文件是smb.conf;也有有户配置文件 smbpasswd、smbusers和lmhosts等(最好您查看一下这些文件的内容);还有一个文件是secrets.tdb,这个文件是 Samba 服务器启动手自动生成的;我们慢慢根据教程的进度来适当地增加这些文件的说明吧;一下子都说出来,感觉内容太多;所以只能一点一点的来了;

3.4、Samba 在Linux 中的一些工具(服务器端和客户端);

smbcacls smbcontrol smbencrypt smbmount smbprint smbstatus smbtree
smbclient smbcquotas smbmnt smbpasswd smbspool smbtar smbumount
smbd nmbd mount

其中服务器端的是smbd、nmbd、smbpasswd ;其它的大多是客户端;这些并不是都需要一定要精通的,但至少得会用几个;比如smbmount(也就是mount 加参数的用法),还用smbclient等;

3.5 在Linux 中的常用工具mount(smbmount)和smbclient;Windows查看Linux共享的方法 ;

3.5.1 在Linux系统中查看网络中Windows共享文件及Linux中的Samba共享文件;

一般的情况下,我们要用到smbclient;常用的用法也无非是下面的;

[root@localhost ~]# smbclient -L //ip地址或计算机名
smbclient 是Samba 的Linux 客户端,在Linux机器上用来查看服务器上的共享资源,也可以象FTP一样,用户可以登录Samba服务器,也可以上传put和下载get文件,遗憾的是对中文支持并不友好;

查看服务器上的资源;

smbclient -L //IP [-U 用户名]
如果您的Samba服务器配置为user模式,就要加 “-U 用户名“,如果是share模式,省略也可;

比如:

[root@localhost ~]# smbclient -L //192.168.1.3 -U sir01
Password: 请输入用户sir01的密码
如果您是用share模式,就可以不必理会用户和密码,直接用;

[root@localhost ~]# smbclient -L //192.168.1.3
Password: 直接按回车
登录用户身份Samba服务器共享

以用户身份登录共享后,能象FTP用户一样,下传和下载文件;用put表示上传,用get表示下载;

smbclient //IP地址/共享文件夹 -U 用户
说明:IP地址大家都知道,你不知道自己的IP地址,可以用/sbin/ifconfig 来查看; 共享文件夹是我们在smb.conf中定义的[共享文件夹],比如[sir01]。-U 用户名表示Samba 的用户;

比如:

[root@localhost ~]# smbclient //192.168.1.3/sir01 -U sir01
Password:
Domain=[LINUXSIR] OS=[Unix] Server=[Samba 3.0.21b-2]
smb: \> ls
说明:登录到Samba服务器上,就可以用smbclient的一些指令,可以象用FTP指令一样上传和下载文件;

smbclient 命令说明

命令 说明?或help [command] 提供关于帮助或某个命令的帮助![shell command] 执行所用的SHELL命令,或让用户进入 SHELL提示符cd [目录] 切换到服务器端的指定目录,如未指定,则 smbclient 返回当前本地目录lcd [目录] 切换到客户端指定的目录;dir 或ls 列出当前目录下的文件;exit 或quit 退出smbclient get file1 file2 从服务器上下载file1,并以文件名file2存在本地机上;如果不想改名,可以把file2省略mget file1 file2 file3 filen 从服务器上下载多个文件;md或mkdir 目录 在服务器上创建目录rd或rmdir 目录 删除服务器上的目录put file1 [file2] 向服务器上传一个文件file1,传到服务器上改名为file2;mput file1 file2 filen 向服务器上传多个文件3.5.2 在Windows中访问Linux Samba服务器共享文件的办法;

这个简单吧,在网上领居,查看工作组就能看得到,或者在浏览器上输入如下的

\\ip地址或计算机名
这样就能看到这个机器上有什么共享的了,点鼠标操作完成;如果访问不了,不要忘记把Linux的防火墙规划清掉,或让相应的端口通过;

3.5.3 在Linux中smbfs文件系统的挂载;

mount 的用法,加载网络中的共享文件夹到本地机;mount就是用于挂载文件系统的,SMB做为网络文件系统的一种,也能用mount挂载;smbmount说到底也是用mount的一个变种;

mount 挂载smbfs的用法;

mount -t smbfs -o codepage=cp936,username=用户名,password=密码 , -l //ip地址/共享文件夹名 挂载点

mount -t smbfs -o codepage=cp936,username=用户名,password=密码 , -l //计算机名/共享文件夹名 挂载点

mount -t smbfs -o codepage=cp936 //ip地址或计算机名/共享文件夹名 挂载点

smbmount的用法:

smbmount -o username=用户名,password=密码 , -l //ip地址或计算机名/共享文件夹名 挂载点
smbmount //ip地址或计算机名/共享文件夹名 挂载点

说明:
如果您的服务器是以share共享的,则无需用户名和密码就能挂载,如果出现要密码的提示,直接回车就行;您也可以用smbmount 来挂载,这样就无需用mount -t smbfs来指定文件系统的类型了;

对于挂载点,我们要自己建立一个文件夹,比如我们可以建在/opt/smbhd,这由您说的算吧;

在mount的命令中,我们发现有这样的一个参数codepage=cp936,这是服务器端文件系统的编码的指定,cp936 就是简体中文,当然您可以用utf8等,尝试一下吧。

如果您挂载了远程的smbfs文件系统出现的是简体中文乱码,就要考虑挂载时要指定编码了;

4、由最简单的一个例子说起,匿名用户可读可写的实现;

第一步: 更改smb.conf

我们来实现一个最简单的功能,让所有用户可以读写一个Samba 服务器共享的一个文件夹;我们要改动一下smb.conf ;首先您要备份一下smb.conf文件;

[root@localhost ~]# cd /etc/samba
[root@localhost samba]# mv smb.conf smb.confBAK
然后我们来重新创建一个smb.conf文件;

[root@localhost samba]#touch smb.conf

然后我们把下面这段写入smb.conf中;

[global]
workgroup = LinuxSir
netbios name = LinuxSir05
server string = Linux Samba Server TestServer
security = share

[linuxsir]
path = /opt/linuxsir
writeable = yes
browseable = yes
guest ok = yes

注解:
[global]这段是全局配置,是必段写的。其中有如下的几行;

workgroup 就是Windows中显示的工作组;在这里我设置的是LINUXSIR (用大写);
netbios name 就是在Windows中显示出来的计算机名;
server string 就是Samba服务器说明,可以自己来定义;这个不是什么重要的;
security 这是验证和登录方式,这里我们用了share ;验证方式有好多种,这是其中一种;另外一种常用的是user的验证方式;如果用share呢,就是不用设置用户和密码了;

[linuxsir] 这个在Windows中显示出来是共享的目录;
path = 可以设置要共享的目录放在哪里;
writeable 是否可写,这里我设置为可写;
browseable 是否可以浏览,可以;可以浏览意味着,我们在工作组下能看到共享文件夹。如果您不想显示出来,那就设置为 browseable=no

guest ok 匿名用户以guest身份是登录;

第二步:建立相应目录并授权;

[root@localhost ~]# mkdir -p /opt/linuxsir
[root@localhost ~]# id nobody
uid=99(nobody) gid=99(nobody) groups=99(nobody)

[root@localhost ~]# chown -R nobody:nobody /opt/linuxsir

注释:关于授权nobody,我们先用id命令查看了nobody用户的信息,发现他的用户组也是nobody,我们要以这个为准。有些系统nobody用户组并非是nobody ;
第三步:启动smbd和nmbd服务器;

[root@localhost ~]# smbd
[root@localhost ~]# nmbd
第四步:查看smbd进程,确认Samba 服务器是否运行起来了;

[root@localhost ~]# pgrep smbd
13564
13568

第五步:访问Samba 服务器的共享;

在Linux 中您可以用下面的命令来访问;

[root@localhost ~]# smbclient -L //LinuxSir05
Password: 注:直接按回车

在Windows中,您可以用下面的办法来访问;

\\LinuxSir05\

5、复杂一点的用户共享模型(适合10人左右的小型企业);

比如一个公司有五个部门,分别是linuxsir,sir01,sir02,sir03,sir04。我们想为这家公司设计一个比较安全的共享文件模型。每个用户都有自己的网络磁盘,sir01到sir04还有共用的网络硬盘;所有用户(包括匿名用户)有一个共享资料库,此库为了安全是只读的;所有的用户(包括匿名用户)要有一个临时文件终转的文件夹… ….

5.1 共享权限设计实现的功能;

1)linuxsir部门具有管理所有SMB空间的权限;
2)sir01到sir04拥有自己的空间,并且除了自身及linuxsir有权限以外,对其它用户具有绝对隐私性;
3)linuxsir01到linuxsir04拥有一个共同的读写权限的空间;
4) 所有用户(包括匿名用户)有一个有读权限的空间,用于资料库,所以不要求写入数据。
5)sir01到sir04还有一个共同的空间,对sir01到sir04的用户来说是隐私的,不能让其它用户来访问。
6) 还要有一个空间,让所有用户可以写入,能删除等功能,在权限上无限制 ,用于公司所有用户的临时文档终转等;

5.2 在服务器上创建相应的目录;

[root@localhost ~]# mkdir -p /opt/linuxsir
[root@localhost ~]# cd /opt/linuxsir
[root@localhost linuxsir]# mkdir sir01 sir02 sir03 sir04 sirshare sir0104rw sirallrw
[root@localhost linuxsir]# ls

sir01 sir0104rw sir02 sir03 sir04 sirallrw sirshare
注:功用如下:

/opt/linuxsir 这是管理员目录,负责管理其下所有目录;
/opt/linuxsir/sir01 是sir01的家目录,用于私用,除了用户本身和linuxsir以外其它用户都是不可读不可写;
/opt/linuxsir/sir02 是sir02的家目录,用于私用,除了用户本身和linuxsir以外其它用户都是不可读不可写;
/opt/linuxsir/sir03 是sir03的家目录,用于私用,除了用户本身和linuxsir以外其它用户都是不可读不可写;
/opt/linuxsir/sir04 是sir04的家目录,用于私用,除了用户本身和linuxsir以外其它用户都是不可读不可写;
/opt/linuxsir/sirshare 所用用户(除了linuxsir有权限写入外)只读目录
/opt/linuxsir/sir0104rw 是用于sir01到sir04用户可读可写共用目录,但匿名用户不能读写;
/opt/linuxsir/sirallrw 用于所有用户(包括匿名用户)的可读可写;

5.3 添加用户用户组,设置相应目录家目录的权限;

5.3.1 添加用户组;

[root@localhost ~]# /usr/sbin/groupadd linuxsir
[root@localhost ~]# /usr/sbin/groupadd sir01
[root@localhost ~]# /usr/sbin/groupadd sir02
[root@localhost ~]# /usr/sbin/groupadd sir03
[root@localhost ~]# /usr/sbin/groupadd sir04
[root@localhost ~]# /usr/sbin/groupadd sir0104

5.3.2 添加用户;

[root@cuc03 ~]# adduser -g sir01 -G sir0104 -d /opt/linuxsir/sir01 -s /sbin/nologin sir01
[root@cuc03 ~]# adduser -g sir02 -G sir0104 -d /opt/linuxsir/sir02 -s /sbin/nologin sir02
[root@cuc03 ~]# adduser -g sir03 -G sir0104 -d /opt/linuxsir/sir03 -s /sbin/nologin sir03
[root@cuc03 ~]# adduser -g sir04 -G sir0104 -d /opt/linuxsir/sir04 -s /sbin/nologin sir04
[root@cuc03 ~]# adduser -g linuxsir -d /opt/linuxsir -G linuxsir,sir01,sir02,sir03,sir04,sir0104 -d /opt/linuxsir -s /sbin/nologin linuxsir
为什么这样添加用户?请参考:

《Linux 文件和目录的属性》
《Linux 用户管理工具介绍》

当然我们还得学会查看用户信息的工具用法,比如 用finger和id来查看用户信息,主要是看用户是否添加正确;比如;请参考《Linux 用户(User)查询篇》

[root@localhost ~]# id linuxsir
[root@localhost ~]# finger linuxsir

5.3.3 添加samba用户,并设置密码;

我们用的方法是先添加用户,但添加的这些用户都是虚拟用户,因为这些用户是不能通过SHELL登录系统的;另外值得注意的是系统用户密码和 Samba用户的密码是不同的。如果您设置了系统用户能登入SHELL,可以设置用户的Samba密码和系统用户通过SHELL登录的密码不同。

我们通过smbpasswd 来添加Samba用户,并设置密码。原理是通过读取/etc/passwd文件中存在的用户名。

[root@localhost sir01]# smbpasswd -a linuxsir
New SMB password: 注:在这里添加Samba用户linuxsir的密码;
Retype new SMB password: 注:再输入一次;
用同样的方法来添加 sir01、sir02、sir03、sir04的密码;

5.3.4 配置相关目录的权限和归属;

[root@cuc03 ~]# chmod 755 /opt/linux
[root@cuc03 ~]# chown linuxsir:linuxsir /opt/linuxsir

[root@cuc03 ~]# cd /opt/linuxsir
[root@cuc03 ~]# chmod 2770 sir0*

[root@cuc03 ~]# chown sir01.linuxsir sir01

[root@cuc03 ~]# chown sir02.linuxsir sir02

[root@cuc03 ~]# chown sir03.linuxsir sir03

[root@cuc03 ~]# chown sir04.linuxsir sir04

[root@cuc03 ~]# chown linuxsir.sir0104 sir0104rw

[root@cuc03 ~]# chown linuxsir.linuxsir sirshare
[root@cuc03 ~]# chmod 755 sirshare

[root@cuc03 ~]# chown linuxsir:linuxsir sirallrw
[root@cuc03 ~]# chmod 3777 sirallrw

5.4 修改Samba配置文件 smb.conf;

配置文件如下,修改/etc/samba/smb.conf后,不要忘记重启smbd和nmbd服务器;

[global]
workgroup = LINUXSIR
netbios name = LinuxSir
server string = Linux Samba Test Server
security = share

[linuxsir]
comment = linuxsiradmin
path = /opt/linuxsir/
create mask = 0664
#create mask是用户创建文件时的权限掩码;对用户来可读可写,对用户组可读可写,对其它用户可读;
directory mask = 0775
#directory mask 是用来设置用户创建目录时的权限掩码,意思是对于用户和用户组可读可写,对其它用户可读可执行;
writeable = yes
valid users = linuxsir
browseable = yes

[sirshare]
path = /opt/linuxsir/sirshare
writeable = yes
browseable = yes
guest ok = yes

[sirallrw]
path = /opt/linuxsir/sirallrw
writeable = yes
browseable = yes
guest ok = yes

[sir0104rw]
comment = sir0104rw
path = /opt/linuxsir/sir0104rw
create mask = 0664
directory mask = 0775
writeable = yes
valid users = linuxsir,@sir0104
#@sir0104是用户组;
browseable = yes

[sir01]
comment = sir01
path = /opt/linuxsir/sir01
create mask = 0664
directory mask = 0775
writeable = yes
valid users = sir01,@linuxsir
browseable = yes

[sir02]
comment = sir02
path = /opt/linuxsir/sir02
create mask = 0664
directory mask = 0775
writeable = yes
valid users = sir02,@linuxsir
browseable = yes

[sir03]
comment = sir03
path = /opt/linuxsir/sir03
create mask = 0664
directory mask = 0775
writeable = yes
valid users = sir03,@linuxsir
browseable = yes

[sir04]
comment = sir04
path = /opt/linuxsir/sir04
create mask = 0664
directory mask = 0775
writeable = yes
valid users = sir04,@linuxsir
browseable = yes

5.5 关于客户端访问;

5.5.1 Windows 访问;

我们打开Windows的IE浏览器,用IP地址的访问方式就能访问了,格式为 \\192.168.1.3 类似的。当然也可以把共享文件夹挂在本地使用。比如我们把sir01文件夹挂在本地中,应该以sir01用户来挂载,挂载流程图如下;

2006年10月19日
2006年08月25日

CoolStreaming/DONet: A Data-Driven Overlay Network for Efficient Live Media Streaming [Xinyan Zhang¤, Jiangchuan Liuy, Bo Liz, and Tak-Shing Peter Yum¤]

1、DONet : There are three key modules: (1) membership manager,which helps the node maintain a partial view of other overlay nodes; (2) partnership manager, which establishes and maintains the partnership with other nodes;(3) scheduler, which schedules the transmission of video data.

2、消息结构 :each message is a 4-tuple <seq num, id, num partner, time to live> last update time>,
seq num is a sequence number of the message,
id is the node’s identifier,
num partner is its current number of partners,
time to live records the remaining valid time of the message.
last update time is the local time of the last update for the entry.
3、SCAM 出自: A. J. Ganesh, A.-M. Kermarrec, and L. Massoulie, “Peer-to-peer membership management for gossip-based protocols,” IEEE Transactions on Computers, 52(2), Feb. 2003.

4、问题: fig2 中,除源节点外各个节点之间都有上传与下载的关系吗?

5、异构网络问题:For a homogenous and static network, a simple round-robin scheduler may work well, but for a dynamic and heterogeneous network, a more intelligent scheduler is necessary.

6、Fig. 3. Scheduling algorithm at a DONet node.  算法暂时未能理解,节点调度算法

7、currently, we adopted the TCP-Friendly Rate Control (TFRC) protocol [31], as in many other systems. 出自: M. Handley, S. Floyd, J. Pahdye, and J. Widmer, “TCP Friendly Rate Control (TFRC): Protocol Specification,” RFC 3448, January 2003.

8、page 6-7 公式 1-8 不甚理解

9、理解 partner 与 node 的区别

10、PERFORMANCE EVALUATION  [A:Performance under Stable Environment; B:Performance under Dynamic Environment; C:Comparison with Tree-based Overlay]

11、树型模式:基于网状模型的退化吗?

12、未完

2006年06月07日

All good geeks know that keyboard

Contents

  1. Google Mail
  2. Google Calendar
  3. Google Reader
  4. Writely
  5. Google Video
  6. Google Maps

Google MailGmail Icon[top]

Note: pressing <shift> and clicking or typing a keyboard shortcut will open the item in a new window (if applicable).

Key Name Action
c Compose Compose a new email
/ Search Moves the cursor to the search field
k Newer conversation Moves to the next conversation
j Older conversation Moves to the previous conversation
n Next message Selects the next message in a conversation
p Previous message Selects the next message in a conversation
o Open Opens the selected element
u Conversation list Goes back to the list of conversations
y Archive Archives, de-stars or un-labels the selected conversation, depending on your current view
x Select conversation Checks the current conversation ready for further commands
s Star Stars a message or conversation
! Report spam Reports the current message as spam and removes it
r Reply Opens a reply to the current message
a Reply to all Replies to all message recipients
f Forward Forwards the current message
<esc> Deselect Removes the cursor from the current input field
g then a All mail Switches to the ‘all mail’ view
g then s Starred mail Switches to the ’starred mail’ view
g then c Contacts Displays your Gmail contacts
g then d Drafts Switches to a list of your drafts
g then i Inbox Returns you to the inbox

Google CalendarGCal Icon[top]

Key Name Action
c Create event Displays the create event dialog
/ Search Places the cursor in the search field
p Previous date range Moves calendar view to previous date range
n Next date range Moves calendar view to next date range
t Jump to “Today” Moves calendar view to include current day
d Day view Switches to day view
w Week view Switches to week view
m Month view Switches calendar to month view
x Customised view Switches calendar to customised view
a Agenda view Displays your upcoming appointments
q Quick Add Opens Quick Add and moves cursor there
Esc Back to calendar Back to default calendar view

Google ReaderGoogle Reader Icon[top]

Key Name Action
j Next Move to the next item in your reading list
k Previous Move to the previous item in your reading list
n Scan down Scroll to the next item without viewing it
p Scan up Scroll to the previous item without viewing it
shift + n Page down Scroll down one screen of items
shift + p Page up Scroll up one screen of items
o Open View the selected item
h Top Go to the top of your reading list
r Refresh Refresh your reading list
s Star Star the current item
l Label Go to the item labelling dialog
v View original Opens the original item in a new window
g then h Home Loads your reading list items
g then s Starred items Opens your starred items
g then u Subscriptions Opens the edit subscriptions pane
g then r Read items Opens your read items
g then l Label selector Lets you type in a label then loads it

WritelyWritely Icon[top]

Note: all the usual word-processor shortcuts are as you would expect, and are accessed using ctrl + letter, where letter can be b = bold, u = underline, i = italic, x = cut, c = copy, v = paste, s = save.

Key Name Action
ctrl + z Undo Undo last action
ctrl + y Redo Redo last undo
ctrl + p Print Open print menu
ctrl + m Add comment Add a comment to the selected text

Google VideoGoogle Video Icon[top]

Key Name Action
space Play / pause Toggles pause on and off
Right arrow Skip forward Skips forward in the video by 5%
Left arrow Skip backward Skips backward in the video by 5%
Up arrow Increase volume Increases volume by 5%
Down arrow Decrease volume Decreases volume by 5%
m Mute Toggles muting on and off
f Full screen Toggles full-screen mode on and off

Google MapsGoogle Maps Icon[top]

Key Name Action
Arrow keys Pan Pan around the map
Page Up / Down Pan wider Pan up and down the map in wider increments
Home / End Pan wider Pan left and right in wider increments
+ Zoom in Zoom in to the map
- Zoom out Zoom out of the map
2006年05月17日

How to Use:

Part 1 – Setup

  1. Lightbox v2.0 uses the Prototype Framework and Scriptaculous Effects Library. You will need to include these three Javascript files in your header.
    <script type="text/javascript" src="js/prototype.js"></script>
    <script type="text/javascript" src="js/scriptaculous.js?load=effects"></script>
    <script type="text/javascript" src="js/lightbox.js"></script>
    
  2. Include the Lightbox CSS file (or append your active stylesheet with the Lightbox styles).
    <link rel="stylesheet" href="css/lightbox.css" type="text/css" media="screen" />
    
  3. Check the CSS and make sure the referenced prev.gif and next.gif files are in the right location. Also, make sure the loading.gif and close.gif files as referenced near the top of the lightbox.js file are in the right location.

Part 2 – Activate

  1. Add a rel="lightbox" attribute to any link tag to activate the lightbox. For example:
    <a href="images/image-1.jpg" rel="lightbox" title="my caption">image #1</a>
    

    Optional: Use the title attribute if you want to show a caption.

  2. If you have a set of related images that you would like to group, follow step one but additionally include a group name between square brackets in the rel attribute. For example:
    <a href="images/image-1.jpg" rel="lightbox[roadtrip]">image #1</a>
    <a href="images/image-2.jpg" rel="lightbox[roadtrip]">image #2</a>
    <a href="images/image-3.jpg" rel="lightbox[roadtrip]">image #3</a>
    

    No limits to the number of image sets per page or how many images are allowed in each set. Go nuts!

2006年05月02日

Lesson5:

关于文字累加。环境:Visual Studio 2003.net & window2000 Server.

声明了一个类成员,CString m_strLine;  在构造函数中初始化为:m_strLine="";

在WM_CHAR消息中使它和nChar累加。编译会出现这个问题:operator += 不明确

解决:

CString m_strLine;UINT nChar;

m_strLine+=nChar 时,右边必须显示转换成char 或者Tchar。

例如:m_strLine+=(char)nChar; m_strLine+=(tchar)nChar ;

CString m_strLine;UINT nChar;

m_strLine+=nChar 时,右边必须显示转换成char 或者Tchar。

例如:m_strLine+=(char)nChar; m_strLine+=(tchar)nChar ;

CString m_strLine;UINT nChar;

m_strLine+=nChar 时,右边必须显示转换成char 或者Tchar。

例如:m_strLine+=(char)nChar; m_strLine+=(tchar)nChar ;

Lesson15:

在.net2003 win32主控制台下,编译不能通过,不能找不到iostream.h

#include <iostream.h>

解决:

改为 
#include <iostream>
using namespace std;

#include <iostream.h>为旧的编译器使用的头文件~~~
using namespace std;
这个是显示声明标准定义
如果你清楚要用什么就可以这么写 还能避免命名空间污染
using std::cout;
using std::endl; 

2006年04月22日

一:Peercast整体架构分析

原文http://blog.csdn.net/bbisonic/archive/2006/04/18/667974.aspx

现在的P2P流媒体主要有两种架构:

1.基于树的架构。这是由流媒体的多播演化而来的,也就是播放同一频道的节点组成一棵树,提供广播的源节点为这棵树的根。每个节点可以为下层几个节点提供数据。但这种架构仍然会对上层的结点造成太大的负担,而且在节点动态加入和退出的情况下树不易维护。另外还存在传输延迟问题,所以树的高度不能太大。

2.基于图(MESH)的架构。通过邻居发现寻找相关的节点。这种架构可以实现完全非中心化。

Peercast采用的是基于图的架构。所有Peercast节点都在同一网络中,而且一个Peercast可以同时转播多个频道。由于Peercast集合了客户端和服务器功能,所以一个Peercast可以同时是广播者、转播者和收听者。

Peercast网络架构可分为三层。

第一层是YP。
YP(yp.peercast.org)从广播者中收集频道信息,是整个网络的根。

第二层是广播者。
广播者向YP发送频道信息,这样YP就能有一个完整的广播者的列表。

第三层是转播者
转播者收听频道,每个广播者维护一份转播者的列表。

P2P流媒体的运行模式
视频/音频输入+编码器+Peercast+播放器

视/音频输入:这是频道的来源,可以是实时事件(电视),也可以是文件(MP3/WMV)
编码器:用于将文件编码成更易于传输的流格式,可以是SHOUTcast DSP和windows media encoder等
播放器:播放编码后传输的文件

Peercast:根据其完成的功能可分为下列几个模块:
1.获取媒体数据:作为广播者读取编码器发送过来的流数据
2.用户界面:以GUI和网页方式提供控制
3.数据传送:在节点间传递控制信息和频道流具体数据
4.节点选择:选择要进行传输的最佳节点
5.缓冲管理:管理流缓冲以实现流媒体下载和播放
6.HTTP服务器。将流数据用HTTP方式送往播放器

节点的加入与退出:

当一个Peercast节点第一次加入Peercast网络时(点击yp.peercast.org中特定频道的PLAY按钮),例如访问地址是peercast://pls/EF49346D72FD05F234D3DA2C33FF3A9C?ip=61.213.94.129:2010 。它会先与广播这个电台的IP(61.213.94.129:2010)建立连接。由于这个广播者是不变的,所以至少会有一个特定的连接。如果广播者是满负荷的,那么这个节点可以通过这个广播者同其他转播同一电台的节点建立连接。
通常会建立8个连接。


在这点上是与Gnutella网络不同的,由于必定存在一个广播者,所以不必实现Gnutella中关于节点第一次加入网络的机制(GWebCache),而此时广播者相当于BT软件的一个原始种子。

与本节点转播同一频道的节点(也就是邻居)的信息保存在ChanHitList中,其中ChanHitList是一份ChanHit的链表,每个ChanHit保存一个相关结点的信息。Hit的意思是你想收听的频道的广播者或转播者。

拥塞控制:

当output队列超过50%时进入拥塞控制模式。系统丢弃一些incoming包,并根据包类型和跳树来给outgoing包区分优先值。当output队列降到25%时,系统会关闭拥塞控制模式。

转播时间更久的节点比刚开始转播的节点拥有更高的优先权,这体现在TTL上。刚开始转播的节点广播的包的TTL值为1,而这个TTL值每5分钟会递增1。这样转播时间超过35分钟的结点会有着最高的TTL值7。

节点间的通信:
节点间的通信通过发送和接收控制信息包来实现。Peercast专用协议PCP规定了控制信息包的类型和格式。
ServMgr负责分配、删除和使用servent对象,每个servent对象负责一个具体的连接,而其中包的发送、接收和解析工作由PCP Stream来实现。

传输者的选择:

在建立初始连接之后,节点需要选择一个最佳的节点来传输频道数据。
选择的顺序依次如下:
1.本地转播者
2.邻居转播者
3.本地广播者
4.邻居转播者

比如说如果找到邻居转播者就不用继续往下找,然后从其中选取出最佳的节点作为传输对象,其他作为备用传输者。
这个最佳可从以下三个方面来衡量:


如果这个传输者退出网络,那么必须重新按上次方法选择下一个传输者。
由于备用传输者较多,所以当节点动态退出网络时不会造成太大的影响。


缓冲机制:

流(stream):流在Peercast中是一个非常重要的概念。所谓流,就是字符串的集合。所以无论是包、视/音频数据都可以看做是一个流。

Peercast的缓冲机制是通过ChanPacketBuffer实现的,里面包含有多个ChanPacket,每个ChanPacket封装了实际的数据。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
二、Peercast收听电台的源代码流程分析

原文:http://blog.csdn.net/bbisonic/archive/2006/04/12/660876.aspx

以收听JOKV-FM(TEST)为例,在YP上点击Play,则其URL地址为
peercast://pls/25838B9F1EAE27079B793C9FBA0E4156?tip=222.148.187.176:7144


case WM_COPYDATA:
   {
    COPYDATASTRUCT *pc = (COPYDATASTRUCT *)lParam;
    LOG_DEBUG("URL request: %s",pc->lpData);
    if (pc->dwData == WM_PLAYCHANNEL)
    {
     ChanInfo info;
     servMgr->procConnectArgs((char *)pc->lpData,info);
     chanMgr->findAndPlayChannel(info,false);
    }
    //sys->callLocalURL((const char *)pc->lpData,servMgr->serverHost.port);
   }
   break;

// 解析连接参数,str表示相应的频道URL,例65051E037A7A2A3433090065051E037A?tip=211.132.83.9:7144
// 从URL中解析频道的相关信息以初始化info

void ServMgr::procConnectArgs(char *str,ChanInfo &info)
{
 char arg[512];
 char curr[256];

 //使args等于?后面的字符串,即tip=211.132.83.9:7144
 char *args = strstr(str,"?");
 if (args)
  *args++=0;

 info.initNameID(str);

 if (args)
 {
  //nextCGIarg分解字符串,把"tip"保存到curr中,"211.132.83.9"保存到arg中
  while (args=nextCGIarg(args,curr,arg))
  {
   LOG_DEBUG("cmd: %s, arg: %s",curr,arg);

   if (strcmp(curr,"sip")==0)
   // sip – add network connection to client with channel
   {
    Host h;
    h.fromStrName(arg,DEFAULT_PORT);
    if (addOutgoing(h,servMgr->networkID,true))
     LOG_NETWORK("Added connection: %s",arg);

   }else if (strcmp(curr,"pip")==0)
   // pip – add private network connection to client with channel
   {
    Host h;
    h.fromStrName(arg,DEFAULT_PORT);
    if (addOutgoing(h,info.id,true))
     LOG_NETWORK("Added private connection: %s",arg);
   }else if (strcmp(curr,"ip")==0)
   // ip – add hit
   {
    Host h;
    h.fromStrName(arg,DEFAULT_PORT);
    ChanHit hit;
    hit.init();
    hit.host = h;
    hit.rhost[0] = h;
    hit.rhost[1].init();
    hit.chanID = info.id;
    hit.recv = true;

    chanMgr->addHit(hit);
   }else if (strcmp(curr,"tip")==0)
   // tip – add tracker hit
   {
    Host h;
    h.fromStrName(arg,DEFAULT_PORT);
    chanMgr->addHit(h,info.id,true);
   }


  }
 }
}

根据info中的信息寻找和播放频道


void ChanMgr::findAndPlayChannel(ChanInfo &info, bool keep)
{
 ChanFindInfo *cfi = new ChanFindInfo;
 cfi->info = info;
 cfi->keep = keep;
 cfi->func = findAndPlayChannelProc;


 sys->startThread(cfi);
}

启动线程

THREAD_PROC findAndPlayChannelProc(ThreadInfo *th)
{
 ChanFindInfo *cfi = (ChanFindInfo *)th;

 ChanInfo info;
 info = cfi->info;


 Channel *ch = chanMgr->findChannelByNameID(info);

 chanMgr->currFindAndPlayChannel = info.id;

 if (!ch)
  ch = chanMgr->findAndRelay(info);

 if (ch)
 {
  // check that a different channel hasn`t be selected already.
  if (chanMgr->currFindAndPlayChannel.isSame(ch->info.id))
   chanMgr->playChannel(ch->info);

  if (cfi->keep)
   ch->stayConnected = cfi->keep;
 }

 delete cfi;
 return 0;
}

创建频道

// 寻找和转播相应频道
Channel *ChanMgr::findAndRelay(ChanInfo &info)
{
 char idStr[64];
 info.id.toStr(idStr);
 LOG_CHANNEL("Searching for: %s (%s)",idStr,info.name.cstr());
 peercastApp->notifyMessage(ServMgr::NT_PEERCAST,"Finding channel…");


 Channel *c = NULL;

 c = findChannelByNameID(info);

 //如果当前没有转播该频道,则新创建一个频道
 if (!c)
 {
  c = chanMgr->createChannel(info,NULL);
  if (c)
  {
   c->setStatus(Channel::S_SEARCHING);   
   c->startGet();
  }
 }

 for(int i=0; i<600; i++) // search for 1 minute.
 {

  c = findChannelByNameID(info);

  if (!c)
  {
   peercastApp->notifyMessage(ServMgr::NT_PEERCAST,"Channel not found");
   return NULL;
  }

  
  if (c->isPlaying() && (c->info.contentType!=ChanInfo::T_UNKNOWN))
   break;

  sys->sleep(100);
 }

 return c;
}

创建频道

Channel *ChanMgr::createChannel(ChanInfo &info, const char *mount)
{
 lock.on();

 Channel *nc=NULL;

 nc = new Channel();

 //将新建的频道加入频道列表
 nc->next = channel;
 channel = nc;


 nc->info = info;
 nc->info.lastPlayStart = 0;
 nc->info.lastPlayEnd = 0;
 nc->info.status = ChanInfo::S_UNKNOWN;
 if (mount)
  nc->mount.set(mount);
 nc->setStatus(Channel::S_WAIT);
 nc->type = Channel::T_ALLOCATED;
 nc->info.createdTime = sys->getTime();

 LOG_CHANNEL("New channel created");

 lock.off();
 return nc;

开始获取数据,即新创建一个Source并调用startStream进行实际传输
void Channel::startGet()
{
 srcType = SRC_PEERCAST;
 type = T_RELAY;
 info.srcProtocol = ChanInfo::SP_PCP;


 sourceData = new PeercastSource();

 startStream();
}

启动传输线程
void Channel::startStream()
{
 thread.data = this;
 thread.func = stream;
 if (!sys->startThread(&thread))
  reset();
}

进行实际的流传输,调用ChannelSource::stream函数
THREAD_PROC Channel::stream(ThreadInfo *thread)
{
// thread->lock();

 Channel *ch = (Channel *)thread->data;

 while (thread->active && !peercastInst->isQuitting)
 {
  LOG_CHANNEL("Channel started");


  ChanHitList *chl = chanMgr->findHitList(ch->info);
  if (!chl)
   chanMgr->addHitList(ch->info);

  ch->sourceData->stream(ch);

  LOG_CHANNEL("Channel stopped");

  if (!ch->stayConnected)
  {
   break;
  }else
  {
   if (!ch->info.lastPlayEnd)
    ch->info.lastPlayEnd = sys->getTime();

   unsigned int diff = (sys->getTime()-ch->info.lastPlayEnd) + 5;

   LOG_DEBUG("Channel sleeping for %d seconds",diff);
   for(unsigned int i=0; i<diff; i++)
   {
    if (!thread->active || peercastInst->isQuitting)
     break;
    sys->sleep(1000); 
   }
  }
 }

 ch->endThread();

 return 0;

}

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

三、用Peercast广播视频文件(WMV格式)

原文:http://blog.csdn.net/bbisonic/archive/2006/04/12/660678.aspx

1.安装Windows media encoder

2.新建会话中选择自定义会话

3.源来自选择文件,在文件名中点击浏览选择要广播的视频(WMV)

4.在输出中选择自编码器拉传递,端口号填8080

5.点击应用

6.在Peercast的Broadcast页面中URL填入http://localhost:8080,其他项根据你的选择设置,然后点击Create Relay

7.在WIndows media encoder中菜单中选择控制->开始编码

8.若广播建立成功,在Relays页面中可以看见刚刚建立的频道,点击Play即可播放

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

四、Peercast各个版本的更新记录

原文:http://blog.csdn.net/bbisonic/archive/2006/04/12/660660.aspx

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

五、Peercast的日志实现

原文:http://blog.csdn.net/bbisonic/archive/2006/04/06/653221.aspx

这里以输出DEBUG信息为例子。

输出日志函数为LOG_DEBUG。具体使用方法为LOG_DEBUG("Play request: %s",pc->lpData);

void LOG_DEBUG(const char *fmt,…)
{
 if (servMgr)
 {
  if ((servMgr->showLog & (1<<LogBuffer::T_DEBUG)) && (!servMgr->pauseLog))
  {
   va_list ap;
     va_start(ap, fmt);
   ADDLOG(fmt,ap,LogBuffer::T_DEBUG);
      va_end(ap); 
  }
 }
}

ADDLOG函数

void ADDLOG(const char *fmt,va_list ap,LogBuffer::TYPE type)
{
 if(sys)
 {
  const int MAX_LINELEN = 1024;

  char str[MAX_LINELEN+1];
  vsnprintf(str,MAX_LINELEN-1,fmt,ap);
  str[MAX_LINELEN-1]=0;

  if (type != LogBuffer::T_NONE)
   sys->logBuf->write(str,type);

  peercastApp->printLog(type,str);
 }
}

void APICALL MyPeercastApp ::printLog(LogBuffer::TYPE t, const char *str)
{
 ADDLOG(str,logID,true,NULL,t);
 if (logFile.isOpen())
 {
  logFile.writeLine(str);
  logFile.flush();
 }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

六、Sys.h源代码分析

原文:http://blog.csdn.net/bbisonic/archive/2006/04/06/652931.aspx

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

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

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

WEvent类:

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

ThreadInfo类:线程信息

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

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

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

WEvent类:

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

ThreadInfo类:线程信息

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

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

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

WEvent类:

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

ThreadInfo类:线程信息

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

七、Peercast的命令行使用方式

原文:http://blog.csdn.net/bbisonic/archive/2006/04/06/652749.aspx

Peercast也可以从命令行启动,有如下几种参数选择

Peercast:正常方式启动

Peercast -inifile:启动,并设置配置文件参数

Peercast -kill:启动后立即关闭

Peercast -url:按照频道的URL地址启动Peercast并播放相应电台

Peercast -multi:以非互斥方式启动

具体实现如下:

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
 char tmpURL[8192];
 tmpURL[0]=0;
 char *chanURL=NULL;   //频道地址


 iniFileName.set(".\\peercast.ini");

 // off by default now
 showGUI = false;  //初始化时不打开图形窗口

 //根据命令行参数进行处理
 if (strlen(lpCmdLine) > 0)
 {
  char *p;
  if ((p = strstr(lpCmdLine,"-inifile"))!=NULL)
   iniFileName.setFromString(p+8);

  if (strstr(lpCmdLine,"-zen"))
   showGUI = false;

  if (strstr(lpCmdLine,"-multi"))
   allowMulti = true;

  if (strstr(lpCmdLine,"-kill"))
   killMe = true;

  if ((p = strstr(lpCmdLine,"-url"))!=NULL)
  {
   p+=4;
   while (*p)
   {
    if (*p==’"’)
    {
     p++;
     break;
    }    
    if (*p != ‘ ‘)
     break;
    p++;
   }
   if (*p)
    strncpy(tmpURL,p,sizeof(tmpURL)-1);
  }
 }

 // get current path
 {
  exePath = iniFileName;
  char *s = exePath.cstr();
  char *end = NULL;
  while (*s)
  {
   if (*s++ == ‘\\’)
    end = s;
  }
  if (end)
   *end = 0;
 }

 
 if (strnicmp(tmpURL,"peercast://",11)==0)
 {
  if (strnicmp(tmpURL+11,"pls/",4)==0)
   chanURL = tmpURL+11+4;
  else
   chanURL = tmpURL+11;
  showGUI = false;
 }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
八、wsocket.h源文件分析
原文:http://blog.csdn.net/bbisonic/archive/2006/04/06/652642.aspx

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;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

2006年04月06日

开源在今天的软件业已经很普遍,但开源是否意味着使用者可以对开源后的代码为所欲为呢?答案是否定的。开源运动同样有自己的游戏规则和道德准则。不遵行这些规则不但损害开源运动的健康发展,也会对违规者造成名誉和市场上的损失,更可能陷入法律纠纷和赔偿。

现今存在的开源协议很多,而经过Open Source Initiative组织通过批准的开源协议目前有58种。我们在常见的开源协议如BSD, GPL, LGPL,MIT等都是OSI批准的协议。如果要开源自己的代码,最好也是选择这些被批准的开源协议。

这里我们来看四种最常用的开源协议及它们的适用范围,供那些准备开源或者使用开源产品的开发人员/厂家参考。

BSD开源协议

BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。

但“为所欲为”的前提当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:

1. 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。

2. 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。

3. 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。

BSD代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次开发。

Apache Licence 2.0

Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。需要满足的条件也和BSD类似:

1. 需要给代码的用户一份Apache Licence

2. 如果你修改了代码,需要再被修改的文件中说明。

3. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明。

4. 如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可,但不可以表现为对Apache Licence构成更改。

Apache Licence也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。

GPL

我们很熟悉的Linux就是采用了GPLGPL协议和BSD, Apache Licence等鼓励代码重用的许可很不一样。GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,但不允许修改后和衍生的代码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux,包括商业公司的linuxlinux上各种各样的由个人,组织,以及商业软件公司开发的免费软件了。

GPL协议的主要内容是只要在一个软件中使用(“使用”指类库引用,修改后的代码或者衍生代码)GPL协议的产品,则该软件产品必须也采用GPL 协议,既必须也是开源和免费。这就是所谓的”传染性”。GPL协议的产品作为一个单独的产品使用没有任何问题,还可以享受免费的优势。

由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议,对于使用GPL协议的开源代码,商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础。

其它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似。

LGPL

LGPLGPL的一个为主要为类库使用设计的开源协议。和GPL要求任何使用/修改/衍生之GPL类库的的软件必须采用GPL协议不同。 LGPL允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码。这使得采用LGPL协议的开源代码可以被商业软件作为类库引用并发布和销售。

但是如果修改LGPL协议的代码或者衍生,则所有修改的代码,涉及修改部分的额外代码和衍生的代码都必须采用LGPL协议。因此LGPL协议的开源代码很适合作为第三方类库被商业软件引用,但不适合希望以LGPL协议代码为基础,通过修改和衍生的方式做二次开发的商业软件采用。

GPL/LGPL都保障原作者的知识产权,避免有人利用开源代码复制并开发类似的产品。

P.S. 随手写写,是因为跟客户交谈的时候发现很多人其实对开源有完全错误的理解,特别是开源协议。很多人认为只要是开源,就意味着随便用,随便改都可以。

dlee补充:

这些知识 Linux Fans 大多比较清楚,因为经常使用开源软件,每天下载源代码,编译、测试、安装是常事。这些问题在各个 Linux 论坛上也早已经是老生常谈,但是有些 Java Fans 以及大多数 M$ 平台的开发人员几乎一无所知。我在这里一般不会谈跟 Java 关系不大的内容。既然 Charlesxp 想谈谈,我也补充一下。

首先,软件有两种分类方法:

商业软件 vs. 免费软件

商业软件:软件是要卖钱的。

免费软件:软件不卖钱,出于兴趣开发,可以随便使用。

开源软件 vs. 私有软件

开源软件:软件的源代码是公开的,可以在遵守某种 license 的条件下加以修改。

私有软件:软件的源代码不公开,用户仅仅只能得到软件的二进制版本。

  从上面的两种区别我们可以看出,开源软件和商业软件不是对等或对立的关系,开源软件同样也可以卖钱。但是给了你源代码,你如果没有一定的功力也未必能看懂。而且用户常常不具备相应的能力(没有从事过软件开发),仅仅想要使用软件的功能。既然这样,开源对于这类用户还有何意义呢?开源可以保护这类用户免于陷入厂商设置的锁定陷阱。因为提供了全部源代码,只要用户能找到有能力维护这些代码的人或公司,他就可以持续发展这些代码的功能。这个人或公司不一定是最初卖软件给他的那一家。用户不会在大部分功能用的好好的,某个关键功能无法使用的时候,买家告诉他,你希望获得的这个功能在我们的下一版本中有,但是你必须支付我们 10w$ 升级费我们才可以提供。这个就叫做厂商锁定。如果软件不是开源的,用户没有办法做出选择,上了贼船后想下来可就难了。所以开源的商业软件对于保护用户自己的利益是非常重要的。用户可以获得真正的安全,而且更体现了软件的本质就是一种高级的服务。

  那么有人会担心,这样做究竟能不能赚到钱?你能不能赚到钱要看市场和需求有多大。只盯着厂商锁定一种方式来赚钱其实是一种短视的行为。现在国内大部分软件开发商都不重视维护和服务,仅仅靠卖软件本身的 copy license 来赚钱,用户感觉不到自己是上帝,怨声载道。其实用户所需要的还是长期的维护和服务。所以这样的软件如果真能把服务做好,用户是会非常欢迎的。既然经过分析预计会有很大的需求,从长期看,我相信肯定是可以赚到钱的。

  还有一类软件:共享软件。这类软件不是开源的,但是有免费的版本可以使用。如果你用的很爽,良心发现了,可以把钱汇给作者。如果希望作者提供服务,也可以找他谈。国内很有名的 FoxMail 就是这类软件。但是对于用户最有价值的一类软件,其实还是开源的商业软件。

  开源软件卖的是长期的服务,不是软件本身的 copy。不是所有的软件都有可能卖服务,也不是所有的用户都需要服务(这个问题我们以后可以讨论)。但是一个大型企业或者重要的政府机关,假设使用了 JBoss+MySQL 这种组合,长期的服务是非常必要的(至少出了事情可以找到一个替罪羊)。你问这个问题就说明你没有多少企业应用的经验,不知道 IBM 这些公司的商业模式就是建立在 IT 服务的基础之上的。开源软件提供了一个新的组合,对于更好地做好这种服务非常有帮助。实际上,IBM 的解决方案中就包括了大量开源的成果。HP 做的方案也有采用 JBoss 的,为了得到长期的服务,HP JBoss 专门签订了长期合作的协议。

    中国国内由于市场容量的限制,以及用户缺乏信息化的经验,对于软件长期服务的重要性认识不足,因此总是只看到软件本身的售价,给了一些夫妻店竞相压价钻空子的机会。但是必须承认,这并不是一种良性竞争的状态。最终还是要走到切实为用户做好服务的道路上来的。服务是值钱的,不是免费的。你去路边的理发馆随便理个发,5 块钱,出来以后仍然蓬头垢面。你去高级发廊做头,500 块,但是你得到了按摩等各种服务(不要联想引申),身心得到了放松,出来以后精神焕发。你只盯着路边的理发馆,但是仍然有别人愿意去高级的发廊。

  当然,真正要做好服务,对于技术等各方面的能力要求是非常高的。这种要求不是随便哪个夫妻店就能够达到的。

robbin补充:

  Richard StallmanGNU创始人和GPL的创造者,Stallman开发的比较著名的GPL软件包括GNU GCCGNU Emacs。其中EmacsStallman独立开发的早期也是卖钱的。Stallman定的Emacs售价为每份150美元。

  因此请记住: 开源软件不等于免费软件。

2006年03月19日

闲逛时发现一个站点,比较新鲜,顺带和下面的十个一块分享。 Web OS ,是不是挺新鲜的,www.YouOS.com 试用了一下,感觉挺不错的,速度也很快啊,不知道是不是用户少的原因,看其介绍是使用了javascript,现在看来javascript前途不可限量啊,原来都没有发掘出来他的潜力。具体的一些应用:聊天(还能创建自己的聊天频道),内嵌的浏览器,内嵌的即时通讯工具(IM),shell命令行,还有文件管理,Rss聚合,文本编辑。。。很多东西啊,还可以自己创建安装应用。大家一块来发掘一下


以下转自  徐新事的blog

最近互联网上出现很多网站,可能其中有一部分是你没听说过的,这也是我列举这些站点的目的。我不想列举想flickr这样已经很有名气的网站,尽管它是否知名还存争议。

第一,http://www.meebo.com 一个小暴发户网站,它现在收购了http://www.trillian.cc ,它把Yahoo! Messanger、Jabber、Gtalk、AIM、ICQ和MSN几款流行的即时通讯软件整合在一起,支持多款软件之间的信息互通。

第二,http://www.rememberthemilk.com 一个典型的web 2.0 网站。如果你经常忘记做事情,你就需要这个网站,它支持手机、邮件和即时通讯等工具的提醒定制功能。

第三,http://www.blinklist.com 这个网站很有可能被关闭了,这是一个书签管理网站,和del.icio.us是竞争关系。但是这个网站有很好的UI设计,一些独特的功能,是的del.icio.us黯然失色不少。经常浏览这个网站看看它有哪些新的功能。

第四,http://www.digg.com 许多人称digg.com是slashdot.org的终结者,这句话可能是对的。Digg.com是一个很好的科技新闻聚合中心,全部依靠用户的参与,而没有任何编辑行为。

第五,http://www.clusty.com Clusty.com可能是永远在google阴影笼罩下的小搜索引擎,但同样值得看看。Clusty(强健的)就像它的意思一样,提供搜索结果的分类,围绕你的搜索目标分成多种不同的类别,这样便于你搜索你想要的信息。此外还有一堆好的特色功能只能你尝试。

第六,http://www.writely.com 文本处理工具的网络版?它的产生是一种巧合。Writely是完美的网络图书馆处理文本工具,信息的不管它的可用性如何,但它确实很酷。

第七,http://www.last.fm Last.fm是很难描述的,它一部分是社会性音乐书签,一部分像个人音乐书签。我认为,如果你电脑里的音乐很多,Last.fm会是你音乐管理的好助手。

第八,http://www.pixel2life.com 是所有事物的实用指南,它拥有庞大的数据库,几乎拥有所有计算机相关的指南,从系统冲突解决到微软的Outlook如何使用等。

第九,http://www.blummy.com 顶呱呱网。Blummy.com提供一个独特的方式保存和共享书签,如果你有很多书签需要管理,你打开这个网站就知道它的好。

第十,http://www.hipcal.com Hipcal是web 2.0类别的网站。主要提供在线日历和提醒功能。