网络原理-OSI七层模型和TCP/IP模型-维基百科摘要

OSI七层模型和TCP/IP模型-维基百科摘要


开放式系统互联通信参考模型英语:Open System Interconnection Reference Model,缩写为 OSI),简称为OSI模型(OSI model),一种概念模型,由国际标准化组织提出,一个试图使各种计算机在世界范围内互连为网络的标准框架。定义于ISO/IEC 7498-1。

应用层(Application Layer)提供为应用软件而设的接口,以设置与另一应用软件之间的通信。例如: HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。

表达层(Presentation Layer)把数据转换为能与接收者的系统格式兼容并适合传输的格式。

会话层(Session Layer)负责在数据传输中设置和维护电脑网络中两台电脑之间的通信连接。

传输层(Transport Layer)把传输表头(TH)加至数据以形成数据包。传输表头包含了所使用的协议等发送信息。例如:传输控制协议(TCP)等。

网络层(Network Layer)决定数据的路径选择和转寄,将网络表头(NH)加至数据包,以形成分组。网络表头包含了网络数据。例如:互联网协议(IP)等。

数据链路层(Data Link Layer)负责网络寻址、错误侦测和改错。当表头和表尾被加至数据包时,会形成帧。数据链表头(DLH)是包含了物理地址和错误侦测及改错的方法。数据链表尾(DLT)是一串指示数据包末端的字符串。例如以太网、无线局域网(Wi-Fi)和通用分组无线服务(GPRS)等。

分为两个子层:逻辑链路控制(LLC)数据通信协议层是七层OSI模型数据链路层的上部子层。LLC子层提供多路复用机制,使得多个网络协议(例如IPIPXDecnetAppletalk)可以在多点网络中共存并通过相同的网络介质传输。它还可以提供流量控制自动重复请求(ARQ)错误管理机制。LLC子层充当媒体访问控制(MAC)子层和网络层之间的接口。

物理层(Physical Layer)在局部局域网上传送数据帧(data frame),它负责管理电脑通信设备和网络媒体之间的互通。包括了针脚、电压、线缆规范、集线器、中继器、网卡、主机适配器等。

 

互联网协议族英语:Internet Protocol Suite,缩写IPS)[1]是一个网络通信模型,以及一整个网络传输协议家族,为互联网的基础通信架构。它常被通称为TCP/IP协议族(英语:TCP/IP Protocol Suite,或TCP/IP Protocols),简称TCP/IP

应用层

该层包括所有和应用程序协同工作,利用基础网络交换应用程序专用的数据的协议。 应用层是大多数普通与网络相关的程序为了通过网络与其他程序通信所使用的层。这个层的处理过程是应用特有的;数据从网络相关的程序以这种应用内部使用的格式进行传送,然后被编码成标准协议的格式。

传输层

传输层的协议,能够解决诸如端到端可靠性(“数据是否已经到达目的地?”)和保证数据按照正确的顺序到达这样的问题。在TCP/IP协议组中,传输协议也包括所给数据应该送给哪个应用程序。 在TCP/IP协议组中技术上位于这个层的动态路由协议通常被认为是网络层的一部分;一个例子就是OSPF(IP协议89)。 TCP(IP协议6)是一个“可靠的”、面向连结的传输机制,它提供一种可靠的字节流保证数据完整、无损并且按顺序到达。

网络互连层

TCP/IP协议族中的网络互连层(internet layer)在OSI模型中叫做网络层(network layer)。

正如最初所定义的,网络层解决在一个单一网络上传输数据包的问题。类似的协议有X.25ARPANETHost/IMP Protocol。 随着因特网思想的出现,在这个层上添加附加的功能,也就是将数据从源网络传输到目的网络。这就牵涉到在网络组成的网上选择路径将数据包传输,也就是因特网。 在因特网协议组中,IP完成数据从源发送到目的的基本任务。IP能够承载多种不同的高层协议的数据;这些协议使用一个唯一的IP协议号进行标识。ICMP和IGMP分别是1和2。 一些IP承载的协议,如ICMP(用来发送关于IP发送的诊断信息)和IGMP(用来管理多播数据),它们位于IP层之上但是完成网络层的功能,这表明因特网和OSI模型之间的不兼容性。所有的路由协议,如BGPOSPF、和RIP实际上也是网络层的一部分,尽管它们似乎应该属于更高的协议栈。

网络接口层

网络接口层实际上并不是因特网协议组中的一部分,但是它是数据包从一个设备的网络层传输到另外一个设备的网络层的方法。这个过程能够在网卡软件驱动程序中控制,也可以在韧体或者专用芯片中控制。这将完成如添加报头准备发送、通过实体媒介实际发送这样一些数据链路功能。另一端,链路层将完成数据帧接收、去除报头并且将接收到的包传到网络层。 然而,链路层并不经常这样简单。它也可能是一个虚拟专有网络(VPN)或者隧道,在这里从网络层来的包使用隧道协议和其他(或者同样的)协议组发送而不是发送到实体的接口上。

 

科普百科-经济与政治-美国与以色列

因为美国政坛由犹太财团把持着,不支持以色列,犹太财团能让美国改朝换代。

财团,自然是有钱。那么谁拥有的金钱是最多的?—毫无疑问,能拥有印钞权力的财团才是真正的大财团。

而美国拥有印钞权力的机构不是美国的国有央行。而是一个私人机构:美联储。美联储背后是由很多私人股东组成的。这些股,最终控制者大部分都是犹太人。

在美国的资本市场,到处弥漫着犹太人的味道。高盛、雷曼兄弟、所罗门兄弟等著名金融公司都是犹太人创建的。在华尔街,金融精英中有50%是犹太人。

在美国媒体和文化产业,犹太势力更是举足轻重。其中代表人物就是媒体大亨默多克和现任纽约市长迈克尔·布隆伯格,前者的新闻集团是世界上最大的跨国媒体集团,后者旗下的彭博资讯是华尔街财经资讯的喉舌。平面媒体里,《纽约时报》、《时代》周刊、《新闻周刊》、《华尔街日报》以及美国三大电视网,到处都是犹太势力。

在好莱坞,华纳兄弟的创始人都是波兰裔犹太人,梦工厂、米高梅等著名电影公司的大股东也是犹太人;以斯皮尔伯格为代表的犹太人制片、编剧、导演、演员也非常强势。当年,演员兼导演梅尔·吉布森由于公开发表反犹太言论,被好莱坞彻底打入冷宫长达8年之久,从此一蹶不振。

而在体育界,高峰时期,NBA的犹太老板曾经多达10人,其中包括活塞队老板戴维森、奇才队老板波林等等。可以说犹太大佬们打个喷嚏,美国绝对会摇一摇。

得犹太人者得美国

目前,全世界大约有1300万犹太人,500多万在以色列,约有600多万人在美国。美国政府的要职上常见到犹太人的身影,如前国务卿基辛格、奥尔布赖特,以及布什父子政府中著名的鹰派人物、有着“伊战教父”之称的沃尔福维茨,奥巴马的新一任白宫办公厅主任伊曼纽尔、现任美联储主席伯南克和白宫国家经济委员会主席萨默斯等等。

30多年来,美国至少否决了32项联合国安理会不利于以色列的决议,超过所有其他安理会成员行使否决权的总和。对此,法国《世界报》撰文评论说:“美国和以色列是钢铁关系,一方面犹太人通过钞票和选票影响美国政府;另一方面,由于他们的巨大影响力,在历次美国大选中,犹太人都是两党竞相讨好的香饽饽。”

所以,说美国总统是犹太人的傀儡,是很有道理的。

科普百科-经济与政治-美联储与美国的经济和政治

一、美联储的由来

美国联邦储备系统为美国的中央银行,与其他国家的中央银行相比,美联储作为美国的中央银行诞生的比较晚。历史上,美国曾多次试图成立一个像美联储这样的中央银行,但因为议员及总统担心央行权力过大,或央行会被少数利益集团绑架而没有成功。这种担心主要是由美国的国家形态所决定的。建国之初,美国是由一些独立的州以联邦的形式组成的松散组织,大部分的行政权力主要集中在州政府,而非联邦政府。因此,成立中央银行这样一个联邦机构的想法会引起各州的警觉,他们担心联邦政府想以此为名来扩大自己的权力范围。

美国第一银行

美国的第一任财政部部长亚历山大·汉密尔顿(Alexander Hamilton)在美国独立战争结束后,向国会提议成立一个全国性的银行。主要负责帮助美国各州发行债券,以逐步偿还独立战争时期累积的债务。另外,该银行还可帮助保管财政部的存款及处理其他联邦政府财务上的收支。汉密尔顿的这个提议当时遭到了强烈反对。反对者主要担心这三个方面:其一,这样一个全国性银行比其他银行有更多竞争优势,会造成它在私营银行业务上的垄断;其二,该银行超出美国宪法规定的联邦政府权限;其三,成立全国性银行会削弱各州政府的权力。尽管汉密尔顿力排众议,于1791年在费城成功建立了这样一个银行——美国第一银行(The First Bank of the United States),但国会只批准第一银行经营20年。20年后,必须获得国会批准,第一银行才能继续存在。

美国第一银行并不是现代意义上的中央银行,它实际上仍是一个私人性质的商业银行。第一银行和其他银行一样吸收储户的存款和发放商业贷款。另外,第一银行按照股份制方式成立,股票在市场上公开出售,股东和其他上市公司股东一样分配红利,决定第一银行如何经营。唯一不同的是,其他商业银行属于州内银行,而美国第一银行是全国性质的银行,在全美开展业务,而且第一银行是唯一一个为美国财政部提供服务的银行。
第一银行除了是私人性质的商业银行外,还有一个很大的硬伤,就是它大概有70%的股权属于外国人,当时主要是欧洲人。这让很多美国人担心第一银行是欧洲金融家族控制美国的一个工具。所以1811年,第一银行的20年经营期限到了之后,国会没有批准它继续存在,第一银行被迫关闭。

美国第二银行

1812年,英美再起战争冲突。由于缺乏一个有效的中央银行系统和全国统一的金融市场,美国的州内银行陷入混乱。为了恢复银行秩序和处理战争债务,美国再次考虑成立一个类似第一银行的中央银行。1816年由美国国会投票通过,麦迪逊总统签署法案成立了美国第二银行(The Second Bank of the United States)。
和第一银行类似,第二银行更接近一个全国性的商业银行,而不是现代意义的中央银行。同样,国会也只给第二银行颁发了20年执照。执照到期后,必须经过国会批准才能继续存在。
由于商业竞争关系,第二银行成立后遭到州内银行的强烈反对。它的反对者声称,第二银行只帮助美国东部经济发达地区的商人和富豪,而不为当时占美国人口多数的农民服务。这种宣传把第二银行推到了舆论上非常孤立的境地。在当时美国第七任总统竞选中,安德鲁·杰克逊(Andrew Jackson)的竞选对手支持第二银行。1833年,杰克逊当上总统后,兑现了自己的竞选承诺,把财政部的存款全部转移到州立银行,削弱了第二银行继续存在的理由。这种情况下,第二银行的执照在20年期限到期后,没有通过国会的审批。1836年,美国第二银行重蹈第一银行覆辙,被迫关闭。

自由金融时期

在美国第二银行关闭后的70多年里,美国政府没有再试图建立一个类似中央银行的机构。在此期间,美国的金融市场经历了放任自流式的大发展。但随着金融市场的不断扩张,放任自流式银行体系的缺点也越来越多地暴露出来,银行挤兑造成市场崩溃的情况经常发生。美国在1873年、1884年、1890年、1893年、1907年都发生过由于挤兑造成银行大规模倒闭的金融危机。尤其是在1893年的危机中,美国有超过500家银行相继倒闭,很多人的毕生积蓄可以说一夜之间付诸东流。
金融危机不仅给个人和家庭造成巨大的财富损失,整个美国经济也由于金融业无法正常运转而陷入了长时间停滞。1893年的金融危机给美国带来了空前严重的经济衰退。沉寂了10年之后,金融危机于1907年在美国再度爆发。危机爆发的导火索是股票市场上一些投机者试图操纵美国联合铜业公司(United Copper Company)的股票,但没有成功。贷款给这些投机者的银行和信托投资公司因此损失惨重。1907年10月,纽约第三大信托投资公司可尼克波克(Knickerbocker Trust Company)被迫宣布破产。和2008年雷曼兄弟破产类似,当年可尼克波克的破产引发了一场席卷美国的金融海啸。存款者人人自危,纷纷从银行等金融机构提款以避免损失。由于金融恐慌,银行间也失去信任,相互间的借贷完全冻结,并发生挤兑。这种银行挤兑很快从纽约蔓延到全国各地,一场类似1893年金融危机的市场崩溃即将发生。当时,美国著名金融公司摩根大通(J.P. Morgan)的创始人摩根意识到了问题的严重性,并立刻召集最重要的几家金融公司的总裁开会,要求大家一起拿出资金帮助面临挤兑的银行。而摩根自己也身先士卒,拿出资金帮助金融市场渡过危机。从一定程度上讲,摩根当时扮演的角色正类似于2008年拯救金融市场的美联储。

联邦储备法案

频繁爆发的金融危机让公众逐渐意识到,放任自流式的金融市场存在很大问题。支持成立一个机构对金融市场进行适当监管的呼声逐渐高起来。1907年的金融危机结束后,美国参议员尼尔森·奥尔德里奇(Nelson Aldrich)组织了一个委员会,专门负责调查金融危机的成因以及教训。在1907年的危机中,尽管仍有将近100家银行因为挤兑倒闭,但与1893年500多家银行倒闭相比,算是一个不错的结局。摩根带领其他金融机构挺身而出,为遭受挤兑的银行提供流动资金是1907年危机成功化解的一个重要因素。但这种依赖金融市场上某个人的能力解决危机的方式存在很多不确定性,最好用法律形式赋予某个机构稳定金融市场的职能来缓解危机。因此奥尔德里奇花了将近两年时间考察欧洲国家的中央银行,并对欧美金融市场进行了系统的比较分析。随后他起草了被称为《奥尔德里奇计划》的提案,主张美国应该模仿欧洲国家成立一个统一的中央银行。今后再发生类似1907年的金融危机时,该中央银行可以向被挤兑的银行提供紧急贷款,以此稳定金融市场信心。
但《奥尔德里奇计划》受到议会很多成员的强烈反对。作为共和党的主要成员,奥尔德里奇非常担心联邦政府权力过大,因此不愿意成立一个新的联邦政府机构来执行中央银行的职能。《奥尔德里奇计划》中的中央银行的各种角色完全由私人银行扮演,而不像欧洲国家的中央银行属于政府机构。这种设置方式从一定程度上也是受到了美国第一银行和第二银行的影响。《奥尔德里奇计划》的反对者主要来自美国中西部的农业州。这些从事传统农业的人们一方面担心银行业过于强大后会绑架整个经济,另一方面这些州的低收入者认为该法案主要服务于东部商业发达地区的富人,而不是美国大众。奥尔德里奇本人和摩根大通的创始人摩根关系非常密切,同时女儿又嫁给了美国超级富翁洛克菲勒唯一的儿子小洛克菲勒。这种和超级富豪家庭千丝万缕的关系,让奥尔德里奇对这些指责有口难辩。民主党人威尔逊于1912年当选美国总统。他的当选险些彻底扼杀《奥尔德里奇计划》和再次提出建立中央银行的设想。好在威尔逊总统认为《奥尔德里奇计划》虽然是共和党提出的,但其整体框架存在很多优点。如果认真修改后实施,会对美国的金融市场有很大促进作用。因此,在威尔逊总统的支持下,参议员欧文和众议员格拉斯以《奥尔德里奇计划》为蓝本,在1913年起草了《联邦储备法案》。《联邦储备法案》中对美联储的机构设置与《奥尔德里奇计划》非常相似,但加强了美国联邦政府在美联储中的影响。最初的《奥尔德里奇计划》把美联储完全定性为一个私人机构,而《联邦储备法案》把华盛顿的联邦储备局定性为联邦政府机构,尽管美联储的12个联邦储备银行被定性为更接近于私人部门的非营利性机构。
1913年美国国会最终达成一致意见,通过了《联邦储备法案》。威尔逊总统签署了该法案,正式宣告美联储成立。

