﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-xiaomage234-随笔分类-network</title><link>http://www.blogjava.net/xiaomage234/category/38027.html</link><description>生命本就是一次凄美的漂流，记忆中放不下的，永远是孩提时代的那一份浪漫与纯真！</description><language>zh-cn</language><lastBuildDate>Thu, 07 Jul 2016 09:05:51 GMT</lastBuildDate><pubDate>Thu, 07 Jul 2016 09:05:51 GMT</pubDate><ttl>60</ttl><item><title>Android微信智能心跳方案【转】</title><link>http://www.blogjava.net/xiaomage234/archive/2016/07/07/431118.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 07 Jul 2016 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/07/07/431118.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/431118.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/07/07/431118.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/431118.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/431118.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: from:http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&amp;mid=207243549&amp;idx=1&amp;sn=4ebe4beb8123f1b5ab58810ac8bc5994&amp;scene=0#rd前言：在13年11月中旬时，因为基础组件组人手紧张，Leo安排我和春哥去广州轮岗支援。刚到广州的时候，Ray让我和春哥对Line...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2016/07/07/431118.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/431118.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-07-07 16:09 <a href="http://www.blogjava.net/xiaomage234/archive/2016/07/07/431118.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lvs之NAT、DR、TUN三种模式的应用配置案例[转]</title><link>http://www.blogjava.net/xiaomage234/archive/2015/09/02/427100.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Wed, 02 Sep 2015 08:07:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/09/02/427100.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/427100.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/09/02/427100.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/427100.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/427100.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: from:http://www.sxt.cn/u/324/blog/3188LVS一、LVS简介&nbsp;&nbsp;&nbsp;&nbsp;LVS是Linux Virtual Server的简写，意即Linux虚拟服务器，是一个虚拟服务器集群系统。本项目在1998年5月由章文嵩博士成立，是中国国内最早出现的自由软件之一。二、LVS的分类LVS-NAT：地址转换LVS-DR： 直接路由LVS-T...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2015/09/02/427100.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/427100.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-09-02 16:07 <a href="http://www.blogjava.net/xiaomage234/archive/2015/09/02/427100.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java版的各种Thrift server实现的比较(转)</title><link>http://www.blogjava.net/xiaomage234/archive/2015/03/12/423410.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 12 Mar 2015 09:25:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/03/12/423410.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/423410.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/03/12/423410.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/423410.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/423410.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: from:http://wuhuajun.iteye.com/blog/1905578Thrift Java Servers ComparedThis article talks only about Java servers. See&nbsp;this page&nbsp;if you are interested in C++ servers.本文仅讨论Java版的Thrift server...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2015/03/12/423410.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/423410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-03-12 17:25 <a href="http://www.blogjava.net/xiaomage234/archive/2015/03/12/423410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Apache Thrift - java开发详解［转］</title><link>http://www.blogjava.net/xiaomage234/archive/2015/03/12/423409.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 12 Mar 2015 09:20:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/03/12/423409.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/423409.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/03/12/423409.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/423409.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/423409.html</trackback:ping><description><![CDATA[<p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong><span style="font-size: 18px;">1、添加依赖 jar</span></strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><pre style="white-space: pre-wrap; word-wrap: break-word; padding: 8.5px; font-family: Menlo, Monaco, 'Courier New', monospace; font-size: 12px; color: #333333; margin-top: 0px; margin-bottom: 9px; line-height: 18px; border-width: 1px; border-style: solid; word-break: break-all; background-color: #f5f5f5;"><code style="padding: 0px; font-family: Menlo, Monaco, 'Courier New', monospace; border: 0px; background-color: transparent;">&lt;dependency&gt;   &lt;groupId&gt;org.apache.thrift&lt;/groupId&gt;   &lt;artifactId&gt;libthrift&lt;/artifactId&gt;   &lt;version&gt;0.8.0&lt;/version&gt; &lt;/dependency&gt; &lt;dependency&gt;   &lt;groupId&gt;org.slf4j&lt;/groupId&gt;   &lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;   &lt;version&gt;1.6.1&lt;/version&gt; &lt;/dependency&gt; </code></pre><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong><span style="font-size: 18px;">2、编写IDL文件&nbsp;Hello.thrift</span></strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">namespace java service.demo<br />service Hello {<br />&nbsp; &nbsp; string helloString(1:string para)<br />&nbsp; &nbsp; i32 helloInt(1:i32 para)<br />&nbsp; &nbsp; bool helloBoolean(1:bool para)<br />&nbsp; &nbsp; void helloVoid()<br />&nbsp; &nbsp; string helloNull()<br />}<br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong><span style="font-size: 18px;">3、生成代码</span></strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><pre style="white-space: pre-wrap; word-wrap: break-word; padding: 8.5px; font-family: Menlo, Monaco, 'Courier New', monospace; font-size: 12px; color: #333333; margin-top: 0px; margin-bottom: 9px; line-height: 18px; border-width: 1px; border-style: solid; word-break: break-all; background-color: #f5f5f5;"><code style="padding: 0px; font-family: Menlo, Monaco, 'Courier New', monospace; border: 0px; background-color: transparent;">thrift -o &lt;output directory&gt; -gen java Hello.thrift</code></pre><span style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">生成代码缩略图：</span><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><img src="http://img.my.csdn.net/uploads/201208/06/1344246293_5921.jpg" alt="" style="border: none; max-width: 100%;" /><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong><span style="font-size: 18px;">4、编写实现类、实现Hello.Iface：</span></strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">缩略图：</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><img src="http://my.csdn.net/uploads/201208/06/1344246424_7730.jpg" alt="" style="border: none; max-width: 100%;" /><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong><span style="font-size: 18px;">5、编写服务端，发布（阻塞式IO + 多线程处理）服务。</span></strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><div bg_java"="" style="width: 693px; color: #362e2b; line-height: 26px;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="view plain" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); background-position: 0% 0%; background-repeat: no-repeat;">view plain</a><a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="copy" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); background-position: 0% 0%; background-repeat: no-repeat;">copy</a><div style="position: absolute; left: 597px; top: 1872px; width: 18px; height: 18px; z-index: 99;"></div></div></div><ol start="1"><li style="line-height: 18px;">/**&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;阻塞式、多线程处理&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;args&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #646464;">@SuppressWarnings</span>({&nbsp;"unchecked",&nbsp;"rawtypes"&nbsp;})&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//设置传输通道，普通通道&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TServerTransport&nbsp;serverTransport&nbsp;=&nbsp;new&nbsp;TServerSocket(<span style="color: #c00000;">7911</span>);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//使用高密度二进制协议&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TProtocolFactory&nbsp;proFactory&nbsp;=&nbsp;new&nbsp;TCompactProtocol.Factory();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//设置处理器HelloImpl&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TProcessor&nbsp;processor&nbsp;=&nbsp;new&nbsp;Hello.Processor(new&nbsp;HelloImpl());&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//创建服务器&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TServer&nbsp;server&nbsp;=&nbsp;new&nbsp;TThreadPoolServer(&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;Args(serverTransport)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.protocolFactory(proFactory)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.processor(processor)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Start&nbsp;server&nbsp;on&nbsp;port&nbsp;7911...");&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server.serve();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li></ol></div><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong><span style="font-size: 18px;">6、编写客户端，调用（阻塞式IO + 多线程处理）服务：</span></strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><div bg_java"="" style="width: 693px; color: #362e2b; line-height: 26px;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="view plain" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); background-position: 0% 0%; background-repeat: no-repeat;">view plain</a><a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="copy" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); background-position: 0% 0%; background-repeat: no-repeat;">copy</a><div style="position: absolute; left: 597px; top: 2613px; width: 18px; height: 18px; z-index: 99;"></div></div></div><ol start="1"><li style="line-height: 18px;">public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;throws&nbsp;Exception&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;设置传输通道&nbsp;-&nbsp;普通IO流通道&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TTransport&nbsp;transport&nbsp;=&nbsp;new&nbsp;TSocket("localhost",&nbsp;<span style="color: #c00000;">7911</span>);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transport.open();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//使用高密度二进制协议&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TProtocol&nbsp;protocol&nbsp;=&nbsp;new&nbsp;TCompactProtocol(transport);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//创建Client&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hello.Client&nbsp;client&nbsp;=&nbsp;new&nbsp;Hello.Client(protocol);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;start&nbsp;=&nbsp;System.currentTimeMillis();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i=<span style="color: #c00000;">0</span>;&nbsp;i&lt;<span style="color: #c00000;">10000</span>;&nbsp;i++){&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloBoolean(false);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloInt(<span style="color: #c00000;">111</span>);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloNull();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloString("dongjian");&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloVoid();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("耗时："&nbsp;+&nbsp;(System.currentTimeMillis()&nbsp;-&nbsp;start));&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//关闭资源&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transport.close();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li></ol></div><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">现在已完成整个开发过程，超级无敌简单。</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">其中<strong>服务端使用的协议需要与客户端保持一致</strong>。</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">-------------------------------------------------------------------------------------------------------------------</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">上面展示了普通且常用的服务端和客户端，下面请看非阻塞IO，即java中的NIO：</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong>基于非阻塞IO（NIO）的服务端</strong>：</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><div bg_java"="" style="width: 693px; color: #362e2b; line-height: 26px;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="view plain" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); background-position: 0% 0%; background-repeat: no-repeat;">view plain</a><a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="copy" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); background-position: 0% 0%; background-repeat: no-repeat;">copy</a><div style="position: absolute; left: 597px; top: 3486px; width: 18px; height: 18px; z-index: 99;"></div></div></div><ol start="1"><li style="line-height: 18px;">public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//传输通道&nbsp;-&nbsp;非阻塞方式&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TNonblockingServerTransport&nbsp;serverTransport&nbsp;=&nbsp;new&nbsp;TNonblockingServerSocket(<span style="color: #c00000;">7911</span>);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//异步IO，需要使用TFramedTransport，它将分块缓存读取。&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TTransportFactory&nbsp;transportFactory&nbsp;=&nbsp;new&nbsp;TFramedTransport.Factory();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//使用高密度二进制协议&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TProtocolFactory&nbsp;proFactory&nbsp;=&nbsp;new&nbsp;TCompactProtocol.Factory();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//设置处理器&nbsp;HelloImpl&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TProcessor&nbsp;processor&nbsp;=&nbsp;new&nbsp;Hello.Processor(new&nbsp;HelloImpl());&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//创建服务器&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TServer&nbsp;server&nbsp;=&nbsp;new&nbsp;TThreadedSelectorServer(&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;Args(serverTransport)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.protocolFactory(proFactory)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.transportFactory(transportFactory)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.processor(processor)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Start&nbsp;server&nbsp;on&nbsp;port&nbsp;7911...");&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server.serve();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li></ol></div><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong>调用非阻塞IO（NIO）服务的客户端</strong>：</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><div bg_java"="" style="width: 693px; color: #362e2b; line-height: 26px;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="view plain" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); background-position: 0% 0%; background-repeat: no-repeat;">view plain</a><a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="copy" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); background-position: 0% 0%; background-repeat: no-repeat;">copy</a><div style="position: absolute; left: 597px; top: 4191px; width: 18px; height: 18px; z-index: 99;"></div></div></div><ol start="1"><li style="line-height: 18px;">public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;throws&nbsp;Exception&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//设置传输通道，对于非阻塞服务，需要使用TFramedTransport，它将数据分块发送&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TTransport&nbsp;transport&nbsp;=&nbsp;new&nbsp;TFramedTransport(new&nbsp;TSocket("localhost",&nbsp;<span style="color: #c00000;">7911</span>));&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transport.open();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//使用高密度二进制协议&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TProtocol&nbsp;protocol&nbsp;=&nbsp;new&nbsp;TCompactProtocol(transport);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//创建Client&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hello.Client&nbsp;client&nbsp;=&nbsp;new&nbsp;Hello.Client(protocol);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;start&nbsp;=&nbsp;System.currentTimeMillis();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i=<span style="color: #c00000;">0</span>;&nbsp;i&lt;<span style="color: #c00000;">10000</span>;&nbsp;i++){&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloBoolean(false);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloInt(<span style="color: #c00000;">111</span>);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloNull();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloString("360buy");&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloVoid();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("耗时："&nbsp;+&nbsp;(System.currentTimeMillis()&nbsp;-&nbsp;start));&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//关闭资源&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transport.close();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li></ol></div><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">-----------------------------------------------------------------------------------------------------------------------------------</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong>客户端异步调用</strong>：</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><div bg_java"="" style="width: 693px; color: #362e2b; line-height: 26px;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="view plain" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); background-position: 0% 0%; background-repeat: no-repeat;">view plain</a><a href="http://blog.csdn.net/m13321169565/article/details/7836006#" title="copy" style="padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); background-position: 0% 0%; background-repeat: no-repeat;">copy</a><div style="position: absolute; left: 597px; top: 4864px; width: 18px; height: 18px; z-index: 99;"></div></div></div><ol start="1"><li style="line-height: 18px;">/**&nbsp;调用[非阻塞IO]服务，异步&nbsp;*/&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//异步调用管理器&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TAsyncClientManager&nbsp;clientManager&nbsp;=&nbsp;new&nbsp;TAsyncClientManager();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//设置传输通道，调用非阻塞IO。&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;TNonblockingTransport&nbsp;transport&nbsp;=&nbsp;new&nbsp;TNonblockingSocket("localhost",&nbsp;<span style="color: #c00000;">7911</span>);&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//设置协议&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TProtocolFactory&nbsp;protocol&nbsp;=&nbsp;new&nbsp;TCompactProtocol.Factory();&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//创建Client&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;Hello.AsyncClient&nbsp;client&nbsp;=&nbsp;new&nbsp;Hello.AsyncClient(protocol,&nbsp;clientManager,&nbsp;transport);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;调用服务&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("开始："&nbsp;+&nbsp;System.currentTimeMillis());&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloBoolean(false,&nbsp;new&nbsp;AsyncMethodCallback&lt;Hello.AsyncClient.helloBoolean_call&gt;()&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;onError(Exception&nbsp;exception)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("错误1：&nbsp;"&nbsp;+&nbsp;System.currentTimeMillis());&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;onComplete(helloBoolean_call&nbsp;response)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("完成1：&nbsp;"&nbsp;+&nbsp;System.currentTimeMillis());&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.helloBoolean(false,&nbsp;new&nbsp;AsyncMethodCallback&lt;Hello.AsyncClient.helloBoolean_call&gt;()&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;onError(Exception&nbsp;exception)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("错误2：&nbsp;"&nbsp;+&nbsp;System.currentTimeMillis());&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;onComplete(helloBoolean_call&nbsp;response)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("完成2：&nbsp;"&nbsp;+&nbsp;System.currentTimeMillis());&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transport.close();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(TException&nbsp;e)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("结束："&nbsp;+&nbsp;System.currentTimeMillis());&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(<span style="color: #c00000;">5000</span>);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li></ol></div><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><br style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">-----------------------------------------------------------------------------------------------------------------------------------<br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong>使用SSL的服务端：</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><img src="http://my.csdn.net/uploads/201208/06/1344247527_4406.jpg" alt="" style="border: none; max-width: 100%;" /><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong>调用基于SSL服务端的客户端：</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><img src="http://img.my.csdn.net/uploads/201208/06/1344247606_9192.jpg" alt="" style="border: none; max-width: 100%;" /><br /></p><div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/423409.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-03-12 17:20 <a href="http://www.blogjava.net/xiaomage234/archive/2015/03/12/423409.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Netty版本升级血泪史之线程篇</title><link>http://www.blogjava.net/xiaomage234/archive/2015/02/10/422845.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 10 Feb 2015 04:03:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/02/10/422845.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/422845.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/02/10/422845.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/422845.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/422845.html</trackback:ping><description><![CDATA[from：http://www.infoq.com/cn/articles/netty-version-upgrade-history-thread-part<br /><br /><h2>1. 背景</h2><h3>1.1. Netty 3.X系列版本现状</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">根据对Netty社区部分用户的调查，结合Netty在其它开源项目中的使用情况，我们可以看出目前Netty商用的主流版本集中在3.X和4.X上，其中以Netty 3.X系列版本使用最为广泛。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty社区非常活跃，3.X系列版本从2011年2月7日发布的netty-3.2.4 Final版本到2014年12月17日发布的netty-3.10.0 Final版本，版本跨度达3年多，期间共推出了61个Final版本。</p><h3>1.2. 升级还是坚守老版本</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">相比于其它开源项目，Netty用户的版本升级之路更加艰辛，最根本的原因就是Netty 4对Netty 3没有做到很好的前向兼容。</p><div style="margin: 0px; border: 0px; height: 0px; clear: both; font-size: 0px; font-family: Arial, sans-serif; background-color: #ffffff;"></div><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">由于版本不兼容，大多数老版本使用者的想法就是既然升级这么麻烦，我暂时又不需要使用到Netty 4的新特性，当前版本还挺稳定，就暂时先不升级，以后看看再说。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">坚守老版本还有很多其它的理由，例如考虑到线上系统的稳定性、对新版本的熟悉程度等。无论如何升级Netty都是一件大事，特别是对Netty有直接强依赖的产品。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">从上面的分析可以看出，坚守老版本似乎是个不错的选择；但是，&#8220;理想是美好的,现实却是残酷的&#8221;，坚守老版本并非总是那么容易，下面我们就看下被迫升级的案例。</p><div id="lowerFullwidthVCR" style="margin: 0px; border: 0px; font-family: Arial, sans-serif; background-color: #ffffff;"></div><h3>1.3. &#8220;被迫&#8221;升级到Netty 4.X</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">除了为了使用新特性而主动进行的版本升级，大多数升级都是&#8220;被迫的&#8221;。下面我们对这些升级原因进行分析。</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">公司的开源软件管理策略：对于那些大厂，不同部门和产品线依赖的开源软件版本经常不同，为了对开源依赖进行统一管理，降低安全、维护和管理成本，往往会指定优选的软件版本。由于Netty 4.X 系列版本已经非常成熟，因为，很多公司都优选Netty 4.X版本。</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">维护成本：无论是依赖Netty 3.X，还是Netty4.X，往往需要在原框架之上做定制。例如，客户端的短连重连、心跳检测、流控等。分别对Netty 4.X和3.X版本实现两套定制框架，开发和维护成本都非常高。根据开源软件的使用策略，当存在版本冲突的时候，往往会选择升级到更高的版本。对于Netty，依然遵循这个规则。</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">新特性：Netty 4.X相比于Netty 3.X,提供了很多新的特性，例如优化的内存管理池、对MQTT协议的支持等。如果用户需要使用这些新特性，最简便的做法就是升级Netty到4.X系列版本。</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">更优异的性能：Netty 4.X版本相比于3.X老版本，优化了内存池，减少了GC的频率、降低了内存消耗；通过优化Rector线程池模型，用户的开发更加简单，线程调度也更加高效。</li></ol><h3>1.4. 升级不当付出的代价</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">表面上看，类库包路径的修改、API的重构等似乎是升级的重头戏，大家往往把注意力放到这些&#8220;明枪&#8221;上，但真正隐藏和致命的却是&#8220;暗箭&#8221;。如果对Netty底层的事件调度机制和线程模型不熟悉，往往就会&#8220;中枪&#8221;。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">本文以几个比较典型的真实案例为例，通过问题描述、问题定位和问题总结，让这些隐藏的&#8220;暗箭&#8221;不再伤人。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">由于Netty 4线程模型改变导致的升级事故还有很多，限于篇幅，本文不一一枚举，这些问题万变不离其宗，只要抓住线程模型这个关键点，所谓的疑难杂症都将迎刃而解。</p><h2>2. Netty升级之后遭遇内存泄露</h2><h3>2.1. 问题描述</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">随着JVM虚拟机和JIT即时编译技术的发展，对象的分配和回收是个非常轻量级的工作。但是对于缓冲区Buffer，情况却稍有不同，特别是对于堆外直接内存的分配和回收，是一件耗时的操作。为了尽量重用缓冲区，Netty4.X提供了基于内存池的缓冲区重用机制。性能测试表明，采用内存池的ByteBuf相比于朝生夕灭的ByteBuf，性能高23倍左右（性能数据与使用场景强相关）。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务应用的特点是高并发、短流程，大多数对象都是朝生夕灭的短生命周期对象。为了减少内存的拷贝，用户期望在序列化的时候直接将对象编码到PooledByteBuf里，这样就不需要为每个业务消息都重新申请和释放内存。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务的相关代码示例如下：</p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">//在业务线程中初始化内存池分配器，分配非堆内存  ByteBufAllocator allocator = new PooledByteBufAllocator(true);  ByteBuf buffer = allocator.ioBuffer(1024); //构造订购请求消息并赋值，业务逻辑省略 SubInfoReq infoReq = new SubInfoReq (); infoReq.setXXX(......); //将对象编码到ByteBuf中 codec.encode(buffer, info); //调用ChannelHandlerContext进行消息发送 ctx.writeAndFlush(buffer);</pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务代码升级Netty版本并重构之后，运行一段时间，Java进程就会宕机，查看系统运行日志发现系统发生了内存泄露（示例堆栈）：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205000.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图2-1 OOM内存溢出堆栈</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">对内存进行监控（切换使用堆内存池，方便对内存进行监控），发现堆内存一直飙升，如下所示（示例堆内存监控）：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205001.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图2-2 堆内存监控</p><h3>2.2. 问题定位</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">使用jmap -dump:format=b,file=netty.bin PID 将堆内存dump出来，通过IBM的HeapAnalyzer工具进行分析，发现ByteBuf发生了泄露。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">因为使用了内存池，所以首先怀疑是不是申请的ByteBuf没有被释放导致？查看代码，发现消息发送完成之后，Netty底层已经调用ReferenceCountUtil.release(message)对内存进行了释放。这是怎么回事呢？难道Netty 4.X的内存池有Bug，调用release操作释放内存失败？</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">考虑到Netty 内存池自身Bug的可能性不大，首先从业务的使用方式入手分析：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">内存的分配是在业务代码中进行，由于使用到了业务线程池做I/O操作和业务操作的隔离，实际上内存是在业务线程中分配的；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">内存的释放操作是在outbound中进行，按照Netty 3的线程模型，downstream（对应Netty 4的outbound，Netty 4取消了upstream和downstream）的handler也是由业务调用者线程执行的，也就是说释放跟分配在同一个业务线程中进行。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">初次排查并没有发现导致内存泄露的根因，一筹莫展之际开始查看Netty的内存池分配器PooledByteBufAllocator的Doc和源码实现，发现内存池实际是基于线程上下文实现的，相关代码如下：</p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">final ThreadLocal&lt;PoolThreadCache&gt; threadCache = new ThreadLocal&lt;PoolThreadCache&gt;() {         private final AtomicInteger index = new AtomicInteger();         @Override         protected PoolThreadCache initialValue() {             final int idx = index.getAndIncrement();             final PoolArena&lt;byte[]&gt; heapArena;             final PoolArena&lt;ByteBuffer&gt; directArena;             if (heapArenas != null) {                 heapArena = heapArenas[Math.abs(idx % heapArenas.length)];             } else {                 heapArena = null;             }             if (directArenas != null) {                 directArena = directArenas[Math.abs(idx % directArenas.length)];             } else {                 directArena = null;             }             return new PoolThreadCache(heapArena, directArena);         }</pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">也就是说内存的申请和释放必须在同一线程上下文中，不能跨线程。跨线程之后实际操作的就不是同一块内存区域，这会导致很多严重的问题，内存泄露便是其中之一。内存在A线程申请，切换到B线程释放，实际是无法正确回收的。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过对Netty内存池的源码分析，问题基本锁定。保险起见进行简单验证，通过对单条业务消息进行Debug，发现执行释放的果然不是业务线程，而是Netty的NioEventLoop线程：当某个消息被完全发送成功之后，会通过ReferenceCountUtil.release(message)方法释放已经发送成功的ByteBuf。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">问题定位出来之后，继续溯源，发现Netty 4修改了Netty 3的线程模型：在Netty 3的时候，upstream是在I/O线程里执行的，而downstream是在业务线程里执行。当Netty从网络读取一个数据报投递给业务handler的时候，handler是在I/O线程里执行；而当我们在业务线程中调用write和writeAndFlush向网络发送消息的时候,handler是在业务线程里执行，直到最后一个Header handler将消息写入到发送队列中，业务线程才返回。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty4修改了这一模型，在Netty 4里inbound(对应Netty 3的upstream)和outbound(对应Netty 3的downstream)都是在NioEventLoop(I/O线程)中执行。当我们在业务线程里通过ChannelHandlerContext.write发送消息的时候，Netty 4在将消息发送事件调度到ChannelPipeline的时候，首先将待发送的消息封装成一个Task，然后放到NioEventLoop的任务队列中，由NioEventLoop线程异步执行。后续所有handler的调度和执行，包括消息的发送、I/O事件的通知，都由NioEventLoop线程负责处理。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">下面我们分别通过对比Netty 3和Netty 4的消息接收和发送流程，来理解两个版本线程模型的差异：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 3的I/O事件处理流程：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205003.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图2-3 Netty 3 I/O事件处理线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 4的I/O消息处理流程：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205004.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图2-4 Netty 4 I/O事件处理线程模型</p><h3>2.3. 问题总结</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 4.X版本新增的内存池确实非常高效，但是如果使用不当则会导致各种严重的问题。诸如内存泄露这类问题，功能测试并没有异常，如果相关接口没有进行压测或者稳定性测试而直接上线，则会导致严重的线上问题。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">内存池PooledByteBuf的使用建议：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">申请之后一定要记得释放，Netty自身Socket读取和发送的ByteBuf系统会自动释放，用户不需要做二次释放；如果用户使用Netty的内存池在应用中做ByteBuf的对象池使用，则需要自己主动释放；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">避免错误的释放：跨线程释放、重复释放等都是非法操作，要避免。特别是跨线程申请和释放，往往具有隐蔽性，问题定位难度较大；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">防止隐式的申请和分配：之前曾经发生过一个案例，为了解决内存池跨线程申请和释放问题，有用户对内存池做了二次包装，以实现多线程操作时，内存始终由包装的管理线程申请和释放，这样可以屏蔽用户业务线程模型和访问方式的差异。谁知运行一段时间之后再次发生了内存泄露，最后发现原来调用ByteBuf的write操作时，如果内存容量不足，会自动进行容量扩展。扩展操作由业务线程执行，这就绕过了内存池管理线程，发生了&#8220;引用逃逸&#8221;。该Bug只有在ByteBuf容量动态扩展的时候才发生，因此，上线很长一段时间没有发生，直到某一天......因此，大家在使用Netty 4.X的内存池时要格外当心，特别是做二次封装时，一定要对内存池的实现细节有深刻的理解。</li></ol><h2>3. Netty升级之后遭遇数据被篡改</h2><h3>3.1. 问题描述</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">某业务产品，Netty3.X升级到4.X之后，系统运行过程中，偶现服务端发送给客户端的应答数据被莫名&#8220;篡改&#8221;。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务服务端的处理流程如下：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">将解码后的业务消息封装成Task，投递到后端的业务线程池中执行；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务线程处理业务逻辑，完成之后构造应答消息发送给客户端；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务应答消息的编码通过继承Netty的CodeC框架实现，即Encoder ChannelHandler;</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">调用Netty的消息发送接口之后，流程继续，根据业务场景，可能会继续操作原发送的业务对象。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务相关代码示例如下：</p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">//构造订购应答消息 SubInfoResp infoResp = new SubInfoResp(); //根据业务逻辑，对应答消息赋值 infoResp.setResultCode(0); infoResp.setXXX()； 后续赋值操作省略...... //调用ChannelHandlerContext进行消息发送 ctx.writeAndFlush(infoResp); //消息发送完成之后，后续根据业务流程进行分支处理，修改infoResp对象 infoResp.setXXX(); 后续代码省略......</pre><h3>3.2. 问题定位</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">首先对应答消息被非法&#8220;篡改&#8221;的原因进行分析，经过定位发现当发生问题时，被&#8220;篡改&#8221;的内容是调用<strong style="margin: 0px; border: 0px; padding: 0px;">writeAndFlush</strong>接口之后，由后续业务分支代码修改应答消息导致的。由于修改操作发生在<strong style="margin: 0px; border: 0px; padding: 0px;">writeAndFlush</strong>操作之后，按照Netty 3.X的线程模型不应该出现该问题。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在Netty3中，downstream是在业务线程里执行的，也就是说对<span style="margin: 0px; border: 0px; padding: 0px;">SubInfoResp</span>的编码操作是在业务线程中执行的，当编码后的ByteBuf对象被投递到消息发送队列之后，业务线程才会返回并继续执行后续的业务逻辑，此时修改应答消息是不会改变已完成编码的ByteBuf对象的，所以肯定不会出现应答消息被篡改的问题。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">初步分析应该是由于线程模型发生变更导致的问题，随后查验了Netty 4的线程模型，果然发生了变化：当调用outbound向外发送消息的时候，Netty会将发送事件封装成Task，投递到NioEventLoop的任务队列中异步执行，相关代码如下：</p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">@Override  public void invokeWrite(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {         if (msg == null) {             throw new NullPointerException("msg");         }         validatePromise(ctx, promise, true);         if (executor.inEventLoop()) {             invokeWriteNow(ctx, msg, promise);         } else {             AbstractChannel channel = (AbstractChannel) ctx.channel();             int size = channel.estimatorHandle().size(msg);             if (size &gt; 0) {                 ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();                 // Check for null as it may be set to null if the channel is closed already                 if (buffer != null) {                     buffer.incrementPendingOutboundBytes(size);                 }             }             safeExecuteOutbound(WriteTask.newInstance(ctx, msg, size, promise), promise, msg);         }     }</pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过上述代码可以看出，Netty首先对当前的操作的线程进行判断，如果操作本身就是由NioEventLoop线程执行，则调用写操作；否则，执行线程安全的写操作，即将写事件封装成Task，放入到任务队列中由Netty的I/O线程执行，业务调用返回，流程继续执行。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过源码分析，问题根源已经很清楚：系统升级到Netty 4之后，线程模型发生变化，响应消息的编码由NioEventLoop线程异步执行，业务线程返回。这时存在两种可能：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">如果编码操作先于修改应答消息的业务逻辑执行，则运行结果正确；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">如果编码操作在修改应答消息的业务逻辑之后执行，则运行结果错误。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">由于线程的执行先后顺序无法预测，因此该问题隐藏的相当深。如果对Netty 4和Netty3的线程模型不了解，就会掉入陷阱。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 3版本业务逻辑没有问题，流程如下：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205005.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图3-1 升级之前的业务流程线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">升级到Netty 4版本之后，业务流程由于Netty线程模型的变更而发生改变，导致业务逻辑发生问题：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205006.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图3-2 升级之后的业务处理流程发生改变</p><h3>3.3. 问题总结</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">很多读者在进行Netty 版本升级的时候，只关注到了包路径、类和API的变更，并没有注意到隐藏在背后的&#8220;暗箭&#8221;- 线程模型变更。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">升级到Netty 4的用户需要根据新的线程模型对已有的系统进行评估，重点需要关注outbound的ChannelHandler，如果它的正确性依赖于Netty 3的线程模型，则很可能在新的线程模型中出问题，可能是功能问题或者其它问题。</p><h2>4. Netty升级之后性能严重下降</h2><h3>4.1. 问题描述</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">相信很多Netty用户都看过如下相关报告：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在Twitter，Netty 4 GC开销降为五分之一：Netty 3使用Java对象表示I/O事件，这样简单，但会产生大量的<a href="http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">垃圾</a>，尤其是在我们这样的规模下。Netty 4在新版本中对此做出了更改，取代生存周期短的事件对象，而以定义在生存周期长的通道对象上的方法处理I/O事件。它还有一个使用池的专用缓冲区分配器。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">每当收到新信息或者用户发送信息到远程端，Netty 3均会创建一个新的堆缓冲区。这意味着，对应每一个新的缓冲区，都会有一个&#8216;new byte[capacity]&#8217;。这些缓冲区会导致GC压力，并消耗内存带宽：为了安全起见，新的字节数组分配时会用零填充，这会消耗内存带宽。然而，用零填充的数组很可能会再次用实际的数据填充，这又会消耗同样的内存带宽。如果Java虚拟机（JVM）提供了创建新字节数组而又无需用零填充的方式，那么我们本来就可以将内存带宽消耗减少50%，但是目前没有那样一种方式。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在Netty 4中，代码定义了粒度更细的API，用来处理不同的事件类型，而不是创建事件对象。它还实现了一个新缓冲池，那是一个纯Java版本的&nbsp;<a href="https://www.facebook.com/notes/facebook-engineering/scalable-memory-allocation-using-jemalloc/480222803919" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">jemalloc</a>&nbsp;（Facebook也在用）。现在，Netty不会再因为用零填充缓冲区而浪费内存带宽了。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">我们比较了两个分别建立在Netty 3和4基础上<a href="http://en.wikipedia.org/wiki/Echo_Protocol" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">echo</a>协议服务器。（Echo非常简单，这样，任何垃圾的产生都是Netty的原因，而不是协议的原因）。我使它们服务于相同的分布式echo协议客户端，来自这些客户端的16384个并发连接重复发送256字节的随机负载，几乎使千兆以太网饱和。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">根据测试结果，Netty 4：</p><ul style="margin: 0px 0px 15px 10px; padding: 0px; list-style-position: initial; list-style-image: initial; border: 0px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;"><strong style="margin: 0px; border: 0px; padding: 0px;">GC中断频率是原来的1/5： 45.5 vs. 9.2次/分钟</strong></li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;"><strong style="margin: 0px; border: 0px; padding: 0px;">垃圾生成速度是原来的1/5： 207.11 vs 41.81 MiB/秒</strong></li></ul><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">正是看到了相关的Netty 4性能提升报告，很多用户选择了升级。事后一些用户反馈Netty 4并没有跟产品带来预期的性能提升，有些甚至还发生了非常严重的性能下降，下面我们就以某业务产品的失败升级经历为案例，详细分析下导致性能下降的原因。</p><h3>4.2. 问题定位</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">首先通过JMC等性能分析工具对性能热点进行分析，示例如下（信息安全等原因，只给出分析过程示例截图）：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205007.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图4-1 JMC性能监控分析</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过对热点方法的分析，发现在消息发送过程中，有两处热点：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">消息发送性能统计相关Handler;</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">编码Handler。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">对使用Netty 3版本的业务产品进行性能对比测试，发现上述两个Handler也是热点方法。既然都是热点，为啥切换到Netty4之后性能下降这么厉害呢？</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过方法的调用树分析发现了两个版本的差异：在Netty 3中，上述两个热点方法都是由业务线程负责执行；而在Netty 4中，则是由NioEventLoop(I/O)线程执行。对于某个链路，业务是拥有多个线程的线程池，而NioEventLoop只有一个，所以执行效率更低，返回给客户端的应答时延就大。时延增大之后，自然导致系统并发量降低，性能下降。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">找出问题根因之后，针对Netty 4的线程模型对业务进行专项优化，性能达到预期，远超过了Netty 3老版本的性能。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 3的业务线程调度模型图如下所示：充分利用了业务多线程并行编码和Handler处理的优势，周期T内可以处理N条业务消息。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205008.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图4-2 Netty 3业务调度性能模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">切换到Netty 4之后，业务耗时Handler被I/O线程串行执行，因此性能发生比较大的下降：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205009.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图4-3 Netty 4业务调度性能模型</p><h3>4.3. 问题总结</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">该问题的根因还是由于Netty 4的线程模型变更引起，线程模型变更之后，不仅影响业务的功能，甚至对性能也会造成很大的影响。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">对Netty的升级需要从功能、兼容性和性能等多个角度进行综合考虑，切不可只盯着API变更这个芝麻，而丢掉了性能这个西瓜。API的变更会导致编译错误，但是性能下降却隐藏于无形之中，稍不留意就会中招。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">对于讲究快速交付、敏捷开发和灰度发布的互联网应用，升级的时候更应该要当心。</p><h2>5. Netty升级之后上下文丢失</h2><h3>5.1. 问题描述</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">为了提升业务的二次定制能力，降低对接口的侵入性，业务使用线程变量进行消息上下文的传递。例如消息发送源地址信息、消息Id、会话Id等。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务同时使用到了一些第三方开源容器，也提供了线程级变量上下文的能力。业务通过容器上下文获取第三方容器的系统变量信息。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">升级到Netty 4之后，业务继承自Netty的ChannelHandler发生了空指针异常，无论是业务自定义的线程上下文、还是第三方容器的线程上下文，都获取不到传递的变量值。</p><h3>5.2. 问题定位</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">首先检查代码，看业务是否传递了相关变量，确认业务传递之后怀疑跟Netty 版本升级相关，调试发现，业务ChannelHandler获取的线程上下文对象和之前业务传递的上下文不是同一个。这就说明执行ChannelHandler的线程跟处理业务的线程不是同一个线程！</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">查看Netty 4线程模型的相关Doc发现，Netty修改了outbound的线程模型，正好影响了业务消息发送时的线程上下文传递，最终导致线程变量丢失。</p><h3>5.3. 问题总结</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通常业务的线程模型有如下几种：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务自定义线程池/线程组处理业务，例如使用JDK 1.5提供的ExecutorService；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">使用J2EE Web容器自带的线程模型，常见的如JBoss和Tomcat的HTTP接入线程等；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">隐式的使用其它第三方框架的线程模型，例如使用NIO框架进行协议处理，业务代码隐式使用的就是NIO框架的线程模型，除非业务明确的实现自定义线程模型。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在实践中我们发现很多业务使用了第三方框架，但是只熟悉API和功能，对线程模型并不清楚。某个类库由哪个线程调用，糊里糊涂。为了方便变量传递，又随意的使用线程变量，实际对背后第三方类库的线程模型产生了强依赖。当容器或者第三方类库升级之后，如果线程模型发生了变更，则原有功能就会发生问题。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">鉴于此，在实际工作中，尽量不要强依赖第三方类库的线程模型，如果确实无法避免，则必须对它的线程模型有深入和清晰的了解。当第三方类库升级之后，需要检查线程模型是否发生变更，如果发生变化，相关的代码也需要考虑同步升级。</p><h2>6. Netty3.X VS Netty4.X 之线程模型</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过对三个具有典型性的升级失败案例进行分析和总结，我们发现有个共性：都是线程模型改变惹的祸!</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">下面小节我们就详细得对Netty3和Netty4版本的I/O线程模型进行对比，以方便大家掌握两者的差异，在升级和使用中尽量少踩雷。</p><h3>6.1 Netty 3.X 版本线程模型</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 3.X的I/O操作线程模型比较复杂，它的处理模型包括两部分：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Inbound：主要包括链路建立事件、链路激活事件、读事件、I/O异常事件、链路关闭事件等；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Outbound：主要包括写事件、连接事件、监听绑定事件、刷新事件等。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">我们首先分析下Inbound操作的线程模型：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205010.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图6-1 Netty 3 Inbound操作线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">从上图可以看出，Inbound操作的主要处理流程如下：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程（Work线程）将消息从TCP缓冲区读取到SocketChannel的接收缓冲区中；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">由I/O线程负责生成相应的事件，触发事件向上执行，调度到ChannelPipeline中；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程调度执行ChannelPipeline中Handler链的对应方法，直到业务实现的Last Handler;</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Last Handler将消息封装成Runnable，放入到业务线程池中执行，I/O线程返回，继续读/写等I/O操作；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务线程池从任务队列中弹出消息，并发执行业务逻辑。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过对Netty 3的Inbound操作进行分析我们可以看出，Inbound的Handler都是由Netty的I/O Work线程负责执行。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">下面我们继续分析Outbound操作的线程模型：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205011.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图6-2 Netty 3 Outbound操作线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">从上图可以看出，Outbound操作的主要处理流程如下：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务线程发起Channel Write操作，发送消息；</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Netty将写操作封装成写事件，触发事件向下传播；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">写事件被调度到ChannelPipeline中，由业务线程按照Handler Chain串行调用支持Downstream事件的Channel Handler;</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">执行到系统最后一个ChannelHandler，将编码后的消息Push到发送队列中，业务线程返回；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Netty的I/O线程从发送消息队列中取出消息，调用SocketChannel的write方法进行消息发送。</li></ol><h3>6.2 Netty 4.X 版本线程模型</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">相比于Netty 3.X系列版本，Netty 4.X的I/O操作线程模型比较简答，它的原理图如下所示：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205012.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图6-3 Netty 4 Inbound和Outbound操作线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">从上图可以看出，Outbound操作的主要处理流程如下：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程NioEventLoop从SocketChannel中读取数据报，将ByteBuf投递到ChannelPipeline，触发ChannelRead事件；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程NioEventLoop调用ChannelHandler链，直到将消息投递到业务线程，然后I/O线程返回，继续后续的读写操作；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务线程调用ChannelHandlerContext.write(Object msg)方法进行消息发送；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">如果是由业务线程发起的写操作，ChannelHandlerInvoker将发送消息封装成Task，放入到I/O线程NioEventLoop的任务队列中，由NioEventLoop在循环中统一调度和执行。放入任务队列之后，业务线程返回；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程NioEventLoop调用ChannelHandler链，进行消息发送，处理Outbound事件，直到将消息放入发送队列，然后唤醒Selector，进而执行写操作。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过流程分析，我们发现Netty 4修改了线程模型，无论是Inbound还是Outbound操作，统一由I/O线程NioEventLoop调度执行。</p><h3>6.3. 线程模型对比</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在进行新老版本线程模型PK之前，首先还是要熟悉下串行化设计的理念：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">我们知道当系统在运行过程中，如果频繁的进行线程上下文切换，会带来额外的性能损耗。多线程并发执行某个业务流程，业务开发者还需要时刻对线程安全保持警惕，哪些数据可能会被并发修改，如何保护？这不仅降低了开发效率，也会带来额外的性能损耗。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">为了解决上述问题，Netty 4采用了串行化设计理念，从消息的读取、编码以及后续Handler的执行，始终都由I/O线程NioEventLoop负责，这就意外着整个流程不会进行线程上下文的切换，数据也不会面临被并发修改的风险，对于用户而言，甚至不需要了解Netty的线程细节，这确实是个非常好的设计理念，它的工作原理图如下：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205014.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图6-4 Netty 4的串行化设计理念</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">一个NioEventLoop聚合了一个多路复用器Selector，因此可以处理成百上千的客户端连接，Netty的处理策略是每当有一个新的客户端接入，则从NioEventLoop线程组中顺序获取一个可用的NioEventLoop，当到达数组上限之后，重新返回到0，通过这种方式，可以基本保证各个NioEventLoop的负载均衡。一个客户端连接只注册到一个NioEventLoop上，这样就避免了多个I/O线程去并发操作它。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty通过串行化设计理念降低了用户的开发难度，提升了处理性能。利用线程组实现了多个串行化线程水平并行执行，线程之间并没有交集，这样既可以充分利用多核提升并行处理能力，同时避免了线程上下文的切换和并发保护带来的额外性能损耗。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">了解完了Netty 4的串行化设计理念之后，我们继续看Netty 3线程模型存在的问题，总结起来，它的主要问题如下：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Inbound和Outbound实质都是I/O相关的操作，它们的线程模型竟然不统一，这给用户带来了更多的学习和使用成本；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Outbound操作由业务线程执行，通常业务会使用线程池并行处理业务消息，这就意味着在某一个时刻会有多个业务线程同时操作ChannelHandler，我们需要对ChannelHandler进行并发保护，通常需要加锁。如果同步块的范围不当，可能会导致严重的性能瓶颈，这对开发者的技能要求非常高，降低了开发效率；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Outbound操作过程中，例如消息编码异常，会产生Exception，它会被转换成Inbound的Exception并通知到ChannelPipeline，这就意味着业务线程发起了Inbound操作！它打破了Inbound操作由I/O线程操作的模型，如果开发者按照Inbound操作只会由一个I/O线程执行的约束进行设计，则会发生线程并发访问安全问题。由于该场景只在特定异常时发生，因此错误非常隐蔽！一旦在生产环境中发生此类线程并发问题，定位难度和成本都非常大。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">讲了这么多，似乎Netty 4 完胜 Netty 3的线程模型，其实并不尽然。在特定的场景下，Netty 3的性能可能更高，就如本文第4章节所讲，如果编码和其它Outbound操作非常耗时，由多个业务线程并发执行，性能肯定高于单个NioEventLoop线程。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">但是，这种性能优势不是不可逆转的，如果我们修改业务代码，将耗时的Handler操作前置，Outbound操作不做复杂业务逻辑处理，性能同样不输于Netty 3，但是考虑内存池优化、不会反复创建Event、不需要对Handler加锁等Netty 4的优化，整体性能Netty 4版本肯定会更高。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">总而言之，如果用户真正熟悉并掌握了Netty 4的线程模型和功能类库，相信不仅仅开发会更加简单，性能也会更优！</p><h3>6.4. 思考</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">就Netty 而言，掌握线程模型的重要性不亚于熟悉它的API和功能。很多时候我遇到的功能、性能等问题，都是由于缺乏对它线程模型和原理的理解导致的，结果我们就以讹传讹，认为Netty 4版本不如3好用等。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">不能说所有开源软件的版本升级一定都胜过老版本，就Netty而言，我认为Netty 4版本相比于老的Netty 3，确实是历史的一大进步。</p><h2>7. 作者简介</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">李林锋，2007年毕业于东北大学，2008年进入华为公司从事高性能通信软件的设计和开发工作，有7年NIO设计和开发经验，精通Netty、Mina等NIO框架和平台中间件，现任华为软件平台架构部架构师，《Netty权威指南》作者。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">联系方式：新浪微博 Nettying 微信：Nettying 微信公众号：Netty之家</p><hr style="margin: 0px; border: 0px; padding: 0px; font-family: Arial, sans-serif; background-color: #ffffff;" /><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">感谢<a href="http://www.infoq.com/cn/author/%E9%83%AD%E8%95%BE" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">郭蕾</a>对本文的策划和审校。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">给InfoQ中文站投稿或者参与内容翻译工作，请邮件至<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#100;&#105;&#116;&#111;&#114;&#115;&#64;&#99;&#110;&#46;&#105;&#110;&#102;&#111;&#113;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">editors@cn.infoq.com</a>。也欢迎大家通过新浪微博（<a href="http://www.weibo.com/infoqchina" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">@InfoQ</a>）或者腾讯微博（<a href="http://t.qq.com/infoqchina" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">@InfoQ</a>）关注我们，并与我们的编辑和其他读者朋友交流。</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/422845.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-02-10 12:03 <a href="http://www.blogjava.net/xiaomage234/archive/2015/02/10/422845.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP三次握手及四次挥手详细图解</title><link>http://www.blogjava.net/xiaomage234/archive/2013/06/24/400910.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 24 Jun 2013 08:33:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2013/06/24/400910.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/400910.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2013/06/24/400910.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/400910.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/400910.html</trackback:ping><description><![CDATA[<div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;相对于SOCKET开发者,TCP创建过程和链接折除过程是由TCP/IP协议栈自动创建的.因此开发者并不需要控制这个过程.但是对于理解TCP底层运作机制,相当有帮助.</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;&nbsp; 而且对于有网络协议工程师之类笔试,几乎是必考的内容.企业对这个问题热情之高,出乎我的意料：-）。有时上午面试前强调这个问题，并重复讲一次，下午几乎每一个人都被问到这个问题。</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">因此在这里详细解释一下这两个过程。</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><strong style="margin: 0px; padding: 0px;"><span size="3" style="margin: 0px; padding: 0px; font-size: small;"><span style="margin: 0px; padding: 0px; text-decoration: underline;">TCP三次握手</span></span></strong></div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">所谓三次握手(Three-way Handshake)，是指建立一个TCP连接时，需要客户端和服务器总共发送3个包。</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">三次握手的目的是连接服务器指定端口，建立TCP连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息.在socket编程中，客户端执行connect()时。将触发三次握手。</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</div><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><div align="center" style="margin: 0px;">&nbsp;</div><div align="center" style="margin: 0px;">&nbsp;</div><div align="center" style="margin: 0px;">&nbsp;</div><div align="center" style="margin: 0px;"><img src="http://blogimg.chinaunix.net/blog/upfile2/100327002629.png" alt="" border="0" width="650" style="margin: 0px; padding: 0px; border: 0px;" /></div></div><ul style="margin: 0px 0px 0px 45px; padding: 0px; word-break: break-all; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><li style="margin: 0px; padding: 0px; list-style: inherit;">第一次握手:<br style="margin: 0px; padding: 0px;" />客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口，以及初始序号X,保存在包头的序列号(Sequence Number)字段里。</li></ul><div align="center" style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><img src="http://blogimg.chinaunix.net/blog/upfile2/100327002911.png" alt="" border="0" style="margin: 0px; padding: 0px; border: 0px;" /></div><ul style="margin: 0px 0px 0px 45px; padding: 0px; word-break: break-all; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><li style="margin: 0px; padding: 0px; list-style: inherit;">第二次握手:<br style="margin: 0px; padding: 0px;" />服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时，将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即X+1。</li></ul><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</p><p align="center" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><img src="http://blogimg.chinaunix.net/blog/upfile2/100327003054.png" alt="" border="0" width="500" style="margin: 0px; padding: 0px; border: 0px;" /></p><ul style="margin: 0px 0px 0px 45px; padding: 0px; word-break: break-all; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><li style="margin: 0px; padding: 0px; list-style: inherit;">第三次握手.<br style="margin: 0px; padding: 0px;" />客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1</li></ul><div align="center" style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><img src="http://blogimg.chinaunix.net/blog/upfile2/100327003214.png" alt="" border="0" width="500" style="margin: 0px; padding: 0px; border: 0px;" /></div><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><strong style="margin: 0px; padding: 0px;"><span size="4" style="margin: 0px; padding: 0px; font-size: medium;"><span style="margin: 0px; padding: 0px; text-decoration: underline;">SYN攻击</span></span></strong></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;&nbsp; 在三次握手过程中，服务器发送SYN-ACK之后，收到客户端的ACK之前的TCP连接称为半连接(half-open connect).此时服务器处于Syn_RECV状态.当收到ACK后，服务器转入ESTABLISHED状态.</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp; Syn攻击就是 攻击客户端 在短时间内伪造大量不存在的IP地址，向服务器不断地发送syn包，服务器回复确认包，并等待客户的确认，由于源地址是不存在的，服务器需要不断的重发直 至超时，这些伪造的SYN包将长时间占用未连接队列，正常的SYN请求被丢弃，目标系统运行缓慢，严重者引起网络堵塞甚至系统瘫痪。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;Syn攻击是一个典型的DDOS攻击。检测SYN攻击非常的方便，当你在服务器上看到大量的半连接状态时，特别是源IP地址是随机的，基本上可以断定这是一次SYN攻击.在Linux下可以如下命令检测是否被Syn攻击</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><strong style="margin: 0px; padding: 0px;">netstat -n -p TCP | grep SYN_RECV</strong></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">一般较新的TCP/IP协议栈都对这一过程进行修正来防范Syn攻击，修改tcp协议实现。主要方法有SynAttackProtect保护机制、SYN cookies技术、增加最大半连接和缩短超时时间等.</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">但是不能完全防范syn攻击。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; text-decoration: underline;"><span size="4" style="margin: 0px; padding: 0px; font-size: medium;">TCP 四次挥手</span></span></strong></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">TCP的连接的拆除需要发送四个包，因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作，在socket编程中，任何一方执行close()操作即可产生挥手操作。</p><div align="center" style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><img src="http://blogimg.chinaunix.net/blog/upfile2/100327022731.jpg" alt="" border="0" width="650" style="margin: 0px; padding: 0px; border: 0px;" /></div><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</p><div style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</div><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">&nbsp;</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">参见wireshark抓包，实测的抓包结果并没有严格按挥手时序。我估计是时间间隔太短造成。</p><div align="center" style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"><img src="http://blogimg.chinaunix.net/blog/upfile2/100327023334.png" alt="" border="0" width="650" style="margin: 0px; padding: 0px; border: 0px;" /></div><div align="center" style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;"></div><div align="center" style="margin: 0px; background-color: #ffffff; color: #494949; font-family: Arial, Helvetica, sans-serif; line-height: 22px;">source url:http://bluedrum.cublog.cn<br /><br /><br /><a href="http://photo.blog.sina.com.cn/showpic.html#blogid=4d2c7ad60100miqj&amp;url=http://s7.sinaimg.cn/orignal/4d2c7ad6t937b6e58c1b6" target="_blank" style="text-decoration: none; color: #765f47; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;"><img src="http://s7.sinaimg.cn/middle/4d2c7ad6t937b6e58c1b6&amp;690" real_src="http://s7.sinaimg.cn/middle/4d2c7ad6t937b6e58c1b6&amp;690" width="501" height="632" alt="TCP状态变迁图及状态说明" title="TCP状态变迁图及状态说明" action-data="http%3A%2F%2Fs7.sinaimg.cn%2Fmiddle%2F4d2c7ad6t937b6e58c1b6%26690" action-type="show-slide" style="margin: 0px; padding: 0px; border: 0px; list-style: none;" /></a><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">状态：描述</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">CLOSED：无连</span><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">接是活动的或正在进行</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">LISTEN：服务器在等待进入呼叫</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">SYN_RECV：一个连接请求已经到达，等待确认</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">SYN_SENT：应用</span><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">已经开始，打开一个连接</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">ESTABLISHED：正常数</span><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">据传输状态</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">FIN_WAIT1：应用说它已经完成</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">FIN_WAIT2：另一边已同意释放</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">ITMED_WAIT：等待所有分组死掉</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">CLOSING：两边同时尝试关闭</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">TIME_WAIT：另一边已初始化一个释放</span><br style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;" /><span style="color: #464646; font-family: simsun; line-height: 21px; text-align: left; background-color: #f8ecd8;">LAST_ACK：等待所有分组死掉</span></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/400910.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2013-06-24 16:33 <a href="http://www.blogjava.net/xiaomage234/archive/2013/06/24/400910.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux配置支持高并发TCP连接(socket最大连接数) 【转】</title><link>http://www.blogjava.net/xiaomage234/archive/2012/03/26/372697.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 26 Mar 2012 03:49:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2012/03/26/372697.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/372697.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2012/03/26/372697.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/372697.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/372697.html</trackback:ping><description><![CDATA[<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px; background-color: #ffffff; ">Linux配置支持高并发TCP连接(socket最大连接数)及优化内核参数&nbsp;&nbsp;</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px; background-color: #ffffff; ">2011-08-09 15:20:58|&nbsp;&nbsp;分类：<a title="LNMP&amp;&amp;LAMP" style="color: #1463c4; ">LNMP&amp;&amp;LAMP</a>|&nbsp;&nbsp;标签：<a style="color: #1463c4; ">内核调优</a>&nbsp;&nbsp;<a style="color: #1463c4; ">文件系统调优</a>&nbsp;&nbsp;<a style="color: #1463c4; ">高并发调优</a>&nbsp;&nbsp;<a style="color: #1463c4; ">socket连接</a>&nbsp;&nbsp;<a style="color: #1463c4; ">ip_conntract</a>&nbsp;&nbsp;|字号大中小&nbsp;<a style="color: #1463c4; ">订阅</a></p><div style="word-wrap: break-word; color: #9a9a9a; font-family: arial; font-size: 12px; line-height: 18px; background-color: #ffffff; "></div><div style="word-wrap: break-word; color: #9a9a9a; font-family: arial; font-size: 12px; line-height: 18px; background-color: #ffffff; "></div><div style="word-wrap: break-word; color: #9a9a9a; font-family: arial; font-size: 12px; line-height: 18px; background-color: #ffffff; "><div style="word-wrap: break-word; ">Linux配置支持高并发TCP连接(socket最大连接数)</div>1、修改用户进程可打开文件数限制<br />在Linux平台上，无论编写客户端程序还是服务端程序，在进行高并发TCP连接处理时，最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个socket句柄，每个socket句柄同时也是一个文件句柄)。可使用ulimit命令查看系统允许当前用户进程打开的文件数限制：<br />[speng@as4 ~]$ ulimit -n<br />1024<br />这表示当前用户的每个进程最多允许同时打开1024个文件，这1024个文件中还得除去每个进程必然打开的标准输入，标准输出，标准错误，服务器监听 socket，进程间通讯的unix域socket等文件，那么剩下的可用于客户端socket连接的文件数就只有大概1024-10=1014个左右。也就是说缺省情况下，基于Linux的通讯程序最多允许同时1014个TCP并发连接。<br />对于想支持更高数量的TCP并发连接的通讯处理程序，就必须修改Linux对当前用户的进程同时打开的文件数量的软限制(soft limit)和硬限制(hardlimit)。其中软限制是指Linux在当前系统能够承受的范围内进一步限制用户同时打开的文件数；硬限制则是根据系统硬件资源状况(主要是系统内存)计算出来的系统最多可同时打开的文件数量。通常软限制小于或等于硬限制。<br />修改上述限制的最简单的办法就是使用ulimit命令：<br />[speng@as4 ~]$ ulimit -n&nbsp;<br />上述命令中，在中指定要设置的单一进程允许打开的最大文件数。如果系统回显类似于&#8220;Operation notpermitted&#8221;之类的话，说明上述限制修改失败，实际上是因为在中指定的数值超过了Linux系统对该用户打开文件数的软限制或硬限制。因此，就需要修改Linux系统对用户的关于打开文件数的软限制和硬限制。<br />第一步，修改/etc/security/limits.conf文件，在文件中添加如下行：<br />speng soft nofile 10240<br />speng hard nofile 10240<br />其中speng指定了要修改哪个用户的打开文件数限制，可用'*'号表示修改所有用户的限制；soft或hard指定要修改软限制还是硬限制；10240则指定了想要修改的新的限制值，即最大打开文件数(请注意软限制值要小于或等于硬限制)。修改完后保存文件。<br />第二步，修改/etc/pam.d/login文件，在文件中添加如下行：<br />session required /lib/security/pam_limits.so<br />这是告诉Linux在用户完成系统登录后，应该调用pam_limits.so模块来设置系统对该用户可使用的各种资源数量的最大限制(包括用户可打开的最大文件数限制)，而pam_limits.so模块就会从/etc/security/limits.conf文件中读取配置来设置这些限制值。修改完后保存此文件。<br />第三步，查看Linux系统级的最大打开文件数限制，使用如下命令：<br />[speng@as4 ~]$ cat /proc/sys/fs/file-max<br />12158<br />这表明这台Linux系统最多允许同时打开(即包含所有用户打开文件数总和)12158个文件，是Linux系统级硬限制，所有用户级的打开文件数限制都不应超过这个数值。通常这个系统级硬限制是Linux系统在启动时根据系统硬件资源状况计算出来的最佳的最大同时打开文件数限制，如果没有特殊需要，不应该修改此限制，除非想为用户级打开文件数限制设置超过此限制的值。修改此硬限制的方法是修改/etc/rc.local脚本，在脚本中添加如下行：<br />echo 22158 &gt; /proc/sys/fs/file-max<br />这是让Linux在启动完成后强行将系统级打开文件数硬限制设置为22158。修改完后保存此文件。<br />完成上述步骤后重启系统，一般情况下就可以将Linux系统对指定用户的单一进程允许同时打开的最大文件数限制设为指定的数值。如果重启后用 ulimit-n命令查看用户可打开文件数限制仍然低于上述步骤中设置的最大值，这可能是因为在用户登录脚本/etc/profile中使用ulimit -n命令已经将用户可同时打开的文件数做了限制。由于通过ulimit-n修改系统对用户可同时打开文件的最大数限制时，新修改的值只能小于或等于上次 ulimit-n设置的值，因此想用此命令增大这个限制值是不可能的。所以，如果有上述问题存在，就只能去打开/etc/profile脚本文件，在文件中查找是否使用了ulimit-n限制了用户可同时打开的最大文件数量，如果找到，则删除这行命令，或者将其设置的值改为合适的值，然后保存文件，用户退出并重新登录系统即可。<br />通过上述步骤，就为支持高并发TCP连接处理的通讯处理程序解除关于打开文件数量方面的系统限制。<br />2、修改网络内核对TCP连接的有关限制（参考对比下篇文章&#8220;优化内核参数&#8221;）<br />在Linux上编写支持高并发TCP连接的客户端通讯处理程序时，有时会发现尽管已经解除了系统对用户同时打开文件数的限制，但仍会出现并发TCP连接数增加到一定数量时，再也无法成功建立新的TCP连接的现象。出现这种现在的原因有多种。<br />第一种原因可能是因为Linux网络内核对本地端口号范围有限制。此时，进一步分析为什么无法建立TCP连接，会发现问题出在connect()调用返回失败，查看系统错误提示消息是&#8220;Can't assign requestedaddress&#8221;。同时，如果在此时用tcpdump工具监视网络，会发现根本没有TCP连接时客户端发SYN包的网络流量。这些情况说明问题在于本地Linux系统内核中有限制。其实，问题的根本原因在于Linux内核的TCP/IP协议实现模块对系统中所有的客户端TCP连接对应的本地端口号的范围进行了限制(例如，内核限制本地端口号的范围为1024~32768之间)。当系统中某一时刻同时存在太多的TCP客户端连接时，由于每个TCP客户端连接都要占用一个唯一的本地端口号(此端口号在系统的本地端口号范围限制中)，如果现有的TCP客户端连接已将所有的本地端口号占满，则此时就无法为新的TCP客户端连接分配一个本地端口号了，因此系统会在这种情况下在connect()调用中返回失败，并将错误提示消息设为&#8220;Can't assignrequested address&#8221;。有关这些控制逻辑可以查看Linux内核源代码，以linux2.6内核为例，可以查看tcp_ipv4.c文件中如下函数：<br />static int tcp_v4_hash_connect(struct sock *sk)<br />请注意上述函数中对变量sysctl_local_port_range的访问控制。变量sysctl_local_port_range的初始化则是在tcp.c文件中的如下函数中设置：<br />void __init tcp_init(void)<br />内核编译时默认设置的本地端口号范围可能太小，因此需要修改此本地端口范围限制。<br />第一步，修改/etc/sysctl.conf文件，在文件中添加如下行：<br />net.ipv4.ip_local_port_range = 1024 65000<br />这表明将系统对本地端口范围限制设置为1024~65000之间。请注意，本地端口范围的最小值必须大于或等于1024；而端口范围的最大值则应小于或等于65535。修改完后保存此文件。<br />第二步，执行sysctl命令：<br />[speng@as4 ~]$ sysctl -p<br />如果系统没有错误提示，就表明新的本地端口范围设置成功。如果按上述端口范围进行设置，则理论上单独一个进程最多可以同时建立60000多个TCP客户端连接。<br />第二种无法建立TCP连接的原因可能是因为Linux网络内核的IP_TABLE防火墙对最大跟踪的TCP连接数有限制。此时程序会表现为在 connect()调用中阻塞，如同死机，如果用tcpdump工具监视网络，也会发现根本没有TCP连接时客户端发SYN包的网络流量。由于 IP_TABLE防火墙在内核中会对每个TCP连接的状态进行跟踪，跟踪信息将会放在位于内核内存中的conntrackdatabase中，这个数据库的大小有限，当系统中存在过多的TCP连接时，数据库容量不足，IP_TABLE无法为新的TCP连接建立跟踪信息，于是表现为在connect()调用中阻塞。此时就必须修改内核对最大跟踪的TCP连接数的限制，方法同修改内核对本地端口号范围的限制是类似的：<br />第一步，修改/etc/sysctl.conf文件，在文件中添加如下行：<br />net.ipv4.ip_conntrack_max = 10240<br />这表明将系统对最大跟踪的TCP连接数限制设置为10240。请注意，此限制值要尽量小，以节省对内核内存的占用。<br />第二步，执行sysctl命令：<br />[speng@as4 ~]$ sysctl -p<br />如果系统没有错误提示，就表明系统对新的最大跟踪的TCP连接数限制修改成功。如果按上述参数进行设置，则理论上单独一个进程最多可以同时建立10000多个TCP客户端连接。<br />3、使用支持高并发网络I/O的编程技术<br />在Linux上编写高并发TCP连接应用程序时，必须使用合适的网络I/O技术和I/O事件分派机制。<br />可用的I/O技术有同步I/O，非阻塞式同步I/O(也称反应式I/O)，以及异步I/O。在高TCP并发的情形下，如果使用同步I/O，这会严重阻塞程序的运转，除非为每个TCP连接的I/O创建一个线程。但是，过多的线程又会因系统对线程的调度造成巨大开销。因此，在高TCP并发的情形下使用同步 I/O是不可取的，这时可以考虑使用非阻塞式同步I/O或异步I/O。非阻塞式同步I/O的技术包括使用select()，poll()，epoll等机制。异步I/O的技术就是使用AIO。<br />从I/O事件分派机制来看，使用select()是不合适的，因为它所支持的并发连接数有限(通常在1024个以内)。如果考虑性能，poll()也是不合适的，尽管它可以支持的较高的TCP并发数，但是由于其采用&#8220;轮询&#8221;机制，当并发数较高时，其运行效率相当低，并可能存在I/O事件分派不均，导致部分TCP连接上的I/O出现&#8220;饥饿&#8221;现象。而如果使用epoll或AIO，则没有上述问题(早期Linux内核的AIO技术实现是通过在内核中为每个 I/O请求创建一个线程来实现的，这种实现机制在高并发TCP连接的情形下使用其实也有严重的性能问题。但在最新的Linux内核中，AIO的实现已经得到改进)。<br />综上所述，在开发支持高并发TCP连接的Linux应用程序时，应尽量使用epoll或AIO技术来实现并发的TCP连接上的I/O控制，这将为提升程序对高并发TCP连接的支持提供有效的I/O保证。</div><img src ="http://www.blogjava.net/xiaomage234/aggbug/372697.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2012-03-26 11:49 <a href="http://www.blogjava.net/xiaomage234/archive/2012/03/26/372697.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SYN_RECV </title><link>http://www.blogjava.net/xiaomage234/archive/2011/09/06/358119.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 06 Sep 2011 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2011/09/06/358119.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/358119.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2011/09/06/358119.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/358119.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/358119.html</trackback:ping><description><![CDATA[<div><div><span style="font-family: arial, 宋体, sans-serif; line-height: 24px; "><h2>SYN_RECV概述</h2>　　TCP SYN Flood是一种常见，而且有效的远端(远程)拒绝服务(Denial of Service)攻击方式，它透过一定的操作破坏TCP<a target="_blank" href="http://baike.baidu.com/view/1003841.htm" style="text-decoration: underline; color: #136ec2; ">三次握手</a>建立正常连接，占用并耗费系统资源，使得提供TCP服务的主机系统无法正常工作。由于TCP SYN Flood是透过网路底层对服务器Server进行攻击的，它可以在任意改变自己的网路IP地址的同时，不被网路上的其他设备所识别，这样就给防范网路犯罪部门追查犯罪来源造成很大的困难。在国内内外的网站中，这种攻击屡见不鲜。在一个拍卖网站上，曾经有犯罪分子利用这种手段，在低价位时阻止其他用户继续对商品拍卖，干扰拍卖过程的正常运作。<div style="height: 30px; line-height: 30px; overflow-x: hidden; overflow-y: hidden; "></div><h2><span editable-title"="" data-edit-id="1520054:1520054:2" style="font-size: 12px; float: right; display: block; margin-top: 10px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; color: #3366cc; font-weight: normal; "><a href="http://baike.baidu.com/view/1520054.htm#" style="text-decoration: underline; color: #136ec2; height: 15px; line-height: 16px; background-image: url(http://img.baidu.com/img/baike/s/edit.gif); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; display: block; width: 52px; padding-left: 18px; background-position: 0% 50%; background-repeat: no-repeat no-repeat; ">编辑本段</a></span><a name="2" style="text-decoration: underline; color: rgb(19, 110, 194); "></a>如何判断</h2>　　一般情况下，可以一些简单步骤进行检查，来判断系统是否正在遭受TCP SYN Flood攻击。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　1、 服务端无法提供正常的TCP服务。连接请求被拒绝或超时。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　2、透过 netstat -an 命令检查系统，发现有大量的SYN_RECV连接状态。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　检查服务器链接，SYN_RECV状态最高时有200多个，访问服务器网页特别慢，甚至超时，所以基本判定是SYN_RECV攻击。<div style="height: 30px; line-height: 30px; overflow-x: hidden; overflow-y: hidden; "></div><h2><span editable-title"="" data-edit-id="1520054:1520054:3" style="font-size: 12px; float: right; display: block; margin-top: 10px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; color: #3366cc; font-weight: normal; "><a href="http://baike.baidu.com/view/1520054.htm#" style="text-decoration: underline; color: #136ec2; height: 15px; line-height: 16px; background-image: url(http://img.baidu.com/img/baike/s/edit.gif); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; display: block; width: 52px; padding-left: 18px; background-position: 0% 50%; background-repeat: no-repeat no-repeat; ">编辑本段</a></span><a name="3" style="text-decoration: underline; color: rgb(19, 110, 194); "></a>解决方法</h2>　　这个攻击的解决方法如下：<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　1，增加未完成连接队列（q0)的最大长度。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　echo 1280&amp;gt;/proc/sys/net/ipv4/tcp_max_syn_backlog<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　2, 启动SYN_cookie。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　echo 1&amp;gt;/proc/sys/net/ipv4/tcp_syncookies<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　这些是被动的方法，治标不治本。而且加大了服务器的负担，但是可以避免被拒绝攻击（只是减缓）<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　治本的方法是在防火墙上做手脚。但是现在能在一定程度上防住syn flood攻击的防火墙都不便宜。并且把这个命令加入"/etc/rc.d/rc.local"文件中<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　如果对 /proc/sys/net/ipv4 下的配置文件进行解释，可以参阅 LinuxAid技术站的文章。查看本文全文也可以参阅。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　关于 syn cookies， 请参阅 &amp;lt;&amp;gt; http://cr.yp.to/syncookies.html<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　也许 使用mod_limitipconn.c来限制apache的并发数 也会有一定的帮助。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　2. iptables的设置，引用自CU<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　防止同步包洪水（Sync Flood）<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　# iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　也有人写作<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　#iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　--limit 1/s 限制syn并发数每秒1次，可以根据自己的需要修改<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　防止各种端口扫描<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　# iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　Ping洪水攻击（Ping of Death）<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　# iptables -A FORWARD -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT<br /><br /><br /></span></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/358119.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2011-09-06 16:26 <a href="http://www.blogjava.net/xiaomage234/archive/2011/09/06/358119.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转老外一篇NGINX 防DDOS配置</title><link>http://www.blogjava.net/xiaomage234/archive/2011/09/06/358120.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 06 Sep 2011 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2011/09/06/358120.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/358120.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2011/09/06/358120.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/358120.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/358120.html</trackback:ping><description><![CDATA[<div><div style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 12px; line-height: 40px; font-size: 12px; color: #555555; font-family: 宋体, 'Arial Narrow', arial, serif; "></div><div style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 20px; margin-right: 15px; margin-bottom: 20px; margin-left: 15px; border-bottom-width: 1px; border-bottom-style: dashed; border-bottom-color: #d9d9d9; word-wrap: break-word; word-break: normal; "><div style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 15px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><div><div><span style="font-size: 12px; line-height: 40px;">标签：nginx ddos 攻击</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp;FreeBSD, network card: Intel fxp, port: 100Мбит, polling, http accept-filter.</span></div><div><span style="font-size: 12px; line-height: 40px;">in sysctl:</span></div><div><span style="font-size: 12px; line-height: 40px;">sysctl kern.maxfiles=90000</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl kern.maxfilesperproc=80000</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl net.inet.tcp.blackhole=2</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl net.inet.udp.blackhole=1</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl kern.polling.burst_max=1000</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl kern.polling.each_burst=50</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl kern.ipc.somaxconn=32768</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl net.inet.tcp.msl=3000</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl net.inet.tcp.maxtcptw=40960</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl net.inet.tcp.nolocaltimewait=1</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl net.inet.ip.portrange.first=1024</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl net.inet.ip.portrange.last=65535</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysctl net.inet.ip.portrange.randomized=0</span></div><div><span style="font-size: 12px; line-height: 40px;">in nginx configuration:</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; worker_processes 1;</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;worker_rlimit_nofile 80000;</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;events {</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;worker_connections 50000;</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</span></div><div><span style="font-size: 12px; line-height: 40px;"><br /></span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;server_tokens off;</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;log_format IP `$remote_addr';</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;reset_timedout_connection on;</span></div><div><span style="font-size: 12px; line-height: 40px;"><br /></span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;listen &nbsp;xx.xx.xx.xx:80 &nbsp;default rcvbuf=8192 sndbuf=16384 backlog=32000 accept_filter=httpready;</span></div><div>&nbsp;</div><div><span style="font-size: 12px; line-height: 40px;">In the following way it is possible to realize filtration of url, in example for POST</span></div><div><span style="font-size: 12px; line-height: 40px;">index.php?action=login which is with empty referral.</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp;set $add 1;</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;location /index.php {</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;limit_except GET POST {</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; deny all;</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;set $ban "";</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if ($http_referer = "" ) {set $ban $ban$add;}</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if ($request_method = POST ) {set $ban $ban$add;}</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if ($query_string = "action=login" ){set $ban $ban$add;}</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if ($ban = 111 ) {</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;access_log /var/log/[133]nginx/ban IP;</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return 404;</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;proxy_pass http://127.0.0.1:8000; #here is a patch</span></div><div><span style="font-size: 12px; line-height: 40px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</span></div><div>&nbsp;</div><div><span style="font-size: 12px; line-height: 40px;">Further we cut it at pf level &#8211; loaded into IP table, hosts from which came too many hits.</span></div><div><span style="font-size: 12px; line-height: 40px;">PF with tables works very quickly. Sources for parsing of logs (ddetect) you can find on http://www.comsys.com.ua/files</span></div><div><span style="font-size: 12px; line-height: 40px;">Then Cron used once in a minute, to add into ip tables new IPs from a log.</span></div><div><span style="font-size: 12px; line-height: 40px;">25 Mbyte DDoS, which cuts IPs, the rests fall on nginx which by it is criterion pass IPs and the rests passed on the apache &#8211; LA 0, site works.</span></div></div></p></div></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/358120.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2011-09-06 16:26 <a href="http://www.blogjava.net/xiaomage234/archive/2011/09/06/358120.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Cookie欺骗 腾讯拍拍秒杀活动的验证码漏洞</title><link>http://www.blogjava.net/xiaomage234/archive/2011/08/16/356653.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 16 Aug 2011 11:30:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2011/08/16/356653.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/356653.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2011/08/16/356653.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/356653.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/356653.html</trackback:ping><description><![CDATA[<div><span style="color: #333333; font-family: 宋体; font-size: 12px; line-height: 18px; "><div bgf8f8f8"="" style="background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f8f8f8; width: 597px; padding-top: 12px; padding-right: 18px; padding-bottom: 0px; padding-left: 18px; margin-top: 10px; border-top-color: #d8d9d9; border-right-color: #d8d9d9; border-left-color: #d8d9d9; border-top-width: 1px; border-right-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-left-style: solid; border-bottom-style: none; border-bottom-width: initial; border-bottom-color: initial; background-position: initial initial; background-repeat: initial initial; "><div style="color: #696969; line-height: 36px; padding-bottom: 8px; text-align: center; height: 30px; position: relative; "><div>2010-12-05 19:36 zetsin newbyter.com&nbsp;<a href="http://netsecurity.51cto.com/art/201012/236575.htm#commment" target="_self" style="color: #004276; text-decoration: none; ">我要评论(<span id="pinglun_1">2)</span></a>&nbsp;字号：<span style="font-family: arial; font-weight: bold; font-size: 14px; "><a href="http://netsecurity.51cto.com/art/201012/236575.htm" target="_self" style="color: #004276; text-decoration: none; ">T</a></span>&nbsp;|&nbsp;<span style="font-family: arial; font-weight: bold; font-size: 16px; "><a href="http://netsecurity.51cto.com/art/201012/236575.htm" target="_self" style="color: #004276; text-decoration: none; ">T</a></span></div><div style="width: 49px; height: 38px; padding-top: 4px; padding-right: 0px; padding-bottom: 0px; padding-left: 8px; position: absolute; top: 5px; right: 0px; "><a href="http://netsecurity.51cto.com/art/201012/236575.htm" title="一键收藏，随时查看，分享好友！" target="_self" style="color: #004276; text-decoration: none; "><img src="http://images.51cto.com/images/art/newart1012/images/Fav.gif" alt="一键收藏，随时查看，分享好友！" border="0" style="border-top-width: medium; border-right-width: medium; border-bottom-width: medium; border-left-width: medium; border-style: initial; border-color: initial; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-color: initial; " /></a></div></div><div style="background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #ffffff; border-top-color: #73a0cf; border-right-color: #73a0cf; border-bottom-color: #73a0cf; border-left-color: #73a0cf; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; line-height: 26px; padding-top: 8px; padding-right: 12px; padding-bottom: 8px; padding-left: 12px; background-position: initial initial; background-repeat: initial initial; "><p green"="" 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; color: #006600; background-color: transparent; font-size: 14px; ">有谁还记得大概几年前突然出现了验证码这东西，许多网站甚至桌面应用程序都陆续实现了验证码技术，主要作用无非就是防止用户利用程序进行自动提交，避免暴力破解，避免服务器遭受恶意攻击。</p><p 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; color: #333333; background-color: transparent; text-align: right; ">AD：</p></div></div><div bgf8f8f8=""  f14"="" style="font-size: 14px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f8f8f8; padding-top: 12px; padding-right: 18px; padding-bottom: 0px; padding-left: 18px; line-height: 28px; margin-bottom: 10px; position: relative; min-height: 500px; "><div id="content"><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; "></p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">首先我想讨论一下验证码这玩意儿。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">有谁还记得大概几年前突然出现了验证码这东西，许多网站甚至桌面应用程序都陆续实现了验证码技术，主要作用无非就是防止用户利用程序进行自动提交，避免暴力破解，避免服务器遭受恶意攻击。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">那么，验证码机制又该如何实现。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">目前主流的实现技术主要有session和cookie两种方式，而这两种方式可以说技术是一样的，区别在于将验证码字符串存储在服务器还是客户端。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">前者工作流程：服务器发送验证码图片到客户端并在服务器保存验证码字符串到session，用户辨认图片并提交验证码字符串到服务器，服务器将用户提交的验证码字符串与session中保存的字符串进行比较。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">后者工作流程：服务器发送验证码图片以及验证码字符串（可能会进行加密）到客户端，客户端将验证码字符串存储到本地cookie，用户辨认图片并提交验证码字符串以及cookie中所存储的字符串到服务器，服务器将用户提交的两个字符串（进行解密后）进行比较。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">相对而言，存放在服务器的session更为安全，只不过消耗服务器内存，程序员除了使用模式识别辨认出验证码，没有其他办法。而对于使用cookie方式的验证码，不增加服务器内存消耗，但我们可以通过对传输数据进行分析轻易破解验证码。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">这种传统的验证码方式，其框架非常简单。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; text-align: center; "><img border="0" alt="" width="397" height="198" src="http://images.51cto.com/files/uploadimg/20101205/1943040.jpg" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-color: initial; text-align: center; border-style: initial; border-color: initial; " />&nbsp;&nbsp;</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">看上图，我们从大的框架分析问题，这里面说的&#8220;提供验证码服务&#8221;已经不区分session还是cookie了。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">接下来从图中我们可以看出什么了呢，对于有多台服务器的大企业是十分平常的，那么，假如每一个服务程序都使用验证码技术，那么每一个服务程序就需要独自实现验证码服务，这样做不是不可以，只是增加了程序员的负担而已。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">好吧，现在我们正式进入今天的主题，也是接着上面的话题，腾讯的验证码框架有何不同。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">我们随便进入一个需要验证码的腾讯页面：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">http://web2.qq.com</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">登录webQQ的时候需要输入验证码，我们通过抓包或者看源代码，可以知道点击&#8220;换一张&#8221;时其实是访问了这个URL：http://captcha.qq.com/getimage，来自tencent http server。多尝试几个需要验证码的页面，发现其验证码依然是是通过访问http://captcha.qq.com/getimage获得。当然也有例外的情况，不过总体看来腾讯的验证码机制的框架与上面讨论的又有些不一样。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; text-align: center; "><img border="0" alt="" width="498" height="219" src="http://images.51cto.com/files/uploadimg/20101205/1943041.jpg" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; border-color: initial; text-align: center; border-style: initial; border-color: initial; " /></p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">看到了吗，在这种框架体系下，验证码成了一个单独的组件，不同服务器不同应用程序都可使用它提供的验证码服务接口。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">具体流程</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">好吧，我们继续进一步研究腾讯验证码的具体流程。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">访问http://captcha.qq.com/getimage，看到&#8220;zqcu&#8221;验证码图片，抓包，查看响应标头：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">键 值</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">响应 HTTP/1.1 200 OK</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">Server tencent http server</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">Accept-Ranges bytes</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">Pragma No-cache</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">Content-Length 2559</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">Set-Cookie verifysession=h0052f5e46e7ca7d3f1bbb1cfa3bbb2a9ea86bded65adbf78e575c50e0d361145fcd232015653790eb</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">4;PATH=/; DOMAIN=qq.com;</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">Connection close</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">Content-Type image/jpeg</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">以上就是HTTP协议中的header内容，请注意其中Set-Cookie的部分，其实这就是验证码&#8220;zqcu&#8221;字符串经过加密后的密文。当我们提交验证码时，正是将我们输入的&#8220;zqcu&#8221;和密文同时提交，然后服务器A/B再通过服务器C的验证接口判断是否正确。这时候，一切浮云都是神马了，原来腾讯验证码居然采用cookie方式。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">那么，接下来高手通常会有两种做法：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">第一，通过模式识别辨认出图片所承载的验证码字符串，然后提交的时候直接发送识别的字符串。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">第二，通过解密破译出verifysession的明文。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">很抱歉，我不是高手，以上两种方法我都无能为力。所以，以下的内容高手可绕过，到此结束了。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">利用cookie欺骗绕过验证码</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">到这里，有人可能会骂，你这不是标题党么，什么cookie欺骗，什么漏洞，啥都没。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">好吧，接下来讲讲我的方法，就是利用cookie欺骗绕过验证码。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">刚才我们访问http://captcha.qq.com/getimage的时候已经获得一个验证码图片，利用人眼识别技术，知道它的验证码字符串为&#8220;zqcu&#8221;，并且截获了其密文verifysession。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">于是，我便开始尝试每次提交都提交&#8220;zqcu&#8221;并且将cookie设为已知的verifysession。嘿嘿，我可以把入哥的介绍信多复印好几份嘛。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">如果真的那么简单的话，腾讯也太二了。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">第一次提交的时候，我十分惊喜，居然成功欺骗了服务器。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">可是当我再次使用这一组验证码及其密文，却返回验证码错误的提示。我的猜想是，某个验证码用过之后，不能马上再次使用，也许要过1分钟或者1个小时之后才能使用，这是一个我称之为过期机制的坎。也就是说，当洪哥问入哥，这是你写的介绍信么，入哥看了看，说是，然后给这封信添了一个标志。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">发现了这个坎，那么悲剧了。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">现在我能做的事只有：提前获得几组甚至几百几千组验证码（需要人眼识别，人手输入）及其密文。而且所获得的验证码还不能闲置太长时间，入哥可能将同一份介绍信发给两个人，当别人先用了它，那么我要过一段时间才能再用。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">注意上面提到了提前两字，但是同样需要人工识别并输入，这到底有什么用呢。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">在做什么事情的时候我们是带着这样一个目的的：提前输入好几组验证码，在特定时间连续并快速的提交。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">好吧，我不多说了，看看标题，我们开始暴力秒杀吧。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">拍拍秒杀流程如下：商品有一个秒杀时间，当到了这个时间，成千上万的玩家开始填写验证码并点击提交，先到先得。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">于是，有了cookie欺骗，我可以提前输入验证码，当时间一到，只要提交即可。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">今天就讲到这里吧，大家可以畅所欲言，提出疑问或者指出错误的地方。最后附上我做的拍拍秒杀助手，为了图个方便用的c#编程，所以需要.net farmework环境，有兴趣的同学可以将其改写成c++。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">PS：修改本地cookie非常简单，但是，腾讯这里的Set-Cookie并不是保存到本地的cookie，它是一个标志为httponly的cookie，只存储在标头。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">我提供一个函数：InternetSetCookieEx()，其msdn如下：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">http://msdn.microsoft.com/zh-cn/library/aa385108.aspx</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: #333333; background-color: transparent; text-indent: 28px; ">附下载链接：http://d.namipan.com/d/efb4375ee41afae5932279334313c00442c995d2ee600800</p></div></div></span></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/356653.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2011-08-16 19:30 <a href="http://www.blogjava.net/xiaomage234/archive/2011/08/16/356653.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈一谈网络编程学习经验</title><link>http://www.blogjava.net/xiaomage234/archive/2011/06/09/352000.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 09 Jun 2011 09:16:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2011/06/09/352000.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/352000.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2011/06/09/352000.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/352000.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/352000.html</trackback:ping><description><![CDATA[<span style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 18px; "><div><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">谈一谈网络编程学习经验</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">陈硕</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">giantchen@gmail.com</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">blog.csdn.net/Solstice</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">2011-06-08</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">PDF 版下载：<a title="https://github.com/downloads/chenshuo/documents/LearningNetworkProgramming.pdf" href="https://github.com/downloads/chenshuo/documents/LearningNetworkProgramming.pdf" style="color: #0066aa; text-decoration: none; ">https://github.com/downloads/chenshuo/documents/LearningNetworkProgramming.pdf</a></p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">本文谈一谈我在学习网络编程方面的一些个人经验。&#8220;网络编程&#8221;这个术语的范围很广，本文指用Sockets API开发基于TCP/IP的网络应用程序，具体定义见&#8220;网络编程的各种任务角色&#8221;一节。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">受限于本人的经历和经验，这篇文章的适应范围是：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; x86-64 Linux服务端网络编程，直接或间接使用 Sockets API</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 公司内网。不一定是局域网，但总体位于公司防火墙之内，环境可控</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">本文可能不适合：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; PC客户端网络编程，程序运行在客户的PC上，环境多变且不可控</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; Windows网络编程</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 面向公网的服务程序</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 高性能网络服务器</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">本文分两个部分：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">1. 网络编程的一些胡思乱想，谈谈我对这一领域的认识</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">2. 几本必看的书，基本上还是W. Richard Stevents那几本</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">另外，本文没有特别说明时均暗指TCP协议，&#8220;连接&#8221;是&#8220;TCP连接&#8221;，&#8220;服务端&#8221;是&#8220;TCP服务端&#8221;。</p><h3>网络编程的一些胡思乱想</h3><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">以下胡乱列出我对网络编程的一些想法，前后无关联。</p><h4>网络编程是什么？</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">网络编程是什么？是熟练使用Sockets API吗？说实话，在实际项目里我只用过两次Sockets API，其他时候都是使用封装好的网络库。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">第一次是2005年在学校做一个羽毛球赛场计分系统：我用C# 编写运行在PC机上的软件，负责比分的显示；再用C# 写了运行在PDA上的计分界面，记分员拿着PDA记录比分；这两部分程序通过 TCP协议相互通信。这其实是个简单的分布式系统，体育馆有不止一片场地，每个场地都有一名拿PDA的记分员，每个场地都有两台显示比分的PC机（显示器是42吋平板电视，放在场地的对角，这样两边看台的观众都能看到比分）。这两台PC机功能不完全一样，一台只负责显示当前比分，另一台还要负责与PDA通信，并更新数据库里的比分信息。此外，还有一台PC机负责周期性地从数据库读出全部7片场地的比分，显示在体育馆墙上的大屏幕上。这台PC上还运行着一个程序，负责生成比分数据的静态页面，通过FTP上传发布到某门户网站的体育频道。系统中还有一个录入赛程（参赛队，运动员，出场顺序等）数据库的程序，运行在数据库服务器上。算下来整个系统有十来个程序，运行在二十多台设备（PC和PDA）上，还要考虑可靠性。将来有机会把这个小系统仔细讲一讲，挺有意思的。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这是我第一次写实际项目中的网络程序，当时写下来的感觉是像写命令行与用户交互的程序：程序在命令行输出一句提示语，等待客户输入一句话，然后处理客户输入，再输出下一句提示语，如此循环。只不过这里的&#8220;客户&#8221;不是人，而是另一个程序。在建立好TCP连接之后，双方的程序都是read/write循环（为求简单，我用的是blocking读写），直到有一方断开连接。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">第二次是2010年编写muduo网络库，我再次拿起了Sockets API，写了一个基于Reactor模式的C++ 网络库。写这个库的目的之一就是想让日常的网络编程从Sockets API的琐碎细节中解脱出来，让程序员专注于业务逻辑，把时间用在刀刃上。Muduo 网络库的示例代码包含了几十个网络程序，这些示例程序都没有直接使用Sockets API。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">在此之外，无论是实习还是工作，虽然我写的程序都会通过TCP协议与其他程序打交道，但我没有直接使用过Sockets API。对于TCP网络编程，我认为核心是处理&#8220;三个半事件&#8221;，见《Muduo 网络编程示例之零：前言》中的&#8220;TCP 网络编程本质论&#8221;。程序员的主要工作是在事件处理函数中实现业务逻辑，而不是和Sockets API较劲。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这里还是没有说清楚&#8220;网络编程&#8221;是什么，请继续阅读后文&#8220;网络编程的各种任务角色&#8221;。</p><h4>学习网络编程有用吗？</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">以上说的是比较底层的网络编程，程序代码直接面对从TCP或UDP收到的数据以及构造数据包发出去。在实际工作中，另一种常见 的情况是通过各种 client library 来与服务端打交道，或者在现成的框架中填空来实现server，或者采用更上层的通信方式。比如用libmemcached与memcached打交道，使用libpq来与PostgreSQL 打交道，编写Servlet来响应http请求，使用某种RPC与其他进程通信，等等。这些情况都会发生网络通信，但不一定算作&#8220;网络编程&#8221;。如果你的工作是前面列举的这些，学习TCP/IP网络编程还有用吗？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">我认为还是有必要学一学，至少在troubleshooting 的时候有用。无论如何，这些library或framework都会调用底层的Sockets API来实现网络功能。当你的程序遇到一个线上问题，如果你熟悉Sockets API，那么从strace不难发现程序卡在哪里，尽管可能你没有直接调用这些Sockets API。另外，熟悉TCP/IP协议、会用tcpdump也大大有助于分析解决线上网络服务问题。</p><h4>在什么平台上学习网络编程？</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">对于服务端网络编程，我建议在Linux上学习。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">如果在10年前，这个问题的答案或许是FreeBSD，因为FreeBSD根正苗红，在2000年那一次互联网浪潮中扮演了重要角色，是很多公司首选的免费服务器操作系统。2000年那会儿Linux还远未成熟，连epoll都还没有实现。（FreeBSD在2001年发布4.1版，加入了kqueue，从此C10k不是问题。）</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">10年后的今天，事情起了变化，Linux成为了市场份额最大的服务器操作系统(http://en.wikipedia.org/wiki/Usage_share_of_operating_systems)。在Linux这种大众系统上学网络编程，遇到什么问题会比较容易解决。因为用的人多，你遇到的问题别人多半也遇到过；同样因为用的人多，如果真的有什么内核bug，很快就会得到修复，至少有work around的办法。如果用别的系统，可能一个问题发到论坛上半个月都不会有人理。从内核源码的风格看，FreeBSD更干净整洁，注释到位，但是无奈它的市场份额远不如Linux，学习Linux是更好的技术投资。</p><h4>可移植性重要吗？</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">写网络程序要不要考虑移植性？这取决于项目需要，如果贵公司做的程序要卖给其他公司，而对方可能使用Windows、Linux、FreeBSD、Solaris、AIX、HP-UX等等操作系统，这时候考虑移植性。如果编写公司内部的服务器上用的网络程序，那么大可只关注一个平台，比如Linux。因为编写和维护可移植的网络程序的代价相当高，平台间的差异可能远比想象中大，即便是POSIX系统之间也有不小的差异（比如Linux没有SO_NOSIGPIPE选项），错误的返回码也大不一样。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">我就不打算把muduo往Windows或其他操作系统移植。如果需要编写可移植的网络程序，我宁愿用libevent或者Java Netty这样现成的库，把脏活累活留给别人。</p><h4>网络编程的各种任务角色</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">计算机网络是个 big topic，涉及很多人物和角色，既有开发人员，也有运维人员。比方说：公司内部两台机器之间 ping 不通，通常由网络运维人员解决，看看是布线有问题还是路由器设置不对；两台机器能ping通，但是程序连不上，经检查是本机防火墙设置有问题，通常由系统管理员解决；两台机器能连上，但是丢包很严重，发现是网卡或者交换机的网口故障，由硬件维修人员解决；两台机器的程序能连上，但是偶尔发过去的请求得不到响应，通常是程序bug，应该由开发人员解决。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">本文主要关心开发人员这一角色。下面简单列出一些我能想到的跟网络打交道的编程任务，其中前三项是面向网络本身，后面几项是在计算机网络之上构建信息系统。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">1. 开发网络设备，编写防火墙、交换机、路由器的固件 firmware</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">2. 开发或移植网卡的驱动</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">3. 移植或维护TCP/IP协议栈（特别是在嵌入式系统上）</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">4. 开发或维护标准的网络协议程序，HTTP、FTP、DNS、SMTP、POP3、NFS</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">5. 开发标准网络协议的&#8220;附加品&#8221;，比如HAProxy、squid、varnish等web load balancer</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">6. 开发标准或非标准网络服务的客户端库，比如ZooKeeper客户端库，memcached客户端库</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">7. 开发与公司业务直接相关的网络服务程序，比如即时聊天软件的后台服务器，网游服务器，金融交易系统，互联网企业用的分布式海量存储，微博发帖的内部广播通知，等等</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">8. 客户端程序中涉及网络的部分，比如邮件客户端中与 POP3、SMTP通信的部分，以及网游的客户端程序中与服务器通信的部分</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">本文所指的&#8220;网络编程&#8221;专指第7项，即在TCP/IP协议之上开发业务软件。</p><h4>面向业务的网络编程的特点</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">跟开发通用的网络程序不同，开发面向公司业务的专用网络程序有其特点：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 业务逻辑比较复杂，而且时常变化</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">如果写一个HTTP服务器，在大致实现HTTP /1.1标准之后，程序的主体功能一般不会有太大的变化，程序员会把时间放在性能调优和bug修复上。而开发针对公司业务的专用程序时，功能说明书（spec）很可能不如HTTP/1.1标准那么细致明确。更重要的是，程序是快速演化的。以即时聊天工具的后台服务器为例，可能第一版只支持在线聊天；几个月之后发布第二版，支持离线消息；又过了几个月，第三版支持隐身聊天；随后，第四版支持上传头像；如此等等。这要求程序员能快速响应新的业务需求，公司才能保持竞争力。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 不一定需要遵循公认的通信协议标准</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">比方说网游服务器就没什么协议标准，反正客户端和服务端都是本公司开发，如果发现目前的协议设计有问题，两边一起改了就是了。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 程序结构没有定论</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">对于高并发大吞吐的标准网络服务，一般采用单线程事件驱动的方式开发，比如HAProxy、lighttpd等都是这个模式。但是对于专用的业务系统，其业务逻辑比较复杂，占用较多的CPU资源，这种单线程事件驱动方式不见得能发挥现在多核处理器的优势。这留给程序员比较大的自由发挥空间，做好了横扫千军，做烂了一败涂地。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 性能评判的标准不同</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">如果开发httpd这样的通用服务，必然会和开源的Nginx、lighttpd等高性能服务器比较，程序员要投入相当的精力去优化程序，才能在市场上占有一席之地。而面向业务的专用网络程序不一定有开源的实现以供对比性能，程序员通常更加注重功能的稳定性与开发的便捷性。性能只要一代比一代强即可。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 网络编程起到支撑作用，但不处于主导地位</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">程序员的主要工作是实现业务逻辑，而不只是实现网络通信协议。这要求程序员深入理解业务。程序的性能瓶颈不一定在网络上，瓶颈有可能是CPU、Disk IO、数据库等等，这时优化网络方面的代码并不能提高整体性能。只有对所在的领域有深入的了解，明白各种因素的权衡(trade-off)，才能做出一些有针对性的优化。</p><h4>几个术语</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">互联网上的很多口水战是由对同一术语的不同理解引起的，比我写的《多线程服务器的适用场合》就曾经人被说是&#8220;挂羊头卖狗肉&#8221;，因为这篇文章中举的 master例子&#8220;根本就算不上是个网络服务器。因为它的瓶颈根本就跟网络无关。&#8221;</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 网络服务器</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#8220;网络服务器&#8221;这个术语确实含义模糊，到底指硬件还是软件？到底是服务于网络本身的机器（交换机、路由器、防火墙、NAT），还是利用网络为其他人或程序提供服务的机器（打印服务器、文件服务器、邮件服务器）。每个人根据自己熟悉的领域，可能会有不同的解读。比方说或许有人认为只有支持高并发高吞吐的才算是网络服务器。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">为了避免无谓的争执，我只用&#8220;网络服务程序&#8221;或者&#8220;网络应用程序&#8221;这种含义明确的术语。&#8220;开发网络服务程序&#8221;通常不会造成误解。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 客户端？服务端？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">在TCP网络编程里边，客户端和服务端很容易区分，主动发起连接的是客户端，被动接受连接的是服务端。当然，这个&#8220;客户端&#8221;本身也可能是个后台服务程序，HTTP Proxy对HTTP Server来说就是个客户端。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 客户端编程？服务端编程？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">但是&#8220;服务端编程&#8221;和&#8220;客户端编程&#8221;就不那么好区分。比如 Web crawler，它会主动发起大量连接，扮演的是HTTP客户端的角色，但似乎应该归入&#8220;服务端编程&#8221;。又比如写一个 HTTP proxy，它既会扮演服务端&#8212;&#8212;被动接受 web browser 发起的连接，也会扮演客户端&#8212;&#8212;主动向 HTTP server 发起连接，它究竟算服务端还是客户端？我猜大多数人会把它归入服务端编程。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">那么究竟如何定义&#8220;服务端编程&#8221;？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">服务端编程需要处理大量并发连接？也许是，也许不是。比如云风在一篇介绍网游服务器的博客<a href="http://blog.codingnow.com/2006/04/iocp_kqueue_epoll.html" style="color: #0066aa; text-decoration: none; ">http://blog.codingnow.com/2006/04/iocp_kqueue_epoll.html</a>中就谈到，网游中用到的&#8220;连接服务器&#8221;需要处理大量连接，而&#8220;逻辑服务器&#8221;只有一个外部连接。那么开发这种网游&#8220;逻辑服务器&#8221;算服务端编程还是客户端编程呢？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">我认为，&#8220;服务端网络编程&#8221;指的是编写没有用户界面的长期运行的网络程序，程序默默地运行在一台服务器上，通过网络与其他程序打交道，而不必和人打交道。与之对应的是客户端网络程序，要么是短时间运行，比如wget；要么是有用户界面（无论是字符界面还是图形界面）。本文主要谈服务端网络编程。</p><h4>7x24重要吗？内存碎片可怕吗？</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">一谈到服务端网络编程，有人立刻会提出7x24运行的要求。对于某些网络设备而言，这是合理的需求，比如交换机、路由器。对于开发商业系统，我认为要求程序7x24运行通常是系统设计上考虑不周。具体见《分布式系统的工程化开发方法》第20页起。重要的不是7x24，而是在程序不必做到7x24的情况下也能达到足够高的可用性。一个考虑周到的系统应该允许每个进程都能随时重启，这样才能在廉价的服务器硬件上做到高可用性。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">既然不要求7x24，那么也不必害怕内存碎片，理由如下：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 64-bit系统的地址空间足够大，不会出现没有足够的连续空间这种情况。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 现在的内存分配器（malloc及其第三方实现）今非昔比，除了memcached这种纯以内存为卖点的程序需要自己设计分配器之外，其他网络程序大可使用系统自带的malloc或者某个第三方实现。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; Linux Kernel也大量用到了动态内存分配。既然操作系统内核都不怕动态分配内存造成碎片，应用程序为什么要害怕？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 内存碎片如何度量？有没有什么工具能为当前进程的内存碎片状况评个分？如果不能比较两种方案的内存碎片程度，谈何优化？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">有人为了避免内存碎片，不使用STL容器，也不敢new/delete，这算是premature optimization还是因噎废食呢？</p><h4>协议设计是网络编程的核心</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">对于专用的业务系统，协议设计是核心任务，决定了系统的开发难度与可靠性，但是这个领域还没有形成大家公认的设计流程。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">系统中哪个程序发起连接，哪个程序接受连接？如果写标准的网络服务，那么这不是问题，按RFC来就行了。自己设计业务系统，有没有章法可循？以网游为例，到底是连接服务器主动连接逻辑服务器，还是逻辑服务器主动连接&#8220;连接服务器&#8221;？似乎没有定论，两种做法都行。一般可以按照&#8220;依赖-&gt;被依赖&#8221;的关系来设计发起连接的方向。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">比新建连接难的是关闭连接。在传统的网络服务中（特别是短连接服务），不少是服务端主动关闭连接，比如daytime、HTTP/1.0。也有少部分是客户端主动关闭连接，通常是些长连接服务，比如 echo、chargen等。我们自己的业务系统该如何设计连接关闭协议呢？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">服务端主动关闭连接的缺点之一是会多占用服务器资源。服务端主动关闭连接之后会进入TIME_WAIT状态，在一段时间之内hold住一些内核资源。如果并发访问量很高，这会影响服务端的处理能力。这似乎暗示我们应该把协议设计为客户端主动关闭，让TIME_WAIT状态分散到多台客户机器上，化整为零。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这又有另外的问题：客户端赖着不走怎么办？会不会造成拒绝服务攻击？或许有一个二者结合的方案：客户端在收到响应之后就应该主动关闭，这样把 TIME_WAIT 留在客户端。服务端有一个定时器，如果客户端若干秒钟之内没有主动断开，就踢掉它。这样善意的客户端会把TIME_WAIT留给自己，buggy的客户端会把 TIME_WAIT留给服务端。或者干脆使用长连接协议，这样避免频繁创建销毁连接。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">比连接的建立与断开更重要的是设计消息协议。消息格式很好办，XML、JSON、Protobuf都是很好的选择；难的是消息内容。一个消息应该包含哪些内容？多个程序相互通信如何避免race condition（见《分布式系统的工程化开发方法》p.16的例子）？系统的全局状态该如何跃迁？可惜这方面可供参考的例子不多，也没有太多通用的指导原则，我知道的只有30年前提出的end-to-end principle和happens-before relationship。只能从实践中慢慢积累了。</p><h4>网络编程的三个层次</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">侯捷先生在《漫談程序員與編程》中讲到 STL 运用的三个档次：&#8220;會用STL，是一種檔次。對STL原理有所了解，又是一個檔次。追蹤過STL源碼，又是一個檔次。第三種檔次的人用起 STL 來，虎虎生風之勢絕非第一檔次的人能夠望其項背。&#8221;</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">我认为网络编程也可以分为三个层次：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">1. 读过教程和文档</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">2. 熟悉本系统TCP/IP协议栈的脾气</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">3. 自己写过一个简单的TCP/IP stack</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">第一个层次是基本要求，读过《Unix网络编程》这样的编程教材，读过《TCP/IP详解》基本理解TCP/IP协议，读过本系统的manpage。这个层次可以编写一些基本的网络程序，完成常见的任务。但网络编程不是照猫画虎这么简单，若是按照manpage的功能描述就能编写产品级的网络程序，那人生就太幸福了。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">第二个层次，熟悉本系统的TCP/IP协议栈参数设置与优化是开发高性能网络程序的必备条件。摸透协议栈的脾气还能解决工作中遇到的比较复杂的网络问题。拿Linux的TCP/IP协议栈来说：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 有可能出现自连接（见《学之者生，用之者死&#8212;&#8212;ACE历史与简评》举的三个硬伤），程序应该有所准备。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; Linux的内核会有bug，比如某种TCP拥塞控制算法曾经出现TCP window clamping（窗口箝位）bug，导致吞吐量暴跌，可以选用其他拥塞控制算法来绕开(work around)这个问题。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这些阴暗角落在manpage里没有描述，要通过其他渠道了解。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">编写可靠的网络程序的关键是熟悉各种场景下的error code（文件描述符用完了如何？本地ephemeral port暂时用完，不能发起新连接怎么办？服务端新建并发连接太快，backlog用完了，客户端connect会返回什么错误？），有的在manpage里有描述，有的要通过实践或阅读源码获得。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">第三个层次，通过自己写一个简单的TCP/IP协议栈，能大大加深对TCP/IP的理解，更能明白TCP为什么要这么设计，有哪些因素制约，每一步操作的代价是什么，写起网络程序来更是成竹在胸。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">其实实现TCP/IP只需要操作系统提供三个接口函数：一个函数，两个回调函数。分别是：send_packet()、on_receive_packet()、on_timer()。多年前有一篇文章《使用libnet与libpcap构造TCP/IP协议软件》介绍了在用户态实现TCP/IP的方法。lwIP也是很好的借鉴对象。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">如果有时间，我打算自己写一个Mini/Tiny/Toy/Trivial/Yet-Another TCP/IP。我准备换一个思路，用TUN/TAP设备在用户态实现一个能与本机点对点通信的TCP/IP协议栈，这样那三个接口函数就表现为我最熟悉的文件读写。在用户态实现的好处是便于调试，协议栈做成静态库，与应用程序链接到一起（库的接口不必是标准的Sockets API）。做完这一版，还可以继续发挥，用FTDI的USB-SPI接口芯片连接ENC28J60适配器，做一个真正独立于操作系统的TCP/IP stack。如果只实现最基本的IP、ICMP Echo、TCP的话，代码应能控制在3000行以内；也可以实现UDP，如果应用程序需要用到DNS的话。</p><h4>最主要的三个例子</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">我认为TCP网络编程有三个例子最值得学习研究，分别是echo、chat、proxy，都是长连接协议。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">Echo的作用：熟悉服务端被动接受新连接、收发数据、被动处理连接断开。每个连接是独立服务的，连接之间没有关联。在消息内容方面Echo有一些变种：比如做成一问一答的方式，收到的请求和发送响应的内容不一样，这时候要考虑打包与拆包格式的设计，进一步还可以写简单的HTTP服务。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">Chat的作用：连接之间的数据有交流，从a收到的数据要发给b。这样对连接管理提出的更高的要求：如何用一个程序同时处理多个连接？fork() per connection似乎是不行的。如何防止串话？b有可能随时断开连接，而新建立的连接c可能恰好复用了b的文件描述符，那么a会不会错误地把消息发给c？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">Proxy的作用：连接的管理更加复杂：既要被动接受连接，也要主动发起连接，既要主动关闭连接，也要被动关闭连接。还要考虑两边速度不匹配，见《Muduo 网络编程示例之十：socks4a 代理服务器》。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这三个例子功能简单，突出了TCP网络编程中的重点问题，挨着做一遍基本就能达到层次一的要求。</p><h4>TCP的可靠性有多高？</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">TCP是&#8220;面向连接的、可靠的、字节流传输协议&#8221;，这里的&#8220;可靠&#8221;究竟是什么意思？《Effective TCP/IP Programming》第9条说：Realize That TCP Is a Reliable Protocol, Not an Infallible Protocol，那么TCP在哪种情况下会出错？这里说的&#8220;出错&#8221;指的是收到的数据与发送的数据不一致，而不是数据不可达。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">我在《一种自动反射消息类型的 Google Protobuf 网络传输方案》中设计了带check sum的消息格式，很多人表示不理解，认为是多余的。IP header里边有check sum，TCP header也有check sum，链路层以太网还有CRC32校验，那么为什么还需要在应用层做校验？什么情况下TCP传送的数据会出错？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">IP header和TCP header的check sum是一种非常弱的16-bit check sum算法，把数据当成反码表示的16-bit integers，再加到一起。这种checksum算法能检出一些简单的错误，而对某些错误无能为力，由于是简单的加法，遇到&#8220;和&#8221;不变的情况就无法检查出错误（比如交换两个16-bit整数，加法满足交换律，结果不变）。以太网的CRC32只能保证同一个网段上的通信不会出错（两台机器的网线插到同一个交换机上，这时候以太网的CRC是有用的）。但是，如果两台机器之间经过了多级路由器呢？</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><img title="router" border="0" alt="router" src="http://www.cppblog.com/images/cppblog_com/Solstice/Windows-Live-Writer/958c781e7d58_E312/router_3.png" width="471" height="305" style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px; " /></p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">上图中Client向Server发了一个TCP segment，这个segment先被封装成一个IP packet，再被封装成ethernet frame，发送到路由器（图中消息a）。Router收到ethernet frame (b)，转发到另一个网段(c)，最后Server收到d，通知应用程序。Ethernet CRC能保证a和b相同，c和d相同；TCP header check sum的强度不足以保证收发payload的内容一样。另外，如果把Router换成NAT，那么NAT自己会构造c（替换掉源地址），这时候a和d的payload不能用tcp header checksum校验。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">路由器可能出现硬件故障，比方说它的内存故障（或偶然错误）导致收发IP报文出现多bit的反转或双字节交换，这个反转如果发生在payload区，那么无法用链路层、网络层、传输层的check sum查出来，只能通过应用层的check sum来检测。这个现象在开发的时候不会遇到，因为开发用的几台机器很可能都连到同一个交换机，ethernet CRC能防止错误。开发和测试的时候数据量不大，错误很难发生。之后大规模部署到生产环境，网络环境复杂，这时候出个错就让人措手不及。有一篇论文《When the CRC and TCP checksum disagree》分析了这个问题。另外《The Limitations of the Ethernet CRC and TCP/IP checksums for error detection》(<a href="http://noahdavids.org/self_published/CRC_and_checksum.html" style="color: #0066aa; text-decoration: none; ">http://noahdavids.org/self_published/CRC_and_checksum.html</a>)也值得一读。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这个情况真的会发生吗？会的，Amazon S3 在2008年7月就遇到过，单bit反转导致了一次严重线上事故，所以他们吸取教训加了 check sum。见<a href="http://status.aws.amazon.com/s3-20080720.html" style="color: #0066aa; text-decoration: none; ">http://status.aws.amazon.com/s3-20080720.html</a></p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">另外一个例证：下载大文件的时候一般都会附上MD5，这除了有安全方面的考虑（防止篡改），也说明应用层应该自己设法校验数据的正确性。这是end-to-end principle的一个例证。</p><h3>三本必看的书</h3><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">谈到Unix编程和网络编程，W. Richard Stevens 是个绕不开的人物，他生前写了6本书，APUE、两卷UNP、三卷TCP/IP。有四本与网络编程直接相关。UNP第二卷其实跟网络编程关系不大，是APUE在多线程和进程间通信(IPC)方面的补充。很多人把TCP/IP一二三卷作为整体推荐，其实这三本书用处不同，应该区别对待。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这里谈到的几本书都没有超出孟岩在《TCP/IP 网络编程之四书五经》中的推荐，说明网络编程这一领域已经相对成熟稳定。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 《<em>TCP/IP Illustrated</em>,&nbsp;<em>Vol. 1: The Protocols</em>》中文名《TCP/IP 详解》，以下简称 TCPv1。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">TCPv1 是一本奇书。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这本书迄今至少被三百多篇学术论文引用过<a href="http://portal.acm.org/citation.cfm?id=161724" style="color: #0066aa; text-decoration: none; ">http://portal.acm.org/citation.cfm?id=161724</a>。一本学术专著被论文引用算不上出奇，难得的是一本写给程序员看的技术书能被学术论文引用几百次，我不知道还有哪本技术书能做到这一点。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">TCPv1 堪称 TCP/IP领域的圣经。作者 W. Richard Stevens 不是 TCP/IP 协议的发明人，他从使用者（程序员）的角度，以 tcpdump 为工具，对 TCP 协议抽丝剥茧娓娓道来（第17~24章），让人叹服。恐怕 TCP 协议的设计者也难以讲解得如此出色，至少不会像他这么耐心细致地画几百幅收发 package 的时序图。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">TCP作为一个可靠的传输层协议，其核心有三点：</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">1. Positive acknowledgement with retransmission</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">2. Flow control using sliding window（包括Nagle 算法等）</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">3. Congestion control（包括slow start、congestion avoidance、fast retransmit等）</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">第一点已经足以满足&#8220;可靠性&#8221;要求（为什么？）；第二点是为了提高吞吐量，充分利用链路层带宽；第三点是防止过载造成丢包。换言之，第二点是避免发得太慢，第三点是避免发得太快，二者相互制约。从反馈控制的角度看，TCP像是一个自适应的节流阀，根据管道的拥堵情况自动调整阀门的流量。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">TCP的 flow control 有一个问题，每个TCP connection是彼此独立的，保存有自己的状态变量；一个程序如果同时开启多个连接，或者操作系统中运行多个网络程序，这些连接似乎不知道他人的存在，缺少对网卡带宽的统筹安排。（或许现代的操作系统已经解决了这个问题？）</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">TCPv1 唯一的不足是它出版太早了，1993 年至今网络技术发展了几代。链路层方面，当年主流的 10Mbit 网卡和集线器早已经被淘汰；100Mbit 以太网也没什么企业在用了，交换机(switch)也已经全面取代了集线器(hub)；服务器机房以 1Gbit 网络为主，有些场合甚至用上了 10Gbit 以太网。另外，无线网的普及也让TCP flow control面临新挑战；原来设计TCP的时候，人们认为丢包通常是拥塞造成的，这时应该放慢发送速度，减轻拥塞；而在无线网中，丢包可能是信号太弱造成的，这时反而应该快速重试，以保证性能。网络层方面变化不大，IPv6 雷声大雨点小。传输层方面，由于链路层带宽大增，TCP window scale option 被普遍使用，另外 TCP timestamps option 和 TCP selective ack option 也很常用。由于这些因素，在现在的 Linux 机器上运行 tcpdump 观察 TCP 协议，程序输出会与原书有些不同。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">一个好消息：TCPv1将于今年10月（2011年）推出第二版，Amazon 的预定页面是：<a href="http://www.amazon.com/gp/product/0321336313" style="color: #0066aa; text-decoration: none; ">http://www.amazon.com/gp/product/0321336313</a>，让我们拭目以待。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 《Unix Network Programming, Vol. 1: Networking API》第二版或第三版（这两版的副标题稍有不同，第三版去掉了 XTI），以下统称 UNP，如果需要会以 UNP2e、UNP3e 细分。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">UNP是Sockets API的权威指南，但是网络编程远不是使用那十几个Sockets API那么简单，作者 W. Richard Stevens深刻地认识到这一点，他在UNP2e的前言中写到：<a href="http://www.kohala.com/start/preface.unpv12e.html" style="color: #0066aa; text-decoration: none; ">http://www.kohala.com/start/preface.unpv12e.html</a></p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">I have found when teaching network programming that about 80% of all network programming problems have nothing to do with network programming, per se. That is, the problems are not with the API functions such as accept and select, but the problems arise from a lack of understanding of the underlying network protocols. For example, I have found that once a student understands TCP's three-way handshake and four-packet connection termination, many network programming problems are immediately understood.</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">搞网络编程，一定要熟悉TCP/IP协议及其外在表现（比如打开和关闭Nagle算法对收发包的影响），不然出点意料之外的情况就摸不着头脑了。我不知道为什么UNP3e在前言中去掉了这段至关重要的话。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">另外值得一提的是，UNP中文版翻译得相当好，译者杨继张先生是真懂网络编程的。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">UNP很详细，面面俱到，UDP、TCP、IPv4、IPv6都讲到了。要说有什么缺点的话，就是太详细了，重点不够突出。我十分赞同孟岩说的</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#8220;（孟岩）我主张，在具备基础之后，学习任何新东西，都要抓住主线，突出重点。对于关键理论的学习，要集中精力，速战速决。而旁枝末节和非本质性的知识内容，完全可以留给实践去零敲碎打。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#8220;原因是这样的，任何一个高级的知识内容，其中都只有一小部分是有思想创新、有重大影响的，而其它很多东西都是琐碎的、非本质的。因此，集中学习时必须把握住真正重要那部分，把其它东西留给实践。对于重点知识，只有集中学习其理论，才能确保体系性、连贯性、正确性，而对于那些旁枝末节，只有边干边学能够让你了解它们的真实价值是大是小，才能让你留下更生动的印象。如果你把精力用错了地方，比如用集中大块的时间来学习那些本来只需要查查手册就可以明白的小技巧，而对于真正重要的、思想性东西放在平时零敲碎打，那么肯定是事倍功半，甚至适得其反。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#8220;因此我对于市面上绝大部分开发类图书都不满&#8212;&#8212;它们基本上都是面向知识体系本身的，而不是面向读者的。总是把相关的所有知识细节都放在一堆，然后一堆一堆攒起来变成一本书。反映在内容上，就是毫无重点地平铺直叙，不分轻重地陈述细节，往往在第三章以前就用无聊的细节谋杀了读者的热情。为什么当年侯捷先生的《深入浅出MFC》和 Scott Meyers 的&nbsp;<em>Effective C++</em>&nbsp;能够成为经典？就在于这两本书抓住了各自领域中的主干，提纲挈领，纲举目张，一下子打通读者的任督二脉。可惜这样的书太少，就算是已故 Richard Stevens 和当今 Jeffrey Richter 的书，也只是在体系性和深入性上高人一头，并不是面向读者的书。&#8221;</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">什么是旁枝末节呢？拿以太网来说，CRC32如何计算就是&#8220;旁枝末节&#8221;。网络程序员要明白check sum的作用，知道为什么需要check sum，至于具体怎么算CRC就不需要程序员操心。这部分通常是由网卡硬件完成的，在发包的时候由硬件填充CRC，在收包的时候网卡自动丢弃CRC不合格的包。如果代码里边确实要用到CRC计算，调用通用的zlib就行，也不用自己实现。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">UNP就像给了你一堆做菜的原料（各种Sockets 函数的用法），常用和不常用的都给了（Out-of-Band Data、Signal-Driven IO 等等），要靠读者自己设法取舍组合，做出一盘大菜来。在第一遍读的时候，我建议只读那些基本且重要的章节；另外那些次要的内容可略作了解，即便跳过不读也无妨。UNP是一本操作性很强的书，读这本这本书一定要上机练习。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">另外，UNP举的两个例子（菜谱）太简单，daytime和echo一个是短连接协议，一个是长连接无格式协议，不足以覆盖基本的网络开发场景（比如 TCP封包与拆包、多连接之间交换数据）。我估计 W. Richard Stevens 原打算在 UNP第三卷中讲解一些实际的例子，只可惜他英年早逝，我等无福阅读。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">UNP是一本偏重Unix传统的书，这本书写作的时候服务端还不需要处理成千上万的连接，也没有现在那么多网络攻击。书中重点介绍的以accept()+fork()来处理并发连接的方式在现在看来已经有点吃力，这本书的代码也没有特别防范恶意攻击。如果工作涉及这些方面，需要再进一步学习专门的知识（C10k问题，安全编程）。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">TCPv1和UNP应该先看哪本？我不知道。我自己是先看的TCPv1，花了大约半学期时间，然后再读UNP2e和APUE。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 《<em>Effective TCP/IP Programming</em>》</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">第三本书我犹豫了很久，不知道该推荐哪本，还有哪本书能与 W. Richard Stevens 的这两本比肩吗？W. Richard Stevens 为技术书籍的写作树立了难以逾越的标杆，他是一位伟大的技术作家。没能看到他写完 UNP 第三卷实在是人生的遗憾。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">《<em>Effective TCP/IP Programming</em>》这本书属于专家经验总结类，初看时觉得收获很大，工作一段时间再看也能有新的发现。比如第6 条&#8220;TCP是一个字节流协议&#8221;，看过这一条就不会去研究所谓的&#8220;TCP粘包问题&#8221;。我手头这本电力社2001年的中文版翻译尚可，但是很狗血的是把参考文献去掉了，正文中引用的文章资料根本查不到名字。人邮2011年重新翻译出版的版本有参考文献。</p><h4>其他值得一看的书</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">以下两本都不易读，需要相当的基础。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 《<em>TCP/IP&nbsp;</em>Illustrated,&nbsp;<em>Vol. 2: The Implementation</em>》以下简称 TCPv2</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">1200页的大部头，详细讲解了4.4BSD的完整TCP/IP协议栈，注释了15,000行C源码。这本书啃下来不容易，如果时间不充裕，我认为没必要啃完，应用层的网络程序员选其中与工作相关的部分来阅读即可。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这本书第一作者是Gary Wright，从叙述风格和内容组织上是典型的&#8220;面向知识体系本身&#8221;，先讲mbuf，再从链路层一路往上、以太网、IP网络层、ICMP、IP多播、IGMP、IP路由、多播路由、Sockets系统调用、ARP等等。到了正文内容3/4的地方才开始讲TCP。面面俱到、主次不明。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">对于主要使用TCP的程序员，我认为TCPv2一大半内容可以跳过不看，比如路由表、IGMP等等（开发网络设备的人可能更关心这些内容）。在工作中大可以把IP视为host-to-host的协议，把&#8220;IP packet如何送达对方机器&#8221;的细节视为黑盒子，这不会影响对TCP的理解和运用，因为网络协议是分层的。这样精简下来，需要看的只有三四百页，四五千行代码，大大减轻了负担。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这本书直接呈现高质量的工业级操作系统源码，读起来有难度，读懂它甚至要有&#8220;不求甚解的能力&#8221;。其一，代码只能看，不能上机运行，也不能改动试验。其二，与操作系统其他部分紧密关联。比如TCP/IP stack下接网卡驱动、软中断；上承inode转发来的系统调用操作；中间还要与平级的进程文件描述符管理子系统打交道；如果要把每一部分都弄清楚，把持不住就迷失主题了。其三，一些历史包袱让代码变复杂晦涩。比如BSD在80年代初需要在只有4M内存的VAX上实现TCP/IP，内存方面捉襟见肘，这才发明了mbuf结构，代码也增加了不少偶发复杂度（buffer不连续的处理）。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">读这套TCP/IP书切忌胶柱鼓瑟，这套书以4.4BSD为底，其描述的行为（特别是与timer相关的行为）与现在的Linux TCP/IP有不小的出入，用书本上的知识直接套用到生产环境的Linux系统可能会造成不小的误解和困扰。（TCPv3不重要，可以成套买来收藏，不读亦可。）</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&#183; 《<em>Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects</em>》以下简称POSA2</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">这本书总结了开发并发网络服务程序的模式，是对UNP很好的补充。UNP中的代码往往把业务逻辑和Sockets API调用混在一起，代码固然短小精悍，但是这种编码风格恐怕不适合开发大型的网络程序。POSA2强调模块化，网络通信交给library/framework去做，程序员写代码只关注业务逻辑，这是非常重要的思想。阅读这本书对于深入理解常用的event-driven网络库（libevent、Java Netty、Java Mina、Perl POE、Python Twisted等等）也很有帮助，因为这些库都是依照这本书的思想编写的。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">POSA2的代码是示意性的，思想很好，细节不佳。其C++ 代码没有充分考虑资源的自动化管理(RAII)，如果直接按照书中介绍的方式去实现网络库，那么会给使用者造成不小的负担与陷阱。换言之，照他说的做，而不是照他做的学。</p><h4>不值一看的书</h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">Douglas Comer 教授名气很大，著作等身，但是他写的网络方面的书不值一读，味同嚼蜡。网络编程与 TCP/IP 方面，有W. Richard Stevens 的书扛鼎；计算机网络原理方面，有Kurose的&#8220;自顶向下&#8221;和Peterson的&#8220;系统&#8221;打旗，没其他人什么事儿。顺便一提，Tanenbaum的操作系统教材是最好的之一（嗯，之二，因为他写了两本：&#8220;现代&#8221;和&#8220;设计与实现&#8221;），不过他的计算机网络和体系结构教材的地位比不上他的操作系统书的地位。体系结构方面，Patterson 和 Hennessy二人合作的两本书是最好的，近年来崭露头角的《深入理解计算机系统》也非常好；当然，侧重点不同。</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">(完)</p><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; font-size: 0.75em; ">posted on 2011-06-06 08:44&nbsp;<a href="http://www.cppblog.com/Solstice/" style="color: #0066aa; text-decoration: none; ">陈硕</a>&nbsp;阅读(13435)&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#Post" style="color: #0066aa; text-decoration: none; ">评论(9)</a>&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/admin/EditPosts.aspx?postid=148129" style="color: #0066aa; text-decoration: none; ">编辑</a>&nbsp;<a href="http://www.cppblog.com/Solstice/AddToFavorite.aspx?id=148129" style="color: #0066aa; text-decoration: none; ">收藏</a>&nbsp;<a href="http://www.cppblog.com/Solstice/services/trackbacks/148129.aspx" style="color: #0066aa; text-decoration: none; ">引用</a>&nbsp;所属分类:&nbsp;<a href="http://www.cppblog.com/Solstice/category/15951.html" style="color: #0066aa; text-decoration: none; ">muduo</a></p></div><img src="http://www.cppblog.com/Solstice/aggbug/148129.html?webview=1" width="1" height="1"  alt="" /><div id="AjaxHolder_UpdatePanel1"><a name="pagedcomment"></a><a name="评论"></a><div id="comments"><a name="评论"><h3>评论</h3></a><h4><a name="评论"></a><a title="permalink: re: 谈一谈网络编程学习经验" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148133" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148133"></a>re: 谈一谈网络编程学习经验&nbsp;<span style="color: #999999; ">2011-06-06 11:55&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl00_NameLink" target="_blank">treapdb</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">果断收藏。&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=treapdb" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl00_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl00_EditLink"></a></p><h4><a title="permalink: re: 谈一谈网络编程学习经验" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148144" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148144"></a>re: 谈一谈网络编程学习经验&nbsp;<span style="color: #999999; ">2011-06-06 14:57&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl01_NameLink" href="http://www.cppblog.com/zblc" target="_blank" style="color: #0066aa; text-decoration: none; ">千暮(zblc)</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">mark!&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=%e5%8d%83%e6%9a%ae(zblc)" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl01_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl01_EditLink"></a></p><h4><a title="permalink: re: 谈一谈网络编程学习经验" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148152" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148152"></a>re: 谈一谈网络编程学习经验&nbsp;<span style="color: #999999; ">2011-06-06 20:45&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl02_NameLink" target="_blank">nomagic</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">我也觉得ACE搞得过于复杂了，小问题多多。学习一下它的某些思路和模式应该还是有些意义的，可以开阔思路，海纳百川。但是把ACE用于实际项目基本是自找麻烦。&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=nomagic" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl02_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl02_EditLink"></a></p><h4><a title="permalink: re: 谈一谈网络编程学习经验" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148162" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148162"></a>re: 谈一谈网络编程学习经验&nbsp;<span style="color: #999999; ">2011-06-06 22:12&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl03_NameLink" href="http://www.cppblog.com/vczh/" target="_blank" style="color: #0066aa; text-decoration: none; ">陈梓瀚(vczh)</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">@nomagic&nbsp;<br />那其实就是一个科学家做的demo吧，如果你仔细观察那些开发者的来源&#8230;&#8230;&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=%e9%99%88%e6%a2%93%e7%80%9a(vczh)" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl03_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl03_EditLink"></a></p><h4><a title="permalink: re: 谈一谈网络编程学习经验" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148163" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148163"></a>re: 谈一谈网络编程学习经验&nbsp;<span style="color: #999999; ">2011-06-06 22:23&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl04_NameLink" target="_blank">jigsaw</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">服了你了。mbuf在你嘴里成了剩饭剩菜。<br /><br />你除了Linux的TCP层，你还看过哪些TCP stack？你知不知道绝大多数_闭源_的TCP stack都是基于mbuf的？不管是低端手持设备还是高端的路由器，只要不是基于Linux的，一定是基于mbuf的TCP stack。<br />Linux的skbuf也是处处有mbuf的影子-尽管Linus说他是从头写过的。<br />我怀疑你连Linux的TCP stack都没看过吧？<br /><br />刚说完基础的重要性，转口又嫌TCPv2太难，还是不求甚解的好 - 这就是博主的态度。<br /><br />&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=jigsaw" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl04_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl04_EditLink"></a></p><h4><a title="permalink: re: 谈一谈网络编程学习经验" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148185" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148185"></a>re: 谈一谈网络编程学习经验&nbsp;<span style="color: #999999; ">2011-06-07 09:55&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl05_NameLink" href="http://www.cppblog.com/zuhd/" target="_blank" style="color: #0066aa; text-decoration: none; ">zuhd</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">我主张，在具备基础之后，学习任何新东西，都要抓住主线，突出重点。对于关键理论的学习，要集中精力，速战速决。而旁枝末节和非本质性的知识内容，完全可以留给实践去零敲碎打。&nbsp;<br /><br />这点我要好好学习下&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=zuhd" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl05_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl05_EditLink"></a></p><h4><a title="permalink: re: 谈一谈网络编程学习经验[未登录]" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148199" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148199"></a>re: 谈一谈网络编程学习经验[未登录]&nbsp;<span style="color: #999999; ">2011-06-07 14:04&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl06_NameLink" target="_blank">vincent</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">tcpv2很难看下去&nbsp;<br />不知为何,也不知从何时起越来越不喜欢厚重的书了&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=vincent" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl06_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl06_EditLink"></a></p><h4><a title="permalink: re: 谈一谈网络编程学习经验(06-08更新)" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148340" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148340"></a>re: 谈一谈网络编程学习经验(06-08更新)&nbsp;<span style="color: #999999; ">2011-06-09 14:38&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl07_NameLink" target="_blank">ISO</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">jigsaw,和陈硕的水平在我眼中都是小菜一碟， 我不但通读rfc各种文档，还深入研究过bsd, linux tcp/ip protocol implementation, 对链路层更是了如指掌，对各种硬件的电子电器特性也很精通。你们都是菜鸟，我的服务器可以并发处理上亿的tcp.&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=ISO" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl07_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl07_EditLink"></a></p><h4><a title="permalink: re: 谈一谈网络编程学习经验(06-08更新)" href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#148354" style="color: #0066aa; text-decoration: none; ">#</a>&nbsp;<a name="148354"></a>re: 谈一谈网络编程学习经验(06-08更新)<a name="Post"></a>&nbsp;<span style="color: #999999; ">2011-06-09 16:36&nbsp;</span><a id="AjaxHolder_Comments_CommentList_ctl08_NameLink" href="http://www.cppblog.com/Solstice/" target="_blank" style="color: #0066aa; text-decoration: none; ">陈硕</a></h4><p style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">@ISO<br />佩服佩服！&nbsp;&nbsp;<a href="http://www.cppblog.com/Solstice/archive/2011/06/06/148129.html#post" style="color: #0066aa; text-decoration: none; ">回复</a>&nbsp;&nbsp;<a title="查看该作者发表过的评论" href="http://www.cppblog.com/comment?author=%e9%99%88%e7%a1%95" target="_blank" style="color: #0066aa; text-decoration: none; ">更多评论</a>&nbsp;<a id="AjaxHolder_Comments_CommentList_ctl08_DeleteLink" href="" style="color: #0066aa; text-decoration: none; "></a>&nbsp;&nbsp;</p></div></div></span><img src ="http://www.blogjava.net/xiaomage234/aggbug/352000.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2011-06-09 17:16 <a href="http://www.blogjava.net/xiaomage234/archive/2011/06/09/352000.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个网卡绑定多个IP和多个网卡用一个ip的设置</title><link>http://www.blogjava.net/xiaomage234/archive/2011/05/18/350462.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Wed, 18 May 2011 04:01:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2011/05/18/350462.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/350462.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2011/05/18/350462.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/350462.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/350462.html</trackback:ping><description><![CDATA[常用到的是&#8220;一个网卡绑定多个IP&#8221; 
<p>一个网卡绑定多个IP 
<p>linux的网络设备配置文件存放在/etc/sysconfig/network-scripts里面， 
<p>对于以太网的第一个网络设备，配置文件名一般为ifcfg-eth0。 
<p>如果需要为第一个网络设备多绑定一个IP地址，只需要在 
<p>/etc/sysconfig/network-scripts目录里面创建一个名为ifcfg-eth0:0的文件， 
<p>内容样例为： 
<p>DEVICE="eth0:0" 
<p>IPADDR="211.100.10.119" 
<p>NETMASK="255.255.255.0" 
<p>ONBOOT="yes" 
<p>其中的DEVICE为设备的名称， 
<p>IPADDR为此设备的IP地址， 
<p>NETMASK为子网掩码 
<p>ONBOOT表示在系统启动时自动启动。 
<p>如果需要再绑定多一个IP地址， 
<p>只需要把文件名和文件内的DEVICE中的eth0:x加一即可。 
<p>LINUX最多可以支持255个IP别名 
<p>&nbsp;
<p>&nbsp;
<p>多个网卡绑定一个IP 
<p>使用多块网卡虚拟成为一块网卡，具有相同的IP地址。 
<p>这项技术其实在sun和cisco中已经存在，分别称为Trunking和etherchannel技术， 
<p>在linux中，这种技术称为bonding。 
<p>因为bonding在内核2.4.x中已经包含了， 
<p>只需要在编译的时候把网络设备选项中的Bondingdriversupport选中就可以了。 
<p>然后，重新编译核心，重新起动<a href="http://it.china-b.com/" target="_blank"><font color="#0000ff">计算机</font></a>，执行如下命令： 
<p>ismodbonding 
<p>ifconfigeth0down 
<p>ifconfigeth1down 
<p>ifconfigbond0ipaddress 
<p>ifenslavebond0eth0 
<p>ifenslavebond0eth1 
<p>现在两块网卡已经象一块一样工作了，这样可以提高集群节点间的数据传输。 
<p>你最好把这几句写成一个脚本，再由/etc/rc.d/rc.local调用， 
<p>以便一开机就生效。 
<p>bonding对于服务器来是个比较好的选择，在没有千兆网卡时， 
<p>用两三块100兆网卡作bonding，可大大提高服务器到交换机之间的带宽。 
<p>但是需要在交换机上设置连接bonding网卡的两个口子映射为同一个虚拟接口。</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/350462.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2011-05-18 12:01 <a href="http://www.blogjava.net/xiaomage234/archive/2011/05/18/350462.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>短连接http服务器陷阱</title><link>http://www.blogjava.net/xiaomage234/archive/2011/05/12/350112.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 12 May 2011 11:26:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2011/05/12/350112.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/350112.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2011/05/12/350112.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/350112.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/350112.html</trackback:ping><description><![CDATA[<div id="app-share-content">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>由于</span><span><font face="Times New Roman">http</font></span><span>协议的简单性以及业务的需要，我们不可避免地需要自己去实现一些走</span><span><font face="Times New Roman">http</font></span><span>协议的</span><span><font face="Times New Roman">server</font></span><span>。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>当我们的短连接</span><span><font face="Times New Roman">http</font></span><span>服务器</span><span><font face="Times New Roman">(</font></span><span>如验证码服务器</span><span><font face="Times New Roman">)</font></span><span>是客户端是浏览器</span><span><font face="Times New Roman">(ie</font></span><span>、</span><span><font face="Times New Roman">firefox</font></span><span>、</span><span><font face="Times New Roman">chrome)</font></span><span>的时候，有一个问题需要特别注意，我称之为短连接</span><span><font face="Times New Roman">http</font></span><span>服务器陷阱。首先，先来看下面一系列同一个请求，但是不同抓包工具抓取的内容的截图：</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span><font face="Times New Roman"><span style="font-size: 10.5pt; mso-bidi-font-size: 12.0pt"><img class="blogimg" src="http://hiphotos.baidu.com/yancncen/pic/item/96ea56a7a51202bed043587f.jpg" border="0" small="0"  alt="" /></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>图</span><span><font face="Times New Roman">1.</font></span><span>使用</span><span><font face="Times New Roman">httpwatch</font></span><span>抓包获取的内容</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span><font face="Times New Roman"><img class="blogimg" src="http://hiphotos.baidu.com/yancncen/pic/item/d68434a4883cc3dc9152ee7e.jpg" border="0" small="0"  alt="" /></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>图</span><span><font face="Times New Roman">2.</font></span><span>使用</span><span><font face="Times New Roman">sniffer</font></span><span>抓包获取的内容</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span><font face="Times New Roman"></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span>其中，两者是同一个请求，从</span><span><font face="Times New Roman">sniffer</font></span><span>抓包内容看、浏览器和</span><span><font face="Times New Roman">server</font></span><span>已经完成了交互过程。但是浏览器</span><span><font face="Times New Roman">(</font></span><span>包括</span><span><font face="Times New Roman">ie</font></span><span>、</span><span><font face="Times New Roman">firefox</font></span><span>、</span><span><font face="Times New Roman">chrome)</font></span><span>的</span><span><font face="Times New Roman">Result</font></span><span>却是</span><span><font face="Times New Roman">ERROR_INTERNET_CONNECTION_RESET</font></span><span>，这是为什么呢？</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>通过走读</span><span><font face="Times New Roman">server</font></span><span>代码，发现</span><span><font face="Times New Roman">server</font></span><span>做了这样一个逻辑，在有请求过来的时候只读取了</span><span><font face="Times New Roman">100</font></span><span>个字节，因为</span><span><font face="Times New Roman">server</font></span><span>认为这</span><span><font face="Times New Roman">100</font></span><span>个字节足够判断客户端的请求行为</span><span><font face="Times New Roman">(GET /HTTP/1.0 </font></span><span>少于</span><span><font face="Times New Roman">100</font></span><span>个字节</span><span><font face="Times New Roman">)</font></span><span>，而其它请求信息</span><span><font face="Times New Roman">(</font></span><span>包括</span><span><font face="Times New Roman">cookie</font></span><span>等等</span><span><font face="Times New Roman">)</font></span><span>是无用的，然后返回</span><span><font face="Times New Roman">response</font></span><span>信息，然后关闭连接。流程如下：</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span><font face="Times New Roman">1)</font></span><span>读取</span><span><font face="Times New Roman">100</font></span><span>个字节</span><font face="Times New Roman"> <span>=&gt; 2)</span></font><span>返回</span><span><font face="Times New Roman">response</font></span><span>信息</span><span><font face="Times New Roman"> =&gt; 3)</font></span><span>关闭</span><span><font face="Times New Roman">socket</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span><font face="Times New Roman"></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>当客户端请求的量不大的时候</span><span><font face="Times New Roman">(</font></span><span>内网开发机并发请求量</span><span><font face="Times New Roman">&lt;100)</font></span><span>、很容易出现上文所述</span><span><font face="Times New Roman">http</font></span><span>服务器陷阱的问题。当客户端请求的量很大的时候，出现上述问题的几率倒不大，但是偶尔也会出现。分析原因如下：</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>当并发量很少的时候，</span><span><font face="Times New Roman">server</font></span><span>读取了</span><span><font face="Times New Roman">100</font></span><span>个字节并返回</span><span><font face="Times New Roman">response</font></span><span>后，马上关闭连接。由于并发量少，</span><span><font face="Times New Roman">server</font></span><span>处理速度很快，这样很快就会关闭了连接。但是，此时客户端</span><span><font face="Times New Roman">(</font></span><span>浏览器</span><span><font face="Times New Roman">)</font></span><span>也许还有一些数据</span><span><font face="Times New Roman">(</font></span><span>例如</span><span><font face="Times New Roman">cookie</font></span><span>信息等</span><span><font face="Times New Roman">)</font></span><span>并没有真正发送到</span><span><font face="Times New Roman">server</font></span><span>的内核缓冲区。于是，浏览器继续把剩余的数据试图发送到</span><span><font face="Times New Roman">server</font></span><span>，但是，此时发现</span><span><font face="Times New Roman">server</font></span><span>已经关闭了连接，就会引发</span><span><font face="Times New Roman">ERROR_INTERNET_CONNECTION_RESET</font></span><span>错误。虽然用</span><span><font face="Times New Roman">sniffer</font></span><span>抓包发现实际上整个交互过程已经完成，但是浏览器缺认为这是一个致命的错误，获取到的返回数据并没有正确的显示在浏览器上。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>当并发量很大的时候，虽然</span><span><font face="Times New Roman">server</font></span><span>只读取了</span><span><font face="Times New Roman">100</font></span><span>个字节，但是由于服务器的处理能力有限，从读取了</span><span><font face="Times New Roman">100</font></span><span>字节到关闭连接的过程有一定的时间差。客户端可以在这个时间差的时间内把剩余的数据发送到了</span><span><font face="Times New Roman">server</font></span><span>内核缓冲区</span><span><font face="Times New Roman">(</font></span><span>虽然应用程序没有读取这部分数据</span><span><font face="Times New Roman">)</font></span><span>。因此，出现</span><span><font face="Times New Roman">ERROR_INTERNET_CONNECTION_RESET</font></span><span>错误的概率就会变小。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span>总结，在实现短连接的</span><span><font face="Times New Roman">http</font></span><span>服务器</span><span><font face="Times New Roman">(</font></span><span>尤其是提供给浏览器使用的服务器</span><span><font face="Times New Roman">)</font></span><span>时，尽量把</span><span><font face="Times New Roman">http header</font></span><span>数据包读取完整，不要因为只需要一小部分</span><span><font face="Times New Roman">header</font></span><span>信息就能处理请求而不读取其它的头部信息，以此来加快处理速度或者节省空间。这往往适得其反，可能会引起一些列不可预料的问题。</span></p>
<span style="font-size: 10.5pt; mso-font-kerning: 1.0pt">由此，引发另一个思考，既然不可避免的需要读取完整的</span><span style="font-size: 10.5pt; mso-bidi-font-size: 12.0pt">http header</span><span style="font-size: 10.5pt; mso-font-kerning: 1.0pt">数据包，那么，浏览器请求的时候，应该尽量保持</span><span style="font-size: 10.5pt; mso-bidi-font-size: 12.0pt">http header</span><span style="font-size: 10.5pt; mso-font-kerning: 1.0pt">不要过于庞大，既是为了减缓</span><span style="font-size: 10.5pt; mso-bidi-font-size: 12.0pt">server</span><span style="font-size: 10.5pt; mso-font-kerning: 1.0pt">的压力也是为了加快</span><span style="font-size: 10.5pt; mso-bidi-font-size: 12.0pt">server</span><span style="font-size: 10.5pt; mso-font-kerning: 1.0pt">的处理速度。而减少</span><span style="font-size: 10.5pt; mso-bidi-font-size: 12.0pt">http header</span><span style="font-size: 10.5pt; mso-font-kerning: 1.0pt">长度的最好方法就是减少</span><span style="font-size: 10.5pt; mso-bidi-font-size: 12.0pt">cookie</span><span style="font-size: 10.5pt; mso-font-kerning: 1.0pt">的内容。</span> </div>
<div id="appShareUrl" style="clear: both">
<div id="appShareSafeLinkTip" style="display: none">
<div class="appShareErrorIcon"></div>
为了您的安全，请只打开来源可靠的网址
<p><a href="http://hi.baidu.com/yancncen/blog/item/2b79c4a2c8e83ba7cbefd091.html" target="_blank">打开网站</a>&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://apps.hi.baidu.com/share/detail/16904056#">取消</a></p>
</div>
来自: <a href="http://hi.baidu.com/yancncen/blog/item/2b79c4a2c8e83ba7cbefd091.html" target="_blank">http://hi.baidu.com/yancncen/blog/item/2b79c4a2c8e83ba7cbefd091.html</a> </div>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/350112.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2011-05-12 19:26 <a href="http://www.blogjava.net/xiaomage234/archive/2011/05/12/350112.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于seo的那些事，搜索引擎最佳化[转]</title><link>http://www.blogjava.net/xiaomage234/archive/2010/11/11/337830.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 11 Nov 2010 09:07:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2010/11/11/337830.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/337830.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2010/11/11/337830.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/337830.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/337830.html</trackback:ping><description><![CDATA[前段时间帮同学做了网站，最后发现流量不怎么好，可能当时写程序的时候也没有注意到一些网页的细节问题，在网上看看了下seo方面的东西，<br />
最近发现在效果不错。分享给大家.<br />
&nbsp; &nbsp; SEO中文名：搜索引擎最佳化（Search Engine Optimization），又称为搜索引擎优化，为近年来较为流行的网络营销方式，主要的目的是增加特定关键字的曝光率以增加网站的能见度，进而增加销售的机会。<br />
&nbsp;&nbsp;&nbsp; SEO的主要工作是通过了解各类搜索引擎如何抓取互联网页面、如何进行索引以及如何确定其对某一特定关键词的搜索结果排名等技术，即是遵循搜索引擎全面而理论的机制，对网站结构、网页文字语言和站点间的互动外交策略等进行合理规划部署来对网页进行相关的优化，使其提高搜索引擎排名，从而提高网站访问量，最终提升网站的销售能力或宣传能力的技术。<br />
&nbsp; 做好seo,先要明白几个相关的术语。<br />
&nbsp; PR值：PR值全称为PageRank(网页级别)， PageRank是Google衡量网站页面的重性以及Google排名的一个重要指标（PR值只对Google起作用），测量值范围为从1至10分别表示某网站页面的重要性。个人认为个人站点做到大于4等于4就已经很不错的了。网站查询：http://pr.chinaz.com/<br />
&nbsp;&nbsp; SEO收录和外链<br />
&nbsp;&nbsp;&nbsp; 通过site查询网站被收录情况，做好相关工作，百度通过domain命令，谷歌通过link命令。个人认为使用谷歌管理员工具查询更准。谷歌为了避免透露&nbsp;&nbsp; 网站外部链接、恶性竞争，把很多外链都屏蔽了!这样做的目的也是为了保护网站的隐私 <br />
&nbsp;白帽：保证用户体验的同时，灵活的运用搜索引擎当中的一些技术规则对网站的结构、文字、权重以及外部链接做出合理部署安排，使用符合主流搜索引擎发行方针规定的seo优化方法。一般有1.结构优化。2.关键词优化。3.外部链接优化。4.内部连接优化。<br />
&nbsp;&nbsp; 黑帽:注重的是短期内的利益,通过作弊手法获得很大的利益.1.伪装.2.关键词堆积.3.欺骗性重定向.4.伪造流量.4.连接工厂,特别是好多网站被黑链控制着，大家在百度一下黑链，可以发现好多卖这方面的。在这里我曾经看过好多政府网站都被挂过。】<br />
&nbsp;&nbsp; 标题title，这个是非常重要的，不要太长，准确规范，主题明确,简明精练,最好在20个字内搞定.主关键词+长尾关键词_栏目名称_公司名称，公司名称可以换成什么**网，大家有看看sina网，我觉的做的很好。<br />
&nbsp;&nbsp; 关键词:keywords，搜索引擎的搜索框中输入的任何文字，也就是您命令搜索引擎寻找的东西.这个要突出中心，特别是主页，可以把产品关键词语写在里面，用,隔开,也不要太多，10个左右，个人感觉。<br />
&nbsp;&nbsp; 描述 description以简洁的语句概括当前页面的主要内容,一定要涵盖主要关键词最好能出现一到两次.<br />
&nbsp;&nbsp; 网页结构优化和内链建设:<br />
&nbsp;&nbsp; 1、减少目录层次<br />
网站扁平化是网站结构优化的第一步。搜索引擎蜘蛛和浏览者都不希望他所需要的内容被隐藏的很深，需要经过多次往返寻找才能到达。<br />
建议：大型网站，目录结构控制在3～4以下<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.中型网站，目录严格控制在3以下<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.小型网站，目录严格控制在2以下<br />
&nbsp;&nbsp;&nbsp; 2 、严格控制动态网页的参数个数<br />
在google已公布的资料中，Google明确指出动态参数小于3的动态网页，蜘蛛可以顺利抓取，达到3个参数的页面，蜘蛛将有选择的进入，而超过3～4个参数的动态页面，Google将基本不予理睬。<br />
a、防止蜘蛛被死循环套牢。<br />
b、参数越多，对应的动态页越多，可能超过蜘蛛的荷载。<br />
c、参数多，则参数失效的几率越大。<br />
d、参数多的站点存在更大的漏洞风险<br />
&nbsp;&nbsp;&nbsp; 采用CSS+DIV进行网页重构<br />
&nbsp;&nbsp; 1.表现和内容相分离<br />
&nbsp;将设计部分剥离出来放在一个独立样式文件中，HTML文件中只存放文本信息。<br />
&nbsp;&nbsp;&nbsp; 2.提高搜索引擎对网页的索引效率<br />
&nbsp;用只包含结构化内容的HTML代替嵌套的标签，搜索引擎将更有效地搜索到你的网页内容，并可能给你一个较高的评价。<br />
&nbsp;&nbsp;&nbsp; 3.提高页面浏览速度<br />
对于同一个页面视觉效果，采用CSS+DIV重构的页面容量要比TABLE编码的页面文件容量小得多，前者一般只有后者的1/2大小。<br />
&nbsp;&nbsp;&nbsp; 4.易于维护和改版<br />
你只要简单的修改CSS文件就可以重新设计整个网站的页面。&nbsp;&nbsp; <span style="font-weight: bold; color: black; font-family: 宋体"><br />
&nbsp;&nbsp; 内容</span><span style="font-weight: bold; color: black; font-family: 宋体">总结:</span><span style="color: black; font-family: Arial"> </span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.与网站主题相关的内容。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.独特的内容，原创内容。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.页面内容少用flash，尽量不要用iframe或者js渲染。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.图片，还有链接alt , title加上。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.页面之间链接伪静态。尽量UrlRewriter。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.保证网站的更新东西，把爬虫喂饱。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.好的网站做友情链接。不要以被K过站的，还有违法的做链接。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8.最好让你的网页有别的网站转载，适当可以炒作一下。<br />
比如大家喜欢上的javaeye,大家搜索java很多东西都会被搜索到这个网站上。<br />
做好以上几点，提高PR值，循环持进，任重道远<br />
<p style="margin-top: 0pt; margin-bottom: 0pt; vertical-align: baseline; direction: ltr; unicode-bidi: embed; text-align: left"><span style="color: black; font-family: Arial">&nbsp;&nbsp;&nbsp; SEO</span><span style="color: black; font-family: 宋体">的将来：</span></p>
<p style="margin-top: 0pt; margin-bottom: 0pt; vertical-align: baseline; direction: ltr; unicode-bidi: embed; text-align: left"><span style="color: black; font-family: 宋体">&nbsp;&nbsp; 1、慢慢摸索每个网站搜索特性。<br />
&nbsp;&nbsp; 2.想学好就不太容易。有很多人都会碰到这样的疑问，关于排名的因素我知道已经很多了，但自己操作起来往往达不到理想的要求。<br />
&nbsp;&nbsp; 3.细节决定一切。SEO想做好，靠的不是众多理论知识的熏陶，而是日常工作当中点点滴滴的细节的积累。<br />
</span></p>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/337830.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2010-11-11 17:07 <a href="http://www.blogjava.net/xiaomage234/archive/2010/11/11/337830.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>轻松获得网通、电信、铁通IP地址分配段[转]</title><link>http://www.blogjava.net/xiaomage234/archive/2009/11/11/301933.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Wed, 11 Nov 2009 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2009/11/11/301933.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/301933.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2009/11/11/301933.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/301933.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/301933.html</trackback:ping><description><![CDATA[APNIC是管理亚太地区IP地址分配的机构 ，它有着丰富准确的IP地址分配库，同时这些信息也是对外公开的！下面就让我们看看如何在Linux下获得一些电信运营商的IP地址分配情况：
<div class="hl-surround">
<div class="hl-main">shell&gt; wget http://ftp.apnic.net/apnic/dbase/tools/ripe-dbase-client-v3.tar.gz<br />
shell&gt; tar xzvf ripe-dbase-client-v3.tar.gz <br />
shell&gt; cd whois-3.1 <br />
shell&gt; ./configure<br />
shell&gt; make</div>
</div>
<p>完成上述编译安装工作后，我们开始获取IP地址段；</p>
<div class="hl-surround">
<div class="hl-main">　　中国网通：shell&gt; ./whois3 -h whois.apnic.net -l -i mb MAINT-CNCGROUP &gt; /var/cnc<br />
<br />
　　中国电信：shell&gt; ./whois3 -h whois.apnic.net -l -i mb MAINT-CHINANET &gt; /var/chinanet<br />
<br />
　　中国铁通：shell&gt; ./whois3 -h whois.apnic.net -l -i mb MAINT-CN-CRTC &gt; /var/crtc</div>
</div>
<p>　　打开获取后的文件可以看到里面的信息非常详细，甚至可以看到各个分公司的负责人、电话、电子邮件等等信息。如果想得到一份整齐干净的IP地址段文件，只要用grep和awk简单过滤就可以了。</p>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/301933.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2009-11-11 10:11 <a href="http://www.blogjava.net/xiaomage234/archive/2009/11/11/301933.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>交换机和路由器的实现原理</title><link>http://www.blogjava.net/xiaomage234/archive/2009/03/02/257391.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 02 Mar 2009 10:05:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2009/03/02/257391.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/257391.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2009/03/02/257391.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/257391.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/257391.html</trackback:ping><description><![CDATA[1.&nbsp;<wbr>请问交换机和路由器分别的实现原理是什么？分别在哪个层次上面实现的？
<p>将网络互相连接起来要使用一些中间设备（或中间系统），ＩＳＯ的术语称之为中继（re<br />
lay）系统。根据中继系统所在的层次，可以有以下五种中继系统：<br />
1)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 物理层（即常说的第一层、层Ｌ1）中继系统，即转发器（repeater）。<br />
2)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 数据链路层（即第二层，层Ｌ2），即网桥或桥接器（bridge）。<br />
3)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 网络层（第三层，层Ｌ3）中继系统，即路由器（router）。<br />
4)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 网桥和路由器的混合物桥路器（brouter）兼有网桥和路由器的功能。<br />
5)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 在网络层以上的中继系统，即网关（gateway）.<br />
当中继系统是转发器时，一般不称之为网络互联，因为这仅仅是把一个网络扩大了，而这<br />
仍然是一个网络。高层网关由于比较复杂，目前使用得较少。因此一般讨论网络互连时都<br />
是指用交换机和路由器进行互联的网络。本文主要阐述交换机和路由器及其区别。&nbsp;<wbr>&nbsp;<wbr><br />
2.&nbsp;<wbr>&nbsp;<wbr>第二层交换机和路由器的区别：<br />
传统交换机从网桥发展而来，属于ＯＳＩ第二层即数据链路层设备。它根据ＭＡＣ地址寻<br />
址，通过站表选择路由，站表的建立和维护由交换机自动进行。路由器属于ＯＳＩ第三层<br />
即网络层设备，它根据ＩＰ地址进行寻址，通过路由表路由协议产生。因特网的路由选择<br />
协议：内部网关协议IGP和外部网关协议EGP<br />
3.&nbsp;<wbr>第三层交换机和路由器的区别：<br />
在第三层交换技术出现之前，几乎没有必要将路由功能器件和路由器区别开来，他们完全<br />
是相同的：提供路由功能正在路由器的工作，然而，现在第三层交换机完全能够执行传统<br />
路由器的大多数功能。<br />
综上所述，交换机一般用于ＬＡＮ－ＷＡＮ的连接，交换机归于网桥，是数据链路层的设<br />
备，有些交换机也可实现第三层的交换。路由器用于ＷＡＮ－ＷＡＮ之间的连接，可以解<br />
决异性网络之间转发分组，作用于网络层。他们只是从一条线路上接受输入分组，然后向<br />
另一条线路转发。这两条线路可能分属于不同的网络，并采用不同协议。相比较而言，路<br />
由器的功能较交换机要强大，但速度相对也慢，价格昂贵，第三层交换机既有交换机线速<br />
转发报文能力，又有路由器良好的控制功能，因此得以广播应用。</p>
<p>----------------------------------------------------------------------</p>
<p>2.1&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OSI和TCP/IP<br />
1.&nbsp;<wbr>&nbsp;<wbr>七层网络结构功能及特点--OSI<br />
1)&nbsp;<wbr>&nbsp;<wbr> 物理层：为数据链路层提供物理连接，在其上串行传送比特流，即所传送数据的单位<br />
是比特。此外，该层中还具有确定连接设备的电气特性和物理特性等功能。<br />
2)&nbsp;<wbr>&nbsp;<wbr> 数据链路层：负责在网络节点间的线路上通过检测、流量控制和重发等手段，无差错<br />
地传送以帧为单位的数据。为做到这一点，在每一帧中必须同时带有同步、地址、差错控<br />
制及流量控制等控制信息。<br />
3)&nbsp;<wbr>&nbsp;<wbr> 网络层：为了将数据分组从源（源端系统）送到目的地（目标端系统），网络层的任<br />
务就是选择合适的路由和交换节点，使源的传输层传下来的分组信息能够正确无误地按照<br />
地址找到目的地，并交付给相应的传输层，即完成网络的寻址功能。<br />
4)&nbsp;<wbr>&nbsp;<wbr> 传输层：传输层是高低层之间衔接的接口层。数据传输的单位是报文，当报文较长时<br />
将它分割成若干分组,然后交给网络层进行传输。传输层是计算机网络协议分层中的最关键<br />
一层，该层以上各层将不再管理信息传输问题。<br />
5)&nbsp;<wbr>&nbsp;<wbr> 会话层：该层对传输的报文提供同步管理服务。在两个不同系统的互相通信的应用进<br />
程之间建立、组织和协调交互。例如，确定是双工还是半双工工作。<br />
6)&nbsp;<wbr>&nbsp;<wbr> 表示层：该层的主要任务是把所传送的数据的抽象语法变换为传送语法，即把不同计<br />
算机内部的不同表示形式转换成网络通信中的标准表示形式。此外，对传送的数据加密（<br />
或解密）、正文压缩（或还原）也是表示层的任务。<br />
7)&nbsp;<wbr>&nbsp;<wbr> 应用层：该层直接面向用户，是OSI中的最高层。它的主要任务是为用户提供应用的<br />
接口，即提供不同计算机间的文件传送、访问与管理，电子邮件的内容处理，不同计算机<br />
通过网络交互访问的虚拟终端功能等。<br />
2.&nbsp;<wbr>TCP/IP功能及特点<br />
1)&nbsp;<wbr>&nbsp;<wbr> 网络接口层：这是TCP/IP协议的最低一层，包括有多种逻辑链路控制和媒体访问协议<br />
。网络接口层的功能是接收IP数据报并通过特定的网络进行传输，或从网络上接收物理帧<br />
，抽取出IP数据报并转交给网际层。<br />
2)&nbsp;<wbr>&nbsp;<wbr> 网际网层（IP层）：该层包括以下协议：IP（网际协议）、ICMP（Internet Contro<br />
l Message Protocol,因特网控制报文协议）、ARP（Address Resolution Protocol，地址<br />
解析协议）、RARP（Reverse Address Resolution Protocol，反向地址解析协议）。该层<br />
负责相同或不同网络中计算机之间的通信,主要处理数据报和路由。在IP层中，ARP协议用<br />
于将IP地址转换成物理地址,RARP协议用于将物理地址转换成IP地址，ICMP协议用于报告差<br />
错和传送控制信息。IP协议在TCP/IP协议组中处于核心地位。<br />
3)&nbsp;<wbr>&nbsp;<wbr> 传输层：该层提供TCP（传输控制协议）和UDP（User Datagram Protocol，用户数据<br />
报协议）两个协议，它们都建立在IP协议的基础上，其中TCP提供可靠的面向连接服务，U<br />
DP提供简单的无连接服务。传输层提供端到端，即应用程序之间的通信，主要功能是数据<br />
格式化、数据确认和丢失重传等。<br />
4)&nbsp;<wbr>&nbsp;<wbr> 应用层：TCP/IP协议的应用层相当于OSI模型的会话层、表示层和应用层，它向用户<br />
提供一组常用的应用层协议,其中包括：Telnet、SMTP、DNS等。此外，在应用层中还包含<br />
有用户应用程序，它们均是建立在TCP/IP协议组之上的专用程序。<br />
3.&nbsp;<wbr>OSI参考模型和TCP/IP参考模型的区别：<br />
1)&nbsp;<wbr>&nbsp;<wbr> OSI模型有7层，TCP/IP只有4层；<br />
2)&nbsp;<wbr>&nbsp;<wbr> OSI先于协议出现，因此不会偏向于任何一组特定的协议，通用性更强，但有些功能<br />
不知该放哪一层上，因此不得不加入一些子层；TCP/IP后于协议出现，仅是将已有协议的<br />
一个描述，因此两者配合的非常好；但他不适合其他的协议栈，不容易描述其他非TCP/IP<br />
的网络；<br />
3)&nbsp;<wbr>&nbsp;<wbr> OSI中网络层同时支持无连接和面向连接的通信，但在传输层上只支持面向连接的通<br />
信；TCP/IP中网络层只支持无连接通信，传输层同时支持两种通信；<br />
4)&nbsp;<wbr>&nbsp;<wbr> 在技术发生变化时，OSI模型比TCP/IP模型中的协议更容易被替换。<br />
4.&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 请你详细的解释一下IP协议的定义，在哪个层上面，主要有什么作用？ TCP与<br />
UDP呢？　<br />
解：与IP协议配套使用的还有三个协议:<br />
ARP-地址解析协议<br />
RARP－逆地址解析协议<br />
ICMP－因特网控制报文协议ICMP<br />
IP协议－网际协议<br />
IP地址、IP包头</p>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/257391.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2009-03-02 18:05 <a href="http://www.blogjava.net/xiaomage234/archive/2009/03/02/257391.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>