<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_首页</title><subtitle type="text">代码改变世界</subtitle><id>http://feed.cnblogs.com/blog/sitehome/rss</id><updated>2012-05-16T22:50:47Z</updated><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/sitehome/rss"/><entry><id>http://www.cnblogs.com/gaozehua/archive/2012/05/17/2505653.html</id><title type="text">[原创]桓泽学音频编解码（12）：AC3 Mantissa(小数部分)模块算法分析</title><summary type="text">AC3（Dolby Digital）音频编码标准 出现在DVD，BlueDisc，电影院系统，北美广播电视系统中，是Dolby公司最赚钱的授权之一。那么他的原理是什么，我们逐步揭开...... AC3的mantissa模块实际上就相当于AAC和MP3的二级量化模块和残差谱线，他的作用是进一步量化压缩频域谱线</summary><published>2012-05-16T22:12:00Z</published><updated>2012-05-16T22:12:00Z</updated><author><name>杭州桓泽</name><uri>http://www.cnblogs.com/gaozehua/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gaozehua/archive/2012/05/17/2505653.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gaozehua/archive/2012/05/17/2505653.html"/><content type="html">&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/04/29/2475899.html" target="_blank"&gt;[原创]桓泽学音频编解码（1）：MPEG1 MP3 系统算法分析&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/02/2478452.html" target="_blank"&gt;[原创]桓泽学音频编解码（2）：AC3/Dolby Digital 系统算法分析&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/03/2479960.html" target="_blank"&gt;[原创]桓泽学音频编解码（3）：AAC 系统算法分析&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/04/2482087.html"&gt;[原创]桓泽学音频编解码（4）：MP3 和 AAC 中反量化原理，优化设计与参考代码中实现&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/05/2484445.html"&gt;[原创]桓泽学音频编解码（5）：MP3 和 AAC 中IMDCT算法的原理，优化设计与参考代码中实现&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/06/2485613.html" target="_blank"&gt;[原创]桓泽学音频编解码（6）：MP3 无损解码模块算法分析&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/08/2489483.html"&gt;[原创]桓泽学音频编解码（7）：MP3 和 AAC 中huffman解码原理，优化设计与参考代码中实现&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/07/2486828.html" target="_blank"&gt;[原创]桓泽学音频编解码（8）：关于MP3和AAC量化器设计的研究&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/09/2491138.html" target="_blank"&gt;[原创]桓泽学音频编解码（9）：MP3 多相滤波器组算法分析&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a id="cb_post_title_url" class="postTitle2" href="http://www.cnblogs.com/gaozehua/archive/2012/05/15/2500864.html"&gt;[原创]桓泽学音频编解码（10）：AAC 无损解码模块算法分析&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 黑体;"&gt;&lt;a id="cb_post_title_url" class="postTitle2" href="http://www.cnblogs.com/gaozehua/archive/2012/05/16/2502738.html"&gt;[原创]桓泽学音频编解码（11）：AC3 exponent（指数部分）模块解码算法分析&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;1 编码概述&lt;/p&gt;&lt;p&gt;如以前的blog所述，编码时将所有的频率谱线分成exponent和mantissa两个部分。编码器将所有mantissa量化到固定等级的精度。尾数量化到15级或少于15级时，用均匀量化。大于15级用非均匀量化，通常用2的补码表示。几个量化的尾数值组合在一起被编码成一个公共的码字。如对于3级量化器，3个量化值组合后用5bit码字表示.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;2 解码过程&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 解码过程中码流中的尾数数据流部分被分解成长度不等的单个尾数或尾数组。在码流中，各个指数集合中的尾数以频率递增的顺序排列。不过，有些组出现与包含在组内的第一个尾数的位置上。组内后续的尾数没有来自数码流的信息。故不需要解包。&lt;/p&gt;&lt;p&gt;① 当比特分配指针值bap的范围是【6，15】时。使用非对称分数2的补码量化。各个尾数与其指数一起位变换系数的浮点表示。小数点在MSB的左边。故尾数字表达的范围是。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;（1.0-2^（qntztab[bap]-1））to -1.0&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;非对称量化的尾数不进行组合。&lt;/p&gt;&lt;p&gt;② 当比特分配指针值bap的范围是【1，5】时，尾数用编码值表示，通过查表把编码值变换到标准的2的补码分数二进制字。自码流中取出bap指出的比特数并确认无误。这个编码值作为查表的索引查找尾数值。将得出尾数安相应的指数右移以产生变换系数值。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Transform_coefficent[k]=mantissa[k]&amp;gt;&amp;gt;exponent[k];&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;③ 当比特分配指针值bap是0时。AC3解码器在标准中提供的是用随即噪声抖动代替量化值。Dithflag为0时使用真的0值。用于各声道的dithflag时变化的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;④ 当比特分配指针值bap是1，2，4的情况下。进一步压缩编码尾数值。包3级字和5级字组合成一些分开的代表3各尾数的组。11级字组合成对尾数的组。安尾数处理的次序把组填满。假设在一个指数集合中尾数的数目不能填满整数各组，将在指数集之间分担这些组。块中下一个指数集集训填入未填满的那些组。假设3级或5级量化的变换系数导出的字，器总数不能被3除尽，或假设11级字不能被2除尽，对一块的最后这些组添以虚设的尾数来组成混合组。有解码器舍弃这些虚设的尾数。用bap得出的长度从码流中取出各个组。3级量化尾数（bap＝1）分成3各组各5bit。5级量化尾数（bap＝2）分成三个一组各7bit。11级量化尾数（bap＝4）分成一对一对各7bit。&lt;/p&gt;&lt;p&gt;&amp;nbsp;解码公式为&lt;/p&gt;&lt;p&gt;bap = 1:&lt;/p&gt;&lt;p&gt;mantissa_code[a] = truncate (group_code / 9) ;&lt;/p&gt;&lt;p&gt;mantissa_code[b] = truncate ((group_code % 9) / 3 ) ;&lt;/p&gt;&lt;p&gt;mantissa_code[c] = (group_code % 9) % 3 ;&lt;/p&gt;&lt;p&gt;bap = 2:&lt;/p&gt;&lt;p&gt;mantissa_code[a] = truncate (group_code / 25) ;&lt;/p&gt;&lt;p&gt;mantissa_code[b] = truncate ((group_code % 25) / 5 ) ;&lt;/p&gt;&lt;p&gt;mantissa_code[c] = (group_code % 25) % 5 ;&lt;/p&gt;&lt;p&gt;bap = 4:&lt;/p&gt;&lt;p&gt;mantissa_code[a] = truncate (group_code / 11) ;&lt;/p&gt;&lt;p&gt;mantissa_code[b] = group_code % 11 ;&lt;/p&gt;&lt;p&gt;where mantissa a comes before mantissa b, which comes before mantissa c&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;table border="1" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;Bap&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;N&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;Bap&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;N&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;Bap&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;N&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;Bap&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;N&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;0&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;0&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;7&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;8&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;7&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;12&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;11&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;9&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;8&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;13&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;12&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;7&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;6&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;10&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;9&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;14&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;14&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;7&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;6&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;11&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;10&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;15&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="71"&gt;&lt;p&gt;16&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;3 C参考代码&lt;/p&gt;&lt;p&gt;重建频率谱线数据的过程要经过2个步骤。首先是计算反量化的mantissa值，其次是用mantissa和exponent一起重建频率谱线数据。如下图&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/134485/2012051706070233.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;在函数static sint_16 coeff_get_mantissa(uint_16 bap, uint_16 dithflag)内实现mantissa的解码。在函数convert_to_float(uint_16 exp, sint_16 mantissa)计算完成重建谱线数据。&lt;/p&gt;&lt;p&gt;函数coeff_get_mantissa的流程图如下。反量化步骤采取查表方式实现。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/134485/2012051706062583.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;函数convert_to_float代码如下&lt;/p&gt;&lt;p&gt;static inline float&amp;nbsp; convert_to_float(uint_16 exp, sint_16 mantissa)&lt;/p&gt;&lt;p&gt;{&lt;/p&gt;&lt;p&gt;float x;&lt;/p&gt;&lt;p&gt;//the scale by 2^-15 is built into the scale factor table&lt;/p&gt;&lt;p&gt;x = mantissa * scale_factor[exp];&lt;/p&gt;&lt;p&gt;//注意15是浮点变整点标志。&lt;/p&gt;&lt;p&gt;return x;&lt;/p&gt;&lt;p&gt;}&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gaozehua/aggbug/2505653.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gaozehua/archive/2012/05/17/2505653.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/JimmyZheng/archive/2012/05/17/2502727.html</id><title type="text">C# 温故而知新：Stream篇（七）</title><summary type="text">C# 温故而知新：Stream篇（七）NetworkStream目录：NetworkStream的作用简单介绍下TCP/IP 协议和相关层次简单说明下 TCP和UDP的区别简单介绍下套接字（Socket）的概念简单介绍下TcpClient,TcpListener,IPEndPoint类的作用使用NetworkStream的注意事项和局限性NetworkStream的构造NetworkStream的属性NetworkStream的方法NetwrokStream的简单示例 创建一个客户端向服务端传输图片的小示例本章总结1.NetworkStream的作用和先前的流有所不同，NetworkStre.</summary><published>2012-05-16T19:32:00Z</published><updated>2012-05-16T19:32:00Z</updated><author><name>逆时针の风</name><uri>http://www.cnblogs.com/JimmyZheng/</uri></author><link rel="alternate" href="http://www.cnblogs.com/JimmyZheng/archive/2012/05/17/2502727.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/JimmyZheng/archive/2012/05/17/2502727.html"/><content type="html">&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;C# 温故而知新：Stream篇（七）&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 15px;"&gt;NetworkStream&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 15px;"&gt;目录：&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#no1"&gt;NetworkStream的作用&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no2"&gt;简单介绍下TCP/IP 协议和相关层次&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no3"&gt;简单说明下 TCP和UDP的区别&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no4"&gt;简单介绍下套接字（Socket）的概念&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no5"&gt;简单介绍下TcpClient,TcpListener,IPEndPoint类的作用&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no6"&gt;使用NetworkStream的注意事项和局限性&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no7"&gt;NetworkStream的构造&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no8"&gt;NetworkStream的属性&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no9"&gt;NetworkStream的方法&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#no10"&gt;NetwrokStream的简单示例&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; 创建一个客户端向服务端传输图片的小示例&lt;/li&gt;&lt;li&gt;&lt;a href="#no11"&gt;本章总结&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;1.NetworkStream的作用&lt;/span&gt;&lt;/p&gt;&lt;p&gt;和&lt;a name="no1"&gt;&lt;/a&gt;先前的流有所不同，NetworkStream 的特殊性可以在它的命名空间中得以了解（System.Net.Sockets）,聪明的你马上会反应过来：&lt;/p&gt;&lt;p&gt;既然是在网络中传输的流，那必然有某种协议或者规则约束它，不错，这种协议便是Tcp/IP协议，这个是什么东东？别急，我先让大家了&lt;/p&gt;&lt;p&gt;解下NetworkStream的作用：&lt;span style="color: #ff0000;"&gt;如果服务器和客户端之间基于TCP连接的，他们之间能够依靠一个稳定的字节流进行相互传输信息，这也是&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;NetworkStream的最关键的作用&lt;/span&gt;，有了这个神奇的协议，NetWorkStream便能向其他流一样在网络中（进行点对点的传输），这种传输的&lt;/p&gt;&lt;p&gt;效率和速度是非常高的（UDP也很快，稍后再介绍）&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;如果大家对这个概念还不是很清晰的话，别怕，后文中我会更详细的说明&lt;/span&gt;&lt;/p&gt;&lt;table style="float: left;" border="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;这里有5点大家先理解就行&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;NetworkStream只能用在具有Tcp/IP协议之中，如果用在UDP中编译不报错，会报异常&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;NetworkStream 是面向连接的&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;在网络中利用流的形式传递信息&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;必须借助Socket (也称之为流式socket)，或使用一些返回的返回值，例如&lt;span style="color: #0000ff;"&gt;TcpClient&lt;/span&gt;类的&lt;span style="color: #0000ff;"&gt;G&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 14px;"&gt;&lt;span style="color: #0000ff;"&gt;etStream&lt;/span&gt;方法&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;用法和普通流方法几乎一模一样，但具有特殊性&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a name="no2"&gt;&lt;/a&gt;2.简单介绍下TCP/IP 协议和相关层次&lt;/span&gt;&lt;/p&gt;&lt;p&gt;提到协议相信许多初学者或者没搞过这块的朋友会一头雾水，&lt;/p&gt;&lt;p&gt;不过别怕，协议也是人定的，肯定能搞懂：&lt;/p&gt;&lt;table style="float: left;" border="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;其实协议可以这么理解，&lt;span style="color: #ff0000;"&gt;是人为定制的为某个活动定义的一些列规则和约束&lt;/span&gt;，&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;就好比足球赛上的红黄牌，这是由世界足联定制的协议或者规范，一旦不按照这个协议&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;足球赛肯定会一片混乱&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;进入正题：&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;TCP/IP&lt;/span&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 10.5pt; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-size: 11.0pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;&lt;br /&gt;全称：&lt;span style="color: #0000ff;"&gt;Transmission Control Protocol/Internet Protocol&lt;/span&gt; （传输控制协议/因特网互联协议，又名网络通讯协议）&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 10.5pt; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-size: 11.0pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;&lt;span style="font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;&lt;span style="color: red;"&gt;这个便是互联网中的最基本的协议，&lt;/span&gt;&lt;/span&gt;&lt;span style="color: red; font-family: 'Calibri','sans-serif'; font-size: 10.5pt; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;Tcp/IP &lt;/span&gt;&lt;span style="color: red; font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;定义了电子设备如何进入到互联网，以及数据如何在网络中传递&lt;/span&gt;&lt;span style="font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;。既然有了协议但是空头支票&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 10.5pt; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-size: 11.0pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;&lt;span style="font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;还是不行地，就好比足联定制了这些规则，但是没有裁判在球场上来实施这些规则一样，&lt;/span&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 10.5pt; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;Tcp/IP&lt;/span&gt;&lt;span style="font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;协议也有它自己的层次结构，关于它的层次&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 10.5pt; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-size: 11.0pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;&lt;span style="font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;结构，大家看图就能明白&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/132191/2012051603205876.png" alt="" width="544" height="311" /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #008080; font-size: 16px;"&gt;&amp;nbsp;发送数据：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;大家不用刻板的去理解这个协议，我还是用我们最普通的浏览网页来让大家理解下，首先打开浏览器输入一个url,这时候&lt;span style="color: #0000ff; font-size: 15px;"&gt;应用层&lt;/span&gt;会判断这个要求是否是http的&lt;/p&gt;&lt;p&gt;，然后http会将请求信息交给传输层来执行，&lt;span style="color: #0000ff; font-size: 16px;"&gt;传输层&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;主要负责信息流的&lt;span style="font-size: 16px;"&gt;格式化并且提供一个可靠地传输&lt;/span&gt;&lt;/span&gt;，这时候，TCP和UDP这两个协议在这里起作用了，&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt; TCP协议规定：接收端必须发回确认，并且假如分组丢失，必须&lt;span style="font-size: 16px;"&gt;重新发送&lt;/span&gt;&lt;/span&gt;，接着&lt;span style="color: #0000ff; font-size: 16px;"&gt;网络层&lt;/span&gt;得到了这些需要发送的数据，（&lt;span style="color: #ff0000;"&gt;网络中的IP协议非常重要，不仅是IP协议，&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;还有ARP协议（查找远程主机MAC地址）），&lt;/span&gt;这时候网络层会命令&lt;span style="color: #0000ff; font-size: 16px;"&gt;网络接口层&lt;/span&gt;去发送这些信息（&lt;span style="color: #ff0000;"&gt;IP层主要负责的是在节点之间（End to End）的数据包传送，&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;这里的节点是一台网络设备，比如计算机，大家便可理解为网络接口层的设备&lt;/span&gt;），最终将请求数据发送至远程网站主机后等待远程主机发送来信息&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;span style="color: #008080; font-size: 16px;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #008080; font-size: 16px;"&gt;接收数据：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;好了，&lt;span style="color: #ff0000;"&gt;远程网站主机会根据请求信息（Ip,数据报等等）发送一些列的网页数据通过网线或者无线路由，回到网络接口层，&lt;span style="font-size: 16px;"&gt;然后逐级上报&lt;/span&gt;&lt;/span&gt;，通过网络层的ip然后通过&lt;/p&gt;&lt;p&gt;传输层的一些列格式化，&lt;span style="color: #ff0000;"&gt;最终通过http返回至浏览器显示网页了&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #333399;"&gt;基于篇幅的关系，还有其他的协议大家可以自行去学习了解学习&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;喜欢足球的朋友的朋友也许会反应过来：这不是&lt;/span&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 10.5pt; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;2-4-5&lt;/span&gt;&lt;span style="font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;阵型么？其实不然，很多协议我还没画上去，其实大致含义就是每个层次上的协议（足球队员有他各自的职责），&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 宋体; font-size: 10.5pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;这些才能构成计算机与计算机之间的传输信息的桥梁。&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;&lt;span style="font-family: 宋体; font-size: 14pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;相信园子里很多大牛都写过&lt;/span&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 14pt; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;http &lt;/span&gt;&lt;span style="font-family: 宋体; font-size: 14pt; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"&gt;协议，大家也可以去学习下&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&amp;nbsp;&lt;a name="no3"&gt;&lt;/a&gt;3.简单说明下 TCP和UDP的区别&lt;/span&gt;&lt;/p&gt;&lt;p&gt;TCP:&lt;/p&gt;&lt;p&gt;1 TCP是面向&lt;span style="color: #ff0000;"&gt;连接的通信协议，通过三次握手建立连接&lt;/span&gt;&lt;/p&gt;&lt;p&gt;2 TCP提供的是一种可靠的数据流服务，&lt;span style="color: #ff0000;"&gt;采用&amp;ldquo;带重传的肯定确认&amp;rdquo;技术来实现传输的可靠性&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;UDP:&lt;/p&gt;&lt;p&gt;1 UDP是&lt;span style="color: #ff0000;"&gt;面向无连接的通讯协议&lt;/span&gt;，UDP数据包括目的端口号和源端口号信息，&lt;span style="color: #ff0000;"&gt;由于通讯不需要连接，所以可以实现广播发送&lt;/span&gt;&lt;/p&gt;&lt;p&gt;2 UDP通讯时不需要接收方确认，属于不可靠的传输，可能会出丢包现象，实际应用中要求在程序员编程验证&lt;/p&gt;&lt;p&gt;3 由于上述2点的关系，&lt;span style="color: #ff0000;"&gt;UDP传输速度更快，但是安全性比较差，很容易发生未知的错误，所以本章的NetworkStream无法使用在UDP的功能上&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #000000; font-size: 18pt;"&gt;&lt;a name="no4"&gt;&lt;/a&gt;4.简单介绍下套接字（Socket）的概念&lt;/span&gt;&lt;/p&gt;&lt;p&gt;关于Socket的概念和功能可能可以写很长一篇博文来介绍，&lt;span style="color: #ff0000;"&gt;这里大家把Socket理解Tcp/IP协议的抽象，并且能够实现Tcp/IP协议栈的工具就行&lt;/span&gt;，换句话说，我们可以&lt;/p&gt;&lt;p&gt;利用Socket实现客户端和服务端双向通信，同样，对于Socket最关键的理解还没到位，很多新人或者不常用的朋友会问：Socket到底功能是什么？怎么工作的？&lt;/p&gt;&lt;p&gt;再次举个例子，女友打电话给我，&lt;span style="color: #ff0000;"&gt;我可以选择连接，或者拒绝&lt;/span&gt;，如果我接了她的电话，也就是说，我和她通过电话&lt;span style="color: #ff0000;"&gt;连接（Connect）&lt;/span&gt;，那电话就是&amp;ldquo;Socket&amp;rdquo;，女友和我&lt;/p&gt;&lt;p&gt;都可以是客户端或服务端，只要点对点就行，&lt;span style="color: #ff0000;"&gt;我们的声音通过电话传递，但是具体传输内容不归Socket管辖范围&lt;/span&gt;，Socket的直接任务可以归纳为以下几点：&lt;/p&gt;&lt;table style="float: left;" border="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;创建客户端或服务端&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;服务端或客户端监听是否有服务端或客户端传来的连接信息(Listening)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;创建点对点的连接(Connect)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;发送accept 信息给对方，表示两者已经建立连接，并且可以互相传递信息了(Send)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;具体发送什么信息内容不是Socket管辖的范围，但是必须是Socket进行发送的动作&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 14px;"&gt;同理可以通过Socket去接受对方发来的信息，并加以处理&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;简单的Socket示例代码：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;a href="#no12"&gt;点击这里&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a name="no5"&gt;&lt;/a&gt;5.简单介绍下TcpClient,TcpListener,IPEndPoint类的作用&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;1: TcpClient&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;此类是微软基于Tcp封装类，用于简化Tcp客户端的开发，主要通过构造带入主机地址或者IPEndPonint对象，然后调用Connect进行和服务器点对点的连接，连接成功后通&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;过GetStream方法返回NetworkStream对象&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;2: TcpListener&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;此类也是微软基于Tcp封装类，用于监听服务端或客户端的连接请求，一旦有连接请求信息，立刻交给TcpClient的AcceptTcpClient方法捕获，Start方法用于开始监听&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;3: IPEndPonint&lt;/span&gt;&lt;/p&gt;&lt;p&gt;处理IP地址和端口的封装类&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;4:IPAddress&lt;/span&gt;&lt;/p&gt;&lt;p&gt;提供包含计算机在 IP 网络上的地址的工具类&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a name="no6"&gt;&lt;/a&gt;6．使用NetworkStream的注意事项和局限性&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;抱歉到目前为止才开始介绍NetworkStream,我相信大家到这里在回过头去看第一节的作用时能够更多的领悟&lt;/span&gt;。前五节意在说明下NetworkStream背后那个必须掌握的知识点，&lt;/p&gt;&lt;p&gt;这样才能在实际编程过程中很快上手，&lt;span style="color: #ff0000;"&gt;毕竟NetworkStream的工作环境和其他流有着很大的差别，&lt;/span&gt;&lt;/p&gt;&lt;p&gt;再回到第一节关于NetworkStream的知识点，在使用时有几点必须注意&lt;/p&gt;&lt;p&gt;首先&lt;/p&gt;&lt;p&gt;1 &lt;span style="color: #ff0000;"&gt;再次强调NetworkStream是稳定的，面向连接的，所以它只适合TCP协议的环境下工作&lt;/span&gt;&lt;/p&gt;&lt;p&gt;所以一旦在UDP环境中，虽然编译不会报错，但是会跳出异常&lt;/p&gt;&lt;p&gt;2 &lt;span style="color: #ff0000;"&gt;我们可以通过NetworkStream简化Socket开发&lt;/span&gt;&lt;/p&gt;&lt;p&gt;3 &lt;span style="color: #ff0000;"&gt;如果要建立NetworkStream一个新的实例，则必须使用已经连接的Socket&lt;/span&gt;&lt;/p&gt;&lt;p&gt;4 &lt;span style="color: #ff0000;"&gt;NetworkStream 使用后不会自动关闭提供的socket，必须使用NetworkStream构造函数时指定Socket所有权（&lt;span style="color: #ff0000;"&gt;NetworkStream &lt;/span&gt;的构造函数中设置）。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;6 &lt;span style="color: #ff0000;"&gt;NetworkStream支持异步读写操作&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 16px;"&gt;NetworkStream的局限性&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;可惜的是NetworkStream基于安全上的考虑不支持 Posion属性或Seek方法，寻找或改变流的位置，如果试图强行使用会报出NotSupport的异常&lt;/li&gt;&lt;li&gt;支持传递数据的种类没有直接使用Socket来的多&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a name="no7"&gt;&lt;/a&gt;7．NetworkStream的构造&lt;/span&gt;&lt;/p&gt;&lt;p&gt;1.NetworkStream (&lt;span style="color: #0000ff;"&gt;Socket&lt;/span&gt;) &amp;nbsp;为指定的 Socket 创建 NetworkStream 类的新实例&lt;/p&gt;&lt;p&gt;2.NetworkStream (&lt;span style="color: #0000ff;"&gt;Socket&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;Boolean&lt;/span&gt; ownsSocket) &amp;nbsp;用指定的 Socket 所属权为指定的 Socket&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;ownsSocket表示指示NetworkStream是否拥有该Socket&lt;/span&gt;&lt;/p&gt;&lt;p&gt;3.NetworkStream (&lt;span style="color: #0000ff;"&gt;Socket&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;FileAccess&lt;/span&gt;) &amp;nbsp;用指定的访问权限为指定的 Socket 创建&lt;/p&gt;&lt;p&gt;&lt;a href="http://msdn.microsoft.com/zh-cn/library/4z36sx0f(v=vs.80)"&gt;FileAccess&lt;/a&gt; 值的按位组合，这些值指定授予所提供的 Socket 上的 NetworkStream 的访问类型&lt;/p&gt;&lt;p&gt;4.NetworkStream (&lt;span style="color: #0000ff;"&gt;Socket&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;FileAccess&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;Boolean&lt;/span&gt; ownsSocket) 。&lt;/p&gt;&lt;table border="0" align="left"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;对于NetworkStream&lt;span style="color: #ff0000;"&gt;构造函数&lt;/span&gt;的理解相信大家经过前文的解释也能够掌握了，&lt;span style="color: #ff0000;"&gt;但是有几点&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000; font-size: 14px;"&gt;必须强调下&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;1如果用构造产生NetworkStream的实例，则必须使用连接的Socket&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;2 如果该NetworkStream拥有对Socket的所有权，则在使用NetworkStream的Close方法时&lt;span style="color: #ff0000;"&gt;会同时关闭Socket&lt;/span&gt;,&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;否则关闭NetworkStream时&lt;span style="color: #ff0000;"&gt;不会关闭Socket&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14px;"&gt;3, 能够创建对指定Socket带有读写权限的NetworkStream&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a name="no8"&gt;&lt;/a&gt;8．NetworkStream的属性&lt;/span&gt;&lt;/p&gt;&lt;p&gt;1. CanSeek :用于指示流是否支持查找，它的值&lt;span style="color: #ff0000;"&gt;始终为 false&lt;/span&gt;&lt;/p&gt;&lt;p&gt;2. DataAvailable &lt;span style="color: #ff0000;"&gt;指示在要读取的 NetworkStream 上是否有可用的数据&lt;/span&gt;。一般来说通过判断这个属性来判断NetworkStream中是否有数据&lt;/p&gt;&lt;p&gt;3. Length:&lt;span style="color: #ff0000;"&gt;NetworkStream不支持使用Length属性，强行使用会发生NotSupportedException异常&lt;/span&gt;&lt;/p&gt;&lt;p&gt;4.Position: &lt;span style="color: #ff0000;"&gt;NetworkStream不支持使用Position属性，强行使用会发生NotSupportedException异常&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a name="no9"&gt;&lt;/a&gt;9．NetworkStream的方法&lt;/span&gt;&lt;/p&gt;&lt;p&gt;同样，NetworkStream的方法大致重写或继承了Stream的方法&lt;/p&gt;&lt;p&gt;但是以下方法必须注意：&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;&lt;span style="color: #000000;"&gt;1&lt;/span&gt; int &lt;span style="color: #000000;"&gt;Read&lt;/span&gt;&lt;/span&gt;(&lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;[] buffer,&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; offset,&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; size)&lt;/p&gt;&lt;p&gt;该方法将数据读入 buffer 参数并返回成功读取的字节数。如果没有可以读取的数据，则 Read 方法返回 0。Read 操作将读取尽可能多的可用数据，&lt;/p&gt;&lt;p&gt;直至达到由 size 参数指定的字节数为止。如果远程主机关闭了连接并且已接收到所有可用数据，Read 方法将立即完成并返回零字节。&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;&lt;span style="color: #000000;"&gt;2&lt;/span&gt; long&lt;/span&gt; Seek(&lt;span style="color: #0000ff;"&gt;long&lt;/span&gt; offset, &lt;span style="color: #0000ff;"&gt;SeekOrigin&lt;/span&gt; origin)&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;将流的当前位置设置为给定值。此方法当前&lt;span style="font-size: 16px;"&gt;不受支持&lt;/span&gt;，总是引发 NotSupportedException。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;3 &amp;nbsp;&lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Write(&lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;[] buffer, &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; offset,&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; size)&lt;/p&gt;&lt;p&gt;Write方法在指&lt;span style="color: #ff0000;"&gt;定的 offset 处启动&lt;/span&gt;，&lt;span style="color: #ff0000;"&gt;并将 buffer 内容中的 size 字节&lt;span style="font-size: 16px;"&gt;发送到网络&lt;/span&gt;&lt;/span&gt;。&lt;span style="color: #ff0000;"&gt;Write 方法将一直处于阻止状态(可以用异步解决)&lt;/span&gt;，直到发送了请求&lt;/p&gt;&lt;p&gt;的字节数或引发 SocketException 为止。如果收到 SocketException，可以使用 &lt;span style="color: #ff0000;"&gt;SocketException.ErrorCode&lt;/span&gt; 属性获取特定的错误代码。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a name="no10"&gt;&lt;/a&gt;10．NetworkStream的简单示例&lt;/span&gt;&lt;/p&gt;&lt;p&gt;创建一个客户端向服务端传输图片的小示例&lt;/p&gt;&lt;p&gt;服务端一直监听客户端传来的图片信息&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;   &lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;   &lt;span style="color: #808080;"&gt;///&lt;/span&gt;&lt;span style="color: #008000;"&gt; 服务端监听客户端信息，一旦有发送过来的信息，便立即处理   &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;    &lt;span style="color: #0000ff;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt; Program    {        &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;全局TcpClient&lt;/span&gt;       &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt;&lt;span style="color: #000000;"&gt; TcpClient client;        &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;文件流建立到磁盘上的读写流&lt;/span&gt;       &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; FileStream fs = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; FileStream(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;E:\\abc.jpg&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, FileMode.Create);        &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;buffer&lt;/span&gt;       &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; bufferlength = &lt;span style="color: #800080;"&gt;200&lt;/span&gt;&lt;span style="color: #000000;"&gt;;       &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;[] buffer = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;&lt;span style="color: #000000;"&gt;[bufferlength];        &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;网络流&lt;/span&gt;       &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt;&lt;span style="color: #000000;"&gt; NetworkStream ns;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Main(&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;[] args)        {            ConnectAndListen();        }       &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; ConnectAndListen()         {           &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;服务端监听任何IP 但是端口号是80的连接&lt;/span&gt;            TcpListener listener = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; TcpListener(IPAddress.Any,&lt;span style="color: #800080;"&gt;80&lt;/span&gt;&lt;span style="color: #000000;"&gt;);           &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;监听对象开始监听&lt;/span&gt;&lt;span style="color: #000000;"&gt;            listener.Start();            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;while&lt;/span&gt;(&lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;)            {                Console.WriteLine(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;等待连接&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;线程会挂在这里，直到客户端发来连接请求&lt;/span&gt;                client =&lt;span style="color: #000000;"&gt; listener.AcceptTcpClient();                Console.WriteLine(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;已经连接&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;得到从客户端传来的网络流&lt;/span&gt;                ns =&lt;span style="color: #000000;"&gt; client.GetStream();                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;如果网络流中有数据&lt;/span&gt;                    &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt; (ns.DataAvailable)                    {                        &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;同步读取网络流中的byte信息                       &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; do                      &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;  {                      &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;  ns.Read(buffer, 0, bufferlength);                      &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;} while (readLength &amp;gt; 0);                        &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;异步读取网络流中的byte信息&lt;/span&gt;                        ns.BeginRead(buffer, &lt;span style="color: #800080;"&gt;0&lt;/span&gt;, bufferlength, ReadAsyncCallBack, &lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;);                    }            }        }       &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;       &lt;span style="color: #808080;"&gt;///&lt;/span&gt;&lt;span style="color: #008000;"&gt; 异步读取       &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;       &lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;param name="result"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;       &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; ReadAsyncCallBack(IAsyncResult result)        {           &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; readCount;           &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;获得每次异步读取数量&lt;/span&gt;           readCount =&lt;span style="color: #000000;"&gt; client.GetStream().EndRead(result);           &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;如果全部读完退出，垃圾回收&lt;/span&gt;           &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (readCount &amp;lt; &lt;span style="color: #800080;"&gt;1&lt;/span&gt;&lt;span style="color: #000000;"&gt;)            {               client.Close();               ns.Dispose();               fs.Dispose();               &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;;            }          &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;将网络流中的图片数据片段顺序写入本地&lt;/span&gt;           fs.Write(buffer, &lt;span style="color: #800080;"&gt;0&lt;/span&gt;, &lt;span style="color: #800080;"&gt;200&lt;/span&gt;&lt;span style="color: #000000;"&gt;);           &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;再次异步读取&lt;/span&gt;           ns.BeginRead(buffer, &lt;span style="color: #800080;"&gt;0&lt;/span&gt;, &lt;span style="color: #800080;"&gt;200&lt;/span&gt;, ReadAsyncCallBack, &lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;);       }    }&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;客户端先&lt;span style="color: #ff0000;"&gt;连接上服务端后&lt;/span&gt;在发送图片，注意如果是双向通信的话最好将客户端和服务端的项目设置为多个启动项便于调试&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;    &lt;span style="color: #0000ff;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt; Program    {       &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;       &lt;span style="color: #808080;"&gt;///&lt;/span&gt;&lt;span style="color: #008000;"&gt; 客户端       &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;       &lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;param name="args"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Main(&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;[] args)        {            SendImageToServer(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;xxx.jpg&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);        }           &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; SendImageToServer(&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt; imgURl)        {            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!File.Exists(imgURl)) &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;;             &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;创建一个文件流打开图片&lt;/span&gt;            &lt;span style="color: #0000ff;"&gt;FileStream&lt;/span&gt; fs =&lt;span style="color: #000000;"&gt;&lt;span style="color: #0000ff;"&gt; File&lt;/span&gt;.Open(imgURl, FileMode.Open);            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;声明一个byte数组接受图片byte信息&lt;/span&gt;            &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;[] fileBytes = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;&lt;span style="color: #000000;"&gt;[fs.Length];            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; (fs)            {                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;将图片byte信息读入byte数组中&lt;/span&gt;                fs.Read(fileBytes, &lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;, fileBytes.Length);                fs.Close();            }            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;找到服务器的IP地址&lt;/span&gt;            IPAddress address = IPAddress.Parse(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;127.0.0.1&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;创建TcpClient对象实现与服务器的连接&lt;/span&gt;            TcpClient client = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; TcpClient();            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;连接服务器&lt;/span&gt;            client.Connect(address, &lt;span style="color: #800080;"&gt;80&lt;/span&gt;&lt;span style="color: #000000;"&gt;);            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; (client)            {                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;连接完服务器后便在客户端和服务端之间产生一个流的通道&lt;/span&gt;                NetworkStream ns =&lt;span style="color: #000000;"&gt; client.GetStream();                &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; (ns)                {                    &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;通过此通道将图片数据写入网络流，传向服务器端接收&lt;/span&gt;                   ns.Write(fileBytes, &lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;, fileBytes.Length);                }            }        }    }&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;a name="no12"&gt;&lt;/a&gt;附件：&amp;nbsp;关于Socket的一个简单示例&lt;/p&gt;&lt;p&gt;服务器端建立服务并且循环监听&amp;nbsp;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;" onclick="cnblogs_code_show('e2297b87-41a2-4af0-ae3a-e136b2e8c04c')"&gt;&lt;img id="code_img_closed_e2297b87-41a2-4af0-ae3a-e136b2e8c04c" class="code_img_closed" src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt="" /&gt;&lt;img id="code_img_opened_e2297b87-41a2-4af0-ae3a-e136b2e8c04c" class="code_img_opened" style="display: none;" onclick="cnblogs_code_hide('e2297b87-41a2-4af0-ae3a-e136b2e8c04c',event)" src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="" /&gt;&lt;div id="cnblogs_code_open_e2297b87-41a2-4af0-ae3a-e136b2e8c04c" class="cnblogs_code_hide"&gt;&lt;pre&gt;        &lt;span style="color: #0000ff;"&gt;const&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; PORT = &lt;span style="color: #800080;"&gt;80&lt;/span&gt;&lt;span style="color: #000000;"&gt;;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;static&lt;/span&gt;&lt;span style="color: #000000;"&gt; Socket clientSocket;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;static&lt;/span&gt;&lt;span style="color: #000000;"&gt; Socket client;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Main(&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;[] args)        {          Thread thread&lt;/span&gt;=&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Thread(&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; ThreadStart(SetUpBlockServer));          thread.Start();        }        &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;        &lt;span style="color: #808080;"&gt;///&lt;/span&gt;&lt;span style="color: #008000;"&gt; 服务器端建立服务并且循环监听        &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; SetUpBlockServer()         {            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;同样建立TcpListener 监听对象监听客户端传来的信息&lt;/span&gt;            TcpListener lis = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; TcpListener(IPAddress.Any, PORT);            Console.WriteLine(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;正在监听任何端口号为80的任意IP的连接&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;启动监听&lt;/span&gt;&lt;span style="color: #000000;"&gt;            lis.Start();            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;while&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;)             {                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;进程会挂起，知道客户端的socket发送连接请求&lt;/span&gt;                clientSocket =&lt;span style="color: #000000;"&gt; lis.AcceptSocket();                Console.WriteLine(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;{0}时刻接收到客户端的连接请求&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;,DateTime.Now.ToString(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;G&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;));                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;连接成功后发送给客户端信息&lt;/span&gt;                &lt;span style="color: #0000ff;"&gt;string&lt;/span&gt; testMessage = &lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;连接成功&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;;                clientSocket.Send(Encoding.Default.GetBytes(testMessage));            }        }&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;客户端连接服务端的请求，和循环监听服务端传来的信息&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;" onclick="cnblogs_code_show('49a82a4e-2581-48ac-8d5a-17d32806999f')"&gt;&lt;img id="code_img_closed_49a82a4e-2581-48ac-8d5a-17d32806999f" class="code_img_closed" src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt="" /&gt;&lt;img id="code_img_opened_49a82a4e-2581-48ac-8d5a-17d32806999f" class="code_img_opened" style="display: none;" onclick="cnblogs_code_hide('49a82a4e-2581-48ac-8d5a-17d32806999f',event)" src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="" /&gt;&lt;div id="cnblogs_code_open_49a82a4e-2581-48ac-8d5a-17d32806999f" class="cnblogs_code_hide"&gt;&lt;pre&gt; &lt;span style="color: #0000ff;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt; Program    {        &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;端口&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;const&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; PORT = &lt;span style="color: #800080;"&gt;80&lt;/span&gt;&lt;span style="color: #000000;"&gt;;                &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Main(&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;[] args)        {            Connect(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;127.0.0.1&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);                 }        &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;        &lt;span style="color: #808080;"&gt;///&lt;/span&gt;&lt;span style="color: #008000;"&gt; 建立与服务器端的异步连接        &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;        &lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;param name="server"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Connect(&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt; server)         {            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;建立一个socket用来和服务的Socket进行通信&lt;/span&gt;            Socket socket = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;获得服务器IP地址&lt;/span&gt;            IPAddress ipAddress =&lt;span style="color: #000000;"&gt; IPAddress.Parse(server);            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;获得服务器端口&lt;/span&gt;            IPEndPoint point = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; IPEndPoint(ipAddress, PORT);            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;开始异步连接，注意将socket放入异步方法的参数中，提供给回调方法使用&lt;/span&gt;            socket.BeginConnect(point, &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; AsyncCallback(ConnectCallBack), socket);            Thread.Sleep(&lt;/span&gt;&lt;span style="color: #800080;"&gt;10000000&lt;/span&gt;&lt;span style="color: #000000;"&gt;);        }        &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;        &lt;span style="color: #808080;"&gt;///&lt;/span&gt;&lt;span style="color: #008000;"&gt;异步连接后的callback事件        &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;        &lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;param name="result"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; ConnectCallBack(IAsyncResult result)         {            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;try&lt;/span&gt;&lt;span style="color: #000000;"&gt;            {                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;建立一个接受信息的byte数组&lt;/span&gt;                &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;[] receiveBuffer = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;[&lt;span style="color: #800080;"&gt;4098&lt;/span&gt;&lt;span style="color: #000000;"&gt;];                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;从回调参数中获取上面Conntect方法中的socket对象&lt;/span&gt;                Socket socket = result.AsyncState &lt;span style="color: #0000ff;"&gt;as&lt;/span&gt;&lt;span style="color: #000000;"&gt; Socket;                &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;判断是否和服务端的socket建立连接&lt;/span&gt;                &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt; (socket.Connected)                {                    &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;开始 异步接收服务端传来的信息，同样将socket传入回调方法的参数中&lt;/span&gt;                    socket.BeginReceive(receiveBuffer, &lt;span style="color: #800080;"&gt;0&lt;/span&gt;, receiveBuffer.Length, SocketFlags.None, &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; AsyncCallback(ReceiveCallBack), socket);                }                &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;else&lt;/span&gt;&lt;span style="color: #000000;"&gt; { ConnectCallBack(result); }            }            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;catch&lt;/span&gt;&lt;span style="color: #000000;"&gt;             {                Console.WriteLine(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;连接出错&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);            }        }        &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;        &lt;span style="color: #808080;"&gt;///&lt;/span&gt;&lt;span style="color: #008000;"&gt;  一旦服务器发送信息，则会触发回调方法        &lt;/span&gt;&lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;        &lt;span style="color: #808080;"&gt;///&lt;/span&gt; &lt;span style="color: #808080;"&gt;&amp;lt;param name="result"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; ReceiveCallBack(IAsyncResult result)         {            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;[] receiveBuffer = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt;[&lt;span style="color: #800080;"&gt;4098&lt;/span&gt;&lt;span style="color: #000000;"&gt;];            Socket socket &lt;/span&gt;= result.AsyncState &lt;span style="color: #0000ff;"&gt;as&lt;/span&gt;&lt;span style="color: #000000;"&gt; Socket;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;读取从服务器端传来的数据，EndReceive是关闭异步接收方法，同时读取数据&lt;/span&gt;            &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; count =&lt;span style="color: #000000;"&gt; socket.EndReceive(result);            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (count &amp;gt; &lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;)             {                &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;try&lt;/span&gt;&lt;span style="color: #000000;"&gt;                {                   &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;接受完服务端的数据后的逻辑&lt;/span&gt;&lt;span style="color: #000000;"&gt;                }                &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;catch&lt;/span&gt;&lt;span style="color: #000000;"&gt;                 {                                }            }            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 递归监听服务器端是否发来信息，一旦服务器再次发送信息，客户端仍然可以接收到&lt;/span&gt;            socket.BeginReceive(receiveBuffer, &lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;, receiveBuffer.Length, SocketFlags.None, ReceiveCallBack,socket);        }    }&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;a name="no11"&gt;&lt;/a&gt;本章总结&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;本章简单介绍了关于NetworkStream以及其周边的一些衍生知识，这些知识的重要性不言而喻，从Tcp/IP协议到期分层结构，&lt;/p&gt;&lt;p&gt;&amp;nbsp; Socket和NetworkStream 的关系和注意事项，以及Socket在Tcp/IP协议中的角色等等，不知不觉Stream篇快接近于尾声了&lt;/p&gt;&lt;p&gt;&amp;nbsp; 期间感谢大家的关注，今后我会写新的系列，&lt;span style="color: #ff0000;"&gt;请大家多多鼓励&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/JimmyZheng/aggbug/2502727.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/JimmyZheng/archive/2012/05/17/2502727.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wufengtinghai/archive/2012/05/17/2505640.html</id><title type="text">跟JBPM学习设计模式之简单工厂模式</title><summary type="text">跟JBPM学习设计模式之简单工厂模式模式简介 简单工厂模式是类的创建模式，其专门负责将大量拥有共同接口或者基类的产品类进行实例化。简单工厂模式由工厂类运行时根据传入的参数动态的决定需要实例化的类，这样客户只需要了解产品的基类，解除客户对具体产品类的依赖。简单工厂模式的结构如下图所示图 1. 简单工厂模式结构图 从图中可以看出，简单工厂模式主要涉及一下三种结构角色1. 工厂类，其有客户端直接调用，并根据具体的业务逻辑要求创建相应的产品对象实例；2. 产品基类，所有具体的产品类的基类或者接口，封装所有具体产品共享的行为（和数据）, 客户需要了解产品基类；3. 具体的产品类，本模式需要创建的实例的.</summary><published>2012-05-16T18:16:00Z</published><updated>2012-05-16T18:16:00Z</updated><author><name>无风听海</name><uri>http://www.cnblogs.com/wufengtinghai/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wufengtinghai/archive/2012/05/17/2505640.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wufengtinghai/archive/2012/05/17/2505640.html"/><content type="html">&lt;div&gt; &lt;p align="center"&gt;&lt;span style="font-family: SimSun;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;跟JBPM学习设计模式之简单工厂模式&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&lt;strong&gt;模式简介&lt;/strong&gt;&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 简单工厂模式是类的创建模式，其专门负责将大量拥有共同接口或者基类的产品类进行实例化。简单工厂模式由工厂类运行时根据传入的参数动态的决定需要实例化的类，这样客户只需要了解产品的基类，解除客户对具体产品类的依赖。简单工厂模式的结构如下图所示&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&lt;img src="http://my.csdn.net/uploads/201205/17/1337191967_3031.png" alt="" /&gt;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;图 1. 简单工厂模式结构图&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 从图中可以看出，简单工厂模式主要涉及一下三种结构角色&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;1.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 工厂类，其有客户端直接调用，并根据具体的业务逻辑要求创建相应的产品对象实例；&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;2.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 产品基类，所有具体的产品类的基类或者接口，封装所有具体产品共享的行为（和数据）, 客户需要了解产品基类；&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;3.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 具体的产品类，本模式需要创建的实例的类型；&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&lt;strong&gt;JBPM中的简单工厂模式&lt;/strong&gt;&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JBPM中对流程引擎的对象的实例化使用了简单工厂模式。具体的模式结构图如下&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&lt;img src="http://my.csdn.net/uploads/201205/17/1337191978_2002.png" alt="" /&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;图 2. JBPM简单工厂结构图&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 从图中我们可以看到ProcessEngine充当了产品基类的角色，由于流程引擎在JBPM中所处的特殊地位，所以这个接口需要能够获取各种服务和业务中需要使用的一些对象实例等。&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 具体的产品类是ProcessEngineImpl和SpringProcessEngine类，JBPM的流程引擎可以使用spring作为承载的环境，默认情况下会使用前者，如果我们需要使用spring，只需要在jbpm.cfg.xml中引入jbpm.tx.spring.cfg.xml即可。&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 工厂类的角色是由ConfigurationImpl来实现的，其根据配置文件中是否启用spring来实例化具体的流程引擎对象。&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其实SpringProcessEngine本身也是一个精简版的简单工厂模式，其自己负责实例化自己，其同时担当了工厂和具体产品的角色。具体的类图结构如下&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&lt;img src="http://my.csdn.net/uploads/201205/17/1337191988_3396.png" alt="" /&gt;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;图 3. SpringProcessEngine精简版简单工厂设计模式&lt;/p&gt;&lt;p align="center" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&lt;strong&gt;简单工厂模式的优劣&lt;/strong&gt;&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 该模式的核心是工厂类，其负责必要的逻辑判断（反射的话可以不需要），决定最终创建哪一个产品类的实例。客户是不负责产品类的实例化，而是仅仅使用返回的产品，实现了两者间的职责分离。&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 该模式中，工厂类负责所有具体产品类的创建逻辑，所以其就对所有的产品类形成了强烈的依赖，如果产品类很多，可能对工厂类的修改就会影响到整个系统。&lt;/p&gt;&lt;p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当我们扩展了新的产品类的时候，需要修改工厂类，违反了开闭原则。&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family: SimSun;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/wufengtinghai/aggbug/2505640.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wufengtinghai/archive/2012/05/17/2505640.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lwhkdash/archive/2012/05/17/2505617.html</id><title type="text">基于WPF+XMPP的IM程序开发日志 之二 WPF线程模型</title><summary type="text">循例地在开始正文前说些废话。正如这篇博客的题目——开发日志，这系列的博客是我在编写这个IM的一些日志，或者另外一个说法：笔记。并不是一些系统的文章，例如“XX学习教程”。这些博客里面的内容主要记录我碰到的问题及对问题领悟，免得日后碰到这些问题又去google一番，而并不是一些“如何编写IM程序”的教程。一：WPF线程模型。 除非已经对WPF体系结构非常熟悉，对多线程开发很了解，不然我们在与WPF打交道的时候经常会遇到这样一个异常：由于其他线程拥有此对象，因此调用线程无法对其进行访问。(The calling thread cannot access this object because a.</summary><published>2012-05-16T16:45:00Z</published><updated>2012-05-16T16:45:00Z</updated><author><name>柯柏文</name><uri>http://www.cnblogs.com/lwhkdash/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lwhkdash/archive/2012/05/17/2505617.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lwhkdash/archive/2012/05/17/2505617.html"/><content type="html">&lt;p&gt;&amp;nbsp; 循例地在开始正文前说些废话。正如这篇博客的题目&amp;#8212;&amp;#8212;开发日志，这系列的博客是我在编写这个IM的一些日志，或者另外一个说法：笔记。并不是一些系统的文章，例如&amp;#8220;XX学习教程&amp;#8221;。这些博客里面的内容主要记录我碰到的问题及对问题领悟，免得日后碰到这些问题又去google一番，而并不是一些&amp;#8220;如何编写IM程序&amp;#8221;的教程。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong style="font-size: 18pt; color: #333300; "&gt;&amp;nbsp;一：WPF线程模型&lt;/strong&gt;&lt;strong style="font-size: 14pt; color: #003300; "&gt;。&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;除非已经对WPF体系结构非常熟悉，对多线程开发很了解，不然我们在与WPF打交道的时候经常会遇到这样一个异常：&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000; "&gt;由于其他线程拥有此对象，因此调用线程无法对其进行访问。(&lt;/span&gt;&lt;span style="color: #ff0000; "&gt;The calling thread cannot access this object because a different thread owns it&lt;/span&gt;&lt;span style="color: #ff0000; "&gt;)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;在WPF中，天生拥有两个线程，一个线程用于&lt;span style="background-color: #ffffff; color: #333333; font-family: verdana, 宋体, Arial; font-size: 13px; line-height: 24px; "&gt;渲染&lt;/span&gt;UI，另一个线程是管理UI（这个我们称之为UI线程）。传说中android的动画效果为什么没有iphone的动画效果好，就是因为iphone的绘制渲染的线程的优先级非常高，只要有关于动画的操作，比如说滑动一个菜单，那么这个动画会被安排到最优先级运行，从而保证动画的流畅。这我没有深入研究，所以可能技术上来说以上描述不是十分正确，但可以按这个方式去理解。大概WPF中也是这样的理念。UI线程创建了那些在XAML或者在c#中定义的控件，并且拥有他们，并且出于对UI的保护，其他线程是不能访问到UI线程里的东西的，如果我们新建一个线程，然后在这个线程里面修改一个在xaml中或者在主线程中定义的Button.Content，那么就会得到这个异常。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;在IM开发过程中，使用agsXMPP库的时候，agsXMPP有很多事件，比如XmppClientConnection.OnStateChanged事件，OnError事件等等，我们会用到很多事件处理函数，在这里必须注意一点就是，当这些事件被触发，代码被执行到事件处理函数里面的时候，执行代码的线程往往不是主线程(这里&amp;#8220;往往&amp;#8221;不知道用的对不对，反正我碰到的都不是在主线程中执行的)，也就是说，如果这时在事件处理函数中写这样的代码：button1.content="something"，就会抛出&lt;span style="color: #ff0000; "&gt;由于其他线程拥有此对象，因此调用线程无法对其进行访问&lt;/span&gt;异常。我们可以再visual studio中调试代码时候在看到当前执行代码的是主线程还是其他线程：&lt;img src="http://images.cnblogs.com/cnblogs_com/lwhkdash/ThreadingView.jpg" alt="" /&gt;如果线程一栏中没有写明是&amp;#8220;主线程&amp;#8221;，那么当前执行代码的线程就不是主线程。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;这个时候，假如我们必须在其他线程中访问控件，怎么办？这就需要通过Dispatcher了。WPF中大多数控件都继承自DispatcherObject，也就拥有Dispatcher属性，这个Dispatatcher具体是什么东西，我就不写了，因为我也不知道，但显浅的来说，它是对他所属的线程进行工作调度的这么一个对象，或者说线程的一个管家，或者中介。你要在一个拥有某个控件的线程的外部(或者说其他线程)访问这个线程的控件，就只能通过这个控件的Dispatcher来处理了，Dispatcher有两个方法:Invoke和BeginInvoke，用来对外开放访问这个Dispatcher所属的线程所拥有的控件的机会。比如我想在其他线程中访问主线程的Button:&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&amp;nbsp;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000ff; "&gt;private&lt;/span&gt;&amp;nbsp;&lt;span style="color: #0000ff; "&gt;void&lt;/span&gt;&amp;nbsp;OnEventFired(&lt;span style="color: #0000ff; "&gt;object&lt;/span&gt;&amp;nbsp;sender,&amp;nbsp;MouseButtonEventArgs&amp;nbsp;e)&lt;/p&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;btn.Dispatcher.Invoke(&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&amp;nbsp;Action(&lt;span style="color: #0000FF;"&gt;delegate&lt;/span&gt;&amp;nbsp;{ button1.Content&amp;nbsp;=&amp;nbsp;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;some&amp;nbsp;text&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;;&amp;nbsp;}));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New'; font-size: 13px; line-height: 19px; background-color: #f5f5f5; "&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;假设OnEventFired正被其他线程执行，代码里面通过btn.Dispatcher的Invoke方法执行一个(匿名的)函数，该函数里面是设置buttton1.content。Invoke和BeginInvoke前者是即时调用和异步调用&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;strong&gt;&lt;span style="font-size: 18pt; "&gt; &amp;nbsp; 二：Thread.&lt;/span&gt;&lt;span style="font-size: 18pt; "&gt;ApartmentState&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;有时我们会遇到这样的异常：&lt;span style="color: #ff0000; "&gt;调用线程必须为 STA，因为许多 UI 组件都需要。&amp;nbsp;(&lt;/span&gt;&lt;span style="color: #ff0000; "&gt;The calling thread must be STA, because many UI components require this&lt;/span&gt;&lt;span style="color: #ff0000; "&gt;)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;这是因为在WPF中，不是什么线程都可以创建控件的，或者说：在WPF中，不是所有控件都能在非STA线程中创建的。这个STA全称 Single Thread Apartment,另一个对应的是MTA (Multi Thread Apartment)。STA 线程是COM里面的概念（具体请另行google），因为WPF底层应该与COM有关联，新创建一个控件时某些地方与COM有勾搭，所以一个线程想新建一个控件，这个线程往往需要设置成STA（Thread.ApartmentState属性）。为什么主线程可以新建一个控件？因为主线程是STA线程。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;以上主要说了一下WPF的一些概念和技术点，但由于我还没有非常深入去研究，所以以上内容中很多地方可能说得不对，即使目前我是这样理解。另一方面，个人感觉要解决技术问题，还是上外国一些网站，例如stackoverflow.com中找一些资料，这就是为什么我将异常的英文也一同写上，便于在google in enlish中搜索。在google上一搜国内的资料，会发现10篇文章中，有8篇是转载第九篇的，而第9篇却是只说了其然，而不说其所以然的。而搜一些英文网站，往往可以看到老外的回复不只是"mark"，还有很多深入到原因，或者详细原理里面去解释，令人更好地了解某个技术点。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lwhkdash/aggbug/2505617.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lwhkdash/archive/2012/05/17/2505617.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/brucewoo/archive/2012/05/17/2505596.html</id><title type="text">Glusterfs之rpc模块源码分析(下）之RDMA over TCP的协议栈工作过程浅析</title><summary type="text">声明：本文转至IT168：因为GlusterFS实现了IB网络（RDMA协议，专用硬件网卡支持），当时就在想普通的网卡能不能实现，就找到一篇这样的文章介绍，基于TCP协议来实现。！附件RDMAoverTCP的协议栈工作过程浅析第一节RDMA概述随着网络带宽和速度的发展和大数据量数据的迁移的需求，网络带宽增长速度远远高于处理网络流量时所必需的计算节点的能力和对内存带宽的需求，数据中心网络架构已经逐步成为计算和存储技术的发展的瓶颈，迫切需要采用一种更高效的数据通讯架构。 传统的TCP/IP技术在数据包处理过程中，要经过操作系统及其他软件层，需要占用大量的服务器资源和内存总线带宽，所产生严重的延迟.</summary><published>2012-05-16T16:10:00Z</published><updated>2012-05-16T16:10:00Z</updated><author><name>蔷薇理想人生</name><uri>http://www.cnblogs.com/brucewoo/</uri></author><link rel="alternate" href="http://www.cnblogs.com/brucewoo/archive/2012/05/17/2505596.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/brucewoo/archive/2012/05/17/2505596.html"/><content type="html">&lt;p&gt;&lt;strong&gt;&lt;span&gt;声明：本文转至IT168：因为GlusterFS实现了IB网络（RDMA协议，专用硬件网卡支持），当时就在想普通的网卡能不能实现，就找到一篇这样的文章介绍，基于TCP协议来实现。！&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;附件&amp;nbsp;&lt;span&gt;RDMA&amp;nbsp;over&amp;nbsp;TCP&lt;/span&gt;&lt;span&gt;的协议栈工作过程浅析&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第一节&amp;nbsp;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;概述&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;随着网络带宽和速度的发展和大数据量数据的迁移的需求，网络带宽增长速度远远高于处理网络流量时所必需的计算节点的能力和对内存带宽的需求，数据中心网络架构已经逐步成为计算和存储技术的发展的瓶颈，迫切需要采用一种更高效的数据通讯架构。&lt;/p&gt;&lt;p&gt;传统的&lt;span&gt;TCP/IP&lt;/span&gt;&lt;span&gt;技术在数据包处理过程中，要经过操作系统及其他软件层，需要占用大量的服务器资源和内存总线带宽，所产生严重的延迟来自系统庞大的开销、数据在系统内存、处理器缓存和网络控制器缓存之间来回进行复制移动，如图&lt;/span&gt;&lt;span&gt;1.1&lt;/span&gt;&lt;span&gt;所示，给服务器的&lt;/span&gt;&lt;span&gt;CPU&lt;/span&gt;&lt;span&gt;和内存造成了沉重负担。特别是面对网络带宽、处理器速度与内存带宽三者的严重&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;不匹配性&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;，更造成了网络延迟效应的加剧。处理器速度比内存速度快得越多，等待相应数据的延迟就越多。而且，处理每一数据包时，数据必须在系统内存、处理器缓存和网络控制器缓存之间来回移动，因此延迟并不是一次性的，而是会对系统性能持续产生负面影响。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;img src="http://my.csdn.net/uploads/201205/14/1337007101_4139.png" alt="" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;图&lt;span&gt;1.1&amp;nbsp;&lt;/span&gt;&lt;span&gt;主机接收传统以太网数据的典型数据流示意图&lt;/span&gt;&lt;/p&gt;&lt;p&gt;这样，以太网的低投入、低运营成本优势就难以体现。为充分发挥万兆位以太网的性能优势，必须解决应用性能问题。系统不能以软件方式持续处理以太网通信；主机&lt;span&gt;CPU&lt;/span&gt;&lt;span&gt;资源必须释放专注于应用处理。业界最初的解决方案是采用&lt;/span&gt;&lt;span&gt;TCP/IP&lt;/span&gt;&lt;span&gt;负荷减轻引擎（&lt;/span&gt;&lt;span&gt;TOE&lt;/span&gt;&lt;span&gt;）。&lt;/span&gt;&lt;span&gt;TOE&lt;/span&gt;&lt;span&gt;方案能提供系统性能，但协议处理不强；它能使&lt;/span&gt;&lt;span&gt;TCP&lt;/span&gt;&lt;span&gt;通信更快速，但还达不到高性能网络应用的要求。解决这类问题的关键，是要消除主机&lt;/span&gt;&lt;span&gt;CPU&lt;/span&gt;&lt;span&gt;中不必要的频繁数据传输，减少系统间的信息延迟。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;（&lt;/span&gt;&lt;span&gt;Remote&amp;nbsp;Direct&amp;nbsp;Memory&amp;nbsp;Access&lt;/span&gt;&lt;span&gt;）全名是&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;远程直接数据存取&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;，&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;让计算机可以直接存取其它计算机的内存，而不需要经过处理器耗时的传输，如图&lt;/span&gt;&lt;span&gt;1.2&lt;/span&gt;&lt;span&gt;所示。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;是一种使一台计算机可以直接将数据通过网络传送到另一台计算机内存中的特性，将数据从一个系统快速移动到远程系统存储器中，而不对操作系统造成任何影响，这项技术通过消除外部存储器复制和文本交换操作，因而能腾出总线空间和&lt;/span&gt;&lt;span&gt;CPU&lt;/span&gt;&lt;span&gt;周期用于改进应用系统性能，从而减少对带宽和处理器开销的需要，显著降低了时延。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;img src="http://my.csdn.net/uploads/201205/14/1337007180_3128.png" alt="" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;图&lt;span&gt;1.2&amp;nbsp;RDMA&lt;/span&gt;&lt;span&gt;数据流传输示意图&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;对以太网来说还是&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;新生事物&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;，但以不同形式存在已有十多年时间，它是&lt;/span&gt;&lt;span&gt;Infiniband&lt;/span&gt;&lt;span&gt;技术的基础。产业标准&lt;/span&gt;&lt;span&gt;API&lt;/span&gt;&lt;span&gt;（应用程序接口）使&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;从技术走向实现成为可能。其中包括用于低时延消息处理、成就高性能计算的&lt;/span&gt;&lt;span&gt;MPI&lt;/span&gt;&lt;span&gt;（消息通过接口），以及&lt;/span&gt;&lt;span&gt;DAPL&lt;/span&gt;&lt;span&gt;（直接接入供应库）。后者包括两部分：&lt;/span&gt;&lt;span&gt;KDAPL&lt;/span&gt;&lt;span&gt;和&lt;/span&gt;&lt;span&gt;UDAPL&lt;/span&gt;&lt;span&gt;，分别用于内核和用户（应用程序）。&lt;/span&gt;&lt;span&gt;Linux&lt;/span&gt;&lt;span&gt;支持&lt;/span&gt;&lt;span&gt;KDAPL&lt;/span&gt;&lt;span&gt;，其它操作系统将来也有可能支持。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;在高性能计算环境广为采纳，在商务应用领域很少，但如今大多应用程序都能直接支持操作系统，透过操作系统（如&lt;/span&gt;&lt;span&gt;NFS&lt;/span&gt;&lt;span&gt;）间接利用&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;技术的优势是完全可能的。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第二节&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;工作原理&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;是一种网卡技术，采用该技术可以使一台计算机直接将信息放入另一台计算机的内存中。通过最小化处理过程的开销和带宽的需求降低时延。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;通过在网卡上将可靠传输协议固化于硬件，以及支持零复制网络技术和内核内存旁路技术这两种途径来达到这一目标。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;模型如图&lt;/span&gt;&lt;span&gt;2.1&lt;/span&gt;&lt;span&gt;所示。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;img src="http://my.csdn.net/uploads/201205/14/1337007207_6110.png" alt="" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;图&lt;span&gt;2.1&amp;nbsp;RDMA&lt;/span&gt;&lt;span&gt;模型演变&lt;/span&gt;&lt;/p&gt;&lt;p&gt;零复制网络技术使&lt;span&gt;NIC&lt;/span&gt;&lt;span&gt;可以直接与应用内存相互传输数据，从而消除了在应用内存与内核内存之间复制数据的需要。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;内核内存旁路技术使应用程序无需执行内核内存调用就可向网卡发送命令。在不需要任何内核内存参与的条件下，&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;请求从用户空间发送到本地&lt;/span&gt;&lt;span&gt;NIC&lt;/span&gt;&lt;span&gt;并通过网络发送给远程&lt;/span&gt;&lt;span&gt;NIC&lt;/span&gt;&lt;span&gt;，这就减少了在处理网络传输流时内核内存空间与用户空间之间环境切换的次数。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;当一个应用程序执行&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;读&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;写请求时，系统并不执行数据复制动作，这就减少了处理网络通信时在内核空间和用户空间上下文切换的次数。在不需要任何内核内存参与的条件下，&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;请求从运行在用户空间中的应用中发送到本地&amp;nbsp;&lt;/span&gt;&lt;span&gt;NIC&lt;/span&gt;&lt;span&gt;（网卡），然后经过网络传送到远程&lt;/span&gt;&lt;span&gt;NIC&lt;/span&gt;&lt;span&gt;。请求完成既可以完全在用户空间中处理&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;通过轮询用户级完成排列&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;，或者在应用一直睡眠到请求完成时的情况下通过内核内存处理。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;RDMA&lt;span&gt;操作使应用可以从一个远程应用的内存中读数据或向这个内存写数据。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;操作用于读写操作的远程虚拟内存地址包含在&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;消息中传送，远程应用程序要做的只是在其本地网卡中注册相应的内存缓冲区。远程节点的&lt;/span&gt;&lt;span&gt;CPU&lt;/span&gt;&lt;span&gt;在整个&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;操作中并不提供服务，因此没有带来任何负载。通过类型值（键值）的使用，一个应用程序能够在远程应用程序对它进行随机访问的情况下保护它的内存。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;发布&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;操作的应用程序必须为它试图访问的远程内存指定正确的类型值，远程应用程序在本地网卡中注册内存时获得这个类型值。发布&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;的应用程序也必须确定远程内存地址和该内存区域的类型值。远程应用程序会将相关信息通知给发布&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;的应用程序，这些信息包括起始虚拟地址、内存大小和该内存区域的类型值。在发布&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;的应用程序能够对该内存区域进行&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;操作之前，远程应用程序应将这些信息通过发送操作传送给发布&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;的应用程序。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第三节&lt;span&gt;RDMA&amp;nbsp;&lt;/span&gt;&lt;span&gt;操作类型&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;具备&lt;span&gt;RNIC&lt;/span&gt;&lt;span&gt;（&lt;/span&gt;&lt;span&gt;RDMA-aware&amp;nbsp;network&amp;nbsp;interface&amp;nbsp;controller&lt;/span&gt;&lt;span&gt;）网卡的设备，不论是目标设备还是源设备的主机处理器都不会涉及到数据传输操作，&lt;/span&gt;&lt;span&gt;RNIC&lt;/span&gt;&lt;span&gt;网卡负责产生&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;数据包和接收输入的&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;数据包，从而消除传统操作中多余的内存复制操作。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;协议提供以下&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;种数据传输操作，除了&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;读操作不会产生&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;消息，其他操作都会产生一条&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;消息。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&amp;nbsp;Send&lt;/span&gt;&lt;span&gt;操作；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;Send&amp;nbsp;operation&lt;/span&gt;&lt;span&gt;；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;Send&amp;nbsp;with&amp;nbsp;invalidate&amp;nbsp;operation&lt;/span&gt;&lt;span&gt;；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;Send&amp;nbsp;with&amp;nbsp;solicited&amp;nbsp;event&lt;/span&gt;&lt;span&gt;；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;Send&amp;nbsp;with&amp;nbsp;solicited&amp;nbsp;event&amp;nbsp;and&amp;nbsp;invalidate&lt;/span&gt;&lt;span&gt;；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&amp;nbsp;Write&lt;/span&gt;&lt;span&gt;操作；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&amp;nbsp;Read&lt;/span&gt;&lt;span&gt;操作；&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;Terminate&lt;/span&gt;&lt;span&gt;操作。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第四节&amp;nbsp;&lt;span&gt;RDMA&amp;nbsp;over&amp;nbsp;TCP&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;以太网凭借其低投入、后向兼容、易升级、低运营成本优势在目前网络互连领域内占据统治地位，目前主流以太网速率是&lt;span&gt;100&amp;nbsp;Mb/s&lt;/span&gt;&lt;span&gt;和&lt;/span&gt;&lt;span&gt;1000&amp;nbsp;Mb/s&lt;/span&gt;&lt;span&gt;，下一代以太网速率将会升级到&lt;/span&gt;&lt;span&gt;10&amp;nbsp;Gb/s&lt;/span&gt;&lt;span&gt;。将&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;特性增加到以太网中，将会降低主机处理器利用率，增加以太网升级到&lt;/span&gt;&lt;span&gt;10&amp;nbsp;Gb/s&lt;/span&gt;&lt;span&gt;的优点，消除由于升级到&lt;/span&gt;&lt;span&gt;10&amp;nbsp;Gb/s&lt;/span&gt;&lt;span&gt;而引入巨大开销的弊端，允许数据中心在不影响整体性能的前提下拓展机构，为未来扩展需求提供足够的灵活性。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&amp;nbsp;over&amp;nbsp;TCP&lt;/span&gt;&lt;span&gt;协议将数据直接在两个系统的应用内存之间进行交互，对操作系统内核几乎没有影响，并且不需要临时复制到系统内存的操作，数据流如图&lt;/span&gt;&lt;span&gt;4.1&lt;/span&gt;&lt;span&gt;所示。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;img src="http://my.csdn.net/uploads/201205/14/1337007238_4259.png" alt="" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;图&lt;span&gt;4.1&amp;nbsp;RDMA&amp;nbsp;over&amp;nbsp;TCP&amp;nbsp;(Ethernet)&lt;/span&gt;&lt;span&gt;数据流示意图&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&amp;nbsp;over&amp;nbsp;TCP&lt;/span&gt;&lt;span&gt;协议能够工作在标准的基于&lt;/span&gt;&lt;span&gt;TCP/IP&lt;/span&gt;&lt;span&gt;协议的网络，如目前在各个数据中心广泛使用的以太网。注意：&lt;/span&gt;&lt;span&gt;RDMA&amp;nbsp;over&amp;nbsp;TCP&lt;/span&gt;&lt;span&gt;并没有指定物理层信息，能够工作在任何使用&lt;/span&gt;&lt;span&gt;TCP/IP&lt;/span&gt;&lt;span&gt;协议的网络上层。&lt;/span&gt;&lt;span&gt;RDMA&amp;nbsp;over&amp;nbsp;TCP&lt;/span&gt;&lt;span&gt;允许很多传输类型来共享相同的物理连接，如网络、&lt;/span&gt;&lt;span&gt;I/O&lt;/span&gt;&lt;span&gt;、文件系统、块存储和处理器之间的消息通讯。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;img src="http://my.csdn.net/uploads/201205/14/1337007261_6733.png" alt="" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;图&lt;span&gt;4.2&amp;nbsp;RDMA&amp;nbsp;over&amp;nbsp;TCP&amp;nbsp;(Ethernet)&lt;/span&gt;&lt;span&gt;协议栈&lt;/span&gt;&lt;/p&gt;&lt;p&gt;图&lt;span&gt;4.2&lt;/span&gt;&lt;span&gt;是&lt;/span&gt;&lt;span&gt;RDMA&amp;nbsp;over&amp;nbsp;TCP&amp;nbsp;(Ethernet)&lt;/span&gt;&lt;span&gt;的协议栈，最上面三层构成&lt;/span&gt;&lt;span&gt;iWARP&lt;/span&gt;&lt;span&gt;协议族，用来保证高速网络的互操作性。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;层协议负责根据&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;写操作、&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;读操作转换成&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;消息，并将&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;消息传向&lt;/span&gt;&lt;span&gt;Direct&amp;nbsp;Data&amp;nbsp;Placement&amp;nbsp;(DDP)&lt;/span&gt;&lt;span&gt;层。&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;层协议负责将过长的&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;消息分段封装成&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;数据包继续向下转发到&lt;/span&gt;&lt;span&gt;Marker-based,&amp;nbsp;Protocol-data-unit-Aligned&amp;nbsp;(MPA)&lt;/span&gt;&lt;span&gt;层。&lt;/span&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;层在&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;数据段的固定间隔位置增加一个后向标志、长度以及&lt;/span&gt;&lt;span&gt;CRC&lt;/span&gt;&lt;span&gt;校验数据，构成&lt;/span&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;数据段。&lt;/span&gt;&lt;span&gt;TCP&lt;/span&gt;&lt;span&gt;层负责对&lt;/span&gt;&lt;span&gt;TCP&lt;/span&gt;&lt;span&gt;数据段进行调度，确保发包能够顺利到达目标位置。&lt;/span&gt;&lt;span&gt;IP&lt;/span&gt;&lt;span&gt;层则在数据包中增加必要的网络路由数据信息。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;层的&lt;/span&gt;&lt;span&gt;PDU&lt;/span&gt;&lt;span&gt;段的长度是固定的，&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;层含有一个成帧机制来分段和组合数据包，将过长的&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;消息分段封装为&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;消息，处理过程如图&lt;/span&gt;&lt;span&gt;4.3&lt;/span&gt;&lt;span&gt;所示。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;img src="http://my.csdn.net/uploads/201205/14/1337007287_8839.png" alt="" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;图&lt;span&gt;4.3&amp;nbsp;DDP&lt;/span&gt;&lt;span&gt;层拆分&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;消息示意图&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;数据段是&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;协议数据传输的最小数据单元，包含&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;协议头和&lt;/span&gt;&lt;span&gt;ULP&lt;/span&gt;&lt;span&gt;载荷。&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;协议头包含&lt;/span&gt;&lt;span&gt;ULP&lt;/span&gt;&lt;span&gt;数据的最终目的地址的位置和相关控制信息。&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;层将&lt;/span&gt;&lt;span&gt;ULP&lt;/span&gt;&lt;span&gt;数据分段的原因之一就是&lt;/span&gt;&lt;span&gt;TCP&lt;/span&gt;&lt;span&gt;载荷的最大长度限制。&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;的数据传输模式分为&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;种：&lt;/span&gt;&lt;span&gt;tagged&amp;nbsp;buffer&lt;/span&gt;&lt;span&gt;方式和&lt;/span&gt;&lt;span&gt;untagged&amp;nbsp;buffer&lt;/span&gt;&lt;span&gt;方式。&lt;/span&gt;&lt;span&gt;tagged&amp;nbsp;buffer&lt;/span&gt;&lt;span&gt;方式一般应用于大数据量传输，例如磁盘&lt;/span&gt;&lt;span&gt;I/O&lt;/span&gt;&lt;span&gt;、大数据结构等；而&lt;/span&gt;&lt;span&gt;untagged&amp;nbsp;buffer&lt;/span&gt;&lt;span&gt;方式一般应用于小的控制信息传输，例如：控制消息、&lt;/span&gt;&lt;span&gt;I/O&lt;/span&gt;&lt;span&gt;状态信息等。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;层在&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;层传递下来的&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;消息中，&lt;/span&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;层通过增加&lt;/span&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;协议头、标志数据以及&lt;/span&gt;&lt;span&gt;CRC&lt;/span&gt;&lt;span&gt;校验数据构成&lt;/span&gt;&lt;span&gt;FPDU(framed&amp;nbsp;PDU&amp;nbsp;)&lt;/span&gt;&lt;span&gt;数据段，处理过程如图&lt;/span&gt;&lt;span&gt;4.4&lt;/span&gt;&lt;span&gt;所示。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;层便于对端网络适配器设备能够快速定位&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;协议头数据，根据&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;协议头内设置的控制信息将数据直接置入相应的应用内存区域。&lt;/span&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;层具备错序校正能力，通过使能&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;，&lt;/span&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;避免内存复制的开销，减少处理乱序数据包和丢失数据包时对内存的需求。&lt;/span&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;将&lt;/span&gt;&lt;span&gt;FPDU&lt;/span&gt;&lt;span&gt;数据段传送给&lt;/span&gt;&lt;span&gt;TCP&lt;/span&gt;&lt;span&gt;层处理。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;img src="http://my.csdn.net/uploads/201205/14/1337007315_6930.png" alt="" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;图&lt;span&gt;4.4&amp;nbsp;MPA&lt;/span&gt;&lt;span&gt;层拆分&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;消息示意图&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;TCP&lt;/span&gt;&lt;span&gt;层将&lt;/span&gt;&lt;span&gt;FPDU&lt;/span&gt;&lt;span&gt;数据段拆放置在&lt;/span&gt;&lt;span&gt;TCP&lt;/span&gt;&lt;span&gt;数据段中，确保每个&lt;/span&gt;&lt;span&gt;TCP&lt;/span&gt;&lt;span&gt;数据段中都包含&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;个单独的&lt;/span&gt;&lt;span&gt;FDPU&lt;/span&gt;&lt;span&gt;。&lt;/span&gt;&lt;span&gt;MPA&lt;/span&gt;&lt;span&gt;接收端重新组装为完整的&lt;/span&gt;&lt;span&gt;FPDU&lt;/span&gt;&lt;span&gt;，验证数据完整性，将无用的信息段去除，然后将完整的&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;消息发送到&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;层进行处理。&lt;/span&gt;&lt;span&gt;DDP&amp;nbsp;&lt;/span&gt;&lt;span&gt;允许&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;数据段中的&lt;/span&gt;&lt;span&gt;ULP&lt;/span&gt;&lt;span&gt;协议（&lt;/span&gt;&lt;span&gt;Upper&amp;nbsp;Layer&amp;nbsp;Protocol&lt;/span&gt;&lt;span&gt;）数据，例如应用消息或磁盘&lt;/span&gt;&lt;span&gt;I/O&lt;/span&gt;&lt;span&gt;数据，不需要经过&lt;/span&gt;&lt;span&gt;ULP&lt;/span&gt;&lt;span&gt;的处理而直接放置在目的地址的内存中，即使&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;数据段乱序也不影响这种操作。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第五节&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;标准组织&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;2001&lt;/span&gt;&lt;span&gt;年&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;月，&lt;/span&gt;&lt;span&gt;Adaptec&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;Broadcom&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;Cisco&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;Dell&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;EMC&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;HP&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;IBM&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;Intel&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;Microsoft&lt;/span&gt;&lt;span&gt;和&lt;/span&gt;&lt;span&gt;NetApp&lt;/span&gt;&lt;span&gt;公司宣布成立&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;远程直接内存访问（&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;）联盟&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;联盟是个独立的开放组织，其制定实施能提供&lt;/span&gt;&lt;span&gt;TCP/IP&amp;nbsp;RDMA&lt;/span&gt;&lt;span&gt;技术的产品所需的体系结构规范，鼓励其它技术公司积极参与新规范的制定。该联盟将负责为整个&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;解决方案制定规范，包括&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;DDP&lt;/span&gt;&lt;span&gt;（直接数据放置）和&lt;/span&gt;&lt;span&gt;TCP/IP&lt;/span&gt;&lt;span&gt;分帧协议。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;联盟是&lt;/span&gt;&lt;span&gt;Internet&lt;/span&gt;&lt;span&gt;工程任务组（&lt;/span&gt;&lt;span&gt;IETF&lt;/span&gt;&lt;span&gt;）的补充，&lt;/span&gt;&lt;span&gt;IETF&lt;/span&gt;&lt;span&gt;是由网络设计师、运营商、厂商和研究公司组成的大型国际组织。其目的是推动&lt;/span&gt;&lt;span&gt;Internet&lt;/span&gt;&lt;span&gt;体系结构的发展，并使&lt;/span&gt;&lt;span&gt;Internet&lt;/span&gt;&lt;span&gt;的运作更加顺畅。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;联盟的成员公司和个人都是&lt;/span&gt;&lt;span&gt;IETF&lt;/span&gt;&lt;span&gt;的积极参与者。另外，&lt;/span&gt;&lt;span&gt;IETF&lt;/span&gt;&lt;span&gt;还认识到了&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;在可行网络方案中的重要性，并计划在以后几个月里成立&lt;/span&gt;&lt;span&gt;"Internet&lt;/span&gt;&lt;span&gt;协议套件&lt;/span&gt;&lt;span&gt;RDMA"&lt;/span&gt;&lt;span&gt;工作组。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;联盟协议规定，联盟将向相应的&lt;/span&gt;&lt;span&gt;IETF&lt;/span&gt;&lt;span&gt;工作组提交规范草案，供&lt;/span&gt;&lt;span&gt;IETF&lt;/span&gt;&lt;span&gt;考虑。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;TCP/IP&amp;nbsp;RDMA&lt;/span&gt;&lt;span&gt;体系结构规范的&lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt;版本于&lt;/span&gt;&lt;span&gt;2002&lt;/span&gt;&lt;span&gt;年&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;月由&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;联盟成员发布，&amp;nbsp;&lt;/span&gt;&lt;span&gt;TCP/IP&amp;nbsp;RDMA&lt;/span&gt;&lt;span&gt;的最终规范将由&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;联盟的业界合作伙伴及相应的业界标准组织派出的代表共同确定。&lt;/span&gt;&lt;span&gt;RDMA&lt;/span&gt;&lt;span&gt;联盟官方网址：&lt;/span&gt;&lt;span&gt;http://www.rdmaconsortium.org&lt;/span&gt;&lt;span&gt;。&lt;/span&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/brucewoo/aggbug/2505596.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/brucewoo/archive/2012/05/17/2505596.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/wbpmrck/archive/2012/05/16/Knockout-introduction.html</id><title type="text">【原创】Javascript MVVM模式前端框架—Knockout 2.1.0系列：目录</title><summary type="text">主要对Knockout的基本功能进行说明</summary><published>2012-05-16T15:41:00Z</published><updated>2012-05-16T15:41:00Z</updated><author><name>刺客之家</name><uri>http://www.cnblogs.com/wbpmrck/</uri></author><link rel="alternate" href="http://www.cnblogs.com/wbpmrck/archive/2012/05/16/Knockout-introduction.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/wbpmrck/archive/2012/05/16/Knockout-introduction.html"/><content type="html">&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;前言&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;最近一段时间抽空学习了一下Knockout.js,发现这个框架十分有趣：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;它提供了一种优雅的解决方案来实现UI元素与Javascript对象之间的绑定&lt;/li&gt;&lt;li&gt;能够实现双向绑定：UI元素变化的时候更新js对象，反之亦然&lt;/li&gt;&lt;li&gt;能够跟踪依赖(dependency tracking),就是对象之间存在依赖的情况下，依赖项的变化会传导到整个依赖链条的末端。&lt;/li&gt;&lt;li&gt;灵活的模板功能&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;可以这么理解，基本上这是一个在web浏览器中实现类似Silverlight,WPF那种&amp;ldquo;数据绑定&amp;rdquo;功能的框架，&lt;strong&gt;不过它的功能远不止绑定这么简单&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;通过学习官方文档，自己也整理了基本ko的所有功能，现在借博客园把整理的东西分享出来，让不喜欢看e文文档的童鞋们可以多一个了解ko的资源吧。&lt;/p&gt;&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&lt;p&gt;备注：列表中使用*2.1 NEW*号标注了哪些特性是2.1版本新加入的功能&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;计划章节(随时更新)&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;1、初识KO:监控属性(Observable) 与 依赖属性(Computed)。&lt;/li&gt;&lt;li&gt;2、列表操作：监控数组(Observable Array)。&lt;/li&gt;&lt;li&gt;3、文本和样式绑定(上篇)：visible绑定、js表达式充当绑定值&lt;/li&gt;&lt;li&gt;4、文本和样式绑定(中篇)：text绑定、html绑定&lt;/li&gt;&lt;li&gt;5、文本和样式绑定(下篇)：css绑定、style绑定、attr绑定&lt;/li&gt;&lt;li&gt;6、控制流Control Flow(上篇)：foreach绑定、虚拟结点绑定&lt;/li&gt;&lt;li&gt;7、控制流Control Flow(中篇)：理解绑定上下文Binding Context&lt;/li&gt;&lt;li&gt;8、控制流Control Flow(下篇)：if绑定与with绑定&lt;/li&gt;&lt;li&gt;9、内建绑定之&amp;mdash;&amp;mdash;Click绑定&lt;/li&gt;&lt;li&gt;10、内建绑定之&amp;mdash;&amp;mdash;Event绑定&lt;/li&gt;&lt;li&gt;11、内建绑定之&amp;mdash;&amp;mdash;Enable绑定、Value绑定、uniqueName绑定&lt;/li&gt;&lt;li&gt;12、内建绑定之&amp;mdash;&amp;mdash;hasFocus绑定&lt;/li&gt;&lt;li&gt;13、内建绑定之&amp;mdash;&amp;mdash;checked绑定&lt;/li&gt;&lt;li&gt;14、内建绑定之&amp;mdash;&amp;mdash;option与selectOption绑定&lt;/li&gt;&lt;li&gt;15、自定义模板：模板绑定&lt;/li&gt;&lt;li&gt;16、创建自己的绑定(上篇）：自定义Binding&lt;/li&gt;&lt;li&gt;17、*2.1 NEW*创建自己的绑定(中篇）：绑定上下文扩展&amp;mdash;&amp;mdash;修改子元素绑定上下文&lt;/li&gt;&lt;li&gt;18、*2.1 NEW*创建自己的绑定(下篇）：自定Binding也可以支持虚拟DOM元素&lt;/li&gt;&lt;li&gt;19、ko数据辅助：viewModel的Json序列化支持&lt;/li&gt;&lt;li&gt;20、进阶技巧：扩展Observable&lt;/li&gt;&lt;li&gt;21、进阶技巧：使用throttle extender延迟更新computed属性&lt;/li&gt;&lt;li&gt;22、进阶技巧：使用dataFor,contextFor配合常规事件绑定&lt;/li&gt;&lt;li&gt;23、进阶技巧：使用fn扩展自定义函数&lt;/li&gt;&lt;li&gt;24、*2.1 NEW*使用$Index关键字绑定自动序号&lt;/li&gt;&lt;li&gt;25、*2.1 NEW*判断属性是否computed&lt;/li&gt;&lt;li&gt;26、*2.1 NEW*控制json序列化范围&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;相关资源：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Knockout官方首页：&lt;a href="http://knockoutjs.com/"&gt;http://knockoutjs.com/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/wbpmrck/aggbug/2505574.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/wbpmrck/archive/2012/05/16/Knockout-introduction.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/keen-allan/archive/2012/05/16/2505541.html</id><title type="text">关于找工作的一些个人看法</title><summary type="text">在五月四号晚上写的那篇博客到目前为止已经有3400+的访问量了,部分朋友也留言相互鼓励. 在写了那篇日记不久,我就去面试咯,第一次去新公司的时候面试结果不是很好,但是好消息是他们还是给了我第二次面试的机会.昨天去参加了二面,先做了一个IQ的测试,然后几分钟做了一个性格测试.最后获得了公司CEO面试的资格.很幸运的是最后虽然在薪资上有一些分歧,没有达到我最起初的期望值.甚至比我现在在公司的能拿到的工资还要低一些,我还是愿意接收这样一份工作.因为有些工作是你愿意去尝试的而你看中的不是他能给你提供多少的薪水、酬劳.今天在原来的公司提交了离职申请.虽然有些不愉快但总的来说一切还是比较顺利的.经理...</summary><published>2012-05-16T15:17:00Z</published><updated>2012-05-16T15:17:00Z</updated><author><name>Shadow-R</name><uri>http://www.cnblogs.com/keen-allan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/keen-allan/archive/2012/05/16/2505541.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/keen-allan/archive/2012/05/16/2505541.html"/><content type="html">&lt;p&gt;&amp;nbsp; &amp;nbsp; 在五月四号晚上写的那篇博客到目前为止已经有3400+的访问量了,部分朋友也留言相互鼓励.&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 在写了那篇日记不久,我就去面试咯,第一次去新公司的时候面试结果不是很好,但是好消息是他们还是给了我第二次面试的机会.昨天去参加了二面,先做了一个IQ的测试,然后几分钟做了一个性格测试.最后获得了公司CEO面试的资格.很幸运的是最后虽然在薪资上有一些分歧,没有达到我最起初的期望值.甚至比我现在在公司的能拿到的工资还要低一些,我还是愿意接收这样一份工作.因为有些工作是你愿意去尝试的而你看中的不是他能给你提供多少的薪水、酬劳.今天在原来的公司提交了离职申请.虽然有些不愉快但总的来说一切还是比较顺利的.经理都试图在给我调薪水希望我能留下来.但是我回绝了.我要象那篇博客的说的那样去做自己热爱.喜欢的东西了.这不是钱不钱能决定的.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 最近有大学实验室的学弟学妹在群里面问我关于工作的一些问题,结合我自己的经历说说我一点点关于应届大学生该如何选择一个什么样的企业的一点点观点.希望对正在找工作或者明年即将毕业的大学生一点点启发:&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;1.外包企业: 在中国这个行业大多数的软件公司都是这样性质的企业,这样的企业在我看来没有过多的企业文化,大多数的时间纯粹的将员工当作他生财的工具.在这样的企业里面刚开始一般不愿意给应届生或者没有经验的学生太高的薪水,而且公司对待遇的考评制度也不完善.但是经过一段时间如果你符合他们公司的要求能够完成相应的工作任务他们还是很愿意给你一份不错的薪水的.但是前提是你能给他们创造更大的利益.&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 2.大型的知名的企业:这类的企业基本上同一年的大学生没有太多的薪水上的差距,因为基本上他们的待遇都是固定的,但是很多时间这样的企业的加班,或者说无偿劳动是比较充足的.但是进入这样的企业你需要的就是坚持了,一定时间和工作经验的积累会让你在下一次选择自己的工作或者别人来帮你选择自己的工作的时候你将拥有一个有利的条件.但是这样的企业在招聘人才的时候有很多硬性的指标了.&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 3.创业企业:在移动互联发展迅猛的今天,这样的小工作室或者团队已经如雨后春笋般出现在各大招聘网址上.这样的企业有很酷的企业文化,比较西方的管理模式.但是相对的对人的水平的考究或者招聘的时候他们往往更看重能力.而且他们提供的待遇也不差.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 结合我自己的工作经历我不太推荐应届毕业生进入第一类企业工作.首先这样的企业没有较为系统的企业培训,以及企业文化的缺失不太适合当今的年轻人,重内心出发我比较推荐大家去第三类的企业,当然第二类企业也是会让很多的学生羡慕的工作.2012年5月16日 23:14:05......OK,就写到这里.明天希望是一个天朗气清的好天气.&lt;/p&gt;&lt;img src="http://www.cnblogs.com/keen-allan/aggbug/2505541.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/keen-allan/archive/2012/05/16/2505541.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/qinyg/archive/2012/05/16/2505523.html</id><title type="text">0-1背包问题  回溯法</title><summary type="text">作为算法设计分析的经典问题，已经写过一次了，不过实现的方法不同，这次是回溯法解决问题。问题还是老问题，但是方法是新的！ 哈哈在这里再简单写一下问题要求：给定n中物品和一个容量为c的背包，物品i的重量为Wi,其价值为Vi,0-1背包问题是如何选择装入背包的物品（物品不可分割），使得装入背包的物品的价值为最大。1.题目分析：考虑到每种物品只有2 种选择，即装入背包或不装入背包，并且物品数和背包容量已给定，要计算装入背包物品的最大价值和最优装入方案，可用回溯法搜索子集树的算法进行求解。2.算法设计：a. 物品有n种，背包容量为C，分别用p[i]和w[i]存储第i种物品的价值和重量，用x[i]标记第i</summary><published>2012-05-16T15:00:00Z</published><updated>2012-05-16T15:00:00Z</updated><author><name>qinyg</name><uri>http://www.cnblogs.com/qinyg/</uri></author><link rel="alternate" href="http://www.cnblogs.com/qinyg/archive/2012/05/16/2505523.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/qinyg/archive/2012/05/16/2505523.html"/><content type="html">&lt;p&gt;作为算法设计分析的经典问题，已经写过一次了，不过实现的方法不同，这次是回溯法解决问题。问题还是老问题，但是方法是新的！ 哈哈&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt; "&gt;在这里再简单写一下问题要求：&lt;/span&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;给定n中物品和一个容量为c的背包，物品i的重量为Wi,其价值为Vi,0-1背包问题是如何选择装入背包的物品（物品不可分割），使得装入背包的物品的价值为最大。&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; font-size: 18pt; "&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; font-size: 18pt; "&gt;1.题目分析：&lt;/span&gt;&lt;/p&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;考虑到每种物品只有2 种选择，即装入背包或不装入背包，并且物品数和背包容量已给定，要计算装入背包物品的最大价值和最优装入方案，可用回溯法搜索子集树的算法进行求解。&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; font-size: 18pt; "&gt;2.算法设计：&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;a. 物品有n种，背包容量为C，分别用p[i]和w[i]存储第i种物品的价值和重量，用&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;x[i]标记第i种物品是否装入背包，用bestx[i]存储第i种物品的最优装载方案；&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;b. 用递归函数Backtrack (i,cp,cw)来实现回溯法搜索子集树（形式参数i表示递归深&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;度，n用来控制递归深度，形式参数cp和cw表示当前总价值和总重量，bestp表示当前&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;最优总价值）：&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;&amp;#9312; 若i &amp;gt;n，则算法搜索到一个叶结点，判断当前总价值是否最优：&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;1&amp;gt; 若cp&amp;gt;bestp，更新当前最优总价值为当前总价值（即bestp=cp），更新&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;装载方案（即bestx[i]=x[i]( 1&amp;#8804;i&amp;#8804;n)）；&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;&amp;#9313; 采用for循环对物品i装与不装两种情况进行讨论（0&amp;#8804;j&amp;#8804;1）：&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;1&amp;gt; x[i]=j；&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;2&amp;gt; 若总重量不大于背包容量（即cw+x[i]*w[i]&amp;lt;=c），则更新当前总价 br=""&amp;gt;&lt;/span&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;&amp;nbsp;值和总重量（即cw+=w[i]*x[i],cp+=p[i]*x[i]）, 对物品i+1调用递归函&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;数Backtrack(i+1,cp,cw) 继续进行装载；&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;3&amp;gt; 函数Backtrack(i+1,cp,cw)调用结束后则返回当前总价值和总重量&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;（即 cw-=w[i]*x[i],cp-=p[i]*x[i]）；&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;4&amp;gt; 当j&amp;gt;1时，for循环结束；&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;&amp;#9314; 当i=1时，若已测试完所有装载方案，外层调用就全部结束；&lt;/span&gt;&lt;br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; " /&gt;&lt;span style="font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 22px; text-align: left; "&gt;c. 主函数调用一次backtrack(1,0,0)即可完成整个回溯搜索过程，最终得到的bestp和bestx[i]即为所求最大总价值和最优装载方案。&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size: 24pt; "&gt;&amp;nbsp;最后给出代码。&lt;/span&gt;很不幸的是我自己没有写出来源码，这个是从网络找到后又经过自己改写的。。。。。&lt;img src="http://www.cnblogs.com/Emoticons/baimantou/103828514.gif"  alt="" /&gt;技术不够没办法啊&lt;img src="http://www.cnblogs.com/Emoticons/qface/055243873.gif"  alt="" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;div&gt;#include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&amp;nbsp;n,c,bestp;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;物品的个数，背包的容量，最大价值&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&amp;nbsp;p[&lt;span style="color: #800080;"&gt;10000&lt;/span&gt;],w[&lt;span style="color: #800080;"&gt;10000&lt;/span&gt;],x[&lt;span style="color: #800080;"&gt;10000&lt;/span&gt;],bestx[&lt;span style="color: #800080;"&gt;10000&lt;/span&gt;];&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;物品的价值，物品的重量，x[i]暂存物品的选中情况,物品的选中情况&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #0000FF;"&gt;void&lt;/span&gt;&amp;nbsp;Backtrack(&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&amp;nbsp;i,&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&amp;nbsp;cp,&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&amp;nbsp;cw)&lt;br /&gt;{&amp;nbsp;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;cw当前包内物品重量，cp当前包内物品价值&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&amp;nbsp;j;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;if&lt;/span&gt;(i&amp;gt;n)&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;回溯结束&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;if&lt;/span&gt;(cp&amp;gt;bestp)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bestp=cp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;for&lt;/span&gt;(i=&lt;span style="color: #800080;"&gt;0&lt;/span&gt;;i&amp;lt;=n;i++)&amp;nbsp;bestx[i]=x[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;else&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;for&lt;/span&gt;(j=&lt;span style="color: #800080;"&gt;0&lt;/span&gt;;j&amp;lt;=&lt;span style="color: #800080;"&gt;1&lt;/span&gt;;j++)&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;x[i]=j;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;if&lt;/span&gt;(cw+x[i]*w[i]&amp;lt;=c)&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cw+=w[i]*x[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cp+=p[i]*x[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Backtrack(i+&lt;span style="color: #800080;"&gt;1&lt;/span&gt;,cp,cw);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cw-=w[i]*x[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cp-=p[i]*x[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&amp;nbsp;main()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&amp;nbsp;i;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bestp=&lt;span style="color: #800080;"&gt;0&lt;/span&gt;;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;请输入背包最大容量:\n&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scanf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;%d&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;,&amp;amp;c);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;请输入物品个数:\n&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scanf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;%d&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;,&amp;amp;n);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;请依次输入物品的重量:\n&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;for&lt;/span&gt;(i=&lt;span style="color: #800080;"&gt;1&lt;/span&gt;;i&amp;lt;=n;i++)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scanf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;%d&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;,&amp;amp;w[i]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;请依次输入物品的价值:\n&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;for&lt;/span&gt;(i=&lt;span style="color: #800080;"&gt;1&lt;/span&gt;;i&amp;lt;=n;i++)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scanf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;%d&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;,&amp;amp;p[i]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Backtrack(&lt;span style="color: #800080;"&gt;1&lt;/span&gt;,&lt;span style="color: #800080;"&gt;0&lt;/span&gt;,&lt;span style="color: #800080;"&gt;0&lt;/span&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;最大价值为:\n&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;%d\n&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;,bestp);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;被选中的物品依次是(0表示未选中，1表示选中)\n&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;for&lt;/span&gt;(i=&lt;span style="color: #800080;"&gt;1&lt;/span&gt;;i&amp;lt;=n;i++)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;%d&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;,bestx[i]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;\n&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style="color: #800080;"&gt;0&lt;/span&gt;;&lt;br /&gt;}&lt;br /&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/qinyg/aggbug/2505523.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/qinyg/archive/2012/05/16/2505523.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/logoove/archive/2012/05/16/2505386.html</id><title type="text">joomla1.5开发一个广告随机显示模块实例</title><summary type="text">说到模块开发，比起组建就要简单的多了，不过初学还是费了好半工夫。今天我就以一个广告显示模块的例子来说说joomla的模块开发，这个广告的显示用的是jquery插件，数据读取采用joomla从数据库取出，这个模块可以控制广告显示大小，和广告显示位置的控制，就是说不同位置我们放置不同的广告。显示效果嘛就是类似flash图片自动切换。joomla的模块就是专门解决需要的小功能，在页面上显示，和组建不同的是，模块只是实现一个页面的小块显示，它可以通过后台来控制哪些菜单页面下需要。基本东西我就不多说了，我主要是想说怎么开发。 拿到一个需求，当然是先来分析，这个要显示的数据表如下第一张表存储...</summary><published>2012-05-16T14:40:00Z</published><updated>2012-05-16T14:40:00Z</updated><author><name>青竹博客</name><uri>http://www.cnblogs.com/logoove/</uri></author><link rel="alternate" href="http://www.cnblogs.com/logoove/archive/2012/05/16/2505386.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/logoove/archive/2012/05/16/2505386.html"/><content type="html">&lt;p&gt;说到模块开发，比起组建就要简单的多了，不过初学还是费了好半工夫。今天我就以一个广告显示模块的例子来说说joomla的模块开发，这个广告的显示用的是jquery插件，数据读取采用joomla从数据库取出，这个模块可以控制广告显示大小，和广告显示位置的控制，就是说不同位置我们放置不同的广告。显示效果嘛就是类似flash图片自动切换。joomla的模块就是专门解决需要的小功能，在页面上显示，和组建不同的是，模块只是实现一个页面的小块显示，它可以通过后台来控制哪些菜单页面下需要。基本东西我就不多说了，我主要是想说怎么开发。&lt;/p&gt;&lt;p&gt;拿到一个需求，当然是先来分析，这个要显示的数据表如下&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/344840/2012051620040433.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/344840/2012051620041815.png" alt="" /&gt;第一张表存储了广告信息，第二的张表存储了显示位置信息，一个位置我们可以放置N条广告。这个插件就是根据位置读取需要的广告展示出来，目的很明确，接下来就开发了。先到modules下面建立mod_ad文件夹，然后建立mod_ad.php，mod_ad.xml文件和tmpl、js文件夹。mod_ad.xml是打包安装文件内容如下&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&amp;lt;install type="module" version="1.5.0"&amp;gt;    &amp;lt;name&amp;gt;Ad&amp;lt;/name&amp;gt;    &amp;lt;author&amp;gt;Yobyt&amp;lt;/author&amp;gt;    &amp;lt;creationDate&amp;gt;5-15-2012&amp;lt;/creationDate&amp;gt;    &amp;lt;copyright&amp;gt;Copyright (C) 2012 Open Source Matters. All rights reserved.&amp;lt;/copyright&amp;gt;    &amp;lt;license&amp;gt;http:&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;www.gnu.org/licenses/gpl-2.0.html GNU/GPL&amp;lt;/license&amp;gt;&lt;/span&gt;    &amp;lt;authorEmail&amp;gt;logove@qq.com&amp;lt;/authorEmail&amp;gt;    &amp;lt;authorUrl&amp;gt;Yoby&amp;lt;/authorUrl&amp;gt;    &amp;lt;version&amp;gt;1.5.0&amp;lt;/version&amp;gt;    &amp;lt;description&amp;gt;This module shows ad! Copyright information&amp;lt;/description&amp;gt;    &amp;lt;files&amp;gt;        &amp;lt;filename module="mod_ad"&amp;gt;mod_ad.php&amp;lt;/filename&amp;gt;        &amp;lt;filename module="mod_ad"&amp;gt;tmpl/&lt;span style="color: #0000ff;"&gt;default&lt;/span&gt;.php&amp;lt;/filename&amp;gt;        &amp;lt;filename module="mod_ad"&amp;gt;js/cycle.js&amp;lt;/filename&amp;gt;    &amp;lt;/files&amp;gt;    &amp;lt;params&amp;gt;    &amp;lt;param name="width" type="text" &lt;span style="color: #0000ff;"&gt;default&lt;/span&gt;="200" label="width" description="Ad Width" /&amp;gt;    &amp;lt;param name="height" type="text" &lt;span style="color: #0000ff;"&gt;default&lt;/span&gt;="200" label="height" description="Ad Height" /&amp;gt;    &amp;lt;param name="location_id" type="text" &lt;span style="color: #0000ff;"&gt;default&lt;/span&gt;="1" label="location id" description="Location ID" /&amp;gt;    &amp;lt;/params&amp;gt;&amp;lt;/install&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我就不解释了，稍微了解joomla开发的都知道啥意思，里面除了打包信息，还有就是配置信息，用来配置广告宽度、高度、位置信息。mod_ad.php是用来处理数据的，就是读取数据&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&amp;lt;?&lt;span style="color: #000000;"&gt;php&lt;/span&gt;&lt;span style="color: #008080;"&gt;defined&lt;/span&gt;('_JEXEC') or &lt;span style="color: #0000ff;"&gt;die&lt;/span&gt;('Restricted access'&lt;span style="color: #000000;"&gt;);&lt;/span&gt;&lt;span style="color: #800080;"&gt;$width&lt;/span&gt; =(int)&lt;span style="color: #800080;"&gt;$params&lt;/span&gt;-&amp;gt;get( 'width',200&lt;span style="color: #000000;"&gt; ) ;&lt;/span&gt;&lt;span style="color: #800080;"&gt;$height&lt;/span&gt; =(int)&lt;span style="color: #800080;"&gt;$params&lt;/span&gt;-&amp;gt;get( 'height' ,200&lt;span style="color: #000000;"&gt;);&lt;/span&gt;&lt;span style="color: #800080;"&gt;$id&lt;/span&gt; =(int)&lt;span style="color: #800080;"&gt;$params&lt;/span&gt;-&amp;gt;get( 'location_id' ,1&lt;span style="color: #000000;"&gt;);&lt;/span&gt;&lt;span style="color: #800080;"&gt;$db&lt;/span&gt;  =   &amp;amp; JFactory::&lt;span style="color: #000000;"&gt;getDBO();&lt;/span&gt;&lt;span style="color: #800080;"&gt;$query&lt;/span&gt; = 'SELECT *  FROM #__ads AS a INNER JOIN #__ads_location AS b ON a.location_id=b.id WHERE b.id='.&lt;span style="color: #800080;"&gt;$id&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;/span&gt;&lt;span style="color: #800080;"&gt;$db&lt;/span&gt;-&amp;gt;setQuery(&lt;span style="color: #800080;"&gt;$query&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;/span&gt;&lt;span style="color: #800080;"&gt;$rs&lt;/span&gt; = &lt;span style="color: #800080;"&gt;$db&lt;/span&gt;-&amp;gt;&lt;span style="color: #000000;"&gt;loadObjectList();&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;require&lt;/span&gt;(JModuleHelper::getLayoutPath('mod_ad'));&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;解释一下，第一行防止意外加载，joomla都这么写，接着是读取配置信息的宽度 高度 位置，get方法第一个参数是名称，第二个是默认值，就是取不到的时候会赋值。接着就是数处理四部曲，这里是内链接两个表，取位置相通的记录条件是我们配置的位置，最后是加载模版。基本上模块都可以这么写，当然复杂点的你可以自定义函数实现更多功能。&lt;/p&gt;&lt;p&gt;进入模版文件tmpl文件夹，里面default.php就是显示样式，这里我们把$rs这个数据对象记录，通过循环foreach显示，随便找一个jquery插件都可以实现，具体的效果是你插件效果，yoby在这里提供一个插件，请看附件里面。注意使用jquery的时候，joomla由于使用很多js库，所以一般我们进行如下处理&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&lt;span style="color: #000000;"&gt;jQuery.noConflict();    jQuery(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(){&lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;js代码&lt;/span&gt;    });&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;jQuery来代替$符号，一般jQuery用得多的时候，在模版里面就加载，以后直接使用，读取图片的时候用joomla函数JURI::Root(false) 输出joomla安装目录。这样基本一个模块开发完毕，打包上传到后台进行设置即可。网上广告组建插件模块很多，这里是自己动手开发的，可以开发出后台广告管理，计算出广告展示次数，广告价格等功能，所以掌握基本开发还是很重要的。以后yoby会写joomla1.5实现单元-一级分类-二级分类-文章的改良，联动菜单的开发，文章关联到多个分类，前台表单提交，后台提交表单，发送Email等功能，这里都是在1.5基础上，2.5暂时不在考虑内。&lt;/p&gt;&lt;p&gt;最后说点题外话Yoby也喜欢以下技术：thinkphp框架，dedecms，discuz，jQuery，前端开发，css3，html5，Mysql，sqlite，aauto，python，bootstrap，less，json，ajax等。希望相同爱好者互相交流。主要是做joomla建站和thinkphp开发应用这两方面。&lt;/p&gt;&lt;p&gt;&lt;a href="http://files.cnblogs.com/logoove/mod_ad.rar"&gt;下载 &lt;/a&gt;文件是rar压缩的包括数据库sql文件使用需要打包成zip并且导入sql。&lt;/p&gt;&lt;p&gt;(原创 Form Yoby)&lt;/p&gt;&lt;img src="http://www.cnblogs.com/logoove/aggbug/2505386.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/logoove/archive/2012/05/16/2505386.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/PurpleTide/archive/2012/05/16/2502547.html</id><title type="text">.net 开发人员的瓶颈和职业发展</title><summary type="text">现在社会比前几年浮躁了,越来越多的人抱怨薪水低,高薪工作不好找;诚然这有CPI的压力,可是也有很多人没有认清自己的职业发展.很多.net程序员个各种纠结,想拿高薪又拿不到,想提高又不知道怎么能提高.我也经历过这样的阶段.......各种纠结和迷茫,各种悲剧......不知道路在何方,在此我把我的经验和看法分享给大家,希望能给大家一点帮助.(本文只代表我的个人观点)关于职业/薪水瓶颈的问题:(在本文中,我们假设薪水就是能力的真实体现,不考虑运气等因素,并且薪水以上海为标准,其他城市乘以相对比例)瓶颈一: 入门 (薪水&lt;0) 在这个阶段没有老师就是各种悲剧,各种概念的混合,各种纠结,各种蒙.</summary><published>2012-05-16T14:31:00Z</published><updated>2012-05-16T14:31:00Z</updated><author><name>听说读写</name><uri>http://www.cnblogs.com/PurpleTide/</uri></author><link rel="alternate" href="http://www.cnblogs.com/PurpleTide/archive/2012/05/16/2502547.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/PurpleTide/archive/2012/05/16/2502547.html"/><content type="html">&lt;p&gt;现在社会比前几年浮躁了,越来越多的人抱怨薪水低,高薪工作不好找;&lt;/p&gt;&lt;p&gt;诚然这有CPI的压力,可是也有很多人没有认清自己的职业发展.&lt;/p&gt;&lt;p&gt;很多.net程序员个各种纠结,想拿高薪又拿不到,想提高又不知道怎么能提高.&lt;/p&gt;&lt;p&gt;我也经历过这样的阶段.......各种纠结和迷茫,各种悲剧......不知道路在何方,在此我把我的经验和看法分享给大家,希望能给大家一点帮助.&lt;/p&gt;&lt;p&gt;(本文只代表我的个人观点)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;关于职业/薪水瓶颈的问题:&lt;/p&gt;&lt;p&gt;(在本文中,我们假设薪水就是能力的真实体现,不考虑运气等因素,并且薪水以上海为标准,其他城市乘以相对比例)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;瓶颈一: 入门 (薪水&amp;lt;0)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在这个阶段没有老师就是各种悲剧,各种概念的混合,各种纠结,各种蒙....基本上这个阶段的薪水&amp;lt;0 (贴钱人家也不要你)&lt;/p&gt;&lt;p&gt;如何突破瓶颈:没啥好说的,看书自学吧, 或者找人培训&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;瓶颈二:初级开发人员(年薪&amp;lt;5万)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在这个阶段,开发人员会拖控件,会处理一般小公司常见的业务需求,懂得搜索一些关键字来完成自己的需求, BOSS交代的任务大部分能完成..不过仅仅限于自己的知识范围内&lt;/p&gt;&lt;p&gt;代表产品: 各种什么小企业站点,几千块那种,偶尔有点稍微难一点的就很难实现,或者用非常挫的方式来实现,代码中各种神奇...&lt;/p&gt;&lt;p&gt;代表人群:北大青鸟等地方出来的.....大学不好好念书 混出来的&lt;/p&gt;&lt;p&gt;关键点是: 加深对.net的理解,了解.net周边的相关技术,要调整好心态,有专研精神&lt;/p&gt;&lt;p&gt;如何突破瓶颈:多找高级人员,看看人家是怎么工作,怎么思考 怎么解决问题的; 听到别人说到某些东西 最好都留心一下,google下看看是什么东西 和你的工作能不能配合起来&lt;/p&gt;&lt;p&gt;书已经可以开始看了,最好是问资深人员你需要看什么书,一般这种问题人家还是愿意回答的, 要注意不要被某些垃圾书误导了&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;瓶颈三:中级开发人员(年薪&amp;lt;12万)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;这个级别的程序员不仅仅经验够了,知识面也相对好一点, BOSS交代下来的任务可以很顺利的完成, 擅长于已知关键字的情况下搜索解决方案&lt;/p&gt;&lt;p&gt;基本上.net附近的知识都懂一些,例如javascript html sqlserver ext componentart 等等&lt;/p&gt;&lt;p&gt;.net内的大部分东西都听过或者用过, 例如linq lambda wcf socket&lt;/p&gt;&lt;p&gt;代表产品:各大中型公司的最底层开发人员,开发点不重要的应用,写的代码在高手看来....好歹还能跑...BUG不是很多....在不重要的应用中就无所谓啦, 稍微重要点的系统或者代码就不敢让这些人做了,&lt;/p&gt;&lt;p&gt; 各中小学公司的中层开发人员,有可能一个人做一个项目,或者带两三个虾米做项目;做的项目修修补补还是能用的,最常见的就是做点增删改查.&lt;/p&gt;&lt;p&gt;代表人群:工作3年内的.net开发人员,经常有重复劳动,最喜欢的就是什么代码生成器,以为牛逼的不得了. 很多人都会卡在这个瓶颈,主要问题是不知道要下一步怎么走&lt;/p&gt;&lt;p&gt;关键点:这个阶段的开发人员由于公司和项目的需要,经常很忙,或者从事重复性的工作, 职业视野不够开阔,不知道路在何方&lt;/p&gt;&lt;p&gt;如何突破瓶颈:看看你的领导或者其他比你高级的人员是怎么工作的,参加各种技术论坛和讲座;看书,视频&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;例如 mix11 chanel9, 可以看看CLR via c#之类的东西, 要考虑长久的 最好一口气突破下一个瓶颈&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;瓶颈四:高级开发人员 (年薪&amp;lt;25万)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在这个阶段的技术人员,狭义上的技术基本上达到一个很大的瓶颈(狭义的技术主要是说具体怎么实现,什么asp.net生命周期,控件开发,多线程 lock等等)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;大部分.net配套的技术和产品都已经会使用或者能在很短时间内学会,例如DNN,Nhibernate之类的&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;有一自己的一套想法或者一套解决问题的办法,有自己最熟悉的一套工作方式和工具&lt;/p&gt;&lt;p&gt;　 &amp;nbsp;由于长期从事某个一个行业或者领域的开发,会某个方面的技术特别的熟悉, 例如长期做ajax应用 对于HTTP HTML CSS javascript都比较了解,&lt;/p&gt;&lt;p&gt;甚至在某个方面的技术达到精通的地步,会在公司方面作为特别的技术牛人出现 可以解决很多其他team member的技术问题&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;有很强的学习能力,没涉及过的技术和体系 可以很快的自学, 有准备的情况下, 狭义的技术面试题难不住他们&lt;/p&gt;&lt;p&gt;　 但是对广义的技术还是比较欠缺,对于.net体系之外的东西知道的还不够, 对于核心的CLR IIS和windows 平台有些了解 不过还不够深入&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 代表人群: 小公司里面的技术牛人, 大中公司里面的team leader 一流公司的底层&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 关键点:这群人现在有较多的职业发展的选择,不过每条路都不是很容易, 而且职业发展前几年的黄金时间过去,接下来的发展速度会降低&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 如何突破瓶颈: 在这里 你会发现你的朋友圈会对你的职业生涯有很重要影响, 大部分这个级别以上的工作不会在招聘网站上见到, 主要还是靠朋友介绍&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 要考虑看广义技术上的书籍,而不是单纯.net的书籍, 如果要单纯的继续在.net上提高,已经很难有书可以帮到你,大部分时候要靠PPT 零碎的资料,朋友的聊天才能有所突破&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;瓶颈四之后 &amp;nbsp;&amp;nbsp;突破瓶颈四以后的路就很宽广了.....&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;这个阶段的职业发展 我认为有以下几条路线:&lt;/p&gt;&lt;p&gt;1.领域专家 &amp;nbsp;例如长期从事电信项目的开发,那么这个时候你的价值就会成为 电信业务知识+技术知识 (其实选行业比选语言赚钱多了) 要走这条路 要提前几年准备, 而且别选错行业...&lt;/p&gt;&lt;p&gt;2.管理层(开发经理,项目经理) &amp;nbsp; 中国大部分人准备走这条路线, 不过说实话 这条路比技术路线要难走;主要是要转变技术人员的思维.还有就是实践机会&lt;/p&gt;&lt;p&gt;3.纯技术路线 &amp;nbsp;这条路在中国的中小企业很难走, 大企业也不是很容易, 最好的办法就是去外企 (所以你需要提前准备你的英文和人际关系)&lt;/p&gt;&lt;p&gt;4.创业 &amp;nbsp;这条路线我不熟悉&lt;/p&gt;&lt;p&gt;5.架构师 这条路可遇不可求......最难的是经验.....例如电子商务方面的,最好找个类似的公司混进去看看人家怎么玩的.&lt;/p&gt;&lt;p&gt;6.换个行业 &amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;一些在我职业生涯中的关键点&lt;/p&gt;&lt;p&gt;1.要保持良好的心态&amp;nbsp;不要因为暂时没法达到你的目标而气馁&lt;/p&gt;&lt;p&gt;2.什么都会不如精通一个,了解多个&lt;/p&gt;&lt;p&gt;3.要时时刻刻保证对自己的投资, 例如学习,看书,扩展社交网络等&lt;/p&gt;&lt;p&gt;4.要做一件事情就做好,要么就不做&lt;/p&gt;&lt;p&gt;5.如果很长一段时间感觉没进步,你可能需要反思一下了&lt;/p&gt;&lt;p&gt;6.能力上去了 一切都不是问题&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;这些都是我的个人看法 希望对大家能有所帮助&lt;/p&gt;&lt;p&gt;如果觉得没啥用,请忽略,谢谢&lt;/p&gt;&lt;img src="http://www.cnblogs.com/PurpleTide/aggbug/2502547.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/PurpleTide/archive/2012/05/16/2502547.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/lengyuhong/archive/2012/05/16/2486420.html</id><title type="text">CSRF攻击原理以及nodejs的实现和防御</title><summary type="text">一、简介 CSRF (Cross-site Request Forgery)，中文名称：跨站伪造。危害是攻击者可以盗用你的身份，以你的名义发送恶意请求。比如可以盗取你的账号，以你的身份发送邮件，购买商品等。 二、原理 具体的原理图如下： 更加恐怖的是使用诸如img之类的标签，甚至不需要用户点击某个链接就可以发起攻击，比如B网站可以添加如下代码： &lt;img src='http://www.company.com/action?k1=v1&amp;k2=v2' width=0 height=0 /&gt; 这里width=0 height=0表示图片是不可见的。这个语句会导致</summary><published>2012-05-16T14:17:00Z</published><updated>2012-05-16T14:17:00Z</updated><author><name>lengyuhong</name><uri>http://www.cnblogs.com/lengyuhong/</uri></author><link rel="alternate" href="http://www.cnblogs.com/lengyuhong/archive/2012/05/16/2486420.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/lengyuhong/archive/2012/05/16/2486420.html"/><content type="html">&lt;p&gt;&amp;nbsp; &amp;nbsp; &lt;strong&gt;&lt;span style="font-size: 16px;"&gt;一、简介&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; CSRF (Cross-site Request Forgery)，中文名称：跨站伪造。危害是攻击者可以盗用你的身份，以你的名义发送恶意请求。比如可以盗取你的账号，以你的身份发送邮件，购买商品等。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &lt;strong&gt;&lt;span style="font-size: 16px;"&gt;二、原理&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 具体的原理图如下：&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&lt;img style="display: block; margin-left: auto; margin-right: auto;" src="http://pic002.cnblogs.com/images/2012/311780/2012050618354859.jpg" alt="" data-markzhi="registered" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 更加恐怖的是使用诸如img之类的标签，甚至不需要用户点击某个链接就可以发起攻击，比如B网站可以添加如下代码：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;    &lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;img &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;src&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;='http://www.company.com/action?k1=v1&amp;amp;k2=v2' &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;width&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;=0 &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;height&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;=0 &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 这里width=0 height=0&amp;nbsp;表示图片是不可见的。这个语句会导致游览器向另外的服务器发送一个请求。游览器不管该图片url实际是否指向一张图片，只要src字段中规定了url，就会按照地址触发这个请求。（游览器默认都是没有禁止下载图片，这是因为禁用图片后大多数web程序的可用性就会打折扣）。加载图片根本不考虑所涉及的图像所在位置（可以跨域）。如果A网站不小心提供了get接口就非常不幸得中招了&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;strong&gt;&lt;span style="font-size: 16px;"&gt;三、攻击&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #0000ff;"&gt;《&lt;a href="http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html"&gt;&lt;span style="color: #0000ff;"&gt;浅谈CSRF攻击方式&lt;/span&gt;&lt;/a&gt;》&lt;/span&gt;一文中已经用实例讲解了一个php的实现和防御方法，我这里主要是讲nodejs的实现和防御。为了简单起见，假设我们有一个应用，它提供了两个接口&amp;ldquo;/get/checkvalue&amp;rdquo;和 &amp;ldquo;'/post/setvalue&amp;rdquo;，它们都接受一个参数&amp;ldquo;value&amp;rdquo;来改变系统的一个某个值。 当然前提是用户已经正常登陆了。具体逻辑是它有一个用户登陆的过程，登陆成功后会将用户信息存到cookie中，之后就根据cookie来判断是否能正常访问，当然cookie信息经过md5加密。（真实应用中千万不要这么做，md5加密并不是非常安全）。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 服务主程序：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; app =&lt;span style="color: #000000;"&gt; http.createServer(function(req, res) {  &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;权限判断&lt;/span&gt;&lt;span style="color: #000000;"&gt;  authMiddle(req, res, function(err, checkValue){    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!checkValue) &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; res.end(html_login);    &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;数据查询和操作&lt;/span&gt;&lt;span style="color: #000000;"&gt;    controller(req, res);    });});&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 用户权限判断逻辑：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;var cookieValue = crypto.createHash('md5').update('jifeng_jifeng').digest('hex');&lt;br /&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt; getCookie(headers){  &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; cookies =&lt;span style="color: #000000;"&gt; {};  headers.cookie &lt;/span&gt;&amp;amp;&amp;amp; headers.cookie.split(';').forEach(&lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(cookie) {    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; parts = cookie.split('='&lt;span style="color: #000000;"&gt;);    cookies[ parts[ &lt;/span&gt;0 ].trim() ] = ( parts[ 1 ] || ''&lt;span style="color: #000000;"&gt; ).trim();  });    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; cookies;}&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt; checkUser(req, res, callback){  &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; chunks =&lt;span style="color: #000000;"&gt; [];  &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; length = 0&lt;span style="color: #000000;"&gt;;  &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; rows = &lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;;  req.on(&lt;/span&gt;'data', &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(data){    chunks.push(data);    length &lt;/span&gt;+=&lt;span style="color: #000000;"&gt; data.length;  })   req.on(&lt;/span&gt;'end', &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt;(){    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; rows = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Buffer(length);    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; len = 0&lt;span style="color: #000000;"&gt;;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;for&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; i = 0, il = chunks.length; i &amp;lt; il; i++&lt;span style="color: #000000;"&gt;) {      chunks[i].copy(rows, len);      len &lt;/span&gt;+=&lt;span style="color: #000000;"&gt; chunks[i].length;    }    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; args =&lt;span style="color: #000000;"&gt; querystring.parse(rows.toString());    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (args &amp;amp;&amp;amp; args.name === 'jifeng' &amp;amp;&amp;amp; args.password ==='jifeng'&lt;span style="color: #000000;"&gt;) {      res.setHeader(&lt;/span&gt;'Set-Cookie', ['cookie1987=' +&lt;span style="color: #000000;"&gt; cookieValue]);      callback(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;);       } &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;else&lt;/span&gt;&lt;span style="color: #000000;"&gt; {      callback(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;false&lt;/span&gt;&lt;span style="color: #000000;"&gt;);    }  }) }&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="color: #000000;"&gt; authMiddle(req, res, callback){  &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; flag = &lt;span style="color: #0000ff;"&gt;false&lt;/span&gt;&lt;span style="color: #000000;"&gt;;  &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; params = urllib.parse(req.url, &lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;);  &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (params.pathname === '/checkuser'&lt;span style="color: #000000;"&gt;) {    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; checkUser(req, res, callback);      } &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;else&lt;/span&gt;&lt;span style="color: #000000;"&gt; {    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; headers =&lt;span style="color: #000000;"&gt; req.headers;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; cookies = getCookie(headers);&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;得到用户cookie&lt;/span&gt;    &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (cookies &amp;amp;&amp;amp;&lt;span style="color: #000000;"&gt; cookies.cookie1987) {      &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; v =&lt;span style="color: #000000;"&gt; cookies.cookie1987;      &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (v ==&lt;span style="color: #000000;"&gt; cookieValue) {        flag &lt;/span&gt;= &lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;;        }    }    callback(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;, flag)  }}&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 那具体怎样进攻呢？&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; get攻击的页面很简单。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;img &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;src&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;='http://test3.data.taobao.com:5678/get/check?func=get&amp;amp;value=10'&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; post攻击的页面相对比较复杂&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;  &lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;head&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;    &lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;title&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;post 测试页面&lt;span style="color: #0000ff;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000;"&gt;title&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;    &lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;script&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;      &lt;span style="background-color: #f5f5f5; color: #0000ff;"&gt;function&lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #000000;"&gt; steal(){        &lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #0000ff;"&gt;var&lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #000000;"&gt; mySubmit &lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #000000;"&gt;=&lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #000000;"&gt; document.getElementById(&lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #000000;"&gt;'&lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #000000;"&gt;steal_form&lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #000000;"&gt;'&lt;/span&gt;&lt;span style="background-color: #f5f5f5; color: #000000;"&gt;);        mySubmit.submit();      }    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000;"&gt;script&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;  &lt;span style="color: #0000ff;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000;"&gt;head&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;  &lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;body &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;onload&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;='steal()'&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;form &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;id &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;= "steal_form"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; method&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="POST"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; action&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="http://test3.data.taobao.com:5678/post/check"&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;  &lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;input &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;type&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="hidden"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; name&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="func"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; value&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="post"&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;　&lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;input &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;type&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="hidden"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; name&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="value"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; value&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="1000"&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000;"&gt;form&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;  &lt;span style="color: #0000ff;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000;"&gt;body&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;但这里强调一点：现在游览器（chrome，firfox）为了安全考虑，默认都做了一定的限制，form标签发送到其他网站的请求会被拦截，大家有兴趣模拟这种情况时需要注意这个问题。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;详细的代码：&lt;span style="color: #0000ff;"&gt;&lt;a href="https://github.com/jifeng/toycode/tree/master/csrf"&gt;&lt;span style="color: #0000ff;"&gt;https://github.com/jifeng/toycode/tree/master/csrf&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;strong&gt;&lt;span style="font-size: 16px;"&gt;四、防范&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;访问csrf的措施虽然很多，但归根到底就是一条：在客户端提交请求时增加伪造随机数。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;nodejs中有些框架已经帮我们做了这件事，比如重用的&lt;span style="color: #0000ff;"&gt;&lt;a href="https://github.com/senchalabs/connect"&gt;&lt;span style="color: #0000ff;"&gt;connect&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;　 &amp;nbsp;它具体的实现：&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #0000ff;"&gt;&lt;a href="http://www.senchalabs.org/connect/csrf.html"&gt;&lt;span style="color: #0000ff;"&gt;http://www.senchalabs.org/connect/csrf.html&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/span&gt; &amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;举例：&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;&lt;span style="color: #0000ff;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/senchalabs/connect/blob/master/examples/csrf.js"&gt;&lt;span style="color: #0000ff;"&gt;https://github.com/senchalabs/connect/blob/master/examples/csrf.js&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 实现还是相对比较简单，有兴趣的同学可以再仔细看下。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;参考文章：&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html"&gt;浅谈CSRF攻击方式&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/lengyuhong/aggbug/2486420.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/lengyuhong/archive/2012/05/16/2486420.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hanyinglong/archive/2012/05/16/2505431.html</id><title type="text">LINQ学习(扩展方法,委托,Lambda表达式) 第二篇</title><summary type="text">LINQ基本查询操作符-获取数据(1) select() 语法是：public static IEnumerable&lt;TResult&gt; select&lt;TSource,TResult&gt;( this IEnumerbale&lt;TSource&gt; source.Func&lt;TSource,TResult&gt; selector)说明：1) select方法本身是一个泛型扩展方法2) 它作用于IEnumerable&lt;TSource&gt;类型3) 他只接受一个Func&lt;TSource,TResult&gt;类型参数4) Func&lt;TSource,</summary><published>2012-05-16T13:29:00Z</published><updated>2012-05-16T13:29:00Z</updated><author><name>韩迎龙</name><uri>http://www.cnblogs.com/hanyinglong/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hanyinglong/archive/2012/05/16/2505431.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hanyinglong/archive/2012/05/16/2505431.html"/><content type="html">&lt;ol&gt;&lt;li&gt;LINQ基本查询操作符-获取数据&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;(1) select()&amp;nbsp; 语法是：&lt;/p&gt;&lt;p&gt;public static IEnumerable&amp;lt;TResult&amp;gt; select&amp;lt;TSource,TResult&amp;gt;(&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; this IEnumerbale&amp;lt;TSource&amp;gt; source.&lt;/p&gt;&lt;p&gt;Func&amp;lt;TSource,TResult&amp;gt; selector)&lt;/p&gt;&lt;p&gt;说明：1) select方法本身是一个泛型扩展方法&lt;/p&gt;&lt;p&gt;&amp;nbsp;2) 它作用于IEnumerable&amp;lt;TSource&amp;gt;类型&lt;/p&gt;&lt;p&gt;&amp;nbsp;3) 他只接受一个Func&amp;lt;TSource,TResult&amp;gt;类型参数&lt;/p&gt;&lt;p&gt;&amp;nbsp;4) Func&amp;lt;TSource,TResult&amp;gt;是一个泛型委托，位于System名字的空间下，System.Core.dll中，在这里Selector是一个提取器。&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;(2) 举例说明，先定义一个静态类ExtraClass，然后再静态类中在定义一个静态的扩展方法，实现输出信息。代码如下：&lt;/p&gt;&lt;p align="left"&gt;public static class ExtraClass&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //为IEnumerable提供输出的方法&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void Print(this IEnumerable&amp;lt;string&amp;gt; ie)&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IEnumerator&amp;lt;string&amp;gt; result = ie.GetEnumerator();&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (result.MoveNext())&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MessageBox.Show(result.Current);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p align="left"&gt;}&lt;/p&gt;&lt;p align="left"&gt;然后如图所示，单击图上的按钮事件，在按钮事件中的代码是：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/359161/2012051621281759.png" alt="" /&gt;&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&lt;/p&gt;&lt;p align="left"&gt;此事件下面的代码实现了将一个泛型集合中的数据循环显示出来，代码如下：&lt;/p&gt;&lt;p align="left"&gt;List&amp;lt;string&amp;gt; persons = new List&amp;lt;string&amp;gt;();&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; persons.Add("zhang san");&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; persons.Add("zhang san feng");&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; persons.Add("li si");&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;persons.Add("wang wu");&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;persons.Add("wang liu");&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; persons.Add("li ba");&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; persons.Add("lao wu");&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; persons.Add("zhang xx");&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //输出persons里面的所有的元素&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = persons.Select(p =&amp;gt; p);&lt;/p&gt;&lt;p align="left"&gt;result.Print();&lt;/p&gt;&lt;p align="left"&gt;执行结果是按顺序循环输出。&lt;/p&gt;&lt;p&gt;2. LINQ基本查询操作符-过滤数据&lt;/p&gt;&lt;p&gt;(1) where() 语法是：&lt;/p&gt;&lt;p&gt;&amp;nbsp; public static IEnumerable&amp;lt;TSource&amp;gt; where&amp;lt;TSource&amp;gt;(&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this IEnumerable&amp;lt;TSource&amp;gt; source,&lt;/p&gt;&lt;p&gt;Func&amp;lt;TSource,bool&amp;gt; predicate)&lt;/p&gt;&lt;p&gt;说明：1) where方法是一个泛型扩展方法&lt;/p&gt;&lt;p&gt;&amp;nbsp;2) 它和select()一样作用与IEnumerable&amp;lt;TSource&amp;gt;类型&lt;/p&gt;&lt;p&gt;&amp;nbsp;3) 它只接受一个Func&amp;lt;TSource,bool&amp;gt;泛型委托参数，在这里predicate是一个判断条件。&lt;/p&gt;&lt;p&gt;(2) 举例说明，还是上面的例子，使用那个泛型集合，实现按照一定的条件输出信息。先创建一个方法，实现输出姓&amp;ldquo;zhang&amp;rdquo;的人的信息，代码如下：&lt;/p&gt;&lt;p align="left"&gt;public bool Judge(string s)&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (s.StartsWith("zhang"))&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false;&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp; 在select控件的事件下面用LINQ代码输出只是姓张的各种各样的实现代码：&lt;/p&gt;&lt;p align="left"&gt;//输出persons里面姓&amp;ldquo;zhang&amp;rdquo;的人&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //var result = persons.Where(p =&amp;gt; p.StartsWith("zhang"));&amp;nbsp; //第一种方法&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //var result = persons.Select(p =&amp;gt; p).Where(p =&amp;gt; p.StartsWith("zhang")); //第二种方法&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //var result = persons.Where(p =&amp;gt; p.StartsWith("zhang")).Select(p =&amp;gt; p);&amp;nbsp; //第三种方法&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = persons.Where(p =&amp;gt; Judge(p));&amp;nbsp;&amp;nbsp; //第四种方法&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result.Print();&lt;/p&gt;&lt;p&gt;3. LINQ基本查询操作符-排序数据&lt;/p&gt;&lt;p&gt;(1) OrderBy()&amp;nbsp; 语法是：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;pblic static IOrderedEnumerable&amp;lt;TSource&amp;gt; orderBy&amp;lt;TSource,TKey&amp;gt;(&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this IEnumerable&amp;lt;TSource&amp;gt; source&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Func&amp;lt;TSource,TKey&amp;gt; keySelector)&lt;/p&gt;&lt;p&gt;注：1) orderBy方法也是一个泛型扩展方法&lt;/p&gt;&lt;p&gt;&amp;nbsp;2) 它和select()一样作用与IEnumerable&amp;lt;TSource&amp;gt;类型&lt;/p&gt;&lt;p&gt;&amp;nbsp;3) 它只接收一个Func&amp;lt;TSource,Tkey&amp;gt;类型参数，在这里keySelctor指定要排序的字段。&lt;/p&gt;&lt;p&gt;&amp;nbsp;4) 如果想要降序排列可以使用orderbyDescending方法。&lt;/p&gt;&lt;p&gt;(2) 举例说明，同上例，实现排序的代码如下：&lt;/p&gt;&lt;p align="left"&gt;//var result = persons.OrderBy(p =&amp;gt; p);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //按照名字的最后一个字母排序&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //var result = persons.OrderBy(p =&amp;gt; p.Substring(p.Length - 1, 1)).Select(p =&amp;gt; p);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //降序排列&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = persons.OrderByDescending(p =&amp;gt; p);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result.Print();&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Linq基本查询操作符-分组数据&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;(1) GroupBy()&amp;nbsp; 语法是：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; public static IEnumerable&amp;lt;IGrouping&amp;lt;Tkey,TSource&amp;gt;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GroupBy&amp;lt;TSource,TKey&amp;gt;(&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this IEnumerable&amp;lt;TSource&amp;gt; source,&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Func&amp;lt;TSource,TKey&amp;gt; keySelector&amp;nbsp; )&lt;/p&gt;&lt;p&gt;说明：1) GroupBy()方式和OrderBy()方式非常类似，它也是一个泛型扩展方法。&lt;/p&gt;&lt;p&gt;2) 它和OrderBy()一样作用与IEnumerable&amp;lt;TSource&amp;gt;类型。&lt;/p&gt;&lt;p&gt;3) 它只接受一个Func&amp;lt;TSource,TKey&amp;gt;类型参数，在这里keySelector指定要分组的字段。&lt;/p&gt;&lt;p&gt;(2) 举例说明，同上例，实现分组的代码是：&lt;/p&gt;&lt;p align="left"&gt;//分组---按照姓名来分组&amp;brvbar;--取出姓名中的空格前的部分&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = persons.GroupBy(p =&amp;gt; p.Split(new char[] { ' ' })[0]);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (var group in result)&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("姓：" + group.Key);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (var name in group)&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("\t" + name);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine();&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;ol&gt;&lt;li&gt;查询执行的时机&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;(1) 从前面的试验中，我们发现一次查询实际上经过以下3步。&lt;/p&gt;&lt;p&gt;&amp;nbsp;1) 第一步：获取数据源&amp;nbsp; int[] numbers=new int[]{3,45,65,76,2,434,54,65,76,76,65,43};&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2) 第二步：定义查询&amp;nbsp; var even=numbers.Where(p=&amp;gt;p%2==0).Select(p=&amp;gt;{&lt;/p&gt;&lt;p&gt;Console.WriteLine(&amp;ldquo;Hi!&amp;rdquo;+p.ToString());&lt;/p&gt;&lt;p&gt;return p;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; });&lt;/p&gt;&lt;p&gt;&amp;nbsp; 3) 第三步：执行查询&amp;nbsp;&amp;nbsp; foreach(var item in even){&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;ol&gt;&lt;li&gt;查询执行的时机小节&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;(1) 查询分为以下三步，获取数据源，定义查询，执行查询&lt;/p&gt;&lt;p&gt;(2) 定义查询后，查询直到需要枚举结果时才被真正执行，这种方法称为&amp;rdquo;延迟执行&amp;rdquo;。&lt;/p&gt;&lt;p&gt;(3) 当查询结果返回单一值时，查询立即执行。举例，如代码：&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; //查询时机---当返回值为单值时马上执行语句，负责延迟执行&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = persons.Select(p =&amp;gt; p).Count();&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("个数是" + result);&lt;/p&gt;&lt;p&gt;(4) 因此，可以通过以下技巧在定义查询时就强制执行查询&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Linq查询的两种方式&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;(1) Method syntax，查询方法方式&lt;/p&gt;&lt;p&gt;&amp;nbsp; 主要利用System.Linq.Enumerable类中定义的扩展方法和Lambda表达式方式进行查询。&lt;/p&gt;&lt;p&gt;(2) Query syntax ，查询语句方式&lt;/p&gt;&lt;ol&gt;&lt;li&gt;查询语句VS查询方法&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;注：查询语句和查询方法存在着紧密的关系&lt;/p&gt;&lt;p&gt;(1) CLR本身并不理解查询语句，它只理解查询方法。&lt;/p&gt;&lt;p&gt;(2) 编译器负责在编译时将查询语句翻译为查询方法。&lt;/p&gt;&lt;p&gt;(3) 大部分查询方法都有对应的查询语句形式，如：Select()对应select，OrderBy()对应orderby。&lt;/p&gt;&lt;p&gt;(4)部分查询方法目前在C#中还没有对应的查询语句：如：Count()和Max()这时只能采用以下替代方案&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1) 查询方法&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2) 查询语句+查询方法的混合方式&lt;/p&gt;&lt;p&gt;(5) 一般情况下，建议使用可读性更好的查询语句。举例说明：&lt;/p&gt;&lt;p&gt;还是用上面多定义的泛型集合数组persons，用查询语句实现几个简单的功能，代码如下：&lt;/p&gt;&lt;p align="left"&gt;//输出persons中的所有的元素&lt;/p&gt;&lt;p align="left"&gt;var result = from p in persons select p;&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;//输出persons中姓zhang的人----语句和方法的混合编排?&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;var result = (from p in persons select p).Where(p =&amp;gt; p.StartsWith("zhang"));&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //排序&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = from p in persons orderby p select p;&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;result.Print();&lt;/p&gt;&lt;ol&gt;&lt;li&gt;高级查询方法&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;(1) 聚合类&amp;nbsp; Count()，Max()/Min()，Average()，Sum()。举例说明：&lt;/p&gt;&lt;p&gt;&amp;nbsp;重新定义一个数组来实现上面的各种各样的方法，代码如下：&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp; //LINQ to Objects查询高级方法&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;//数组数据arr&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int[] arr = { 23, 34, 5, 5, 23, 45, 65, 33, 43, 76, 67, 87 };&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //聚合类&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;Console.WriteLine("arr的最大值： + arr.Max());&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("arr的最小值&amp;brvbar;： " + arr.Min());&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("arr的平均值&amp;brvbar;： " + arr.Average());&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("arr的数组元素个是" + arr.Count());&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("arr的总和是： " + arr.Sum());&lt;/p&gt;&lt;p&gt;(2) 排序类&amp;nbsp; thenBy()&amp;nbsp; 代码如下：&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//排序类&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = arr.OrderBy(p =&amp;gt; p.ToString().Substring(0, 1));&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //2次排序混编模式没有达到要求&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = arr.OrderBy(p =&amp;gt; p.ToString().Substring(0, 1)).ThenBy(p =&amp;gt; p);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //按照语句排序&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;var t = arr.OrderBy(p =&amp;gt; p.ToString().Substring(0, 1));&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = from p in t orderby p descending select p;&lt;/p&gt;&lt;p&gt;(3) 分区类&amp;nbsp; Take,TakeWhile,Skip,SkipWhile&amp;nbsp;&amp;nbsp; 代码如下：&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//分区类&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = arr.Skip(3).Take(3);&amp;nbsp; //跳过三个值取三个值&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;var result = arr.Skip(3);&amp;nbsp; //跳过几个值&lt;/p&gt;&lt;p align="left"&gt;var result = arr.SkipWhile(p =&amp;gt; p &amp;gt; 4); //方法体部分是该Linq语句不在往后执行的条件,当第一次遇到条件成立时，取剩下的所有元素&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;var result = arr.TakeWhile(p =&amp;gt; p &amp;gt; 4); //方法体部分是该Linq语句提取数据的条件，当第一次遇到条件不成立的情况就停止执行&lt;/p&gt;&lt;p&gt;(4) 集合类&amp;nbsp;&amp;nbsp; Distinct&amp;nbsp;&amp;nbsp; 代码如下：&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//集合类&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = arr.Distinct();&lt;/p&gt;&lt;p&gt;(5) 生成类&amp;nbsp;&amp;nbsp; Range,Repeat&amp;nbsp;&amp;nbsp; 代码如下：&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//生成类-----静态类&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = System.Linq.Enumerable.Range(10, 50);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var result = System.Linq.Enumerable.Range('a', 50); //生成连续的序列&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;var result = System.Linq.Enumerable.Repeat(40, 10);&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result.Print();&lt;/p&gt;&lt;ol&gt;&lt;li&gt;生成类查询方法小结&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;注：使用生成类查询方法时，需要注意以下几点：&lt;/p&gt;&lt;p&gt;(1) 和其它几类方法不同，Range/Repeat不是扩展方法，而是普通的静态方法&lt;/p&gt;&lt;p&gt;(2) Range只能产生整数序列。&lt;/p&gt;&lt;p&gt;(3) Repeat可以产生泛型序列。&lt;/p&gt;&lt;p&gt;(4) 所有的查询方法都存放在System.Linq.Enumerable静态类中。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hanyinglong/aggbug/2505431.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hanyinglong/archive/2012/05/16/2505431.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/leoo2sk/archive/2012/05/16/opdumper-and-web-opcode-dumper.html</id><title type="text">发布一个查看PHP opcode的扩展模块及Web服务</title><summary type="text">最近花了大约一星期的时间写了一个PHP扩展模块Opdumer，并封装成了Web服务（点击这里访问）。这个模块的主要内容是输出PHP代码对应的opcode。其实之前已经有一些用于查看opcode的扩展模块，如比较有名的vld。之所以重新实现一个这样的模块，主要是因为vld不支持PHP_FUNCTION API，也就是说vld只能用于CLI形式，而Opdumer同时拥有CLI API和PHP_FUNCTION API，另外，也想借助编写这个模块的机会学习Zend Engine中opcode的编译和执行机制。个人打算后面专门针对opcode的编译执行机制写一篇文章，而本文主要描述Opcode的使用方法及对应Web服务的使用。</summary><published>2012-05-16T12:43:00Z</published><updated>2012-05-16T12:43:00Z</updated><author><name>T2噬菌体</name><uri>http://www.cnblogs.com/leoo2sk/</uri></author><link rel="alternate" href="http://www.cnblogs.com/leoo2sk/archive/2012/05/16/opdumper-and-web-opcode-dumper.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/leoo2sk/archive/2012/05/16/opdumper-and-web-opcode-dumper.html"/><content type="html">&lt;p&gt;最近花了大约一星期的时间写了一个PHP扩展模块&lt;a href="https://github.com/ericzhang-cn/opdumper" target="_blank"&gt;Opdumer&lt;/a&gt;，并封装成了Web服务（&lt;a href="http://supercompiler.com/app/opcode_dumper" target="_blank"&gt;点击这里访问&lt;/a&gt;）。这个模块的主要内容是输出PHP代码对应的opcode。其实之前已经有一些用于查看opcode的扩展模块，如比较有名的&lt;a href="http://pecl.php.net/package/vld" target="_blank"&gt;vld&lt;/a&gt;。之所以重新实现一个这样的模块，主要是因为vld不支持PHP_FUNCTION API，也就是说vld只能用于CLI形式，而Opdumer同时拥有CLI API和PHP_FUNCTION API，另外，也想借助编写这个模块的机会学习Zend Engine中opcode的编译和执行机制。个人打算后面专门针对opcode的编译执行机制写一篇文章，而本文主要描述Opcode的使用方法及对应Web服务的使用。&lt;/p&gt;&lt;!--more--&gt;&lt;p&gt;&lt;strong&gt;Opdumper&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;安装&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Opdumper的源码已经托管在github上，其地址为：&lt;a href="https://github.com/ericzhang-cn/opdumper" target="_blank"&gt;https://github.com/ericzhang-cn/opdumper&lt;/a&gt;。可以通过以下命令克隆源代码：&lt;/p&gt;&lt;pre class="brush:bash;"&gt;git clone https://github.com/ericzhang-cn/opdumper.git&lt;/pre&gt;&lt;p&gt;Opdumper是一个标准的PHP Extension，安装方法如下： 首先将Opdumper源码放到PHP源码包的ext/opdumper目录下，进入此目录执行如下命令：&lt;/p&gt;&lt;pre class="brush:bash;"&gt;phpize./configuremakemake install&lt;/pre&gt;&lt;p&gt;然后在php.ini中添加一行配置：&lt;/p&gt;&lt;pre class="brush:bash;"&gt;extension=opdumper.so&lt;/pre&gt;&lt;p&gt;目前opdumper支持PHP&amp;gt;=5.3，在Linux和MacOS下测试通过，Windows下未做测试。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;CLI API&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Opdumper支持类似vld的命令行方式输出opcode，只需在执行php命令时通过-d参数将opdumper.active=1传入。例如我们有一个foo.php：&lt;/p&gt;&lt;pre class="brush:php;"&gt;&amp;lt;?php$a = 'hello';echo $a;?&amp;gt;&lt;/pre&gt;&lt;p&gt;执行如下命令：&lt;/p&gt;&lt;pre class="brush:bash;"&gt;php -d opdumper.active=1 foo.php&lt;/pre&gt;&lt;p&gt;结果如下： &lt;img style="display: block; margin-left: auto; margin-right: auto;" title="Snip20120516_4.png" src="http://www.codinglabs.org/wp-content/uploads/2012/05/Snip20120516_4.png" alt="Snip20120516 4" width="575" height="593" border="0" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;PHP_FUNCTION API&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Opdumper还支持vld不支持的PHP_FUNCTION API，Opdumper提供了两个PHP函数：od_dump_opcodes_string和od_dump_opcodes_file。前者接受一个字符串作为产生，字符串是一段PHP代码；后者接受一个PHP文件作为参数，返回值均是一个存有opcode结果的PHP数组。以od_dump_opcodes_file为例，我们在foo.php同一目录下再写一个bar.php：&lt;/p&gt;&lt;pre class="brush:php;"&gt;&amp;lt;?php$opcodes = od_dump_opcodes_file('./foo.php');var_dump($opcodes);?&amp;gt;&lt;/pre&gt;&lt;p&gt;执行结果如下：&lt;/p&gt;&lt;pre class="brush:bash;"&gt;array(3) {  [0]=&amp;gt;  array(8) {    ["lineno"]=&amp;gt;    int(2)    ["opcode"]=&amp;gt;    string(11) "ZEND_ASSIGN"    ["op1_type"]=&amp;gt;    string(2) "CV"    ["op2_type"]=&amp;gt;    string(5) "CONST"    ["result_type"]=&amp;gt;    string(0) ""    ["op1"]=&amp;gt;    string(2) "~0"    ["op2"]=&amp;gt;    string(5) "hello"    ["result"]=&amp;gt;    string(0) ""  }  [1]=&amp;gt;  array(8) {    ["lineno"]=&amp;gt;    int(3)    ["opcode"]=&amp;gt;    string(9) "ZEND_ECHO"    ["op1_type"]=&amp;gt;    string(2) "CV"    ["op2_type"]=&amp;gt;    string(6) "UNUSED"    ["result_type"]=&amp;gt;    string(6) "UNUSED"    ["op1"]=&amp;gt;    string(2) "~0"    ["op2"]=&amp;gt;    string(6) "UNUSED"    ["result"]=&amp;gt;    string(6) "UNUSED"  }  [2]=&amp;gt;  array(8) {    ["lineno"]=&amp;gt;    int(5)    ["opcode"]=&amp;gt;    string(11) "ZEND_RETURN"    ["op1_type"]=&amp;gt;    string(5) "CONST"    ["op2_type"]=&amp;gt;    string(6) "UNUSED"    ["result_type"]=&amp;gt;    string(6) "UNUSED"    ["op1"]=&amp;gt;    string(1) "1"    ["op2"]=&amp;gt;    string(6) "UNUSED"    ["result"]=&amp;gt;    string(6) "UNUSED"  }}&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Opdumper的Web服务：Opcode Dumper&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;坦白说，安装PHP模块还是挺麻烦的。所以为了方便朋友们查看opcode，我为Opdumper搭建了一个在线Web服务：&lt;a href="http://supercompiler.com/app/opcode_dumper" target="_blank"&gt;http://supercompiler.com/app/opcode_dumper&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Web页面访问&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;只要访问这个页面，在编辑框中输入或粘贴进PHP代码，就可以快速看到相应的opcode： &lt;img style="display: block; margin-left: auto; margin-right: auto;" title="Snip20120516_5.png" src="http://www.codinglabs.org/wp-content/uploads/2012/05/Snip20120516_5.png" alt="Snip20120516 5" width="600" height="398" border="0" /&gt;同时，也可以将结果下载到本地（CSV文件格式）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;HTTP API方式访问&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;您可以通过访问如下API获取PHP代码的opcode： URI: &lt;strong&gt;http://supercompiler.com/api/dump_opcodes&lt;/strong&gt; Method: &lt;strong&gt;POST&lt;/strong&gt; Params: &lt;strong&gt;php_script=[您的PHP代码]&lt;/strong&gt;返回值为JSON格式，成功时success字段为"true"，data字段存储opcodes；失败时success字段为"false"，msg字段存放失败原因。 由于跨越的关系，目前只能使用Curl而不能使用Ajax方式调用这个API，后续会为其增加JSONP接口。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;结语&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;目前这个模块还比较初级，有很多需要完善的地方。也欢迎有兴趣的朋友通过github贡献代码。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/leoo2sk/aggbug/2505380.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/leoo2sk/archive/2012/05/16/opdumper-and-web-opcode-dumper.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/aawolf/archive/2012/05/16/2505146.html</id><title type="text">OpenXLive Push Notification Hosting服务开发指南</title><summary type="text">作者：马宁 示例代码下载： Download Windows Phone上支持Push Notification，用户可以使用微软的推送服务器（MPNS）向安装了自己软件的Windows Phone设备上推送三种不同的通知消息。 Push Notification的机制大致是这样的： 1. 运行在Windows Phone设备端的应用注册一个Push Notification Channel，获得一个唯一标识这个Channel的URI； 2. 将URI传回到开发者自己的服务器； 3. 当开发者需要发送Push Notification消息时，将规定格式的数据发送到之前获得的URI...</summary><published>2012-05-16T11:43:00Z</published><updated>2012-05-16T11:43:00Z</updated><author><name>马宁</name><uri>http://www.cnblogs.com/aawolf/</uri></author><link rel="alternate" href="http://www.cnblogs.com/aawolf/archive/2012/05/16/2505146.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/aawolf/archive/2012/05/16/2505146.html"/><content type="html">&lt;p&gt;&lt;strong&gt;作者：马宁&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;示例代码下载：&lt;/p&gt;  &lt;p&gt;&lt;a title="Download" href="http://files.cnblogs.com/aawolf/OpenXLive_Hosting_Demo.zip"&gt;Download&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Windows Phone上支持Push Notification，用户可以使用微软的推送服务器（MPNS）向安装了自己软件的Windows Phone设备上推送三种不同的通知消息。&lt;/p&gt;  &lt;p&gt;Push Notification的机制大致是这样的：&lt;/p&gt;  &lt;p&gt;1. 运行在Windows Phone设备端的应用注册一个Push Notification Channel，获得一个唯一标识这个Channel的URI；&lt;/p&gt;  &lt;p&gt;2. 将URI传回到开发者自己的服务器；&lt;/p&gt;  &lt;p&gt;3. 当开发者需要发送Push Notification消息时，将规定格式的数据发送到之前获得的URI上；&lt;/p&gt;  &lt;p&gt;4. 微软的推送服务器（MPNS）获得开发者发送的请求后，会通知到对应的Windows Phone设备上。&lt;/p&gt;  &lt;p&gt;整个过程如下图所示：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942405613.jpg"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="Ff402558_AP_Push_NotificationArch(en-us,VS_92)" border="0" alt="Ff402558_AP_Push_NotificationArch(en-us,VS_92)" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942418612.jpg" width="575" height="377" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;在整个过程中，对于开发者来说，有一个难点很难克服&amp;#8212;&amp;#8212;必须有自己的服务器，否则无法接收从Windows Phone应用中发送过来的URI。这个问题对于那些没有自己服务器的Windows Phone开发者来说，他们只能放弃在自己的应用中加入Push Notification的功能。&lt;/p&gt;  &lt;p&gt;OpenXLive为了解决这个问题，为开发者提供了Push Notification Hosting服务（OpenXLive Push Notification Hosting Server，简称OPNHS），允许开发者将OpenXLive的服务器作为自己的Push Notification服务器。只要开发者调用OpenXLive SDK中的API，就可以让OpenXLive的服务器来保存URI，当开发者需要发送Push Notification消息时，只需要登录OpenXLive开发者后台就可以发送了。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Silverlight应用中添加Push Notification&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;接下来，我们以Silverlight for Windows Phone应用为例，看看如何使用OpenXLive Push Notification Hosting功能，向 Silverlight应用发送三种不同的Push Notification。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1. 添加OpenXLive功能&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;首先，我们要为创建好的Silverlight应用添加OpenXLive引用。如果没有安装OpenXLive SDK的话，请在下面的链接中下载SDK。请注意，只有在0.9.8及其以上版本中，才包含OpenXLive Push Notification Hosting功能。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://developer.openxlive.net/sdk/download/"&gt;http://developer.openxlive.net/sdk/download/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;如果您已经安装了OpenXLive SDK，可以在安装路径（默认为C:\Program Files (x86)\Fulcrum Mobile Networks, Inc\OpenXLiveSDK\Bin\Silverlight）下找到OpenXLive.dll和OpenXLive.Silverlight.dll。&lt;/p&gt;  &lt;p&gt;找到这两个DLL后，我们需要将其拷贝到Silverlight应用的目录下，否则会产生一个访问异常。&lt;/p&gt;  &lt;p&gt;在Visual Studio的Solution Explorer中，选择Add References，添加OpenXLive.dll和OpenXLive.Silverlight.dll的引用。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942418579.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="clip_image003" border="0" alt="clip_image003" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942416038.png" width="527" height="424" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;然后，打开App.xaml.cs文件，添加下面的代码：&lt;/p&gt;  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; OpenXLive;&lt;span class="kwrd"&gt;using&lt;/span&gt; OpenXLive.Features;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Application_Launching(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, LaunchingEventArgs e){&lt;span class="kwrd"&gt;if&lt;/span&gt; (XLiveGameManager.CurrentSession == &lt;span class="kwrd"&gt;null&lt;/span&gt;){GameSession session = XLiveGameManager.CreateSession(&lt;span class="str"&gt;"enMQ3rEC5jsw8XygdFHuGpfk"&lt;/span&gt;);session.CreateSessionCompleted += &lt;span class="kwrd"&gt;new&lt;/span&gt; AsyncEventHandler(session_CreateSessionCompleted);session.Open();}}&lt;span class="kwrd"&gt;void&lt;/span&gt; session_CreateSessionCompleted(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, AsyncEventArgs e){AsyncProcessResult result = e.Result;&lt;span class="kwrd"&gt;if&lt;/span&gt; (!result.ReturnValue){&lt;span class="rem"&gt;// TODO: Error Handler&lt;/span&gt;}}&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Application_Activated(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, ActivatedEventArgs e){XLiveGameManager.Activated();}&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Application_Deactivated(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, DeactivatedEventArgs e){XLiveGameManager.Deactivated();}&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Application_Closing(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, ClosingEventArgs e){&lt;span class="kwrd"&gt;if&lt;/span&gt; (XLiveGameManager.CurrentSession != &lt;span class="kwrd"&gt;null&lt;/span&gt;){XLiveGameManager.CurrentSession.Close();}}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;代码具体的解释可以参考《在Windows Phone应用中添加OpenXLive数据分析功能》，链接如下：&lt;a href="http://www.cnblogs.com/aawolf/archive/2011/10/03/2198607.html"&gt;http://www.cnblogs.com/aawolf/archive/2011/10/03/2198607.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;当OpenXLive的引用和功能调用添加完成后，我们就可以添加Push Notification代码了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2. 发送Tile Notification&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;为了保证足够的灵活性，所以，开发者仍然调用Push Notification的API来注册Push Notification的Channel。我们首先来演示，如何创建并发送Tile Notification。关于Tile Notification的详细信息，可以参考MSDN上的文章：&lt;/p&gt;&lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/hh202970(VS.92).aspx"&gt;http://msdn.microsoft.com/en-us/library/hh202970(VS.92).aspx&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在Visual Studio中打开MainPage.xaml，添加三个ToggleSwitch控件，分别对应三种不同的Notification类型，选中控件添加Checked事件处理函数，然后进入代码编辑界面，添加如下的代码来创建Push Notification的Channel。&lt;/p&gt;&lt;p&gt;首先在MainPage.xaml.cs文件的顶部，添加引用：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Notification;&lt;span class="kwrd"&gt;using&lt;/span&gt; OpenXLive;&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;接下来，在与Tile Notification对应的ToggleSwitch控件Checked事件中，添加创建Push Notification Channel的代码：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;// The name of our push channel.&lt;/span&gt;&lt;span class="kwrd"&gt;string&lt;/span&gt; channelName = &lt;span class="str"&gt;"OpenXLivePushNotificationHostingChannel"&lt;/span&gt;;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; toggleSwitchTile_Checked(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, RoutedEventArgs e){&lt;span class="rem"&gt;// Try to find the push channel.&lt;/span&gt;HttpNotificationChannel pushChannel = HttpNotificationChannel.Find(channelName);&lt;span class="rem"&gt;// If the channel was not found, then create a new connection to the push service.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel == &lt;span class="kwrd"&gt;null&lt;/span&gt;){pushChannel = &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpNotificationChannel(channelName);&lt;span class="rem"&gt;// Register for all the events before attempting to open the channel.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);pushChannel.Open();&lt;span class="rem"&gt;// Bind this new channel for Tile events.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; !pushChannel.IsShellTileBound){var allowedDomains = &lt;span class="kwrd"&gt;new&lt;/span&gt; Collection&amp;lt;Uri&amp;gt; { &lt;span class="kwrd"&gt;new&lt;/span&gt; Uri(&lt;span class="str"&gt;"http://picture.openxlive.com/"&lt;/span&gt;) };pushChannel.BindToShellTile(allowedDomains);}}&lt;span class="kwrd"&gt;else&lt;/span&gt;{&lt;span class="rem"&gt;// The channel was already open, so just register for all the events.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);}RegisterNotificationUri(pushChannel.ChannelUri);}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;创建Push Notification Channel的代码与MSDN上的一致，三种不同的Notification在创建时其实是一样的，只是在创建之后进行了不同的操作，所以才会分成不同的Notification。比如，Tile Notification就需要在创建后，调用BindToShellTile方法，将其绑定到Tile上；在关闭Notification Channel前，还要记得调用UnbindToShellTile方法，解除绑定。&lt;/p&gt;&lt;p&gt;特别要强调的是，如果您的背景图片是放在OpenXLive服务器上的话，需要在调用BindToShellTile方法之前，首先将OpenXLive的图片服务器（picture.openxlive.com）添加到允许访问的域名列表中。当然，如果您的图片来自其他域名的服务器，您也要将对应的域名加入这个列表中。&lt;/p&gt;&lt;p&gt;接下来，我们来看将Notification Channel的Uri注册到OpenXLive服务器上的代码：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RegisterNotificationUri(Uri uri){&lt;span class="rem"&gt;// Send Channel Uri to OpenXLive Hosting Server&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (OpenXLive.XLiveGameManager.CurrentSession != &lt;span class="kwrd"&gt;null&lt;/span&gt;&amp;amp;&amp;amp;OpenXLive.XLiveGameManager.CurrentSession.IsValid&amp;amp;&amp;amp;uri != &lt;span class="kwrd"&gt;null&lt;/span&gt;){OpenXLive.XLiveGameManager.CurrentSession.RegistNotificationUriCompleted+= &lt;span class="kwrd"&gt;new&lt;/span&gt; OpenXLive.Features.AsyncEventHandler(CurrentSession_RegistNotificationUriCompleted);OpenXLive.XLiveGameManager.CurrentSession.RegisterNotificationUri(uri);}}&lt;span class="kwrd"&gt;void&lt;/span&gt; CurrentSession_RegistNotificationUriCompleted(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, OpenXLive.AsyncEventArgs e){OpenXLive.Features.AsyncProcessResult result = e.Result;&lt;span class="kwrd"&gt;if&lt;/span&gt; (result.ReturnValue){Debug.WriteLine(&lt;span class="str"&gt;"Channel Uri has been send to OpenXLive Hosting Server"&lt;/span&gt;);}&lt;span class="kwrd"&gt;else&lt;/span&gt;{Debug.WriteLine(result.ErrorMessage);}}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;调用OpenXLive Push Notification Hosting的方法非常简单，首先判断一下XLiveGameManager对象中CurrentSession属性中的GameSession对象是否有效，如果有效则首先添加RegistNotificationUriCompleted事件处理方法，然后调用GameSession的RegisterNotificationUri方法，将Push Notification Channel的Uri传递到OpenXLive Notification Hosting服务器（OPNHS）上即可。OPNHS没有提供解除绑定的方法，因为Notification Channel在解除绑定后会自动失效，OPNHS会将失效的Uri自动抛弃，所以客户端引用就没有必要解除绑定了。&lt;/p&gt;&lt;p&gt;还有两个需要提示的最佳实践：因为Push Notification Channel的注册需要GameSession有效，所以，在XLiveGameManager的CreateSession方法的CreateSessionCompleted事件处理函数中创建Push Notification Channel，并将其注册到OPNHS中，是最为安全的。&lt;/p&gt;&lt;p&gt;另外，由于HttpNotificationChannel创建Notification Channel本身也是一个异步调用，在某些情况下，如果我们在创建Notification Channel之后，马上注册到OPNHS上，Uri可能为空。所以，我们必须在HttpNotificationChannel的ChannelUriUpdated事件处理函数中增加注册代码，防止注册处理被漏掉。&lt;/p&gt;&lt;p&gt;好了，接下来看一下HttpNotificationChannel的ChannelUriUpdated事件处理函数的代码：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; PushChannel_ChannelUriUpdated(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, NotificationChannelUriEventArgs e){&lt;span class="kwrd"&gt;if&lt;/span&gt; (e.ChannelUri != &lt;span class="kwrd"&gt;null&lt;/span&gt;){RegisterNotificationUri(e.ChannelUri);}Dispatcher.BeginInvoke(() =&amp;gt;{&lt;span class="rem"&gt;// Display the new URI for testing purposes. Normally, the URI would be passed back to your web service at this point.&lt;/span&gt;System.Diagnostics.Debug.WriteLine(e.ChannelUri.ToString());});}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;虽然有一些Push Notification Channel创建的代码没有完整展示，这部分代码不影响OPNHS的使用，您可以在示例代码中看到完整的代码。&lt;/p&gt;&lt;p&gt;好了，到这里，我们就完成了客户端的代码工作，接下来就可以看一下运行的效果了。在客户端代码运行前，您必须确认已经在OpenXLive的开发者网站上创建了OpenXLive的游戏/应用，并且已经取得了有效的API Secrecy Key，具体的做法您可以参考《在开发者网站上创建OpenXLive游戏》：&lt;/p&gt;&lt;p&gt;&lt;a href="http://wiki.openxlive.net/Tutorial-4-Create-OpenXLive-Game-in-website.ashx"&gt;http://wiki.openxlive.net/Tutorial-4-Create-OpenXLive-Game-in-website.ashx&lt;/a&gt;&lt;/p&gt;&lt;p&gt;客户端程序开始运行之后，我们会看到下面的界面，并打开Tile Notification对应的ToggleSwitch控件：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942422417.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="3" border="0" alt="3" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942428480.png" width="282" height="468" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;然后点击模拟器上的Windows键，回到主界面，并切换到应用程序列表界面，将我们的示例程序pin到主界面上来显示：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942426811.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="2" border="0" alt="2" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942427334.png" width="270" height="448" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942437301.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="4" border="0" alt="4" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942436188.png" width="270" height="447" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;好了，我们现在已经打开了Tile Notification的Channel，并且将其注册到了OPNHS上，而且把应用程序的Tile pin到主界面上。客户端的工作就完成了。&lt;/p&gt;&lt;p&gt;作为开发者，想向客户端发送Tile Notification的时候，只需要登录到OpenXLive的开发者网站，在开发者后台的控制面板中选择对应的游戏/应用，然后在右侧的功能选项中，选择&amp;#8220;推送通知&amp;#8221;（Push Notification），在二级菜单中，选择&amp;#8220;Tile推送通知&amp;#8221;，我们会看到如下界面：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942437791.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="z1" border="0" alt="z1" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942446122.png" width="562" height="485" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Tile Notification一共能够传递六个参数，分为两组。前景部分，是标题（Title）、数字（Count）和前景图片（BackgroundImage），需要指出的是，数字的现实范围是0-99之间的整形数字；而背景组，是背景标题（BackTitle）、内容（BackContent）和背景图片（BackBackgroundImage）。Tile显示的布局与基本样式无法修改，如下图所示：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942446646.jpg"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="clip_image013" border="0" alt="clip_image013" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942447659.jpg" width="406" height="175" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;背景图片即可以是应用程序内部的资源图片，也可以是来自网络的图片。所以，OpenXLive的服务器也提供了图片上传的功能，开发者可以将Tile图片上传至OpenXLive服务器，然后再推送至Windows Phone手机。&lt;/p&gt;&lt;p&gt;如果使用应用程序内部的资源图片，在BackgroundImage栏输入图片在xap包中的相对路径即可。将图片添加到程序中作为背景图片，可以按以下步骤操作：&lt;/p&gt;&lt;p&gt;1. 右键项目，单击 &lt;strong&gt;添加&lt;/strong&gt;，然后再单击 &lt;strong&gt;添加现有项&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;2. 选中要添加的图片，单击 &lt;strong&gt;打开&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;3. 鼠标右键单击添加的图片，然后选择 &lt;strong&gt;属&lt;/strong&gt;&lt;strong&gt;性&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;4. 找到 &lt;strong&gt;属性&lt;/strong&gt; 对话框中的 &lt;strong&gt;生成操作&lt;/strong&gt; 属性，并设置为 &lt;strong&gt;内&lt;/strong&gt;&lt;strong&gt;容&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;5. 将 &lt;strong&gt;复制到输出目&lt;/strong&gt;&lt;strong&gt;录 &lt;/strong&gt;设置为 &lt;strong&gt;如果较新则复&lt;/strong&gt;&lt;strong&gt;制&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;发送Tile Notification 时，要使用该图片只需在BackgroundImage栏输入图片名称（包括后缀名）即可。如果图片不在根目录下则输入 Folder/Image.jpg。&lt;/p&gt;&lt;p&gt;目前OPNHS的功能相对简单，每次发送Push Notification时，开发者只能登录OpenXLive服务器，手工发送，还没有实现定时发送的功能。复杂功能会随着开发者的要求，在后续的版本中逐步实现。&lt;/p&gt;&lt;p&gt;好了，当您填写好Tile Notification的内容后，点击&amp;#8220;发送&amp;#8221;按钮。在Windows Phone的主界面上，您很快就可以看到推送过来的Tile内容了。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942456546.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="clip_image015" border="0" alt="clip_image015" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/20120516194245974.png" width="254" height="420" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. 发送Toast Notification&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;第二个推送通知类型是Toast Notification，当程序运行在后台时，Toast Notification会在屏幕的最上端显示一个提示条，和来短信、发现无线网络的系统提示一致。Toast Notification一般用在需要唤醒应用程序的时候，比如应用程序现在有新的内容等。&lt;/p&gt;&lt;p&gt;需要指出的是，每个应用程序最多支持打开一个Notification Channel，在示例程序中，我们并没有做几个Notification Channel的互斥，打开一个新的Channel前，应该关闭另外的Channel，否则会引发异常。&lt;/p&gt;&lt;p&gt;我们先来看一下表示Toast的ToggleSwitch控件的Checked和Unchecked事件处理函数：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; toggleSwitchToast_Checked(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, RoutedEventArgs e){&lt;span class="rem"&gt;// Try to find the push channel.&lt;/span&gt;HttpNotificationChannel pushChannel = HttpNotificationChannel.Find(ToastChannelName);&lt;span class="rem"&gt;// If the channel was not found, then create a new connection to the push service.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel == &lt;span class="kwrd"&gt;null&lt;/span&gt;){pushChannel = &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpNotificationChannel(ToastChannelName);&lt;span class="rem"&gt;// Register for all the events before attempting to open the channel.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);&lt;span class="rem"&gt;// Register for this notification only if you need to receive the notifications while your application is running.&lt;/span&gt;pushChannel.ShellToastNotificationReceived += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationEventArgs&amp;gt;(PushChannel_ShellToastNotificationReceived);pushChannel.Open();&lt;span class="rem"&gt;// Bind this new channel for Tile events.&lt;/span&gt;pushChannel.BindToShellToast();}&lt;span class="kwrd"&gt;else&lt;/span&gt;{&lt;span class="rem"&gt;// The channel was already open, so just register for all the events.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);&lt;span class="rem"&gt;// Register for this notification only if you need to receive the notifications while your application is running.&lt;/span&gt;pushChannel.ShellToastNotificationReceived += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationEventArgs&amp;gt;(PushChannel_ShellToastNotificationReceived);}&lt;span class="rem"&gt;// Send Channel Uri to OpenXLive Hosting Server&lt;/span&gt;RegisterNotificationUri(pushChannel.ChannelUri);}&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; toggleSwitchToast_Unchecked(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, RoutedEventArgs e){&lt;span class="rem"&gt;// Try to find the push channel.&lt;/span&gt;HttpNotificationChannel pushChannel = HttpNotificationChannel.Find(ToastChannelName);&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel != &lt;span class="kwrd"&gt;null&lt;/span&gt;){pushChannel.UnbindToShellToast();&lt;span class="rem"&gt;// Remove push notification channel&lt;/span&gt;pushChannel.Close();}}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;大部分代码与之前的Tile Notification相同，只是增加了Notification Channel的ShellToastNotificationReceived事件处理函数，并且调用BindToShellToast方法，将Notification Channel绑定到Toast上。&lt;/p&gt;&lt;p&gt;接下来，我们要处理两种情况：第一种，接收到Toast Notification时，应用程序正在运行；第二种，接收到Toast Notification时，应用程序在后台。当程序运行在后台时，Toast提示条会显示，当用户点击时，会跳转到Toast Notification中包括的命令行所指定的应用程序页面中。如果应用在前台，我们就要处理Notification Channel的ShellToastNotificationReceived事件处理函数，代码如下：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; PushChannel_ShellToastNotificationReceived(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, NotificationEventArgs e){StringBuilder message = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringBuilder();&lt;span class="kwrd"&gt;string&lt;/span&gt; relativeUri = &lt;span class="kwrd"&gt;string&lt;/span&gt;.Empty;message.AppendFormat(&lt;span class="str"&gt;"Received Toast {0}:\n"&lt;/span&gt;, DateTime.Now.ToShortTimeString());&lt;span class="rem"&gt;// Parse out the information that was part of the message.&lt;/span&gt;&lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; key &lt;span class="kwrd"&gt;in&lt;/span&gt; e.Collection.Keys){message.AppendFormat(&lt;span class="str"&gt;"{0}: {1}\n"&lt;/span&gt;, key, e.Collection[key]);&lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt;.Compare(key,&lt;span class="str"&gt;"wp:Param"&lt;/span&gt;,System.Globalization.CultureInfo.InvariantCulture,System.Globalization.CompareOptions.IgnoreCase) == 0){relativeUri = e.Collection[key];}}&lt;span class="rem"&gt;// Display a dialog of all the fields in the toast.&lt;/span&gt;Dispatcher.BeginInvoke(() =&amp;gt; MessageBox.Show(message.ToString()));}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;为了处理第二种情况，我们要创建一个新的页面，命名为Page2.xaml。我们要在Page2的OnNavigatedTo方法中增加一段代码，以显示导航来源是哪里：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e){&lt;span class="kwrd"&gt;base&lt;/span&gt;.OnNavigatedTo(e);textBlockFrom.Text = &lt;span class="str"&gt;"Navigated here from "&lt;/span&gt; + &lt;span class="kwrd"&gt;this&lt;/span&gt;.NavigationContext.QueryString[&lt;span class="str"&gt;"NavigatedFrom"&lt;/span&gt;];}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;好了，到这里，我们客户端的代码写完了。我们运行代码示例，并且打开Toast Notification的开关，然后将应用切换到后台。&lt;/p&gt;&lt;p&gt;然后，我们打开OpenXLive的开发者后台，切换至&amp;#8220;推送消息&amp;#8221;界面的&amp;#8220;Toast推送消息&amp;#8221;中。界面截图如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942455085.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="z4" border="0" alt="z4" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942457560.png" width="562" height="227" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Toast Notification有三个参数：Text1，是Toast消息的标题；Text2，是副标题；Param，是需要传递给应用程序的命令行参数，所以必须保证这个参数能够被应用程序解析，否则会引起应用程序崩溃，如果不填，则默认打开应用程序的主界面。&lt;/p&gt;&lt;p&gt;好了，接下来看运行效果如何了。当应用程序在后台的情况下，首先会显示Toast提示条，点击提示条后，会进入命令行指定的应用程序页面。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942468607.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="5" border="0" alt="5" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942463906.png" width="258" height="427" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942476348.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="6" border="0" alt="6" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942474364.png" width="257" height="426" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在应用程序运行在前台的情况下，我们会用一个MessageBox来显示收到的消息：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942475967.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="7" border="0" alt="7" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942472346.png" width="294" height="487" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;好了，我们完成了第二个Toast Notification的全部工作。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. 发送Raw Notification&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Raw Notification是三种推送通知中用的比较少的一种，因为Raw Notification只有在应用程序在前台的情况下才有用。在这种情况下，一般的开发者会倾向于使用自己更加能够掌控的Pull方式，而不是由服务器端发起的Push方式。无所谓了，反正对于Raw Notification，我们是要介绍的。&lt;/p&gt;&lt;p&gt;首先还是客户端的代码，已经是轻车熟路了：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; toggleSwitchRaw_Checked(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, RoutedEventArgs e){&lt;span class="rem"&gt;// Try to find the push channel.&lt;/span&gt;HttpNotificationChannel pushChannel = HttpNotificationChannel.Find(RawChannelName);&lt;span class="rem"&gt;// If the channel was not found, then create a new connection to the push service.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel == &lt;span class="kwrd"&gt;null&lt;/span&gt;){pushChannel = &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpNotificationChannel(RawChannelName);&lt;span class="rem"&gt;// Register for all the events before attempting to open the channel.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);pushChannel.HttpNotificationReceived += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;HttpNotificationEventArgs&amp;gt;(PushChannel_HttpNotificationReceived);pushChannel.Open();}&lt;span class="kwrd"&gt;else&lt;/span&gt;{&lt;span class="rem"&gt;// The channel was already open, so just register for all the events.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);pushChannel.HttpNotificationReceived += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;HttpNotificationEventArgs&amp;gt;(PushChannel_HttpNotificationReceived);&lt;span class="rem"&gt;// Display the URI for testing purposes. Normally, the URI would be passed back to your web service at this point.&lt;/span&gt;System.Diagnostics.Debug.WriteLine(pushChannel.ChannelUri.ToString());MessageBox.Show(String.Format(&lt;span class="str"&gt;"Channel Uri is {0}"&lt;/span&gt;,pushChannel.ChannelUri.ToString()));}&lt;span class="rem"&gt;// Send Channel Uri to OpenXLive Hosting Server&lt;/span&gt;RegisterNotificationUri(pushChannel.ChannelUri);}&lt;span class="kwrd"&gt;void&lt;/span&gt; PushChannel_HttpNotificationReceived(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, HttpNotificationEventArgs e){&lt;span class="kwrd"&gt;string&lt;/span&gt; message;&lt;span class="kwrd"&gt;using&lt;/span&gt; (System.IO.StreamReader reader = &lt;span class="kwrd"&gt;new&lt;/span&gt; System.IO.StreamReader(e.Notification.Body)){message = reader.ReadToEnd();}Dispatcher.BeginInvoke(() =&amp;gt;MessageBox.Show(String.Format(&lt;span class="str"&gt;"Received Notification {0}:\n{1}"&lt;/span&gt;,DateTime.Now.ToShortTimeString(), message)));}&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; toggleSwitchRaw_Unchecked(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, RoutedEventArgs e){&lt;span class="rem"&gt;// Try to find the push channel.&lt;/span&gt;HttpNotificationChannel pushChannel = HttpNotificationChannel.Find(RawChannelName);&lt;span class="rem"&gt;// If the channel was not found, then create a new connection to the push service.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel != &lt;span class="kwrd"&gt;null&lt;/span&gt;){pushChannel.Close();}}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;唯一需要多说的是，增加了Notification Channel的HttpNotificationReceived事件处理方法，我们从e.Notification.Body中获取到发送的Raw消息，然后使用StreamReader将其读取为文本，还可以继续进行进一步的处理。&lt;/p&gt;&lt;p&gt;运行客户端程序，打开Raw Notification的选项，将应用程序保持在运行状态。登录OpenXLive开发者后台，&amp;#8220;推送消息&amp;#8221;界面的&amp;#8220;Raw推送消息&amp;#8221;中，将想要发送的文本粘贴到文本框中，点击&amp;#8220;发送&amp;#8221;按钮。&lt;/p&gt;&lt;p&gt;手机客户端的运行效果如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942487853.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="8" border="0" alt="8" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942485311.png" width="310" height="514" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;好了，到这里，我们就将OpenXLive Push Notification Hosting Server（OPNHS）的功能，如何发送三种Push Notification，以及在Silverlight for Windows Phone应用中如何创建Notification Channel、注册到OPNHS中和接收推送通知等。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;XNA游戏中添加Push Notification&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;接下来，我们会介绍，如何在XNA游戏中实现与Silverlight应用中相同的功能。其实，XNA中绝大部分功能与Silverlight类似，只是XNA是一个事件驱动的应用程序框架，而Silverlight是事件驱动，所以在选择创建Notification Channel的时间点上略有差异。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1. 添加OpenXLive功能&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;首先，我们还是首先添加OpenXLive的引用，在OpenXLive SDK的安装路径（默认为C:\Program Files (x86)\Fulcrum Mobile Networks, Inc\OpenXLiveSDK\Bin\XNA）下找到OpenXLive.dll和OpenXLive.Form.dll。这两个DLL是在XNA程序中使用的。&lt;/p&gt;&lt;p&gt;XNA游戏中，我们必须首先在Solution Explorer中打开Properties目录下的WMAppManifest.xml文件，找到下面这句代码，Publisher的名字不能为空，否则无法正确获得Push Notification的消息：&lt;/p&gt;&lt;p&gt;&amp;lt;App xmlns="" ProductID="{aee39b6f-5faf-41e6-a992-5055fdf3a13e}" Title="PushDemoXNA" RuntimeType="XNA" Version="1.0.0.0" Genre="Apps.Normal" Author="" Description="" Publisher="OpenXLive"&amp;gt;&lt;/p&gt;&lt;p&gt;我们打开Game1.cs文件，首先，在文件顶部添加引用：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; OpenXLive;&lt;span class="kwrd"&gt;using&lt;/span&gt; OpenXLive.Features;&lt;span class="kwrd"&gt;using&lt;/span&gt; OpenXLive.Forms;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Notification;&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;然后，声明一个XLiveFormManger变量，该对象主要是用来控制XNA中所有的UI Framework，如果不需要显示OpenXLive的XNA UI，该变量也可以不加：&lt;/p&gt;&lt;p&gt;XLiveFormManager manager;&lt;/p&gt;&lt;p&gt;接下来，定位到Initialize方法，接下来添加下面的代码：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Initialize(){&lt;span class="rem"&gt;// TODO: Add your initialization logic here&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (XLiveGameManager.CurrentSession == &lt;span class="kwrd"&gt;null&lt;/span&gt;){GameSession session = XLiveGameManager.CreateSession(&lt;span class="str"&gt;"enMQ3rEC5jsw8XygdFHuGpfk"&lt;/span&gt;);session.CreateSessionCompleted += &lt;span class="kwrd"&gt;new&lt;/span&gt; AsyncEventHandler(session_CreateSessionCompleted);manager = &lt;span class="kwrd"&gt;new&lt;/span&gt; XLiveFormManager(&lt;span class="kwrd"&gt;this&lt;/span&gt;, session);session.Open();Components.Add(manager);}&lt;span class="kwrd"&gt;base&lt;/span&gt;.Initialize();}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;在OpenXLive SDK 0.9.8及以上版本中，我们修改了创建XNA UI框架的方法，您可以先创建GameSession对象，然后再将GameSession对象传递给XLiveFormManger，这样可以保证Silverlight和XNA调用方式的基本一致。如果您的游戏中只适用了OpenXLive的逻辑代码，而没有使用UI代码的话，也可以不必创建XLiveFormManger对象。但是，调用GameSession的Open方法是必须的。然后，将XLiveFormManger对象作为Game Compoent添加到系统中，也能够保证OpenXLive的XNA UI系统能够被系统释放掉。&lt;/p&gt;&lt;p&gt;好了，短短几句话，我们就可以将OpenXLive加入到我们的XNA游戏中了。接下来，我们就要在GameSession创建成功的事件处理函数中添加Push Notification的代码了。&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; session_CreateSessionCompleted(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, AsyncEventArgs e){AsyncProcessResult result = e.Result;&lt;span class="kwrd"&gt;if&lt;/span&gt; (result.ReturnValue){&lt;span class="rem"&gt;// TODO: Error Handler&lt;/span&gt;CreateNotificationChannel();}}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;&lt;strong&gt;2. 发送Tile Notification&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;由于XNA的UI控制代码写起来比较费事，所以，我们就在这里简化了调用流程，每次运行程序，只能创建一种Push Notification，代码如下：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CreateNotificationChannel(){CreateTileNotification();&lt;span class="rem"&gt;//CreateToastNotification();&lt;/span&gt;&lt;span class="rem"&gt;//CreateRawNotification();&lt;/span&gt;}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;而创建Notification Channel的代码与Silverlight版本是一模一样的：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CreateTileNotification(){&lt;span class="kwrd"&gt;if&lt;/span&gt; (XLiveGameManager.CurrentSession != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; XLiveGameManager.CurrentSession.IsValid){&lt;span class="rem"&gt;// Try to find the push channel.&lt;/span&gt;HttpNotificationChannel pushChannel = HttpNotificationChannel.Find(TileChannelName);&lt;span class="rem"&gt;// If the channel was not found, then create a new connection to the push service.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel == &lt;span class="kwrd"&gt;null&lt;/span&gt;){pushChannel = &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpNotificationChannel(TileChannelName);&lt;span class="rem"&gt;// Register for all the events before attempting to open the channel.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);pushChannel.Open();&lt;span class="rem"&gt;// Bind this new channel for Tile events.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; !pushChannel.IsShellTileBound){var allowedDomains = &lt;span class="kwrd"&gt;new&lt;/span&gt; Collection&amp;lt;Uri&amp;gt; { &lt;span class="kwrd"&gt;new&lt;/span&gt; Uri(&lt;span class="str"&gt;"http://picture.openxlive.com/"&lt;/span&gt;) };pushChannel.BindToShellTile(allowedDomains);}}&lt;span class="kwrd"&gt;else&lt;/span&gt;{&lt;span class="rem"&gt;// The channel was already open, so just register for all the events.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);}RegisterNotificationUri(pushChannel.ChannelUri);}}&lt;span class="kwrd"&gt;void&lt;/span&gt; PushChannel_ChannelUriUpdated(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, NotificationChannelUriEventArgs e){&lt;span class="kwrd"&gt;if&lt;/span&gt; (e.ChannelUri != &lt;span class="kwrd"&gt;null&lt;/span&gt;){RegisterNotificationUri(e.ChannelUri);}&lt;span class="rem"&gt;// Display the new URI for testing purposes. Normally, the URI would be passed back to your web service at this point.&lt;/span&gt;System.Diagnostics.Debug.WriteLine(e.ChannelUri.ToString());}&lt;span class="kwrd"&gt;void&lt;/span&gt; PushChannel_ErrorOccurred(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, NotificationChannelErrorEventArgs e){&lt;span class="rem"&gt;// Error handling logic for your particular application would be here.&lt;/span&gt;MessageBox.Show(String.Format(&lt;span class="str"&gt;"A push notification {0} error occurred. {1} ({2}) {3}"&lt;/span&gt;,e.ErrorType, e.Message, e.ErrorCode, e.ErrorAdditionalData));}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;需要提示的是，这里的MessageBox是OpenXLive封装的MessageBox，用法和Silverlight中系统提供的一致。&lt;/p&gt;&lt;p&gt;最后，也是最核心的代码是注册Uri的部分代码：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RegisterNotificationUri(Uri uri){&lt;span class="rem"&gt;// Send Channel Uri to OpenXLive Hosting Server&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (OpenXLive.XLiveGameManager.CurrentSession != &lt;span class="kwrd"&gt;null&lt;/span&gt;&amp;amp;&amp;amp;OpenXLive.XLiveGameManager.CurrentSession.IsValid&amp;amp;&amp;amp;uri != &lt;span class="kwrd"&gt;null&lt;/span&gt;){OpenXLive.XLiveGameManager.CurrentSession.RegistNotificationUriCompleted+= &lt;span class="kwrd"&gt;new&lt;/span&gt; OpenXLive.Features.AsyncEventHandler(CurrentSession_RegistNotificationUriCompleted);OpenXLive.XLiveGameManager.CurrentSession.RegisterNotificationUri(uri);}}&lt;span class="kwrd"&gt;void&lt;/span&gt; CurrentSession_RegistNotificationUriCompleted(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, OpenXLive.AsyncEventArgs e){OpenXLive.Features.AsyncProcessResult result = e.Result;&lt;span class="kwrd"&gt;if&lt;/span&gt; (result.ReturnValue){Debug.WriteLine(&lt;span class="str"&gt;"Channel Uri has been send to OpenXLive Hosting Server"&lt;/span&gt;);}&lt;span class="kwrd"&gt;else&lt;/span&gt;{Debug.WriteLine(result.ErrorMessage);}}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;好了，我们运行程序，然后将程序切换到后台，将游戏图标pin到主界面上，最后发送Tile Notification的效果如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942485835.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="clip_image026" border="0" alt="clip_image026" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942498625.png" width="263" height="436" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. 发送Toast Notification&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;发送Toast Notification的代码如下：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CreateToastNotification(){&lt;span class="rem"&gt;// Try to find the push channel.&lt;/span&gt;HttpNotificationChannel pushChannel = HttpNotificationChannel.Find(ToastChannelName);&lt;span class="rem"&gt;// If the channel was not found, then create a new connection to the push service.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel == &lt;span class="kwrd"&gt;null&lt;/span&gt;){pushChannel = &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpNotificationChannel(ToastChannelName);&lt;span class="rem"&gt;// Register for all the events before attempting to open the channel.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);&lt;span class="rem"&gt;// Register for this notification only if you need to receive the notifications while your application is running.&lt;/span&gt;pushChannel.ShellToastNotificationReceived += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationEventArgs&amp;gt;(PushChannel_ShellToastNotificationReceived);pushChannel.Open();&lt;span class="rem"&gt;// Bind this new channel for Tile events.&lt;/span&gt;pushChannel.BindToShellToast();}&lt;span class="kwrd"&gt;else&lt;/span&gt;{&lt;span class="rem"&gt;// The channel was already open, so just register for all the events.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);&lt;span class="rem"&gt;// Register for this notification only if you need to receive the notifications while your application is running.&lt;/span&gt;pushChannel.ShellToastNotificationReceived += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationEventArgs&amp;gt;(PushChannel_ShellToastNotificationReceived);}&lt;span class="rem"&gt;// Send Channel Uri to OpenXLive Hosting Server&lt;/span&gt;RegisterNotificationUri(pushChannel.ChannelUri);}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;如果发送Toast Notification时，XNA游戏在运行，会调用下面的ShellToastNotificationReceived事件处理函数，显示MessageBox：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; PushChannel_ShellToastNotificationReceived(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, NotificationEventArgs e){StringBuilder message = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringBuilder();&lt;span class="kwrd"&gt;string&lt;/span&gt; relativeUri = &lt;span class="kwrd"&gt;string&lt;/span&gt;.Empty;message.AppendFormat(&lt;span class="str"&gt;"Received Toast {0}:\n"&lt;/span&gt;, DateTime.Now.ToShortTimeString());&lt;span class="rem"&gt;// Parse out the information that was part of the message.&lt;/span&gt;&lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; key &lt;span class="kwrd"&gt;in&lt;/span&gt; e.Collection.Keys){message.AppendFormat(&lt;span class="str"&gt;"{0}: {1}\n"&lt;/span&gt;, key, e.Collection[key]);&lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt;.Compare(key,&lt;span class="str"&gt;"wp:Param"&lt;/span&gt;,System.Globalization.CultureInfo.InvariantCulture,System.Globalization.CompareOptions.IgnoreCase) == 0){relativeUri = e.Collection[key];}}&lt;span class="rem"&gt;// Display a dialog of all the fields in the toast.&lt;/span&gt;MessageBox.Show(message.ToString());}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;需要特别提示的是，在XNA游戏中，命令行是无效的，如果添加一个无效的命令行参数，会造成程序崩溃，建议将命令行设为空即可。OpenXLive服务器发送Toast Notification后，运行效果如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942492737.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="clip_image028" border="0" alt="clip_image028" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942497164.png" width="248" height="410" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. 发送Raw Notification&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;最后是Raw Notification，代码如下：&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CreateRawNotification(){&lt;span class="rem"&gt;// Try to find the push channel.&lt;/span&gt;HttpNotificationChannel pushChannel = HttpNotificationChannel.Find(RawChannelName);&lt;span class="rem"&gt;// If the channel was not found, then create a new connection to the push service.&lt;/span&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (pushChannel == &lt;span class="kwrd"&gt;null&lt;/span&gt;){pushChannel = &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpNotificationChannel(RawChannelName);&lt;span class="rem"&gt;// Register for all the events before attempting to open the channel.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);pushChannel.HttpNotificationReceived += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;HttpNotificationEventArgs&amp;gt;(PushChannel_HttpNotificationReceived);pushChannel.Open();}&lt;span class="kwrd"&gt;else&lt;/span&gt;{&lt;span class="rem"&gt;// The channel was already open, so just register for all the events.&lt;/span&gt;pushChannel.ChannelUriUpdated += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelUriEventArgs&amp;gt;(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;NotificationChannelErrorEventArgs&amp;gt;(PushChannel_ErrorOccurred);pushChannel.HttpNotificationReceived += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;HttpNotificationEventArgs&amp;gt;(PushChannel_HttpNotificationReceived);&lt;span class="rem"&gt;// Display the URI for testing purposes. Normally, the URI would be passed back to your web service at this point.&lt;/span&gt;System.Diagnostics.Debug.WriteLine(pushChannel.ChannelUri.ToString());MessageBox.Show(String.Format(&lt;span class="str"&gt;"Channel Uri is {0}"&lt;/span&gt;,pushChannel.ChannelUri.ToString()));}&lt;span class="rem"&gt;// Send Channel Uri to OpenXLive Hosting Server&lt;/span&gt;RegisterNotificationUri(pushChannel.ChannelUri);}&lt;span class="kwrd"&gt;void&lt;/span&gt; PushChannel_HttpNotificationReceived(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, HttpNotificationEventArgs e){&lt;span class="kwrd"&gt;string&lt;/span&gt; message;&lt;span class="kwrd"&gt;using&lt;/span&gt; (System.IO.StreamReader reader = &lt;span class="kwrd"&gt;new&lt;/span&gt; System.IO.StreamReader(e.Notification.Body)){message = reader.ReadToEnd();}MessageBox.Show(String.Format(&lt;span class="str"&gt;"Received Notification {0}:\n{1}"&lt;/span&gt;,DateTime.Now.ToShortTimeString(), message));}&lt;/pre&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;运行效果如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942499639.png"&gt;&lt;img style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline;background-image: none;" title="clip_image030" border="0" alt="clip_image030" src="http://images.cnblogs.com/cnblogs_com/aawolf/201205/201205161942507654.png" width="472" height="285" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;写在最后&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;到这里，我们就介绍了OpenXLive Push Notification Hosting Server的全部功能，需要指出的是，OpenXLive并没有提供自己的Push Notification服务，我们仍旧使用微软提供的Push Notification机制。OpenXLive Push Notification Hosting Server只是帮助那些没有服务器、又希望能够使用Push Notification服务的开发者，来管理Push Notification Channel，给他们一个友好的Web界面，供他们来发送自己的Push Notification消息。&lt;/p&gt;&lt;p&gt;如果您还有任何的疑问，或者需要更详细的帮助，欢迎您访问OpenXLive的官方网站&lt;/p&gt;&lt;p&gt;英文版：&lt;a href="http://www.openxlive.com/"&gt;http://www.openxlive.com/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;中文版：&lt;a href="http://www.openxlive.net/"&gt;http://www.openxlive.net/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;中文论坛：&lt;a href="http://bbs.openxlive.com/"&gt;http://bbs.openxlive.com/&lt;/a&gt;&lt;/p&gt; &lt;img src="http://www.cnblogs.com/aawolf/aggbug/2505146.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/aawolf/archive/2012/05/16/2505146.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/xuxn/archive/2012/05/16/real-hungarian-notation.html</id><title type="text">这才是真正的“匈牙利命名法”</title><summary type="text">从刚进大学开始学习 C 语言，就听说了实际开发中会用到的各种变量命名方法，例如常见的匈牙利命名法、骆驼命名法、Pascal 命名法等。后来自己真正开始用 C/C++ 写程序，开始使用匈牙利命名法，总觉得十分别扭。好好的变量名 name，严格按照命名规则，非得在前面加类型前缀，改写成 lpszName。如今的 IDE 都会自动检查变量类型，而且类型错误在编译时也比较容易发现，在变量名前面强制加上类型信息实在不知道有什么意义。直到无意中在《More Joel on Software》[1] 这本书第 23 章看到匈牙利命名法作者——Charles Simonyi 的本意。1. 应用型匈牙利命名法—</summary><published>2012-05-16T11:29:00Z</published><updated>2012-05-16T11:29:00Z</updated><author><name>XuXn</name><uri>http://www.cnblogs.com/xuxn/</uri></author><link rel="alternate" href="http://www.cnblogs.com/xuxn/archive/2012/05/16/real-hungarian-notation.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/xuxn/archive/2012/05/16/real-hungarian-notation.html"/><content type="html">&lt;p&gt;从刚进大学开始学习 C 语言，就听说了实际开发中会用到的各种变量命名方法，例如常见的匈牙利命名法、骆驼命名法、Pascal 命名法等。&lt;/p&gt;&lt;p&gt;后来自己真正开始用 C/C++ 写程序，开始使用匈牙利命名法，总觉得十分别扭。好好的变量名 name，严格按照命名规则，非得在前面加类型前缀，改写成 lpszName。&lt;/p&gt;&lt;p&gt;如今的 IDE 都会自动检查变量类型，而且类型错误在编译时也比较容易发现，在变量名前面强制加上类型信息实在不知道有什么意义。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;直到无意中在《More Joel on Software》[1] 这本书第 23 章看到匈牙利命名法作者&amp;mdash;&amp;mdash;Charles Simonyi 的本意。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1. 应用型匈牙利命名法&lt;span style="font-size: 12px;"&gt;&amp;mdash;&amp;mdash;鲜为人知的正统&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&lt;p&gt;Simonyi 的匈牙利命名法的原型在微软公司内部最初被叫做&amp;ldquo;&lt;strong&gt;应用型匈牙利命名法&lt;/strong&gt;&amp;rdquo;（Apps Hungarian），因为它是在&amp;ldquo;应用程序部&amp;rdquo;（Applications Division）中使用的，也就是用在 Word 和 Excel 身上。在 Excel 的源码中，你可以看到大量的 rw 和 col 。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;使用这种&amp;ldquo;应用型匈牙利命名法&amp;rdquo;，我们可以在看到变量后很快理解其含义，并很容易发现代码中的问题。&lt;/p&gt;&lt;p&gt;例如在代码中看到 xl = cb，&lt;/p&gt;&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&lt;p&gt;xl 表示&amp;ldquo;相对于页面的横坐标&amp;rdquo;，horizontal coordinates relatives to the layout；cb 表示&amp;ldquo;字节个数&amp;rdquo;，count of bytes&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;显然是有问题的，虽然 xl 和 cb 都是整数，但是这二者之间的赋值基本一定会导致 bug。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2. 系统型匈牙利命名法&lt;span style="font-size: 12px;"&gt;&amp;mdash;&amp;mdash;广为流传的冒牌货&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&lt;p&gt;然而，一定程度上由于 Simonyi 自己在编写文档时，用了&amp;ldquo;type&amp;rdquo;这个词，而不是&amp;ldquo;kind&amp;rdquo;，于是被人误以为 Simonyi 指的是数据类型。尽管 Simonyi 很详细、很准确地解释了他所说的&amp;ldquo;type&amp;rdquo;到底是什么意思。可惜于事无补，危害已经酿成了。悲剧的结果就是产生了我们现在熟悉的&amp;ldquo;&lt;strong&gt;系统型匈牙利命名法&lt;/strong&gt;&amp;rdquo;（System Hungarian）。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;还是上面的例子，改用&amp;ldquo;系统型匈牙利命名法&amp;rdquo;以后，可以改成 nWidth = nCount，看起来好像还不错哈～&lt;/p&gt;&lt;p&gt;bug 就是这样隐藏起来的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;ldquo;应用型匈牙利命名法&amp;rdquo;的前缀是非常有用的、有含义的，比如：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&amp;ldquo;ix&amp;rdquo; 表示数组的索引值（index）&lt;/li&gt;&lt;li&gt;&amp;ldquo;c&amp;rdquo; 表示一个计数器（count）&lt;/li&gt;&lt;li&gt;&amp;ldquo;d&amp;rdquo; 表示两个数量之间的差（difference），&amp;ldquo;dx&amp;rdquo; 就可以表示宽度&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&amp;nbsp;&amp;ldquo;系统性匈牙利命名法&amp;rdquo;的前缀就差远了，比如&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&amp;ldquo;l&amp;rdquo; 表示长整型（long）&lt;/li&gt;&lt;li&gt;&amp;ldquo;ul&amp;rdquo; 表示无符号长整型（unsigned long）&lt;/li&gt;&lt;li&gt;&amp;ldquo;dw&amp;rdquo; 表示双精度值（double word），这实际上也是一个无符号的长整型&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;这种差别虽然细微，但是完全误解了 Simonyi 的意图和做法。&amp;ldquo;系统型匈牙利命名法&amp;rdquo;传播的又远又广，在 Windows 编程文档中，它是标准的变量命名法。难怪很多人都觉得匈牙利命名法很奇怪、很别扭。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;参考文章：&lt;/p&gt;&lt;p&gt;[1] Joel Spolsky 著，阮一峰 译，中文名《软件随想录》&lt;a href="http://book.douban.com/subject/4163938/"&gt;http://book.douban.com/subject/4163938/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;[2] 匈牙利命名法的衰落和建议&amp;nbsp;&lt;a href="http://blog.kingsamchen.com/archives/618#comment-1310"&gt;http://blog.kingsamchen.com/archives/618#comment-1310&lt;/a&gt;&lt;/p&gt;&lt;p&gt;[3]&amp;nbsp;&lt;a class="url fn n" title="View all posts by lifeicd" href="http://lifeicd.wordpress.com/author/lifeicd/"&gt;lifeicd&lt;/a&gt;的&lt;a href="http://lifeicd.wordpress.com/2010/04/12/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%EF%BC%9A%E8%BD%AF%E4%BB%B6%E9%9A%8F%E6%83%B3%E5%BD%95-joel-on-software%E2%80%94%E2%80%94%E5%A4%96%E8%A1%8C%E7%9C%8B%E7%83%AD%E9%97%B9%EF%BC%8C%E5%86%85%E8%A1%8C/"&gt;读书笔记&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/xuxn/aggbug/2504140.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/xuxn/archive/2012/05/16/real-hungarian-notation.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