二、美联储组织架构

美联储货币政策的最高决策机构包括联邦储备局的7名执行委员(Members of the Board of Governors)和12名联邦储备银行的主席(Presidents of the Federal Reserve Banks)。美联储最重要的货币政策被称作公开市场操作,日常说的美联储公开市场会议(或者议息会议)就是为制定这个政策而召开的。公开市场政策由华盛顿的联邦储备局和各个地区的联邦储备银行共同制定。美联储的12个联邦储备银行是相对独立的机构,比如联邦储备银行的主席可以担任公开市场委员会的委员,在担任委员期间,联邦储备银行的主席与联邦储备局主席、联邦储备局的其他委员拥有同等的投票权。
联邦储备局的7名执行委员由主席、副主席及其他5位委员组成。这7名执行委员必须全部由总统提名,经过国会确认后才能上任。每个执行委员的任期为14年,到任后不能连任,注意这里的14年任期指的是执行委员的任期,和美联储主席任期并不是一个概念。执行委员上任后,总统没有权利罢免这些委员。如果需要罢免他们,必须有国会2/3的成员投票通过才可以。
美联储 核心:联邦公开市场委员( 一年开8次会议纪要,12个人有投票权,投票可能少数服从多数吧)
它由十二名成员组成,包括:联邦储备委员会全部成员七名,纽约联邦储备银行行长,其它四个名额由另外11个联邦储备银行行长轮流担任。
 联邦公开市场委员作用: 公开市场操作、制定贴现率、制定法定准备金率
09年纽约联储银行的资产 就已经把  同行们 (其他联储银行) 远远甩在身后。
美联储纽约银行,它在1914年5月19日向货币审计署(Comptroller of the Currency)报备的文件上记录着股份发行总数为203053股,其中: 

洛克菲勒和库恩雷波公司所控制的纽约国家城市银行,拥有最多的股份,持有3万股。 
JP摩根的第一国家银行拥有1.5万股。 
当这两家公司在1955年合并成花旗银行后,它拥有美联储纽约银行近1/4的股份,它实际上决定着美联储主席的候选人,美国总统的任命只是一枚橡皮章。 

保罗-沃伯格的纽约国家商业银行拥有2.1万股。 
由罗斯柴尔德家族担任董事的汉诺威银行拥有1.02万股。 
大通银行拥有6000股 
汉化银行拥有6000股 

这六家银行共持有40%的美联储纽约银行股份,到1983年,他们总共拥有53%的股份。经过调整以后,他们的持股比例是:花旗银行15%,大通曼哈顿14%,摩根信托9%,汉诺威制造7%,汉化银行8%。其中,花旗银行控制了97个董事席位;J.P.摩根公司控制了99个;汉华银行控制了96个;大通曼哈顿控制了89个;汉诺威制造控制了89个

从1914年美联储建立以来,无可辩驳的事实显示了银行家们操纵着美国金融命脉、工商业命脉和政治命脉,过去如此,现在仍然如此。而这些华尔街的银行家都与伦敦城的罗斯柴尔德家族保持着密切联系。

如果你对这个问题感兴趣,建议你看看《货币战争》这本书,里面有较完全的介绍,也会发现一个真正幕后的,其力量可以决定多任美国总统命运的,古老的罗斯柴尔德家族的秘密。
美联储的老板们是些什么人?这原来是个非常神秘的秘密。现代的人们总是喜欢寻根究底,据美国的各方面的人士长久的跟踪深入追究,并披露于媒体,大概地知道,美联储的真正的老板是由完全私有化的银行和个人所组成。他们之中有花旗银行(其前身是纽约第一国家城市银行)、纽约银行、第一国家银行、纽约国家商业银行、汉诺威银行、摩根大通银行(前身是大通银行)、汉华银行、大通曼哈顿银行、摩根信托公司、银行家信托公司……,还有相当大的一部分股份是个人持有者如洛克菲勒家族、摩根家族、罗斯柴尔德家族、哈里曼家族、希夫家族、贝克家族、斯蒂尔曼家族、斯特郎家族、希尔家族、沃博格家族……!虽然有些银行挂着国家银行的名头,其实,都是私人银行。有些银行就是上述这些家族控制的银行。例如纽约第一国家城市银行绝对控股股东是洛克菲勒家族,第一国家银行的绝对控股股东是摩根家族,纽约国家商业银行的绝对控股股东是沃博格家族,在这些银行名字中的“国家”正反映了美国社会的“国家即朕,朕即国家”的政治和经济现象的本质!由于美联储的股东严格保密,美国媒体从浩瀚的资料中得来的零碎的资料堆里艰难寻迹中,得到并披露的这些股东名字不一定都达到精确的程度,但是,有一点是可以肯定的,即从披露的美国联邦储备委员会的股东名单中,可以清楚地说明的是,美国联邦储备委员会完全是一个私人银行!

美联储不是”一家银行”,而是将50个州和哥伦比亚特区划分为12个联邦储备区,在每个联邦储备区设立一家联邦储备银行和若干分行,与联邦储备委员会,顾问咨询委员会,以及作为会员银行的若干商业银行共同组成的多元中央银行体系.除了联邦储备银行,所有的国民银行(在货币监理处注册的商业银行)都必须是联邦储备体系的会员,而在州注册的商业银行可以自由选择是否加入联邦储备体系。目前大约有三分之一左右的商业银行是联邦储备体系的成员。
从作用和功能上来说美联储的的确确是美国的中央银行,但是它又的的确确是注册在特拉华州(Delaware)的私人企业(Private Company),它和美国联邦政府机构根本不是一回事,尽管美联储的主席是由总统任命,国会核准的 它倒确实是被控制着。
关于这一点看一看看这本书《货币战争》,里面的第三章里面有一节的题目是:华尔街七巨头:美联储的幕后推手。 真的存在,不过这个家族很低调
罗斯柴尔德家族(rothschild family):地球上最为神秘的古老家族,一个隐藏在这个世界阴暗面的控制者,一个控制了这个星球近两个世纪经济命脉的强大家族!或许对绝大多数普通人来说它是陌生的,因为在大众传媒时代,人们的目光或许只会关注到类似“洛克菲勒家族”或者“摩根家族”这些声明显赫的名字上。而二十世纪二战前的美国,曾经有一句经典的话形容当时美国的情况“民主党是属于摩根家族的,而共和党是属于洛克菲勒家族的……”其实在这句话后面还应该跟一句“而洛克菲勒和摩根,都曾经是属于罗斯柴尔德的!”
美联储,英格兰银行,洛克菲勒家族,摩根家族全是他家掌控的;策划1939年世界经济危机,第1第2次世界大战希特勒是他们扶持的。
“大道无形”的世界首富
“只要 能控制一个国家的货币发行, 不在乎谁制订法律。”[1]
——梅耶·罗斯柴尔德

当国际媒体成天炒作身家500亿美元的比尔·盖茨蝉联世界首富宝座的时候,如果你信以为真,你就上当了。因为在人们耳熟能详的所谓富豪排行榜上,你根本找不到“大道无形”的超级富豪们的身影,因为他们早已严密地控制了西方主要的媒体。
所谓“大隐隐于朝”。如今,罗斯柴尔德家族仍在经营着银行业务,但是如果 们随机在北京或上海的街头问100个中国人,其中可能有99个知道美国花旗银行,而不见得有1个知道罗斯柴尔德银行。
究竟谁是罗斯柴尔德?如果一个从事金融行业的人,从来没有听说过“罗斯柴尔德”这个名字,就如同一个军人不知道拿破仑,研究物理学的人不知道爱因斯坦一样不可思议。奇怪却并不意外的是,这个名字对绝大多数中国人来说是非常陌生的,但它对中国人民乃至世界人民的过去、现在和未来的影响力是如此巨大,而其知名 度是如此之低,其隐身能力让人叹为观止。

罗斯柴尔德家族究竟拥有多少财富?这是一个世界之迷。保守的估计是50万亿美元!

据估计,1850年左右,罗斯柴尔德家族总共积累了相当于60亿美元的财富。如果罗斯柴尔德家族后来没有衰落的话,以6%的回报率计算,在150多年后的今天,他们家族的资产至少超过了50万亿美元。到20世纪初,罗斯柴尔德家族所控制的财富估计达到了当时世界总财富的一半。
严密的家族控制,完全不透明的黑箱操作,像钟表一般精确的协调,永远早于市场的信息获取,彻头彻尾的冷酷理智,永无止境的金权欲望,以及基于这一切的对金钱和财富的深刻洞察和天才的预见力,使得罗斯柴尔德家族在两百多年金融、政治和战争的残酷旋涡中所向披靡,建立了一个迄今为止人类历史上最为庞大的金融帝国。
世界上最大的10个财团,有8个是它的下属分支

还有,美联储就是他们家族开得,林肯,肯尼迪就是为了和他作斗争,挂了


美国民主党是属于摩根家族的,共和党是属于洛克菲勒家族的,是真的吗?

民主党是属于摩根家族的,共和党是属于洛克菲勒家族的,而洛克菲勒和摩根,都曾经是属于罗斯柴尔德的!
都是属于财团的,洛克菲勒和摩根先后脱离了罗斯柴尔德家族财团

科普百科-经济与政治-美国建国后政党的历史由来

在当代美国,在地方政府层次,很多地区并不分党派;在州一级,目前仅有内布拉斯加州议会仍保持不分党派性质。在全国层次,现在当然是驴象泾渭分明。但是如果我们溯美国历史之源,会发现原来美国建国之初并没有政党。

美国建国史上,有四位“国父”与美国政党发展历史息息相关:第一任总统华盛顿,第一任财政部长汉密尔顿,《独立宣言》起草人、第一任国务卿、第三位总统杰斐逊,《宪法》起草者,后来成为美国第四位总统的麦迪逊。麦迪逊与汉密尔顿都是美国建国早期经典文献《联邦党人文集》的作者。《文集》第十篇是麦迪逊所著,麦迪逊警告了政党政治的危害。他说,党派“不利于其他公民的权利,也不利于共同体的永久集体利益”。

华盛顿最著名的演讲之一是他卸任总统时的《告别演说》。这篇演讲稿的撰稿人是汉密尔顿。演说最重要的主旨之一就是警告美国人不要陷入党争。华盛顿(实际上也是汉密尔顿)表示,要“最郑重地反对政党精神所带来的有害效果”。

可是,在华盛顿的任期里,虽然没有政党政治之名,却开始有了党争之实。领头的,就是以上四位对政党政治严词警告的革命同志。

起因主要还是在于对国家前景的不同理念:究竟是要把美国建设成一个类似当时欧洲列强那样的、联邦权力集中、工商业发达的国家,还是一个充分保障州权民权,以农场、种植园为主的农业经济

汉密尔顿是前一派的领导人,虽然并没有正式结党,但是人们把他们称为联邦党。当时在国会的麦迪逊非常担心这一趋势,于是暗中拉拢与他理念相近的人士,并让杰斐逊担任领导人,结成“民主共和党”。

华盛顿虽然超脱于外,但是从政治理念上,他还是更接近于汉密尔顿的联邦党。这样,美国早期历史上的“两党制”就形成了。需要指出,这两党仍属于松散的政治联盟,并非现代政党。但是美国两党主导政治的格局就此开始。

1796年,华盛顿退休。在总统选举中,联邦党人亚当斯击败了民主共和党领导人杰斐逊。根据当时的选举制度,杰斐逊同意担任亚当斯的副总统。1800年,杰斐逊再度挑战亚当斯并且成功,民主共和党由此开始长期执政,联邦党从此式微,到1816年之后几乎不复存在。

这是美国政党历史的第一阶段。1796年和1800年的总统选举为美国政党政治与选举政治开创了良好的先例:杰斐逊1796年选举失败接受担任副总统的妥协结果;1800年政权在两党之间和平轮替。

此后的历史笔者在前文曾有所涉及。联邦党消失后,民主共和党内部出现了不同派系,反映在总统选举上,1824年选举成为美国历史上一次肮脏的争议选举。1828年,上一次选举中的受害者杰克逊卷土重来,当选总统。他身边和他的支持者很快形成了民主党。

这个杰克逊的民主党就是我们今天看到的民主党,日后产生了威尔逊、罗斯福、肯尼迪、克林顿的政党。这是美国甚至也是世界上第一个真正意义上的政党组织。1833年,杰克逊的反对者们组织了辉格党,联邦党人的一些残余势力也加入其中。从1833年到1856年,民主党与辉格党的对峙形成美国第二个两党制阶段。辉格党的主要主张是国会权力要高于总统,对外主张贸易保护。在这个时期,奴隶制的存废已经成为美国政治当中最重要的问题。两党内部却都无法对此达成一致。到1850年代后,辉格党因奴隶制问题而发生分裂,影响逐渐缩小,到1856年选举时正式崩盘。

此前的1854年,美国国内主张废除奴隶制的力量组成共和党。从辉格党中脱离出来的林肯代表共和党赢得1856年大选。共和党由此成为全国性政党,这是林肯的党,也是其后老罗斯福、里根和布什的政党。美国的两党政治由此进入第三阶段并持续至今。此后美国所有总统均出自民主、共和两党。


民主党是”左派””自由派”, 代表中产阶层和贫民阶层, 城市力量较强,主要支持者是工会和知识分子, 及社会边缘化势力如移民, 女权主义, 少数族群, 同性恋团体,等. 政治上, 民主党重视内政, 环境保护, 九 健康保险与社会福利, 教育等领域.
共和党是”右派””保守派”, 代表资产阶层和社会保守势力, 在乡村特别是南方力量强大. 主要支持者有宗教组织, 大企业, 退伍军人, 白人特别是男性白人是共和党最重要资源. 政策上, 共和党支持商界, 削减政府规模开支和福利计划, 但保证军力, 与民主党对立, 后者强调政府投资同时削减军队把钱用于国内. 共和党比较偏重外交, 特别是动用军力干涉国际事物, 是所谓”鹰派”, 与民主党”鸽派”相对.
共和党也称为老大党(GOP), 象征是大象,现任领袖是美国总统乔治.布什. 民主党象征是驴子,现任领袖是其全国委员会主席霍华德.丁.
当代的民主党与共和党在政治理念上是对立的.
民主党是”左派”, “自由派”. 共和党是”右派”, “保守派”.
对外政策上, 民主党强调外交, 共和党主张武力.
政府管理上, 民主党强调机构扩大, 共和党主张机构精简.
宗教文化问题上, 民主党强调多元化, 共和党主张保持基督教精神.
社会问题上, 民主党强调多元化, 共和党主张传统责任道德.
经济政策上, 民主党强调保护主义, 支持工会, 共和党主张自由贸易, 支持大公司.
人权问题上, 民主党支持坠胎和人体基因研究, 废除死刑, 共和党反对坠胎和人体基金研究, 支持死刑.


民主党是左翼政党,是改革性质浓厚点的政党,政策比较偏向中产阶级,重视福利和公平,推动改革。另外民主党是比较主张减少对外战争的,从伊拉克撤军这一条很受军人欢迎。

共和党是右翼政党,代表的是大资本家的利益,政策比较容易被利益集团所左右,是保守性政党。格外重视军力的发展,认为美国要去维护世界政治秩序和贸易秩序,讨伐全世界的疯子。所以共和党也会受到跨国利益集团、军事科技集团的拥护。

实际上民主、共和两党的左、右之分是被媒体夸大了。大多数政客,不论那个党,都会在基本盘稳定的情况下向中间靠。 在外交上,不论哪个党都会坚持一件事,那就是美国利益高于一切。这就是说,大家的对外政策其实没什么区别,比如反恐、打击朝鲜、颠覆非亲美政权、遏制中国……等等,手段虽略有差别,实质其实一样。

另外,无论哪个政党,其根本利益总是受到各个大的利益集团操控,比如军火集团想做大生意,那么,美国政府就要在各地“整事”,有战乱,军事集团才有生意做!美国在世界各地的各种活动,其实都是打着国家利益的旗号,体现着各个利益集团的利益。从本质上讲,美国的两党的根本利益是一致的。

科普百科-经济与政治-美前财长保尔森揭秘金融危机

芮成钢:尊敬的保尔森先生,很高兴您接受我们的采访。
保尔森先生:很高兴来这里。

 

芮成钢:我们知道,您将去参加博鳌亚洲论坛,可能这对您来说是首次。您对这次论坛有什么期待?您会如何参与到这次论坛中去?
保尔森:我很开心去参加博鳌亚洲论坛。我是博鳌亚洲论坛的理事,这是我在卸任财长之后接受的唯一的理事会职务。我对金融问题、经济改革问题、亚洲、中国等都非常感兴趣。当我看了参会代表名单后,我觉得这将是一次非常令人兴奋的论坛。

芮成钢:您曾说过,您会永远与应对处理金融危机联系在一起,与金融世界联系在一起,如果我们退回去,可能这个问题您已经回答过很多次了,如果您或您的同事当时做出了不同的决定,危机就不会发生或者程度会更轻些?
保尔森:你指的是我在美国政府的同事。有趣的是,我刚写完一本书《峭壁边缘》,这本书就谈到了这样的事情。尽管我认为我们犯了很大的错误,我们也做出了很多重大决定,这些决定大多数都是正确的。我可能是事后诸葛亮 。大家认为的那些错误实际上是不可避免的,因为在那时我们没有所需的权力。我确信我们是避免了金融崩溃,也就是说我们避免了一场灾难。

芮成钢:您认为这次危机更多地是政府造成的还是市场自身的原因?
保尔森:我想一定会有很多批判的声音。首先,关于政府的政策带来的不平衡,美国人储蓄太少,个人和国家都借债太多,住房政策过分刺激了房产市场,我们拥有房产的人数比例达到69%,这是不可持续的。我们的监管制度过时了,监管部门过时了,他们没有随着金融制度的成长而成长。当然就金融体系而言,金融产品过于复杂,不够透明,投资者、评级机构犯了很多错误。这些都是长期积累的结果,需要被纠正。

芮成钢:您提到了您犯的错误,在您的书《峭壁边缘》中提过的。大家对不救助雷曼兄弟有很大的争议,您认为不救雷曼个错误吗?
保尔森:不,因为我们没有从政府获得救雷曼的权力。我曾非常努力地去帮助雷曼,想阻止它的倒闭,有趣的是,在我们国家,在我给出这样的答案时,很多人都不相信,尽管伯南克、盖特纳和我都说了同样的话。我们没有法律权力和紧急救助权力来处理非银行机构,在破产程序之外清算他们。如果美联储或财政部有权保证偿还债务,或者注入资金,我们并不愿意去向国会要资产救助资金,所以当贝尔斯登需要救助时我们是幸运的,因为有摩根大通这样的买家,它可以和美联储一起,在困难时期保证交易进行。但是对于雷曼来说,我们没有这样的权力。

芮成钢:显然,当政府决定救助AIG时,情况发生了很大的变化。有些理论,甚至是阴谋论说,救AIG的原因是因为高盛。你怎么看这样的说法?
保尔森:我知道有这样的说法,但是我认为这些非常荒谬。当时的体系实际上已经处在崩溃边缘。如果AIG倒下的话,整个金融体系会随之崩溃,我们会经历经济灾难。幸运的是,雷曼是个征兆,当我们努力救助雷曼的那个周末,我们也在同时考虑救助AIG和美林。如果雷曼有买家的话,也许美林就没有买家了。对于AIG来说,控股公司有很严重的流动性的问题,但旗下的保险公司却有不同的信用评级,那些被认为是稳定的,而且资金充裕。所以美联储可以合法地借钱给这些机构,来阻止AIG的倒下。

芮成钢:所以可以说是一种阴谋论的论调?
保尔森:对我来说,当时作为美国的财长,我结束了和高盛的关系,卖掉了高盛的股票,我的责任是对美国人负责。我认为任何一个理性的、有知识的美国人和其它国家的人都不会希望AIG破产。事实上,如果当时我们再让任何一家主要的金融机构破产,我们将面临巨大的灾难。在雷曼倒下之后,如我所说,雷曼是个征兆,因为在雷曼倒下的两周内,有6个欧洲国家介入拯救他们的银行,所以我们为华盛顿互惠银行和美林表示担心,因为要在同一个周末被收购。但就在雷曼倒下、AIG被救之后,资本市场冻结了。当那些蓝筹公司遇到资金困难时,我知道我们到了崩溃边缘。因为如果整个体系崩溃了,会有更多的公司破产,全国范围内的工业公司将不会有能力获得发展所需要的短期资金,将无法支付他们的供应商、客户、员工,这将对经济造成重大影响,失业率将很容易达到大萧条时期的25%,将会有几千万人失去他们的积蓄,百万人失去工作,很多房屋被收回,这些都是我们已经避免了的。在美国我们有3.8万亿的货币市场基金。3000万美国人,一些中国投资者,还有其它国家的投资者,都在这个账户有投资,当他们要缩减投资时,我们有储备金,我们可以支撑保证他们的账户。这些货币市场基金在持续变化,所以我们必须介入保护这些基金。那是我们当时面临的状况,所以我们想的绝对不是说为某个银行好,我们是在努力阻止一场经济灾难的出现。

芮成钢:回过头看,也许在您的书里也提到了,我知道用事后诸葛亮说很容易,如果有一件或两件事情,让您重新做,您会做的不同吗?
保尔森:有各种各样的说法。其中有一点我在书中提到了,是这次危机来的速度,我们要在信息不完全的情况下快速做出决定。还有政府和市场之间的摩擦,因为危机很可能在最坏的时候出现,在距总统选举仅有几周的时间里,所以信息交流是个大挑战。比如说,我说的我们是在边缘的那一周,我们作出决定,认为需要去找国会,去获得从未有过的处理危机的权力。那是个危险的时期,因为我们一旦去国会,我们就需要得到相应的权力,如果说我们需要却没有得到,那是真的不幸。所以我们给国会递上去三页的提纲,我们需要政府的帮助。我当时应该开个媒体见面会,告诉大家这是谈判的起点。我没有这么做。另一个例子是,我始终没有机会让美国人知道,这不是和华尔街、银行有关,这是和所有美国人有关。如果说体系崩溃了,付出最大代价的将是美国人。那是非常困难和具有挑战性的,像我跟你说的那样,当我们去国会时,市场冻结了。伯南克和我知道,我们很肯定,再过几周,几个月,美国经济将放缓,失业率将上升,而且事实上已经发生了。国会的人还不了解情况。所以我们必须让他们相信我们,他们所作的将是在避免一场灾难。他们给了我们需要的权力,我们行使了这些权力,我们阻止了一场灾难的发生。当经济变糟时,国会和美国人说,我们赋予了你们权力,你们救助了银行,看看发生了什么,失业率上升了,但他们没有看到更没有感谢我们阻止了一场他们从未见过的灾难。

芮成钢:说到去国会,你是不是在议长佩洛西下跪,促成了财政救助方案的通过?
保尔森:我在书中提到的一段内容是,我们去国会之后,麦凯恩说他要去华盛顿帮忙。那是非常紧张的时期,如果当时任何一个总统候选人,无论是奥巴马还是麦凯恩反对银行救助资金,我们将不会得到那些钱,我们将会处于无保护状态。麦凯恩曾让我失眠了好多次,我和他进行过多次困难的、氛围紧张的谈话,但令我高兴的是,他最终没有把问题政治化,他支持救助资金。他回来之后我们在内阁室开了一次非比寻常的会议,与总统、副总统和两边的领导,结果是大家惊人的一致。我们当时迫切地需要那些权力,在国会取得了进展,最后我们取得了一致。之后,人们聚集在内阁室之外,民主党把奥巴马留在了罗斯福室,在西翼,就在内阁室旁边,我担心他们出去会说些有煽动性的话,我就突然出现在罗斯福室,当他们见到我时,他们不太高兴。所以我想带来些快乐,让大家笑,我跪了下来。演讲者说不知道我原来是个天主教徒。我说请不要毁了这个协议,她说他们不会。那确实是紧张的时刻。

芮成钢:这也解释了要把正确的经济学观点向政客讲清楚,并防止他们干预不是件容易的事情?
保尔森:恩,你说的对,会有冲突。有两次难忘的经历,一次是获得无限权力,从政治上讲,我这样说是不对的,所以我换个说法,没有经过界定的权力,来处理房地美房利美的事情;另一次是让国会在体系崩溃之前通过救助资金的方案。但是,国会的人还是必须一边考虑选举人,因为那是他们的工作,一边去做那些该做的事情。任何一个投票通过救助资金的人,知道他们在做一个从政治角度来说不受欢迎的投票。

芮成钢:提到高盛还有林林总总的阴谋论调,不仅是媒体,还有一些知名经济学家,例如几周前刚刚接受我采访的斯蒂格利茨就提到,普通人都认为用纳税人的钱去拯救银行是不合道德的,特别是这些银行还用救市资金去支付奖金和高薪。他说也许银行家不会觉得这不值得大惊小怪。
保尔森:我必须说,或许我是有偏见的,在美国有不同的观点。我还必须说,在危机中,我没发现太多专家或经济学家有什么帮助,他们有他们的模型和理论,但我认为让我们成功的是,我在书中提到的一个主题,我们是一个团队,我们有伯南克,世界级的经济学家,明白经济的历史规律。我们有盖特纳,终身投入在公共事业当中,在财政部工作。我个人呢,有在金融机构的工作经验,知道如何做决定,如何快速做决定,我本身是个有决断力的人。我认为这样的合作,还是那句话,我们需要做的是,阻止可能出现的系统崩溃。我相信历史会证明,在我在职时我们给银行的钱,会带来收益的,系统并没有崩溃。我相信不会有经济学家认为,应该让系统崩溃,或者说我们不该给银行注资。还有一件没有被充分理解但应该被理解的事情是,当我们快速行动给银行注资时,我认为系统是严重资本不足,所以我设计了一个方案,会很有吸引力,会让大多数银行都愿意接受,将有3000个银行接受注资。当银行申请救助时,很快有一些批判开始影响这项计划,说你们应该有所限制,应该限制给银行家的钱,应该让银行开始放贷,但并没有说我们怎么能使得银行放贷。当国会开始处理这件事情时,国会立刻改变了计划。所以有很多申请了救助的银行,撤回了申请,其它一些已经接受了救助的,急于偿还这些钱。所以说,这项方案很有效,确实阻止了系统崩溃,钱回到了纳税人手中,而且带来了利润。但如果有几千家银行接受了注资,这项计划将会更成功,而且这将是银行愿意放贷的强大基础。所以与其说我们需要让银行开始放贷,不如让银行接受注资。这样的话,银行将会有更多的放贷,会让经济好转的更快。所以,我们做了该做的决定,我认为是正确的决定,起了效果,许多经济学家今天都支持我们当时的政策,当然也有一些不支持。

芮成钢:这让我想起黑格将军曾经对我说的话,在一次与基辛格先生的争论中,基辛格先生说,如果你知道我所知道的,你就会完全同意我的观点。所以你一定认为有一些经济学家,像斯蒂格利茨,你知道很多他们不知道的内容,他们不了解的信息,如果他们知道的话……
保尔森:他是一位卓越的经济学家。实际上今天绝大多数的经济学家都认为应该让银行资本金充足,这是我们需要做的。同时,我认为任何人站在我的立场上,发现这些大型金融机构资金如此集中,都会意识到危险。1990年,美国排名前十的金融机构所占金融资产为总量的10%,今天已经达到60%。例如AIG为数百亿美元的养老金提供担保,还有数以亿计的保单。一旦你注意到这个公司的规模和其千丝万缕的联系,并理解到一个金融机构的崩溃,会对修补整个金融系统造成何困难,那么我觉得任何理性的人如果了解这些因素都不会反对我们的做法。所以,对我的批评主要是“救市”、“挽救行动”或是不作为。比如为何不救雷曼兄弟,为何不救其他机构。不过我认为都是缺乏理由的,我去华盛顿不是为了去救银行而是因为深谙不救的后果。伯南克他精通经济学史,算是美国大萧条时期经济学相关理论的权威。所以他知道在这个资金以光速流动的时代,金融系统互相依存,欧洲美国发生了种种状况。他了解一旦系统崩溃,想重新构建将异常艰难。而我从实践的角度理解其后果的灾难性。所以我们没有花很多时间争论如何行动,而是讨论采取何种有效的手段。

芮成钢:您卸任之后那些银行高管们的奖金和高薪到底是怎么回事?
保尔森:我还在华尔街工作的时候就觉得(高管)薪酬水平有些荒唐。所以想让我去捍卫什么奖金、高薪是不可能的。经济不景气的时候,例如我个人过去三年就没拿过现金薪酬,而且从未出于私利抛售个人持有的股份。我持有股票是觉得有信心。拯救金融系统,我们面临两个选择,其一是延续过去的做法,或是英国的做法,也就是当某机构濒临崩溃,将其国有化;其二是未雨绸缪,从整个系统入手,防范于未然,避免重大倒闭事件。至于薪酬的问题,我认为从政府政策的角度来看,关键在与薪酬的结构必须符合公司和股东的利益、管理层的利益、银行的利益。这方面已经有了长足的进步,也是我在担任财长的时候反复强调的问题。实际上我在书中也写到这一点。11月的时候,我和四大监管机构合作出台了薪酬指南,要求薪酬结构不得诱发过度承担风险。

芮成钢:所以您认为华尔街的高管们薪酬一直过高?而且用纳税人的钱去支付奖金是不道德的?
保尔森:是的。我想说两点。首先,银行家的高薪结构让我解释起来,我看即使我做教师的妻子都不一定比某位火星来客理解的更清楚。至于救市的行动是否不受欢迎,当然,我想在美国我们认为承担风险的人也要为损失负责,私营部门也不例外。所以任何救市措施都会遭到非议,特别是涉及到银行业的时候。因为银行业的问题是他们自己造成的。由于利率降低,银行业的利润恢复高于总体经济水平。那么我对这个问题开什么处方呢,例如在监管改革方面?雷曼兄弟倒台前两个月,我在一次演讲中就呼吁,美国需要清算机构和紧急权力,这样就不存在某个机构大到“不能倒台”的问题。任何类型的机构濒临崩溃,政府应动用紧急权力将其清算而不走破产程序,以防影响其他经济部门。这样就不会动用纳税人的钱来支撑这些机构。美国人气愤的不仅仅是高管薪酬,而是这些金融机构令政府不得不去救助,去挽救。

芮成钢:所以一方面,您认为华尔街高管们的薪酬结构的确有问题;另一方面,您也认为向普通人去解释他们的薪酬结构是很难的事情,所以有时高管的薪酬就成了替罪羊。
保尔森:我想说两点。银行高管的薪酬涉及两个问题。其一是薪酬的形式,其二是薪酬的数额。从公共政策的角度看,其形式和背后结构的是问题的关键。如果交易员的薪酬和奖金与其创造的利润按照一定公式挂钩,那么风险就不可避免。所以我认为从公共政策的角度看,合理的薪酬结构是关键。这应是政府要下大力气的工作。我认为,公众很难理解薪酬的高昂数额,特别是在大规模的非常规救助行动之后,整个金融体系,包括那些濒临倒闭的机构,都从纳税人采取的行动中受益。

芮成钢:所以应该像影星、运动员一样。影星、运动员薪水也很高,但是不会制造灾难,无论他们是否和政府合作?
保尔森:您说得对。毫无疑问,电影明星和演员中至少一部分收入很高,还有运动员等。但是针对金融高管,必须有稳健的监管。监管当局必须关注的一个问题就是薪酬的结构,一定要防止其引发如过度承担风险在内的各种问题。我认为监管当局还应加强资本金、流动性等要求。这方面有很多工作要做。我认为美国应设立风险监管机构,享有搜集信息、审查所有类型的金融机构、干预和限制威胁整个金融系统的业务行为的权力。目前的监管体制只能照顾到一棵棵树木,我们需要的是能覆盖整片森林的监管体制。我想应该能通过相应的立法。

芮成钢:再谈谈高盛。现在一条重大新闻就是欧洲主权债务危机,特别是希腊。媒体称高盛也参与其中。有人认为其中有利害冲突。一方面,高盛给政府提供建议,另一方面,高盛通过抛售政府债券获益。您对这些报道有何回应?
保尔森:我对这个问题无法发表评论。希腊政府有权对其发表意见。我想他们已经做出回应。

芮成钢:如果您仍是高盛的董事长,是否会有不同做法?
保尔森:我已经不是主席的身份,无法对任何一个金融机构的事务发表意见。

芮成钢:高盛有员工服务社会的传统吗?从鲁宾到佐利克再到您本人?
保尔森:我认为政府工作是崇高的事业,可以为国家服务,也是难得的机会。我在书中写到,当小布什政府聘请我出任财长的时候,我开始没有答应,家人也不支持。所以我拒绝了两次。但后来想了想,虽然这些年事业有成,但我不想等到退休之后才后悔当初没有抓住为国家服务的机会。我在高盛供职时值得自豪的不仅是履新财长一职,还骄傲于之前高盛的员工所从事的各种高尚事业,比如对大学、慈善机构、各种事业的支持。我认为这是很多杰出的企业希望员工做到的。

芮成钢:曾有人说您是美国历史上最强势的财长吗?就您的影响而言?
保尔森:历史就交给史学家们去撰写吧!我和历史缺乏距离感。我非常赞赏和小布什总统的良好关系。我书中专门提到他,但某些对总统有偏见的美国读者也许不赞同的是,我认为小布什总统对市场有丰富的知识。他也不希望采取救市措施去挽救诸如房地美和房利美等华尔街机构。但是他支持我做出所有那些艰难的政治抉择。他曾对我说“这些决定不会容易,政治方面会导致批评,肯定看上去糟糕。但是我们必须采取必要行动保护美国人民和世界人民的工作。”

芮成钢:我们知道您在中国有位老朋友,就是中国人民银行的周小川先生。二位过去商讨过的一个问题就是人民币汇率问题。现在好像中国离被贴上“汇率操纵国”标签越来越近了。您认为中国是汇率操纵国吗?
保尔森:首先我想说,货币问题在中国和美国媒体都十分关注,它仅是中美关系中的一部分,特别是经济关系。所以每次我都会向中美双方人士解释,危急中或危机后,美国人民可能遇到的最糟糕情况就是中国经济停止增长。中国经济增长对美国有益,倘若停止,美国会受害。反之亦然,美国经济景气中国受益,美国经济滑坡中国遭殃。就货币问题,当我还是财长时,我在公开和私人场合都表示货币(汇率)灵活性是关键。人民币(汇率)发展和改革对中国和世界有益,有益于中国向更多依赖消费的经济模式转变,更加和谐、更加均衡、普遍繁荣,给予决策者更多控制通胀的工具。这点很重要。您必须了解,在美国,货币是一种符号,是不断改革的符号。所以美国的政治家和其他人士期望看到货币(汇率)的变化。我总是强调,中国必须按照最符合本国利益的方式采取行动。我认为(汇率改革)符合中国的利益。有时我把中美的分歧描述为时机问题,而不是方向问题。我还担任财长时,双方有了进展我都会提及,例如很多重要经济领域都有进展,除了货币(汇率)灵活性,还有食品、产品安全,双边合作等。危机当中,中国是美国的良好伙伴,给予大力支持。这些进展和努力避免了美国国会的敌对情绪,特别是在大选年将近时,促成我成功阻止国会通过一些列惩罚性的立法。

芮成钢:找替罪羊于事无补。但中国人会把目前的中美关系与上世纪八十年代的美日关系做比较,比如《广场协定》的签署。日元大幅升值,导致日本经济数十年停滞不前。日本经济衰退和目前情况有可比性吗?
保尔森:我认为完全不同。不同的国家,不同的经济状况。这种论调我有耳闻。我不是说中国今天已经准备好完全让市场确定货币(汇率)。我的意思是,中国是个不错的特例。但是历史上从未有哪个国家如此庞大、经济地位如此重要、如此紧密地融入全球商品服务贸易体系,却同时没有市场主导的货币(汇率),而其他主要经济体都是市场主导。所以我认为关键的问题,这点中国朋友应该赞同,就是不断内部改革。中国“奇迹”,包括改革、资本市场、银行业等,是全系统的。这种势头必须保持下去,而其重要指标之一就是货币(汇率)的水平。灵活的货币(汇率)符合中国的国家利益。这是我的观点。这个方面中日没有可比性。

来自:芮成钢的博客

科普百科-经济与政治-美联储救两房和AIG但不救雷曼的原因

拯救贝尔斯登(3月16日被收购)


经常以“国际成熟资本市场”为自己学习榜样、经常口出“绝不政策救市”的国内资本市场管理层,很可能忽略掉一个极其重要的事实是:从20世纪以来,全球最大最先进的资本市场所在地美国,拥有一个越来越积极主动救市的“最后拯救者”———美联储。

贝尔斯登危机爆发后,美联储紧急通过一向有“救市传统”的摩根大通银行之手,将一笔为期28天的抵押担保贷款注入贝尔斯登,使后者的流动性不至于枯竭。美国财长鲍尔森也在接受电视采访时表达了要维护华尔街“第一块多米诺骨牌”的意思。对此,人们不禁要问两个问题:第一,为什么美联储必须拯救贝尔斯登?第二,这种拯救对中国资本市场有何启示?

贝尔斯登是一家什么机构?简单地说,它是一家中介类型的交易商,为不计其数的对冲基金以及小券商提供担保和清算服务。我们最近频频听到的“次级抵押贷款支持证券”及其衍生品,就必须由贝尔斯登这样的交易商提供服务。

在华尔街,类似贝尔斯登这样的角色,被认为是“秘密的最后守护者”(贝尔斯登是其中较大的),因为数以千计的次级债衍生品在市场上交易,甚至美联储都不确切知道总交易规模有多大,但贝尔斯登却很可能知道,因为不论你怎样交易,最后总要经过清算服务这一环节。1998年发生危机的长期资本管理公司(LTCM)的指定清算交易商就是贝尔斯登,当时,LTCM以40亿美元的自有资金,运作着超过1000亿美元的债券和证券交易,更通过金融衍生品合约进行着超过12000亿美元的全球金融交易,华尔街上的每家机构几乎都有资金卷入,但最终损失最小的就是贝尔斯登,因为它知道“底牌”。

今天,贝尔斯登终于自尝不顾风险放大交易的恶果,导致资金链断裂,不过,贝尔斯登却“不能”破产清算,甚至美联储都直接拿出现金为它输血。这其中的奥妙不在于有些人士评论的“太大而不能倒闭”,而在于一旦贝尔斯登这个清算商的资产负债表曝光,天文数字的交易数据将被华尔街上各神通广大的机构获取,一个本能的反应就是大家赶紧抛空所有这些与贝尔斯登相关的岌岌可危的交易合约,其情景类似于银行发生了“挤兑”。更恐怖的是,一旦这个趋势形成,将发生多米诺骨牌般的连锁反应,那些原本就价格暴跌的抵押品将跌无可跌,而相关的参与机构的信用评级也将节节败退,因为“交易对手”不复存在而让大量本来经过对冲避险的交易全部成为“裸露交易”,到时候恐怕华尔街上的投行倒掉一半都有可能。

近100年来,美联储越来越走向前台,甚至在很多“溺爱华尔街坏孩子”的质疑声中直接出手拯救资本市场危机,其深刻的背景是全球金融衍生品交易的数量已经是一个天文数字,远远不是一个金融机构甚至是整条华尔街可以承受的。

在当今美国面临通胀、经济停滞、美元持续下跌的情况下,依然把市场流动性、把维护“第一块多米诺骨牌”为所有任务的第一环节,固然是会留下很多后遗症的,但也是无奈中的最佳选择。当今的全球资本市场已经陷入了事实上的流动性危机之中,华尔街正源源不断地将原先散布在世界各地的现金流回收去补洞,这个洞如果能在美联储的帮助下补掉,市场也要至少几个月休养生息,如果补不掉,那么全球金融危机已无可避免。

拯救两房(9月7日宣布拯救)


美国的次贷危机在肆虐全球的同时,也给美国的两大按揭机构房利美(联邦国民抵押贷款协会)和房地美(联邦住宅抵押贷款公司)带来重创。过去一年里,两家公司股价惨跌,融资成本不断上升,一度濒临破产。由于担心两家公司破产对美国金融市场和整体经济造成严重冲击,9月7日,美国财政部长保尔森宣布:政府将接管美国这两家最大的房贷机构,这意味着,上述两家目前由私人控股的上市公司将被“国有化”。这也将是美国有史以来规模最大的金融救助案例。

但是格林斯潘以及一些经济学家对此持完全相反的意见,其理由是应按市场化的原则处理“两房”危机,任其自生自灭,破产倒闭或者采取收购兼并等市场化运作方案。那么,作为世界上最强大的自由市场经济国家,为什么美国当局非要力保“两房”而置市场化原则于不顾呢?笔者分析主要有以下三个原因:

第一,维护美国政府的信誉和形象。房利美和房地美作为收购银行等抵押贷款的主要金融机构,其业务量占全美5万多亿美元抵押贷款的一半左右,而且“两房”用于集资所发行的债券对美国十分重要,几乎与美国国债的信誉相当,甚至全球各国央行也大量持有“两房”发行的债券。如果美国任由“两房”坍塌,会使世界对美国的信誉彻底失去信心。

第二,“两房”的盈利能力能为美国赚取巨额利润。除了其全球融资功能支持政策的正常利益之外,通过为“两房”收拾市场残局,将以极低的价格购入大量抵押贷款证券,特别是次级抵押贷款证券最终会流入“两房”及其相关企业,未来房产市场回暖之后获利将十分丰厚。美国任何当局都不会放任“两房”被市场力量兼并重组,这样政府将失去实施隐形政策的渠道和能力,对美国赤字政策将形成巨大挑战,而且也会危及“两房”传统的优势地位和筹资功能。

第三,维护市场稳定,防范金融危机。“两房”的规模是如此之庞大,它们与金融系统的关系又如此之紧密和错综复杂,以致其中任何一家一旦失败,都将导致美国以及全球金融市场的大骚动和混乱,美国和世界很多地方的消费者的住房贷款、汽车贷款以及消费等能力将会受到明显影响。挽救“两房”是为了稳定金融市场、保证提供房屋贷款、保护纳税人。

在权衡利弊之后,美国政府终于出手托管“两房”。这充分说明,世界上没有完全自由的市场经济。一般来说,当一个市场需要运用政府的行政资源来强行扭转市场运行轨迹的时候,总是这个市场处于严重的危机、靠市场自身力量已经很难疗救的时候,因此政府必须及时有效地进行干预。在当前全球金融动荡和自身金融市场制度不健全的内外环境条件下,新兴市场经济国家尤其要重视对金融市场安全稳定运行监控、干预和救助。

(作者为本报特约评论员、同济大学经济与管理学院教授)

来自人民日报 海外版

不救雷曼(9月15号破产)


雷曼的财务报表显示,其财务杠杆率(总资产/总股东权益) 仍然在20倍以上,2008年2月底达到了31.7倍的高峰。以30倍杠杆为例。只要资产价格上涨1%,就相当于赚到30%的收益,而一旦价格下跌导致亏损3.3%。次贷危机中,美国住房市场价格已经下降了20%,持有大量相关债券的雷曼,最终倒下了!

雷曼公司是私营企业。美国政府动用纳税人的钱对股市、对贝尔斯登公司实施救助,对“两房”托底等,已经引发了国会中不少议员的强烈不满,更是招致民众的普遍抨击。另外,从目前的实际情况来看,雷曼兄弟绝不是最后一家需要救助的金融机构。如果再出手,很有可能把美国政府财政也“拉”下水。如果要防止金融危机进一步扩大,挽救已经陷入困境的金融企业,政府就不能不考虑市场化之路。美林证券刘二飞说:“现在看来,美国政府已经不愿自己出大量资金(救市场),美国政府现在政策的主要导向,第一是鼓励企业联合起来自自救援。

雷曼伸手太过,势头咄咄逼人,导致华尔街老大老二们的愤怒,联手做了雷曼。……大资本,一样江湖险恶,一山不容二虎,尤其不容想篡位富有野心的雷曼——这是逻辑使然,不需要特别的细节证明。

雷曼兄弟公司虽然名列美国五大投资银行的“五虎将”之列,但与高盛公司和摩根斯坦利公司比起来,它与美联储和美国政府的渊源的关系,一个是嫡系部队,一个是杂牌军。大家知道,控制美国经济命脉的是美联储,不是美国政府。

高盛次贷产品有在AIG买保险,美林,摩根斯坦利有政府房利美,房贷美担保,而雷曼没有买任何保险。

当保尔森跪求国会用纳税人的钱救助美国金融机构,危胁不救明天将没美国时,国会为了做给纳税人看,只能放弃雷曼,才能平民愤。

令美国政府没有想到的是,雷曼兄弟公司的倒闭的消息,会引起全世界资本市场的这么大的震动,世界各大股市出现暴跌。引发美联储和美国政府对资本市场的脆弱性的恐惧,失去了美国一贯崇尚的对自由市场经济的自我调节能力的信心,立即对其他三家也处于危机之中的高盛公司、美林公司、摩根斯坦利公司,进行全面的大手术,以避免更大的资本市场的大动荡的出现。

雷曼就只有被破产了,造成国外投资者损失惨重,引发股市血流成河,从一万五千点直下六千多点,无人幸免为难。

——————————-
雷曼垮台一直以来被视为引发了多米诺效应,导致很多其他公司纷纷崩溃:美林公司(Merrill Lynch)被仓促出售给美国银行(Bank of America);美国国际集团(American International Group,简称AIG)获得救援;货币市场基金的资产净值跌破一美元;高盛(Goldman Sachs)和摩根士丹利(Morgan Stanley)几近垮台,变为银行控股公司;政府决定执行7000亿美元的问题资产救助计划(Troubled Asset Relief Program,简称TARP)以帮助整个银行业脱离困境。
不救雷曼的决定曾被认为是错误的,而且是相当糟糕的错误。时任法国财政部长的克里斯蒂娜·拉加德(Christine Lagarde)称,这是一个“可怕的”决定。
没人说过雷曼值得挽救。但提出的观点却是,如果雷曼获救,也许危机就不会那么严重。雷曼的破产为市场带来了严重的不安情绪,投资者对于政府的作用感到迷茫,不知政府是否在插手干预,决定不同机构的存亡。政府此前救助了贝尔斯登(Bear Stearns),然后对房利美(Fannie Mae)和房地美(Freddie Mac)进行了国有化,仅留下雷曼等死,却回过头又救助了AIG。
当时美国财长小亨利·M·保尔森(Henry M. Paulson Jr.)曾暗示,政府别无选择。由于没有买家愿意出手相助,政府缺乏合适手段接管雷曼。英国政府在最后一刻叫停了巴克莱银行的收购计划。回头来看,这确实是个英明的决定。
保尔森可能是正确的:他没有手段。但这并未阻止政府巧妙布局,救助AIG,然后再使用强力手段——如强制美国银行完成对美林的收购,同时避免向持股人透露美林问题的严重性——若非国家身陷危机,如此下策会被视为良心的缺失。
当然,政府虽然放言进行帮助,但却不太情愿参与救助雷曼。同一时期的证据非常有力:“看起来保尔森将亲临纽约来解决雷曼这个乱摊子……想象不出什么样的局面需要政府进行注资……拭目以待吧,”保尔森的幕僚长吉姆·威尔金森(Jim Wilkinson)在 9月12日给一个招聘人员的邮件中写到。
两天后,他向摩根大通(JPMorgan Chase)全球资产管理部门的负责人杰斯·斯塔利(Jes Staley)又发了一封邮件:“政府不可能出钱……我正在写政府关于有序退出的表态口径……刚和白宫通话,政府部门统一意见了,不出钱。保尔森是打死也不会让步了。”
如果当时政府挺身而出,接下来又会发生什么呢?
这就是假想游戏开始的地方。但我们完全可以做出一些非常合理的设想。救助雷曼会一石激起千层浪。我们经常忘记,在雷曼破产之后,主流观点认为这是件好事。《纽约时报》(The New York Times)刊登社论:“看到财政部和美联储(Federal Reserve)会坐视雷曼兄弟崩溃,让人有种奇怪的欣慰感。”(《华尔街日报》[The Wall Street Journal]也持类似观点。)
同样值得注意的是,华尔街的多数人相信雷曼兄弟的失败不会危及系统。“我认为市场能承受雷曼的解散,但在本周内需要有人给美林出个价,”斯塔利在周末给威尔金森的邮件里写到。
真相是,在童话版的救助雷曼故事里,作为多米诺牌阵的下一位,AIG会摔得更惨。如果说围绕着救助雷曼的政治斗争是激烈的,那么围绕着救助AIG的政治斗争只会更激烈。而且和雷曼解体相比,AIG垮台对系统造成的风险远超雷曼几个数量级。
如果美联储不顾政治考量,在那一刻出手帮助AIG,那么财政部就益发难以取得足够的国会支持以通过TARP。至少,该计划的规模和覆盖面会大打折扣。请记住,尽管当时股市和经济都开始了自由落体运动,国会起初仍驳回了TARP,后来市场似乎坠入死亡漩涡,TARP才时来运转,得以通过。
正如拉姆·伊曼纽尔(Rahm Emanuel)2008年所言:“你绝不想浪费一个严重的危机。我的意思是,这是个机会,去做你以前无法做到的事。”
雷曼的垮台使得政府能够给予经济更多支持。历史学家尼尔·弗格森(Niall Ferguson)在危机结束后第二年在《金融时报》的一篇评论文章中这样写道:“雷曼就像是伏尔泰名著中那位被处死的英国海军上将,它必须牺牲才能使其它银行认识到注入公共资本的必要性,也才能说服立法机构批准注资计划。”
乔治·W·布什总统(George W. Bush)的经济顾问艾德·拉齐尔(Ed Lazear)却告诉芝加哥大学布斯商学院(the University of Chicago Booth School of Business)的学生们,把危机比为一系列多米诺骨牌效应可能是错误的比喻。
“根据多米诺理论里的传染性,一个多米诺牌倒下,并碰倒其他多米诺牌,它们全部倒下,你手里就是一个烂摊子,”他说道。“在我们救助贝尔斯登的时候,就是这么想的。而且看起来局势就会那样发展下去。不幸的是,那并不是一个多米诺模式,而是爆米花模式。”
“做爆米花的时候,你把锅加热,玉米粒受热后爆开了。把第一个爆开的玉米粒从锅里拿走没有任何用。其他玉米粒仍在受热,只要热量在,无论怎样它们都会爆开,”他解释道。
当然,如果专栏还有足够的地方的话,还有一个问题就是,如果政府没有救助贝尔斯登,又会怎样呢……
—————————

雷曼的悲剧在于它的按揭产品与不动产投资太过冒进,而且其进行投资的资金使用了大量杠杆工具,使其背负的负债是自身价值的四十余倍(导致破产清算时累计上千亿美元天量负债)。也就是说雷曼的财务状况是最差的,也是最先被媒体与投资者发现的。

综上所述,百年雷曼的逝去,是多方面因素共同作用的结果。如果从外部找原因,可以追溯到克林顿签署的《金融服务现代化法案》(这个法案的签署是大型金融并购和危险性极大的杠杆交易高危玩法的源头),但主要就是乔治布什总统的心腹——美国财长保尔森的见死不救。如果从自身找原因那就是福尔德高压独裁 疯狂揽入不动产按揭工具的愚蠢大跃进领航方式最终撞向冰山,但 但 但 当时所有投行都在玩高收益按揭产品,你不玩就赚不到钱! 纵使雷曼交易团队与高管中像是加德沃德、亚历克斯等最聪明的头脑早在灾难发生两年前向雷曼总裁福尔德进言危险并悲壮的以辞职相逼但依然没有改变福尔德的顽固(毕竟美国房地产市场以及其衍生金融产品在大灾前表现太优异,而且让穷人有房子住降低首付款放宽按揭条件甚至是自克林顿开始推动的类似于国策的趋势,历史成绩说话加国家政策撑腰的好生意,傻瓜才不玩)

作者:张數
链接:https://www.zhihu.com/question/22102220/answer/149323658

—————————

拯救AIG(9月16日放贷款)

本文刊于2008年9月18日《21世纪经济报道》3版

雷曼的客户都是机构投资者,而AIG除了与大量金融机构交易外,与普通老百姓的生活关系也非常密切,美联储会让雷曼倒,而不会允许AIG倒。

特派记者 吴晓鹏 纽约报道

在拒绝救助投资银行雷曼兄弟两天之后,美国政府在当地时间9月16日向全美最大的保险公司美国国际集团(AIG)伸出了援助之手,提供850亿美元的紧急贷款,防止AIG破产给美国经济带来系统性风险。

美联储当地时间周二晚间宣布,将向AIG提供最高达850亿美元的两年期贷款,并获得该公司79.9%的股权。AIG由此成为有史以来美国政府救助的最大一家私人企业。

美联储在其网站的一份声明中说,AIG“无序倒闭”将会加剧金融市场已经“相当严重”的脆弱程度,还将导致借款成本大幅上升,家庭财富减少并“严重削弱”美国经济的表现。

美国财政部长保尔森随即发表声明支持该项交易,他认为这将“减轻更广泛市场混乱的产生,同时保护纳税人的利益”。

保尔森说,财政部和美联储以及美国证券交易委员会等监管部门一直在紧密合作,试图稳定金融市场,并极力减少金融市场的动荡对经济的破坏。

纽约一家对冲基金的衍生品交易主管Chang Jinpeng周一在接受记者采访时就表示,雷曼的客户都是机构投资者,而AIG除了与大量金融机构交易外,与普通老百姓的生活关系也非常密切,美联储会让雷曼倒,而不会允许AIG倒。

AIG在全球130多个国家开展寿险、财险、事故险以及飞机租赁等业务,资产超过1万亿美元,拥有雇员11万余名。

苏格兰皇家银行在一份报告中表示,AIG若走向破产,将危及全球更多的金融机构,造成约1800亿美元损失。

周一晚些时候,美国三大信用评级机构一致下调了AIG的债务信用级别,使得该公司的财务状况进一步恶化。

面临巨额亏损的AIG原本急需大量的资本金注入。市场对于该公司可能破产的担忧导致其周一股价下跌61%,同时拖累道琼斯指数下跌超过500点。周二再跌31%,在连续三个交易日中跌幅达79%。

美联储称,这笔贷款“将帮助AIG按期履行债务义务”,同时,AIG将以有序的方式出售一部分业务资产,以尽可能减少影响。

AIG将向联储支付的利率为较三个月伦敦银行间拆放款利率(LIBOR)高出8.5个百分点,这让当前利率达到约11.4%。这将会鼓励AIG展开大规模资产出售,以快速偿还贷款。

与此同时,为避免让股东从中获利,美国政府还将有权否决支付给优先股和普通股股东的分红。

美联储公开市场委员会(FOMC)在当天的会议上决定,将联邦基金利率保持在2%的原有水平

高盛在周二晚间发送给记者的一份研究报告中说,如果考虑到当天晚上AIG的行动,理解FOMC为什么不降息就容易很多了。

“美联储的主要官员都知道这项交易正在进行之中,他们或许感觉到如果能让AIG不倒闭,既能化解市场的系统性风险,同时还避免了降息。”

高盛执行董事苏晖对本报记者说,AIG从事的传统保险业务一直都是中等的成长型业务,但最近几年公司为了追求成长性,为大量的金融产品提供保险,包括风险极高的CDO和次级债产品。这些金融产品造成AIG在过去三个季度共亏损了180亿美元。

她说,AIG接下来可能会出售一些传统的保险业务给其他保险公司,如房屋保险或者飞机租赁等。

websocket协议-细说WebSocket – java篇

tcp/udp 是协议,而 socket 是实现 该协议的 接口。所以 网络编程 就是 socket 编程。因为 http 和 websocket 都是 http 80 端口 tcp 协议。所以 web 服务器 肯定要实现 ,socket 区分 http 字节流还是  websocket 字节流。 这个 暂时 没有深入,有机会 去研究  web 开源 服务器 ,看看 如何 做到 区分 。


文章待整理

http://www.111cn.net/wy/html5/69508.htm (手动解析 websocket   )

https://www.cnblogs.com/jingmoxukong/p/7755643.html (框架解析websocket)


关于网络编程 :

百度搜 tcp ip 网络编程

【Java TCP/IP Socket】Socket编程大合集
https://blog.csdn.net/ns_code/article/details/17526127

怎样算得上熟悉 TCP/IP 协议编程?
链接:https://www.zhihu.com/question/20795067/answer/16233370

常见的三个网络协议:NetBEUI、IPX/SPX、TCP/IP
http://network.51cto.com/art/200701/38792.htm

三大协议:NetBEUI、IPX/SPX 和TCP/IP
https://searchnetworking.techtarget.com.cn/12-15241/

NetBIOS
https://zh.wikipedia.org/wiki/NetBIOS

Internet协议
https://baike.baidu.com/item/Internet%E5%8D%8F%E8%AE%AE/11049108

网络拓扑 锁定
https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E6%8B%93%E6%89%91/4804125?fr=aladdin

链路层包括 物理链路层 和 数据链路层

websocket协议-细说WebSocket – php篇

下面我画了一个图演示 client 和 server 之间建立 websocket 连接时握手部分,这个部分在 node 中可以十分轻松的完成,因为 node 提供的 net 模块已经对 socket 套接字做了封装处理,开发者使用的时候只需要考虑数据的交互而不用处理连接的建立。而 php 没有,从 socket 的连接、建立、绑定、监听等,这些都需要我们自己去操作,所以有必要拿出来再说一说。

   +--------+    1.发送Sec-WebSocket-Key        +---------+
    |        | --------------------------------> |        |
    |        |    2.加密返回Sec-WebSocket-Accept  |        |
    | client | <-------------------------------- | server |
    |        |    3.本地校验                      |        |
    |        | --------------------------------> |        |
    +--------+                                   +--------+

看了我写的上一篇文章的同学应该是对上图有了比较全面的理解。① 和 ② 实际上就是一个 HTTP 的请求和响应,只不过我们在处理的过程中我们拿到的是没有经过解析的字符串。如:

GET /chat HTTP/1.1
Host: server.example.com
Origin: http://example.com

我们往常看到的请求是这个样子,当这东西到了服务器端,我们可以通过一些代码库直接拿到这些信息。

一、php 中处理 websocket

WebSocket 连接是由客户端主动发起的,所以一切要从客户端出发。第一步是要解析拿到客户端发过来的 Sec-WebSocket-Key 字符串。

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

前文中也提到了 client 请求的格式(如上),首先 php 建立一个 socket 连接,监听端口的信息。

1. socket 连接的建立

关于 socket 套接字的建立,相信很多大学修过计算机网络的人都知道了,下面是一张连接建立的过程:

// 建立一个 socket 套接字
$master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($master, $address, $port);
socket_listen($master);

相比 node,这个地方的处理实在是太麻烦了,上面几行代码并未建立连接,只不过这些代码是建立一个 socket 套接字必须要写的东西。由于处理过程稍微有复杂,所以我把各种处理写进了一个类中,方便管理和调用。

//demo.php
Class WS {
    var $master;  // 连接 server 的 client
    var $sockets = array(); // 不同状态的 socket 管理
    var $handshake = false; // 判断是否握手

    function __construct($address, $port){
        // 建立一个 socket 套接字
        $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)   
            or die("socket_create() failed");
        socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1)  
            or die("socket_option() failed");
        socket_bind($this->master, $address, $port)                    
            or die("socket_bind() failed");
        socket_listen($this->master, 2)                               
            or die("socket_listen() failed");

        $this->sockets[] = $this->master;

        // debug
        echo("Master socket  : ".$this->master."\n");

        while(true) {
            //自动选择来消息的 socket 如果是握手 自动选择主机
            $write = NULL;
            $except = NULL;
            socket_select($this->sockets, $write, $except, NULL);

            foreach ($this->sockets as $socket) {
                //连接主机的 client 
                if ($socket == $this->master){
                    $client = socket_accept($this->master);
                    if ($client < 0) {
                        // debug
                        echo "socket_accept() failed";
                        continue;
                    } else {
                        //connect($client);
                        array_push($this->sockets, $client);
                        echo "connect client\n";
                    }
                } else {
                    $bytes = @socket_recv($socket,$buffer,2048,0);
                    if($bytes == 0) return;
                    if (!$this->handshake) {
                        // 如果没有握手,先握手回应
                        //doHandShake($socket, $buffer);
                        echo "shakeHands\n";
                    } else {
                        // 如果已经握手,直接接受数据,并处理
                        $buffer = decode($buffer);
                        //process($socket, $buffer); 
                        echo "send file\n";
                    }
                }
            }
        }
    }
}

上面这段代码是经过我调试了的,没太大的问题,如果想测试的话,可以在 cmd 命令行中键入 php /path/to/demo.php;当然,上面只是一个类,如果要测试的话,还得新建一个实例。

$ws = new WS('localhost', 4000);

客户端代码可以稍微简单点:

var ws = new WebSocket("ws://localhost:4000");
ws.onopen = function(){
    console.log("握手成功");
};
ws.onerror = function(){
    console.log("error");
};

运行服务器代码,当客户端连接的时候,我们可以看到:

通过上面的代码可以清晰的看到整个交流的过程。首先是建立连接,node 中这一步已经封装到了 net 和 http 模块,然后判断是否握手,如果没有的话,就 shakeHands。这里的握手我直接就 echo 了一个单词,表示进行了这个东西,前文我们提到过握手算法,这里就直接写了。

2. 提取 Sec-WebSocket-Key 信息

function getKey($req) {
    $key = null;
    if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $req, $match)) { 
        $key = $match[1]; 
    }
    return $key;
}

这里比较简单,直接正则匹配,websocket 信息头一定包含 Sec-WebSocket-Key,所以我们匹配起来也比较快捷~

3. 加密 Sec-WebSocket-Key

function encry($req){
    $key = $this->getKey($req);
    $mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

    return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
}

将 SHA-1 加密后的字符串再进行一次 base64 加密。如果加密算法错误,客户端在进行校检的时候会直接报错:

4. 应答 Sec-WebSocket-Accept

function dohandshake($socket, $req){
    // 获取加密key
    $acceptKey = $this->encry($req);
    $upgrade = "HTTP/1.1 101 Switching Protocols\r\n" .
               "Upgrade: websocket\r\n" .
               "Connection: Upgrade\r\n" .
               "Sec-WebSocket-Accept: " . $acceptKey . "\r\n" .
               "\r\n";

    // 写入socket
    socket_write(socket,$upgrade.chr(0), strlen($upgrade.chr(0)));
    // 标记握手已经成功,下次接受数据采用数据帧格式
    $this->handshake = true;
}

这里千万要注意,每一个请求和相应的格式,最后有一个空行,也就是 \r\n,开始测试的时候把这东西给弄丢了,纠结了半天。

当客户端成功校检key后,会触发 onopen 函数:

5. 数据帧处理

// 解析数据帧
function decode($buffer)  {
    $len = $masks = $data = $decoded = null;
    $len = ord($buffer[1]) & 127;

    if ($len === 126)  {
        $masks = substr($buffer, 4, 4);
        $data = substr($buffer, 8);
    } else if ($len === 127)  {
        $masks = substr($buffer, 10, 4);
        $data = substr($buffer, 14);
    } else  {
        $masks = substr($buffer, 2, 4);
        $data = substr($buffer, 6);
    }
    for ($index = 0; $index < strlen($data); $index++) {
        $decoded .= $data[$index] ^ $masks[$index % 4];
    }
    return $decoded;
}

这里涉及的编码问题在前文中已经提到过了,这里就不赘述,php 对字符处理的函数太多了,也记得不是特别清楚,这里就没有详细的介绍解码程序,直接把客户端发送的数据原样返回,可以算是一个聊天室的模式吧。

// 返回帧信息处理
function frame($s) {
    $a = str_split($s, 125);
    if (count($a) == 1) {
        return "\x81" . chr(strlen($a[0])) . $a[0];
    }
    $ns = "";
    foreach ($a as $o) {
        $ns .= "\x81" . chr(strlen($o)) . $o;
    }
    return $ns;
}

// 返回数据
function send($client, $msg){
    $msg = $this->frame($msg);
    socket_write($client, $msg, strlen($msg));
}

客户端代码:

var ws = new WebSocket("ws://localhost:4000");
ws.onopen = function(){
    console.log("握手成功");
};
ws.onmessage = function(e){
    console.log("message:" + e.data);
};
ws.onerror = function(){
    console.log("error");
};
ws.send("李靖");

在连通之后发送数据,服务器原样返回:

二、注意问题

1. websocket 版本问题

客户端在握手时的请求中有Sec-WebSocket-Version: 13,这样的版本标识,这个是一个升级版本,现在的浏览器都是使用的这个版本。而以前的版本在数据加密的部分更加麻烦,它会发送两个key:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Key1: xxxx
Sec-WebSocket-Key2: xxxx

如果是这种版本(比较老,已经没在使用了),需要通过下面的方式获取

function encry($key1,$key2,$l8b){ //Get the numbers preg_match_all('/([\d]+)/', $key1, $key1_num); preg_match_all('/([\d]+)/', $key2, $key2_num);

    $key1_num = implode($key1_num[0]);
    $key2_num = implode($key2_num[0]);
    //Count spaces
    preg_match_all('/([ ]+)/', $key1, $key1_spc);
    preg_match_all('/([ ]+)/', $key2, $key2_spc);

    if($key1_spc==0|$key2_spc==0){ $this->log("Invalid key");return; }
    //Some math
    $key1_sec = pack("N",$key1_num / $key1_spc);
    $key2_sec = pack("N",$key2_num / $key2_spc);

    return md5($key1_sec.$key2_sec.$l8b,1);
}

只能无限吐槽这种验证方式!相比 nodeJs 的 websocket 操作方式:

//服务器程序
var crypto = require('crypto');
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
require('net').createServer(function(o){
    var key;
    o.on('data',function(e){
        if(!key){
            //握手
            key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
            key = crypto.createHash('sha1').update(key + WS).digest('base64');
            o.write('HTTP/1.1 101 Switching Protocols\r\n');
            o.write('Upgrade: websocket\r\n');
            o.write('Connection: Upgrade\r\n');
            o.write('Sec-WebSocket-Accept: ' + key + '\r\n');
            o.write('\r\n');
        }else{
            console.log(e);
        };
    });
}).listen(8000);

多么简洁,多么方便!有谁还愿意使用 php 呢。。。。

2. 数据帧解析代码

本文没有给出 decodeFrame 这样数据帧解析代码,前文中给出了数据帧的格式,解析纯属体力活。

3. 代码展示

对这部分感兴趣的同学可以再去深究。代码用到了demo.php和ws.html两个文件。

<?php
class WS {
	var $master;
	var $sockets = array();
	var $debug = false;
	var $handshake = false;

	function __construct($address, $port){
		$this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
		socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
		socket_bind($this->master, $address, $port)                    or die("socket_bind() failed");
		socket_listen($this->master,20)                                or die("socket_listen() failed");
		
		$this->sockets[] = $this->master;
		$this->say("Server Started : ".date('Y-m-d H:i:s'));
		$this->say("Listening on   : ".$address." port ".$port);
		$this->say("Master socket  : ".$this->master."\n");
		
		while(true){
			$socketArr = $this->sockets;
			$write = NULL;
			$except = NULL;
			socket_select($socketArr, $write, $except, NULL);  //自动选择来消息的socket 如果是握手 自动选择主机
			foreach ($socketArr as $socket){
				if ($socket == $this->master){  //主机
					$client = socket_accept($this->master);
					if ($client < 0){
						$this->log("socket_accept() failed");
						continue;
					} else{
						$this->connect($client);
					}
				} else {
					$this->log("^^^^");
					$bytes = @socket_recv($socket,$buffer,2048,0);
					$this->log("^^^^");
					if ($bytes == 0){
						$this->disConnect($socket);
					}
					else{
						if (!$this->handshake){
							$this->doHandShake($socket, $buffer);
						}
						else{
							$buffer = $this->decode($buffer);
							$this->send($socket, $buffer); 
						}
					}
				}
			}
		}
	}
	
	function send($client, $msg){
		$this->log("> " . $msg);
		$msg = $this->frame($msg);
		socket_write($client, $msg, strlen($msg));
		$this->log("! " . strlen($msg));
	}
	function connect($socket){
		array_push($this->sockets, $socket);
		$this->say("\n" . $socket . " CONNECTED!");
		$this->say(date("Y-n-d H:i:s"));
	}
	function disConnect($socket){
		$index = array_search($socket, $this->sockets);
		socket_close($socket);
		$this->say($socket . " DISCONNECTED!");
		if ($index >= 0){
			array_splice($this->sockets, $index, 1); 
		}
	}
	function doHandShake($socket, $buffer){
		$this->log("\nRequesting handshake...");
		$this->log($buffer);
		list($resource, $host, $origin, $key) = $this->getHeaders($buffer);
		$this->log("Handshaking...");
		$upgrade  = "HTTP/1.1 101 Switching Protocol\r\n" .
					"Upgrade: websocket\r\n" .
					"Connection: Upgrade\r\n" .
					"Sec-WebSocket-Accept: " . $this->calcKey($key) . "\r\n\r\n";  //必须以两个回车结尾
		$this->log($upgrade);
		$sent = socket_write($socket, $upgrade, strlen($upgrade));
		$this->handshake=true;
		$this->log("Done handshaking...");
		return true;
	}

	function getHeaders($req){
		$r = $h = $o = $key = null;
		if (preg_match("/GET (.*) HTTP/"              ,$req,$match)) { $r = $match[1]; }
		if (preg_match("/Host: (.*)\r\n/"             ,$req,$match)) { $h = $match[1]; }
		if (preg_match("/Origin: (.*)\r\n/"           ,$req,$match)) { $o = $match[1]; }
		if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)) { $key = $match[1]; }
		return array($r, $h, $o, $key);
	}

	function calcKey($key){
		//基于websocket version 13
		$accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
		return $accept;
	}

	function decode($buffer) {
		$len = $masks = $data = $decoded = null;
		$len = ord($buffer[1]) & 127;

		if ($len === 126) {
			$masks = substr($buffer, 4, 4);
			$data = substr($buffer, 8);
		} 
		else if ($len === 127) {
			$masks = substr($buffer, 10, 4);
			$data = substr($buffer, 14);
		} 
		else {
			$masks = substr($buffer, 2, 4);
			$data = substr($buffer, 6);
		}
		for ($index = 0; $index < strlen($data); $index++) {
			$decoded .= $data[$index] ^ $masks[$index % 4];
		}
		return $decoded;
	}

	function frame($s){
		$a = str_split($s, 125);
		if (count($a) == 1){
			return "\x81" . chr(strlen($a[0])) . $a[0];
		}
		$ns = "";
		foreach ($a as $o){
			$ns .= "\x81" . chr(strlen($o)) . $o;
		}
		return $ns;
	}

	
	function say($msg = ""){
		echo $msg . "\n";
	}
	function log($msg = ""){
		if ($this->debug){
			echo $msg . "\n";
		} 
	}
}
	

new WS('localhost', 4000);
<script type="text/javascript">
var ws = new WebSocket("ws://localhost:4000");
ws.onopen = function(){
	console.log("握手成功");
}
ws.onmessage = function(e){
	console.log("message:" + e.data);
}
ws.onerror = function(){
	console.log("error");
}
</script>

 

4. 相关开源库参考

http://socketo.me Ratchet 为 php 封装的一个 WebSockets 库。

Google 上搜索 php+websoket+class,也能找到不少相关的资料。

三、参考资料

来自:https://www.barretlee.com/blog/2013/12/25/cb-websocket-with-php/     可以关注微信公众号 (小胡子哥)


文章错误纠正:

1、握手函数中不应该加chr(0),只应该发送$upgrade,否则后续消息发到浏览器中都会提示A server must not mask any frames that it sends to the client.

———————————————————

2、我想问下 在demo.php 中 您用了一个全局变量‘$handshake’ 来标记是否握手 好像这边会导致只有第一次连接的client能够成功连上,第二个client,第三个client的就连接不上了

3、我试过了可以改成 ,在把new socket变量加到全局变量$socket之后 ,就立即进行握手动作。 可以解决我之前遇到的只能第一个client连接上的问题。

websocket协议-细说WebSocket – Node篇

在上一篇提高到了 web 通信的各种方式,包括 轮询、长连接 以及各种 HTML5 中提到的手段。本文将详细描述 WebSocket协议 在 web通讯 中的实现。

一、WebSocket 协议

1. 概述

websocket协议允许不受信用的客户端代码在可控的网络环境中控制远程主机。该协议包含一个握手和一个基本消息分帧、分层通过TCP。简单点说,通过握手应答之后,建立安全的信息管道,这种方式明显优于前文所说的基于 XMLHttpRequest 的 iframe 数据流和长轮询。该协议包括两个方面,握手链接(handshake)和数据传输(data transfer)。

2. 握手连接

这部分比较简单,就像路上遇到熟人问好。

Client:嘿,大哥,有火没?(烟递了过去)
Server:哈,有啊,来~ (点上)
Client:火柴啊,也行!(烟点上,验证完毕)

握手连接中,client 先主动伸手:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

客户端发了一串 Base64 加密的密钥过去,也就是上面你看到的 Sec-WebSocket-Key。 Server 看到 Client 打招呼之后,悄悄地告诉 Client 他已经知道了,顺便也打个招呼。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

Server 返回了 Sec-WebSocket-Accept 这个应答,这个应答内容是通过一定的方式生成的。生成算法是:

mask  = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";  // 这是算法中要用到的固定字符串
accept = base64( sha1( key + mask ) );

key 和 mask 串接之后经过 SHA-1 处理,处理后的数据再经过一次 Base64 加密。分解动作:

1. t = "GhlIHNhbXBsZSBub25jZQ==" + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
   -> "GhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
2. s = sha1(t) 
   -> 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 
      0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea
3. base64(s) 
   -> "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="

上面 Server 端返回的 HTTP 状态码是 101,如果不是 101 ,那就说明握手一开始就失败了~

下面就来个 demo,跟服务器握个手:

var crypto = require('crypto');

require('net').createServer(function(o){
    var key;
    o.on('data',function(e){
        if(!key){
            // 握手
            // 应答部分,代码先省略
            console.log(e.toString());
        }else{

        };
    });
}).listen(8000);

客户端代码:

var ws=new WebSocket("ws://127.0.0.1:8000");
ws.onerror=function(e){
  console.log(e);
};

上面当然是一串不完整的代码,目的是演示握手过程中,客户端给服务端打招呼。在控制台我们可以看到:

看起来很熟悉吧,其实就是发送了一个 HTTP 请求,这个我们在浏览器的 Network 中也可以看到:

但是 WebSocket协议 并不是 HTTP 协议,刚开始验证的时候借用了 HTTP 的头,连接成功之后的通信就不是 HTTP 了,不信你用 fiddler2 抓包试试,肯定是拿不到的,后面的通信部分是基于 TCP 的连接。

服务器要成功的进行通信,必须有应答,往下看:

//服务器程序
var crypto = require('crypto');
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
require('net').createServer(function(o){
    var key;
    o.on('data',function(e){
        if(!key){
            //握手
            key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
            key = crypto.createHash('sha1').update(key + WS).digest('base64');
            o.write('HTTP/1.1 101 Switching Protocols\r\n');
            o.write('Upgrade: websocket\r\n');
            o.write('Connection: Upgrade\r\n');
            o.write('Sec-WebSocket-Accept: ' + key + '\r\n');
            o.write('\r\n');
        }else{
            console.log(e);
        };
    });
}).listen(8000);

关于crypto模块,可以看看官方文档,上面的代码应该是很好理解的,服务器应答之后,Client 拿到 Sec-WebSocket-Accept ,然后本地做一次验证,如果验证通过了,就会触发 onopen 函数。

//客户端程序
var ws=new WebSocket("ws://127.0.0.1:8000/");
ws.onopen=function(e){
    console.log("握手成功");
};

可以看到

3. 数据帧格式

官方文档提供了一个结构图

 0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

第一眼瞟到这张图恐怕是要吐血,如果大学修改计算机网络这门课应该不会对这东西陌生,数据传输协议嘛,是需要定义字节长度及相关含义的。

FIN      1bit 表示信息的最后一帧,flag,也就是标记符
RSV 1-3  1bit each 以后备用的 默认都为 0
Opcode   4bit 帧类型,稍后细说
Mask     1bit 掩码,是否加密数据,默认必须置为1 (这里很蛋疼)
Payload  7bit 数据的长度
Masking-key      0 or 4 bit 掩码
Payload data     (x + y) bytes 数据
Extension data   x bytes  扩展数据
Application data y bytes  程序数据
https://tools.ietf.org/html/rfc6455#section-5.2

 Masking-key:  0 or 4 bytes

      All frames sent from the client to the server are masked by a
      32-bit value that is contained within the frame.  This field is
      present if the mask bit is set to 1 and is absent if the mask bit
      is set to 0.  See Section 5.3 for further information on client-
      to-server masking.

每一帧的传输都是遵从这个协议规则的,知道了这个协议,那么解析就不会太难了,下面我就直接拿了次碳酸钴同学的代码。

4. 数据帧的解析和编码

数据帧的解析代码:

function decodeDataFrame(e){
  var i=0,j,s,frame={
    //解析前两个字节的基本数据
    FIN:e[i]>>7,Opcode:e[i++]&15,Mask:e[i]>>7,
    PayloadLength:e[i++]&0x7F
  };
  //处理特殊长度126和127
  if(frame.PayloadLength==126)
    frame.length=(e[i++]<<8)+e[i++];
  if(frame.PayloadLength==127)
    i+=4, //长度一般用四字节的整型,前四个字节通常为长整形留空的
    frame.length=(e[i++]<<24)+(e[i++]<<16)+(e[i++]<<8)+e[i++];
  //判断是否使用掩码
  if(frame.Mask){
    //获取掩码实体
    frame.MaskingKey=[e[i++],e[i++],e[i++],e[i++]];
    //对数据和掩码做异或运算
    for(j=0,s=[];j<frame.PayloadLength;j++)
      s.push(e[i+j]^frame.MaskingKey[j%4]);
  }else s=e.slice(i,frame.PayloadLength); //否则直接使用数据
  //数组转换成缓冲区来使用
  s=new Buffer(s);
  //如果有必要则把缓冲区转换成字符串来使用
  if(frame.Opcode==1)s=s.toString();
  //设置上数据部分
  frame.PayloadData=s;
  //返回数据帧
  return frame;
}

数据帧的编码:

//NodeJS
function encodeDataFrame(e){
  var s=[],o=new Buffer(e.PayloadData),l=o.length;
  //输入第一个字节
  s.push((e.FIN<<7)+e.Opcode);
  //输入第二个字节,判断它的长度并放入相应的后续长度消息
  //永远不使用掩码
  if(l<126)s.push(l);
  else if(l<0x10000)s.push(126,(l&0xFF00)>>2,l&0xFF);
  else s.push(
    127, 0,0,0,0, //8字节数据,前4字节一般没用留空
    (l&0xFF000000)>>6,(l&0xFF0000)>>4,(l&0xFF00)>>2,l&0xFF
  );
  //返回头部分和数据部分的合并缓冲区
  return Buffer.concat([new Buffer(s),o]);
}

有些童鞋可能没有明白,应该解析哪些数据。这的解析任务主要是服务端处理,客户端送过去的数据是二进制流形式的,比如:

var ws = new WebSocket("ws://127.0.0.1:8000/"); 
ws.onopen = function(){ 
  ws.send("握手成功"); 
};

Server 收到的信息是这样的:

一个放在Buffer格式的二进制流。而当我们输出的时候解析这个二进制流:

//服务器程序
var crypto = require('crypto');
var WS = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
require('net').createServer(function(o){
    var key;
    o.on('data',function(e){
        if(!key){
            //握手
            key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
            key = crypto.createHash('sha1').update(key + WS).digest('base64');
            o.write('HTTP/1.1 101 Switching Protocols\r\n');
            o.write('Upgrade: websocket\r\n');
            o.write('Connection: Upgrade\r\n');
            o.write('Sec-WebSocket-Accept: ' + key + '\r\n');
            o.write('\r\n');
        }else{
            // 输出之前解析帧
            console.log(decodeDataFrame(e));
        };
    });
}).listen(8000);

那输出的就是一个帧信息十分清晰的对象了:

5. 连接的控制

上面我买了个关子,提到的Opcode,没有详细说明,官方文档也给了一张表:

 |Opcode  | Meaning                             | Reference |
-+--------+-------------------------------------+-----------|
 | 0      | Continuation Frame                  | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 1      | Text Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 2      | Binary Frame                        | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 8      | Connection Close Frame              | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 9      | Ping Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 10     | Pong Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|

decodeDataFrame 解析数据,得到的数据格式是:

{
    FIN: 1,
    Opcode: 1,
    Mask: 1,
    PayloadLength: 4,
    MaskingKey: [ 159, 18, 207, 93 ],
    PayLoadData: '握手成功'
}

那么可以对应上面查看,此帧的作用就是发送文本,为文本帧。因为连接是基于 TCP 的,直接关闭 TCP 连接,这个通道就关闭了,不过 WebSocket 设计的还比较人性化,关闭之前还跟你打一声招呼,在服务器端,可以判断frame的Opcode:

var frame=decodeDataFrame(e);
console.log(frame);
if(frame.Opcode==8){
    o.end(); //断开连接
}

客户端和服务端交互的数据(帧)格式都是一样的,只要客户端发送 ws.close(), 服务器就会执行上面的操作。相反,如果服务器给客户端也发送同样的关闭帧(close frame):

o.write(encodeDataFrame({
    FIN:1,
    Opcode:8,
    PayloadData:buf
}));

客户端就会相应 onclose 函数,这样的交互还算是有规有矩,不容易出错。

二、注意事项

1. WebSocket URIs

很多人可能只知道 ws://text.com:8888,但事实上 websocket 协议地址是可以加 path 和 query 的。

ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]

如果使用的是 wss 协议,那么 URI 将会以安全方式连接。 这里的 wss 大小写不敏感。

2. 协议中”多余”的部分(吐槽)

握手请求中包含Sec-WebSocket-Key字段,明眼人一下就能看出来是websocket连接,而且这个字段的加密方式在服务器也是固定的,如果别人想黑你,不会太难。

再就是那个 MaskingKey 掩码,既然强制加密了(Mask为1表示加密,加密方式就是 MaskingKey 与 PayLoadData 进行异或处理),还有必要让开发者处理这个东西么?直接封装到内部不就行了?

3. 与 TCP 和 HTTP 之间的关系

WebSocket协议是一个基于TCP的协议,就是握手链接的时候跟HTTP相关(发了一个HTTP请求),这个请求被Server切换到(Upgrade)websocket协议了。websocket把 80 端口作为默认websocket连接端口,而websocket的运行使用的是443端口。

三、参考资料

四、特别感谢

再次感谢 次碳酸钴 跟我交流了几个小时 : ),本文部分 node 代码参考自他的博客。

下次将以php作为后台,讲解websocket的相关知识。

细说WebSocket-php篇


文章部分内容纠正:

1、t = “GhlIHNhbXBsZSBub25jZQ==”,串最前面少了一个d
2、console.log(crypto.createHash(‘sha1’).update(‘dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11’).digest(‘sha1’)); //输出:<SlowBuffer b3 7a 4f 2c c0 62 4f 16 90 f6 46 06 cf 38 59 45 b2 be c4 ea>,不像你的,前面带0x,你是怎么弄的?

3、解码部分:if(frame.PayloadLength==126) frame.length=(e[2]<<8) + e[3], i=4; //其中的frame.length,好像应该是frame.Payloadlength。下一行中也是。看了一下,次碳酸钴,那边已经改了。

4、编码部分:s.push(126,(l&0xFF00)>>2,l&0xFF); 其中的>>2,好像应该是>>8。下一行类似。

websocket协议-websocket简史

WebSocket 简介及应用实例

HTML5 的出现,标志着后 Flash 时代各种现代浏览器的集体爆发,也是谨防 Adobe 一家独大的各家厂商们,历经多年各自为战,想换个活法儿并终于达成一定共识后,所积kao累bei的技术的一次集中释放 — 正所谓 “H5 是个筐,什么都可以往里装”。

其中引人瞩目并被广泛支持的一项,就是此次要谈论的 WebSocket 了。本文将尝试说明它被用来解决什么问题,以及与久经沙场的“传统” Socket 又有什么异同等基础问题。

I. 定义及由来

望文而生义,面对 WebSocket 这个名称,web 无需做太多解释,傻傻分不清楚的 socket 看着也是相当的面熟;甭管有没有联系,先来了解一下也无妨:

(1.1) 传统的 Socket API

Socket 往往指的是 TCP/IP 网络环境中的两个连接端,以及为方便此类开发所设计的一组编程 API

如图,英文单词 “socket” 的字面原义是 “孔” 或 “插座”。

作为一个技术用语时,socket 通常取后一种意思,像一个多孔插座。用于描述一个通信链路两端的 IP 地址和端口等,可以用来实现不同设备之间的通信。SocketTCP Socket都是通用的叫法,中文一般习惯性的译作**“套接字”、“TCP套接字”** 等。

…至于为嘛把“插座儿”翻译成“套接字”,好奇的程序猿并不在少数,科考文章在文章底部参考链接中可以找到。

可以将服务端主机想象成一个布满各种插座的房间,每个插座有一个编号,有的插座提供 220 伏交流电,有的提供固定电话信号,有的则提供有线电视节目。客户端软件将插头接入不同编号的插座,就可以得到不同的服务

Socket API 所处的楼层

OSI 模型作为一种概念模型,由国际标准化组织(ISO)提出,一个试图使各种计算机在世界范围内互连为网络的标准框架。我们熟悉的 HTTP、FTP 等协议都工作在最顶端的应用层(Application Layer)。

而 **TCP/IP 协议族(Protocol Suite)**将软件通信过程抽象化为四个抽象层,常被视为是简化的七层OSI模型。当多个层次的协议共同工作时,类似数据结构中的堆栈,因此又被称为 TCP/IP 协议栈(Protocol Stack)

单说 TCP 的话,指的是面向连接的一种传输控制协议。TCP 连接之后,客户端和服务器可以互相发送和接收消息,在客户端或者服务器没有主动断开之前,连接一直存在,故称为长连接。

Socket 其实并不是一个标准的协议,而是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口,工作位置基本在 OSI 模型会话层(第5层),是为了方便大家直接使用更底层协议(一般是 TCP 或 UDP )而存在的一个抽象层。

在设计模式中,Socket其实就是一个门面(facade)模式,它把复杂的 TCP/IP 协议族隐藏在 Socket API 后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议

最早的一套 Socket API 是采用 C 语言实现的,也就成为了 Socket 的事实标准。

常见的 Socket API 实现

一些语言的实现

传统的后端编程语言基本都有 Socket API 的封装;而在 HTML5 出现之前,要想用纯前端技术实现 TCP Socket 的客户端,也基本只有 Java Applet (java.net.Socketjava.net.DatagramSocketjava.net.MulticastSocket) 、Flash (flash.net.Socketflash.net.XMLSocket) 或 Silverlight(System.Net.Sockets) 等可以选择。

下面以 PHP 的 服务器/客户端 实现为例,演示一个最基础的例子:

<?php
//server.php

set_time_limit(0);
$ip = '127.0.0.1';
$port = 1999;
// 创建一个Socket
$sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
// 绑定Socket地址和端口
$ret = socket_bind($sock,$ip,$port);
// 开始监听链接
$ret = socket_listen($sock,4);

$count = 0; //最多接受几次请求后就退出
do {
	// 另一个Socket来处理通信
    if (($msgsock = socket_accept($sock)) >= 0) {        
        // 发到客户端
        $msg ="server: HELLO CLIENTS!\n";
        if (socket_write($msgsock, $msg, strlen($msg))) {
        	echo "发送成功!\n";
        }
        // 获得客户端的输入
        $buf = socket_read($msgsock,8192);
        
        $talkback = "接受成功!内容为:$buf\n";
        echo $talkback;
        
        if(++$count >= 5){
            break;
        };    
    }
    // 关闭sockets
    socket_close($msgsock);
} while (true);
socket_close($sock);
echo "TCP 连接关闭OK\n";
?>
<?php
//client.php

error_reporting(E_ALL);
set_time_limit(0);
$port = 1999;
$ip = "127.0.0.1";

// 创建Socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 绑定Socket地址和端口
$result = socket_connect($socket, $ip, $port);
if ($result >= 0) echo "TCP 连接OK\n";

$in = "client: HELLO SERVER!\r\n";
if(socket_write($socket, $in, strlen($in))) {
    echo "发送成功!\n";
}
$out = '';
while($out = socket_read($socket, 8192)) {
    echo "接受成功!内容为:",$out;
}

socket_close($socket);
echo "TCP 连接关闭OK\n";
?>

(1.2) HTML5 带来的 WebSocket 协议

WebSockets 为 C/S 两端提供了实时交互通信的能力,允许服务器主动发送信息给客户端,是一种区别于 HTTP 的全新双向数据流协议

简单的说,传统的 TCP Socket 是一套相对标准化的 API,而出现时间不久的 WebSocket 是一种网络协议 — 两码事。

WebSocket 底层是基于 TCP 协议的,所以早期草案中叫做 TCPConnection,最后之所以改名,其实是借用了传统 Socket 沟通 TCP 网络两端的意思而已。

要解决的问题

*HTTP 的工作方式*
在基于 请求/响应 模式的 HTTP/HTTPS 下,如果是对实时性要求较高的场景,客户端就需要不停的询问服务端有无可用的数据,这在各方面都是笨拙而不划算的。

*WebSocket 的工作方式*
而在 WebSocket 的全双工(允许数据在两个方向上同时传输)方式下,客户端只要静静地听招呼即可,有可用数据时服务端会自动通知它。

WebSocket 的用武之地

大部分传统的方式既浪费带宽(HTTP HEAD 是比较大的),又消耗服务器 CPU 占用(没有信息也要接受请求);而 WebSocket 则会大幅降低上述的消耗,更适用于以下场景:

  • 实时性要求高的应用
  • 聊天室
  • IoT (物联网 – internet of things)
  • 在线多人游戏

兼容性也令人满意,非要说何时不适用的话,大概就是少数必须兼容老旧浏览器,或者对实时要求明显不高的情况下了。

HTTP 的扩展

WebSocket 连接的 URL 使用 ws://wss:// 等开头,其加密、cookie 等策略和对应的 HTTP/HTTPS 基本相同。

HTTP、WebSocket 等应用层协议,都是基于 TCP 协议来传输数据的,可以把这些高级协议理解成对 TCP 的封装。


Websocket协议是为了解决web即时应用中服务器与客户端浏览器全双工通信的问题而设计的,是完全意义上的Web应用端的双向通信技术,可以取代之前使用半双工HTTP协议而模拟全双工通信,同时克服了带宽和访问速度等的诸多问题。协议定义为ws和wss协议,分别为普通请求和基于SSL的安全传输,占用端口与http协议系统,ws为80端口,wss为443端口,这样可以支持HTTP代理。

协议包含两个部分,第一个是“握手”,第二个是数据传输。

一、Websocket URI

定义的两个协议框架ws和wss与http类似,而且各自部分的要求也是在HTTP协议中使用的一样,各自的URI如下:

ws-URI = “ws:” “//” host [ “:” port ] path [ “?” query ]
wss-URI = “wss:” “//” host [ “:” port ] path [ “?” query ]

其中port是可选项,query前接“?”。

二、握手(Opening & Closing Handshake)打开连接

当建立一个Websocket连接时,为了保持基于HTTP协议的服务器软件和中间件进行兼容工作,客户端打开一个连接时使用与HTTP连接的同一个端口到服务器进行连接,这样被设计为一个升级的HTTP请求。

1、发送握手请求

此时的连接状态是CONNECTING,客户端需要提供host、port、resource-name和一个是否是安全连接的标记,也就是一个WebSocket URI。

客户端发送的一个到服务器端握手请求如下:

 

        GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
        Origin: http://example.com
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13

这个升级的HTTP请求头中的字段顺序是可以随便的。与普通HTTP请求相比多了一些字段。

    • Connection必须设置Upgrade,表示客户端希望连接升级。
    • Upgrade字段必须设置Websocket,表示希望升级到Websocket协议。
    • Sec-WebSocket-Key是随机的字符串,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算SHA-1摘要,之后进行BASE-64编码,将结果做为“Sec-WebSocket-Accept”头的值,返回给客户端。如此操作,可以尽量避免普通HTTP请求被误认为Websocket协议。
    • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均应当弃用。
    • Origin字段是可选的,通常用来表示在浏览器中发起此Websocket连接所在的页面,类似于Referer。但是,与Referer不同的是,Origin只包含了协议和主机名称。
    • 其他一些定义在HTTP协议中的字段,如Cookie等,也可以在Websocket中使用。
    • Sec-WebSocket-Protocol:字段表示客户端可以接受的子协议类型,也就是在Websocket协议上的应用层协议类型。上面可以看到客户端支持chat和superchat两个应用层协议,当服务器接受到这个字段后要从中选出一个协议返回给客户端。
发送请求的要求:
  • 请求的WebSocket URI必须要是定义的有效的URI。
  • 如果客户端已经有一个WebSocket连接到远程服务器端,不论是否是同一个服务器,客户端必须要等待上一个连接关闭后才能发送新的连接请求,也就是同一客户端一次只能存在一个WebSocket连接。如果想同一个服务器有多个连接,客户端必须要串行化进行。如果客户端检测到多个到不同服务器的连接,应该限制一个最大连接数,在web浏览器中应该设定最多可以打开的标签页的数目。这样可以防止到远程服务器的DDOS攻击,但这是对到多个服务器的连接,如果是到同一个服务器连接,并没有数目限制。
  • 如果使用了代理服务器,那么客户端建立连接的时候需要告知代理服务器向目标服务器打开TCP连接。
  • 如果连接没有打开,一定是某一方出现错误,此时客户端必须要关闭再次连接的尝试。
  • 连接建立后,握手必须要是一个有效的HTTP请求
  • 请求的方式必须是GET,HTTP协议的版本至少是1.1
  • Upgrade字段必须包含而且必须是”websocket”,Connection字段必须内容必须是“Upgrade”
  • Sec-Websocket-Version必须,而且必须是13 (固定版本号)

2、返回握手应答

服务器返回正确的相应头后,客户端验证后将建立连接,此时状态为OPEN。服务器响应头如下:
        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        Sec-WebSocket-Protocol: chat

响应头握手过程中是服务器返回的是否同意握手的依据。

  • 首行返回的是HTTP/1.1协议版本和状态码101,表示变换协议(Switching Protocol)
  • Upgrade 和 Connection:这两个字段是服务器返回的告知客户端同意使用升级并使用websocket协议,用来完善HTTP升级响应
  • Sec-WebSocket-Accept:服务器端将加密处理后的握手Key通过这个字段返回给客户端表示服务器同意握手建立连接。
  • Sec-Websocket-Procotol:服务器选择的一个应用层协议。

上述响应头字段被客户端浏览器解析,如果验证到Sec-WebSocket-Accept字段的信息符合要求就会建立连接,同时就可以发送WebSocket的数据帧了。如果该字段不符合要求或者为空或者HTTP状态码不为101,就不会建立连接。

服务器端响应步骤:
  • 解析握手请求头:获取握手依据Key并进行处理,检测HTTP的GET请求和版本是否准确,Host字段是否有权限,Upgrade字段中websocket是一个与大小写无关的ASCII字符串,Connection字段是一个大小写无关的”Upgrade”ASCII字符串,Websocket协议版本必须为13,其他的关于Origin、Protocol和Extensions可选。
  • 发送握手响应头:检测是否是wss协议连接,如果是就是用TLS握手连接,否则就是普通连接。服务器可以添加额外的验证信息到客户端进行验证。当进行一系列验证之后,服务器必须返回一个有效的HTTP响应头。响应头中每一行一个字段,结束必须为“\r\n”,使用的ABNF语法。
除了上述必要头字段之外,其他的HTTP协议定义的字段都可以使用,如Set-Cookie等。
websocket 采用 帧格式传输数据,详见  有关 websocket 基础 的博客。

 浏览器中的实现

在浏览器中可以直接调用 WebSocket 对象,其定义如下:

enum BinaryType { "blob", "arraybuffer" };
[Constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols = []), Exposed=(Window,Worker)]
interface WebSocket : EventTarget {
  readonly attribute USVString url;

  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSING = 2;
  const unsigned short CLOSED = 3;
  readonly attribute unsigned short readyState;
  readonly attribute unsigned long long bufferedAmount;

  // networking
  attribute EventHandler onopen;
  attribute EventHandler onerror;
  attribute EventHandler onclose;
  readonly attribute DOMString extensions;
  readonly attribute DOMString protocol;
  void close([Clamp] optional unsigned short code, optional USVString reason);

  // messaging
  attribute EventHandler onmessage;
  attribute BinaryType binaryType;
  void send(USVString data);
  void send(Blob data);
  void send(ArrayBuffer data);
  void send(ArrayBufferView data);
};

使用起来大概是这样的:

var ws = new WebSocket('ws://www.xxx.com/some.php');
ws.send('xxx'); //每次只能发送字符串
ws.onmessage = function(event) {
	var data = event.data;
};
ws.onerror = function() {
	ws.close();
};

II. 一个多用户交互的 WebSocket 实例

这里随便设想一个用户场景,比如我们要做一个在线纸牌游戏,肯定就是一个多人进入同一个房间的形式,并且每个人的动作能广播给其他人。

下面用 WebSocket 做一个最基础的验证原型,让每个玩家知道其他人的进入、离开、出牌、悔牌,甚至是耍赖换牌等:

(2.1) 服务器端的实现

我们用 nodejs+expressjs 搭建基础服务器,并用 https://github.com/websockets/ws 封装的库实现 WebSocket 协议的服务器端逻辑:

// server.js

var express = require('express')
var ws = require('./ws')

var app = express()

app.get('/', function (req, res) {
    res.sendFile(__dirname + '/ws.html');
})

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})
// ws.js

const { Server, OPEN } = require('ws');

const clients = []; //array of websocket clients
const cardsArr = []; //array of {cardId, count, title, ...}

let _lock = false;

const wss = new Server({port: 40510})
wss.on('connection', function (ws) {

	const _cid = clients.push(ws) - 1;

	ws.on('message', function (json) {

		const {
			act,
			cid,
			data
		} = JSON.parse(json);

		switch (act) {
			case 'client:join':
				_onCustomerJoin(ws, _cid);
				break;
			case 'client:leave':
				_onCustomerLeave(cid);
				break;
			case 'client:add': //增加牌
				_onAddCard(cid, data);
				break;
			case 'client:update': //修改牌
				_onUpdateCard(cid, data);
				break;
			case 'client:remove': //删除牌
				_onRemoveCard(cid, data);
				break;
			case 'client:win': //下单
				_onWin(cid);
				break;
			default:
				console.log('received: %s', act, cid)
				break;
		}

	});
});

function _ensureLock(func) {
	return function() {
		if (_lock) return;
		_lock = true;
		const rtn = func.apply(null, arguments);
		_lock = false;
		return rtn;
	};
}

function _findCard(cardId) {
	const cidx = cardsArr.map(Card=>Card.cardId).indexOf(cardId);
	return cidx;
}

const _broadcast = (excludeId, msg, data=null)=>{
	clients.forEach( (client, cidx)=>{
		if (cidx === excludeId) return;
		if (client && client.readyState === OPEN) {
			client.send(JSON.stringify({
				act: 'server:broadcast',
				msg: msg,
				data: data
			}));
		}
	} );
};

const _onCustomerJoin = (ws, cid)=>{
	ws.send(JSON.stringify({
		act: 'server:regist',
		data: {
			cid: cid
		}
	}));
	_broadcast(cid, '玩家加入:', {cid: cid});
};
const _onCustomerLeave = (cid)=>{
	clients[cid].terminate();
	clients.splice(cid, 1);
	_broadcast(cid, '玩家退出:', {cid: cid});
};
const _onAddCard = _ensureLock( (cid, data)=>{
	const d = _findCard(data.cardId);
	if (d !== -1) {
		cardsArr.splice(d, 1);
	}
	cardsArr.push(data);
	_broadcast(-1, '玩家添加了牌', {
		cid: cid,
		Card: data,
		cardsArr: cardsArr
	});
} );
const _onUpdateCard = _ensureLock( (cid, data)=>{
	const d = _findCard(data.cardId);
	if (d === -1) return;
	cardsArr[d] = data;
	_broadcast(-1, '玩家更改了牌', {
		cid: cid,
		Card: data,
		cardsArr: cardsArr
	});
} );
const _onRemoveCard = _ensureLock( (cid, data)=>{
	const d = _findCard(data.cardId);
	if (d === -1) return;
	cardsArr.splice(d, 1);
	_broadcast(-1, '玩家删除了牌', {
		cid: cid,
		Card: data,
		cardsArr: cardsArr
	});
} );
const _onWin = _ensureLock( (cid)=>{
	//do sth. here
	_broadcast(cid, '玩家胡牌了');
} );

(2.2) 客户端的实现

<h1></h1>
<div></div>

<button onclick="_add()">出牌</button>
<button onclick="_update()">换牌</button>
<button onclick="_remove()">悔牌</button>
<button onclick="_win()">胡牌</button>
<button onclick="_leave()">离开</button>

<script>
    let cid = null; 

    const ws = new WebSocket('ws://localhost:40510');

    ws.onopen = function () {
        console.log('websocket is connected ...');

        _send({
            act: 'client:join'
        });
    };

    ws.onmessage = function (ev) {
        
        const {
            act,
            msg,
            data
        } = JSON.parse(ev.data);

        switch(act) {
            case 'server:regist':
                cid = data.cid;
                console.log(`regist: cid is ${cid}`);
                document.querySelector('h1').innerHTML = 'I AM: ' + cid;
                break;
            case 'server:broadcast':
                console.log('从服务器端接收的广播:', msg, data);
                if (data && data.cardsArr) {
                    document.querySelector('div').innerHTML = JSON.stringify(
                        data.cardsArr, null, 4
                    );
                }
                break;
            default:
                console.log(ev);
                break;
        }
    }

    function _send(json) {
        ws.send(JSON.stringify(json));
    }

    function _add() {
        _send({
            act: 'client:add',
            cid: cid,
            data: {
                cardId: 111,
                count: 1,
                title: '红桃A'
            }
        })
    }
    function _update() {
        _send({
            act: 'client:update',
            cid: cid,
            data: {
                cardId: 111,
                count: 2,
                title: '黑桃9'
            }
        })
    }
    function _remove() {
        _send({
            act: 'client:remove',
            cid: cid,
            data: {
                cardId: 111
            }
        })
    }
    function _win() {
        _send({
            act: 'client:win',
            cid: cid
        })
    }
    function _leave() {
        _send({
            act: 'client:leave',
            cid: cid
        })
    }
</script>

(2.3) 运行效果

玩家 0 加入:

玩家 1 加入:

玩家 1 出牌:

玩家 1 胡牌并退出:

与 WebSocket 类似的技术

实际上,每当谈到实时双向通信的问题时,我们自然会想起历年来一些基于 HTTP 技术的尝试;也正是基于这些之前工作中的实践和困扰,WebSocket 才应运而生。让我们大概回顾一下相关的方案及其缺陷:

轮询 (Polling)

借助于 setInterval() 等方式,客户端不断的发送请求并得到响应。这种做法比较简单,可以在一定程度上解决问题。不过对于轮询的时间间隔需要进行仔细考虑。轮询的间隔过长,会导致用户不能及时接收到更新的数据;轮询的间隔过短,会导致查询请求过多,增加服务器端的负担。

长轮询 (Long Polling)

这是对轮询的一种改进。客户端发出请求后,服务器端用 while(true) 等方式阻塞住请求,直到有可用数据才发送响应数据,而客户端收到响应后再发送下一个请求。

这种方式又被成为 “Hanging GET”、“反向 Ajax” 或 “Comet” 等,虽然看上去很像服务器推送,但仍然是基于 HTTP 的一种慢响应;且在数据更新频繁的情况下,其效率并不优于一般的轮询。

HTTP 流 (Streaming)

使用 HTTP 1.1 且响应头中包含 Transfer-Encoding: chunked 的情况下,服务器发送给客户端的数据可以分成多个部分,保持打开(while-true, sleep等),并周期性 flush() 分块传输。

客户端只发送一个HTTP连接,在 xhr.readyState==3 状态下,用 xhr.responseText.substring 获取每次的数据。

但是数据响应可能会因 代理服务器 或 防火墙 等中间人造成延迟,所以可能还要额外探测这种情况以切换到长轮询方式。

SSE (Server-Sent Events)

SSE 规范也是 HTML 5 规范的一个组成部分。服务器端响应的内容类型是text/event-stream,在浏览器端使用 EventSource 对象处理返回的数据。

比之于 WebSocket,SSE 的缺点在于:

  • 不支持 CORS
  • 单向通道,只能服务器向浏览器端发送
  • 浏览器兼容性稍差

III. 总结

  • 传统的 TCP Socket 往往指的是 TCP/IP 网络环境中的两个连接端,以及为方便此类开发所设计的一组编程 API
  • WebSockets 为 C/S 两端提供了实时交互通信的能力,允许服务器主动发送信息给客户端
  • WebSockets 是 HTML 5 规范的一个组成部分,是一种区别于 HTTP 的全新双向数据流协议
  • 全双工通信的 WebSockets 有效改善了之前 长轮询 等方式的弊端
  • WebSockets 适用于实时性要求高的应用、聊天室、多人游戏等

参考:

https://juejin.im/post/5ae3eb9b51882567382f5767

https://www.cnblogs.com/oshyn/p/3574497.html


http://www.cnblogs.com/hustskyking/p/websocket-with-node.html

http://www.cnblogs.com/hustskyking/p/websocket-with-php.html

https://www.cnblogs.com/zxtceq/p/6963964.html