﻿<?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>C++博客-to myself 的分类学习日志-随笔分类-network programming</title><link>http://www.cppblog.com/toMyself/category/9701.html</link><description>做自己想做的事</description><language>zh-cn</language><lastBuildDate>Sun, 20 Nov 2011 16:35:48 GMT</lastBuildDate><pubDate>Sun, 20 Nov 2011 16:35:48 GMT</pubDate><ttl>60</ttl><item><title>protobuf的使用</title><link>http://www.cppblog.com/toMyself/archive/2010/12/02/135268.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Thu, 02 Dec 2010 06:21:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/12/02/135268.html</guid><wfw:comment>http://www.cppblog.com/toMyself/comments/135268.html</wfw:comment><comments>http://www.cppblog.com/toMyself/archive/2010/12/02/135268.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/toMyself/comments/commentRss/135268.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/toMyself/services/trackbacks/135268.html</trackback:ping><description><![CDATA[<h1 style="color: #000000;" class="ha">使用protobuf<wbr>定义消息</h1>
下载protobuf-2.3.0：<br>&nbsp;&nbsp;&nbsp; <a href="http://protobuf.googlecode.com/files/protobuf-2.3.0.zip" target="_blank">http://protobuf.googlecode.<wbr>com/files/protobuf-2.3.0.zip</a><br>安装： <br>unzip protobuf-2.3.0.zip<br>
cd protobuf-2.3.0<br>./configure<br>make <br>make check <br>make install<br><br>结果：<br>Libraries have been installed in:<br>&nbsp;&nbsp; /usr/local/lib<br>Head files hava been installed in:<br>/usr/local/include/google/
<div id=":2g"><wbr>protobuf/<br>
<br><br>开始写.proto文件：<br><strong>BaseMessage.proto：</strong><br>message MessageBase<br style="background-color: #c0c0c0;">{<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; required int32 opcode = 1;<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; // other: sendMgrId, sendId, recvMgrId, recvId, ...<br style="background-color: #c0c0c0;">
}<br style="background-color: #c0c0c0;"><br style="background-color: #c0c0c0;">message BaseMessage<br style="background-color: #c0c0c0;">
{<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; required MessageBase msgbase = 1;<br style="background-color: #c0c0c0;">
}<br><br>BaseMessage.<wbr>proto是其它消息proto文件的基础，<wbr>以容器模块的C2S_GetContainerInfo为例：<br><strong>ContainerMessage.proto：</strong><br>import "BaseMessage.proto";<br style="background-color: #c0c0c0;">
<br style="background-color: #c0c0c0;">message C2SGetContainerInfoMsg<br style="background-color: #c0c0c0;">{<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; required MessageBase msgbase = 1;<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; optional int32 containerType = 2;<br style="background-color: #c0c0c0;">
}<br><br><strong>.proto文件编写规则</strong>：<br>1）所有消息都需要包含msgbase这项，并编号都为1，即：<br>&nbsp; required MessageBase msgbase = 1;<br>2）除了msgbase这项写成required外，<wbr>其它所有项都写成optional。<br><br>编译 .proto 文件<br>
protoc -I=. --cpp_out=. BaseMessage.proto<br>protoc -I=. --cpp_out=. ContainerMessage.proto<br>生成BaseMessage.pb.h、<a href="http://basemessage.pb.cc/" target="_blank">BaseMessage<wbr>.pb.cc</a><br>&nbsp;&nbsp;&nbsp; ContainerMessage.pb.h、<a href="http://containermessage.pb.cc/" target="_blank">Containe<wbr>rMessage.pb.cc</a><br>
将它们添加到工程文件中。<br><br>编写C++代码：<br>1）发送消息：<br>C2SGetContainerInfoMsg msg;<br style="background-color: #c0c0c0;">msg.mutable_msgbase()-&gt;set_<wbr>opcode(C2S_GetContainerInfo);<br style="background-color: #c0c0c0;">
msg.set_containertype(1);<br style="background-color: #c0c0c0;">std::string out = msg.SerializeAsString();<br style="background-color: #c0c0c0;">
send(sockfd, out.c_str(), out.size(), 0);<br>2）接收消息<br>char buf[MAXBUF + 1];<br style="background-color: #c0c0c0;">
int len;<br style="background-color: #c0c0c0;">bzero(buf, MAXBUF + 1);<br style="background-color: #c0c0c0;">
len = recv(new_fd, buf, MAXBUF, 0);<br style="background-color: #c0c0c0;">if (len &gt; 0)<br style="background-color: #c0c0c0;">
{<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; printf("%d接收消息成功:'%s'，共%<wbr>d个字节的数据\n",<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new_fd, buf, len);<br style="background-color: #c0c0c0;"><br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; BaseMessage baseMsg;<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; std::string data = buf;<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; baseMsg.ParseFromString(data);<br style="background-color: #c0c0c0;">
<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; int opcode = baseMsg.mutable_msgbase()-&gt;<wbr>opcode();<br style="background-color: #c0c0c0;"><br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; printf("opcode=%d\n", opcode);<br style="background-color: #c0c0c0;"><br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; switch (opcode)<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; {<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; case C2S_GetContainerInfo:<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; {<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; C2SGetContainerInfoMsg msg;<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; msg.ParseFromString(data);<br style="background-color: #c0c0c0;"><br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; printf("containerType=%d\n", msg.containertype());<br style="background-color: #c0c0c0;">
<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; break;<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; }<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; default:<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; {<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; break;<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; }<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; }<br style="background-color: #c0c0c0;">}<br style="background-color: #c0c0c0;">
else<br style="background-color: #c0c0c0;">{<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; if (len &lt; 0)<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("消息接收失败！错误代码是%d，错误信息是'%<wbr>s'\n",<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errno, strerror(errno));<br style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; close(new_fd);<br style="background-color: #c0c0c0;">
&nbsp;&nbsp;&nbsp; return -1;<br style="background-color: #c0c0c0;">}<br><br><br>编译C++代码：<br>Need to link lib:<br>
protobuf<br>pthread<br><br><br>参考： <br>1， Google Protocol Buffer 的使用和原理: <a href="http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html?ca=drs-" target="_blank">http://www.ibm.com/<wbr>developerworks/cn/linux/l-cn-<wbr>gpb/index.html?ca=drs-</a><br>
2，<a href="http://code.google.com/p/protobuf/" target="_blank">http://code.google.com/p/<wbr>protobuf/</a><br></div>
<br><img src ="http://www.cppblog.com/toMyself/aggbug/135268.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-12-02 14:21 <a href="http://www.cppblog.com/toMyself/archive/2010/12/02/135268.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络数据格式</title><link>http://www.cppblog.com/toMyself/archive/2010/09/30/128125.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Thu, 30 Sep 2010 06:16:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/09/30/128125.html</guid><wfw:comment>http://www.cppblog.com/toMyself/comments/128125.html</wfw:comment><comments>http://www.cppblog.com/toMyself/archive/2010/09/30/128125.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/toMyself/comments/commentRss/128125.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/toMyself/services/trackbacks/128125.html</trackback:ping><description><![CDATA[<p>第一种类型,二进制格式的网络数据包,通常要首先接收包头,在包头中有校验数据校验获取的数据是否正确,同时包头中还有数据域存放接下来的内容域的大小,得到该大小之后开始接收内容包,然后对内容包进行解析,包头的大小是固定的,否则无法知道何时接收包头完毕进行解析.</p>
<p>第二种类型,XML格式组织的数据包,通常以连续几个\r\n之类的字符表示结束,在接收包的时候无法知道所要获取数据包的大小,只有每次判断时候已经接收到了表示结束的字符.</p>
<p><strong>两种传送数据包优缺点比较:<br></strong>1)网络传送效率比较:第一种的优点是接收数据包的效率高,首先按照包头的数据大小接收包头可以获知内容包的大小,再按照此大小获取数据包;而第二种数据包无法在接收的时候获取该数据包的大小,只能在每次接收的时候判断时候已经到达包的结尾,因此相比较而言第一种格式的数据包在网络传送效率上高一些.同时,由于第一种格式可以在包头中加入一些校验字段判断包是否合法,在数据校验这一块也具有优势.</p>
<p>2)解析数据包:第一种数据包没有固定的格式,或者准确的说没有固定的解析器用于解析这种格式的数据,因为每个人定出的协议都不尽相同;而第二种数据包有完备的解析XML格式数据的第三方库可用(libxml2,tinyxml,expat等),但是并不见得有了第三方的库解析起数据起来效率就一定高(这里指的是程序的效率,而不是编码的效率),因为XML解析比普通的数据解析要复杂的多,效率也就更加慢一些.</p>
<p>3)可扩展性:第一种数据包的格式不同,可扩展性也不尽相同,具体与每种格式的包有区别.第二种格式的数据包由于采用了XML格式,天正的具备很好的可扩展性.</p>
<p>4)数据安全性:第一种格式的数据包可以方便的实现数据的加密,而XML格式的数据实现加密不容易,基本上抓包就能看到数据.</p>
<p>综上，个人认为XML格式的数据包仅在可扩展性上有较大的优势，但是对于安全性，性能要求不太高而扩展性要求较大的协议还是建议使用XML格式的协议，毕竟如果协议制定的不好造成扩展性差也是麻烦的事情，因为客户端一旦放出去就收不回来的。目前jabber的通讯协议就是采用的XML格式的协议。</p>
<p>来自：<a href="http://www.cppblog.com/converse/archive/2008/03/26/45472.html"><u><font color=#810081>http://www.cppblog.com/converse/archive/2008/03/26/45472.html</font></u></a></p>
<br><br><br>&nbsp;
<p><span>&nbsp; </p>
<h2><a name=_Toc266438134></a><a name=_Toc266091190></a><a name=_Toc266091106></a><a name=_Toc266091094></a><a name=_Toc266090989></a><a name=_Toc266090876></a><a name=_Toc266089819></a><a name=_Toc266088725><span><span><span><span><span><span><span><span><span>一、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>json</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span><span><span><span>介绍</span></span></span></span></span></span></span></span></span></h2>
<p><strong><span>JSON</span></strong><span>(JavaScript Object Notation) </span><span>是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。</span><span>它基于</span><span><a href="http://www.crockford.com/javascript"><span>JavaScript Programming Language</span></a></span><span>, </span><span><a href="http://www.ecma-international.org/publications/files/ecma-st/ECMA-262.pdf"><span>Standard ECMA-262 3rd Edition - December 1999</span></a></span><span>的一个子集。<span> JS</span></span><span>ON</span><span>采用完全独立于语言的文本格式，但是也使用了类似于<span>C</span>语言家族的习惯（包括<span>C, C++, C#, Java, JavaScript, Perl, Python</span>等）。这些特性使<span>JSON</span>成为理想的数据交换语言。</span></p>
<p><span>JSON</span><span>建构于两种结构：</span></p>
<ul type=disc>
    <li><span>&#8220;</span><span>名称<span>/</span>值<span>&#8221;</span>对的集合（<span>A collection of name/value pairs</span>）。不同的语言中，它被理解为<em>对象（<span>object</span>）</em>，纪录（<span>record</span>），结构（<span>struct</span>），字典（<span>dictionary</span>），哈希表（<span>hash table</span>），有键列表（<span>keyed list</span>），或者关联数组 （<span>associative array</span>）。 </span></li>
    <li><span>值的有序列表（<span>An ordered list of values</span>）。在大部分语言中，它被理解为数组（<span>array</span>）。 </span></li>
</ul>
<p><span>这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。</span></p>
<p><span>JSON</span><span>具有以下这些形式：</span></p>
<p><span>对象是一个无序的<span>&#8220;&#8216;</span>名称<span>/</span>值<span>&#8217;</span>对<span>&#8221;</span>集合。一个对象以<span>&#8220;{&#8221;</span>（左括号）开始，<span>&#8220;}&#8221;</span>（右括号）结束。每个<span>&#8220;</span>名称<span>&#8221;</span>后跟一个<span>&#8220;:&#8221;</span>（冒号）；<span>&#8220;&#8216;</span>名称<span>/</span>值<span>&#8217; </span>对<span>&#8221;</span>之间使用<span>&#8220;,&#8221;</span>（逗号）分隔。 </span></p>
<p>&#160;</p>
<p><span>数组是值（<span>value</span>）的有序集合。一个数组以<span>&#8220;[&#8221;</span>（左中括号）开始，<span>&#8220;]&#8221;</span>（右中括号）结束。值之间使用<span>&#8220;,&#8221;</span>（逗号）分隔。</span></p>
<p>&#160;</p>
<p><span>值（<em><span>value</span></em>）可以是双引号括起来的字符串（<em><span>string</span></em>）、数值<span>(number)</span>、</span><span>true</span><span>、</span><span>false</span><span>、 </span><span>null</span><span>、对象（<span>object</span>）或者数组（<span>array</span>）。这些结构可以嵌套。 </span></p>
<p>&#160;</p>
<p><span>字符串（<em><span>string</span></em>）是由双引号包围的任意数量<span>Unicode</span>字符的集合，使用反斜线转义。一个字符（<span>character</span>）即一个单独的字符串（<span>character string</span>）。 </span></p>
<p><span>字符串（<em><span>string</span></em>）与<span>C</span>或者<span>Java</span>的字符串非常相似。 </span></p>
<p>&#160;</p>
<p><span>数值（<em><span>number</span></em>）也与<span>C</span>或者<span>Java</span>的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。</span></p>
<p>&#160;</p>
<p><span>空白可以加入到任何符号之间。</span></p>
<p><span>参考：</span></p>
<p><span><span>1、<span>&nbsp;&nbsp; </span></span></span><span><a href="http://www.json.org/">http://www.json.org/</a></span></p>
<p><span><span>2、<span>&nbsp;&nbsp; </span></span></span><span><a href="http://www.json.org/json-zh.html">http://www.json.org/json-zh.html</a> </span></p>
<h2><a name=_Toc266438135></a><a name=_Toc266091191></a><a name=_Toc266091107></a><a name=_Toc266091095></a><a name=_Toc266090990></a><a name=_Toc266090877></a><a name=_Toc266089820></a><a name=_Toc266088726><span><span><span><span><span><span><span><span><span>二、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>jsoncpp</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span><span><span><span>介绍</span></span></span></span></span></span></span></span></span></h2>
<p><span>本文选择一个第三方库</span><span> jsoncpp </span><span>来解析</span><span> JSON</span><span>。</span><span>jsoncpp </span><span>是比较出名的</span><span> C++ JSON </span><span>解析库。下载地址为：<span>http://sourceforge.net/projects/jsoncpp</span>。本文使用的<span> jsoncpp </span>版本为：<span>0.5.0</span>。</span><strong><span>License:</span></strong><span> </span><span><a href="http://sourceforge.net/softwaremap/?&amp;fq%5B%5D=trove%3A197"><span>Public Domain</span></a></span><span>。</span></p>
<p><span>Jsoncpp</span><span>是<span>Json</span>数据格式的编码解码器，使用<span>c++</span>编写，提供<span>reader</span>和<span>writer</span>来进行解码和编码。</span></p>
<p><span>jsconcpp </span><span>进行</span><span> JSON </span><span>解析的源码文件分布在</span><span> include/json</span><span>、</span><span>src/lib_json </span><span>下。</span></p>
<p><span>jsoncpp </span><span>主要包含三种类型的<span> class</span>：<span>Value</span>、<span>Reader</span>、<span>Writer</span>。<span>jsoncpp </span>中所有对象、类名都在<span> namespace Json </span>中，包含<span> json.h </span>即可。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Json::Value </span><span>只能处理<span> ANSI </span>类型的字符串，如果<span> C++ </span>程序是用<span> Unicode </span>编码的，最好加一个<span> Adapt </span>类来适配。</span></p>
<p><span>1</span><span>、<span>Reader<br></span>该库中的<span>Reader</span>类用来将字串或者流载入解析器。是的后期可以用<span>Reader</span>里面的解析方法来解码<span>Json</span>字串为<span>C++</span>认识的数据。可以用<span>Json::Reader</span>来声明一个<span>Reader</span>实例。<span>Reader</span>中最常用的就是一个<span>parse</span>方法，该方法用来将载入的<span>json</span>字串解析为<span>C++</span>格式的数据。<span><br><span>&nbsp;&nbsp;&nbsp; </span>2</span>、<span>Value<br></span>这是该库中的核心类，用于存储各样格式的数据，可以包括<span>int,double,short,char*,string,bool,object,array</span>等几乎所有格式的数据。该库的编码和解码的核心功能都是用<span>Value</span>类实现的。就用以上的<span>Reader</span>的<span>parse</span>方法来说，需要传入一个<span>Value</span>类别的引用值，就是用来存储<span>Json</span>数据的根值，并且可以用这个根值来存取其他的所有值。<span><br><span>&nbsp;&nbsp;&nbsp; </span>3</span>、<span>Writer<br></span>这是该库的一个虚类，没有真正的实现<span>encode</span>的功能。需要重载里头的方法来实现真正的<span>encode</span>功能。</span></p>
<p><span>4.FastWriter<br></span>这是该库中真正实现<span>encode</span>功能的类，用来实现将<span>Value</span>编码称为<span>Json</span>串。</p>
<p><span>参考：</span></p>
<p><span><span>1、<span> </span></span></span><span><a href="http://sourceforge.net/projects/jsoncpp/"><span>http://sourceforge.net/projects/jsoncpp/</span></a></span></p>
<p><span><span>2、<span> </span></span></span><span><a href="http://jsoncpp.sourceforge.net/index.html"><span>http://jsoncpp.sourceforge.net/index.html</span></a></span></p>
<p><span><span>3、<span> </span></span></span><span><a href="http://blog.csdn.net/xt_xiaotian/archive/2010/06/04/5648388.aspx"><span>http://blog.csdn.net/xt_xiaotian/archive/2010/06/04/5648388.aspx</span></a></span></p>
<p><span><span>4、<span> </span></span></span><span><a href="http://adebugger.cn/2009/11/cpp-json-data-communication/"><span>http://adebugger.cn/2009/11/cpp-json-data-communication/</span></a></span><span> </span></p>
<h2><a name=_Toc266438136></a><a name=_Toc266091192></a><a name=_Toc266091108></a><a name=_Toc266091096></a><a name=_Toc266090991></a><a name=_Toc266090878></a><a name=_Toc266089821></a><a name=_Toc266088727><span><span><span><span><span><span><span><span><span>三、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>protobuf</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span><span><span><span>介绍</span></span></span></span></span></span></span></span></span></h2>
<p><span>ProtoBuf</span><span>，全称是<span>Protocol Buffers, </span>它是谷歌内部用的一种高效的、可扩展的对结构化数据进行编码的格式规范。谷歌自己内部很多程序之间的通信协议都用了<span>ProtoBuf</span>。</span></p>
<p><span>ProtoBuf</span><span>可以支持多种编程语言，目前已经<span>C++, Java</span>和<span>Python</span>，本文中所前的内容用到例子的话，会以<span>C++</span>为例。</span></p>
<p><span>ProtoBuf</span><span>在<span>Google Code</span>上的主页是：</span><span><a href="http://code.google.com/p/protobuf/"><span>http://code.google.com/p/protobuf/</span></a></span><span>， 感兴趣的可以在这里下载<span>ProtoBuf</span>的源码，也可以在这里阅读<span>ProtoBuf</span>的详细的文档。</span></p>
<h3><a name=_Toc266438137></a><a name=_Toc266091193></a><a name=_Toc266091109></a><a name=_Toc266091097></a><a name=_Toc266090992></a><a name=_Toc266090879></a><a name=_Toc266089822></a><a name=_Toc266088728><span><span><span><span><span><span><span><span><span>1、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>序列化和反序列化</span></span></span></span></span></span></span></span></a></h3>
<p><span>在开始本部分的内容之前，首先有必要介绍两个基本概念，一个是序列化，一个是反序列化。这两个概念的定义在网上搜一下都很多的，但大多都讲得比较晦涩，不太好理解，在这里我会用比较通俗的文字来解释，尽可能让读都朋友们一读就明白是怎么回事：</span></p>
<p><span>序列化：是指将结构化的数据按一定的编码规范转成指定格式的过程。</span></p>
<p><span>反序列化：是指将转成指定格式的数据解析成原始的结构化数据的过程。</span></p>
<p><span>举个例子，<span>Person</span>是一个表示人的对象类型，<span>person</span>是一个<span>Person</span>类型的对象，将<span>person</span>存到一个对应的<span>XML</span>文档中的过程就是一种序列化，而解析<span>XML</span>生成对应<span>Person</span>类型对象<span>person</span>的过程，就是一个反序列化的过程。在这里结构化数据指的就是<span>Person</span>类型的数据，一定的编码规范指的就是<span>XML</span>文档的规范。<span>XML</span>是一种简单的序列化方式，用<span>XML</span>序列化的好处是，<span>XML</span>的通用性比较好，另外，<span>XML</span>是一种文本格式，对人阅读比较友好，但是<span>XML</span>方式比较占空间，效率也不是很高。通常，比较高效的序列化都是采用二进制方式的，将要序列化的结构化数据，按一定的编码规范，转成为一串二进制的字节流存储下来，需要用的时候再从这串二进制的字节流中反序列化出对应的结构化的数据。</span></p>
<p><span>通过上面的介绍，我们给<span>protobuf</span>下一个比较正式的定义了：<strong><span>Google ProtoBuf</span>是<span>Google</span>制定的一种用来序列化结构化数据的程序库。</strong></span></p>
<h3><a name=_Toc266438138></a><a name=_Toc266091194></a><a name=_Toc266091110></a><a name=_Toc266091098></a><a name=_Toc266090993></a><a name=_Toc266090880></a><a name=_Toc266089823></a><a name=_Toc266088729><span><span><span><span><span><span><span><span><span>2、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>ProtoBuf</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span><span><span><span>中的编码</span></span></span></span></span></span></span></span></span></h3>
<p><span>1) ProtoBuf</span><span>编码基础<span>——Varints, varints</span>是一种将一个整数序列化为一个或者多个<span>Bytes</span>的方法，越小的整数，使用的<span>Bytes</span>越少。</span></p>
<p><span>Varints</span><span>的基本规则是：</span></p>
<p><span>（<span>a</span>） 每个<span>Byte</span>的最高位<span>(msb)</span>是标志位，如果该位为<span>1</span>，表示该<span>Byte</span>后面还有其它<span>Byte</span>，如果该位为<span>0</span>，表示该<span>Byte</span>是最后一个<span>Byte</span>。</span></p>
<p><span>（<span>b</span>）每个<span>Byte</span>的低<span>7</span>位是用来存数值的位</span></p>
<p><span>（<span>c</span>）<span>Varints</span>方法用<span>Litte-Endian(</span>小端）字节序</span></p>
<p><span>举个例子：<span>300</span>用<span>Varints</span>序列化的结果是<span>1010 1100 0000 0010</span>，运算过程如下 所示：</span></p>
<p><span>1010 1100 0000 0010-&gt;010 1100 000 0010</span><span>（去标志位）<span>-&gt;</span></span></p>
<p><span>000 0010 010 1100</span><span>（调整字节序）<span>-&gt; 1 0010 1100 -&gt;256+32+8+4=300</span>（计算值）</span></p>
<p><span>300</span><span>用<span>Varints</span>序列化的过程：<span>-&gt; (</span>二进制<span>)0000 0001 0010 1100 -&gt; (7</span>位一组<span>)000 0010 010 1100 -&gt; (</span>调整字节序<span>)010 1100 000 0010 -&gt; (</span>增加标志位<span>)1010 1100 0000 0010</span>。</span></p>
<p><span>2)ProtoBuf</span><span>中消息的编码规则：</span></p>
<p><span>（<span>a</span>）每条消息<span>(message)</span>都是有一系列的<span>key-value</span>对组成的<span>, key</span>和<span>value</span>分别采用不同的编码方式。</span></p>
<p><span>（<span>b</span>）对某一条件消息<span>(message)</span>进行编码的时候，是把该消息中所有的<span>key-value</span>对序列化成二进制字节流；而解码的时候，解码程序读入二进制的字节流，解析出每一个<span>key-value</span>对，如果解码过程中遇到识别不出来的类型，直接跳过。这样的机制，保证了即使该消息添加了新的字段，也不会影响旧的编<span>/</span>解码程序正常工作。</span></p>
<p><span>（<span>c</span>）<span>key</span>由两部分组成，一部分是在定义消息时对字段的编号（<span>field_num</span>），另一部分是字段类型（<span>wire_type</span>）。字段类型定义如下表所示。</span></p>
<p>
<table border=1 cellSpacing=0 cellPadding=0 width="100%">
    <tbody>
        <tr>
            <td vAlign=top>
            <p><strong><span>Type</span></strong></p>
            </td>
            <td vAlign=top>
            <p><strong><span>Meaning</span></strong></p>
            </td>
            <td vAlign=top>
            <p><strong><span>Used For</span></strong></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>0</span></p>
            </td>
            <td vAlign=top>
            <p><span>Varint</span></p>
            </td>
            <td vAlign=top>
            <p><span>int32, int64, uint32, uint64, sint32, sint64, bool, enum</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>1</span></p>
            </td>
            <td vAlign=top>
            <p><span>64-bit</span></p>
            </td>
            <td vAlign=top>
            <p><span>fixed64, sfixed64, double</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>2</span></p>
            </td>
            <td vAlign=top>
            <p><span>Length-delimited</span></p>
            </td>
            <td vAlign=top>
            <p><span>string, bytes, embedded messages, packed repeated fields</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>3</span></p>
            </td>
            <td vAlign=top>
            <p><span>Start group</span></p>
            </td>
            <td vAlign=top>
            <p><span>groups (deprecated)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>4</span></p>
            </td>
            <td vAlign=top>
            <p><span>End group</span></p>
            </td>
            <td vAlign=top>
            <p><span>groups (deprecated)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>5</span></p>
            </td>
            <td vAlign=top>
            <p><span>32-bit</span></p>
            </td>
            <td vAlign=top>
            <p><span>fixed32, sfixed32, float</span></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><span>（<span>d</span>）<span>key</span>的编码方式：<span>field_num &lt;&lt; 3 | wire_type</span></span></p>
<p><span>（<span>e</span>）<span>varint</span>类型<span>(wire_type=0)</span>的编码，与第<span>(1)</span>部分中介绍的方法基本一致，但是<span>int32, int64</span>和<span>sint32,sint64</span>有些特别之处：<span>int32</span>和<span>int64</span>就是简单的按<span>varints</span>方法来编码，所以像<span>-1</span>、<span>-2</span>这样负数也会占比较多的<span>Bytes</span>。于是<span>sint32</span>和<span>sint64</span>采用了一种改进的方法：先采用<span>Zigzag</span>方法将所有的整数（正数、<span>0</span>和负数）一一映射到所有的无符号数上，然后再采用<span>varints</span>编码方法进行编码。<span>Zigzag</span>映射函数为：</span></p>
<p><span>Zigzag(n) = (n &lt;&lt; 1) ^ (n &gt;&gt; 31), &nbsp;n</span><span>为<span>sint32</span>时</span></p>
<p><span>Zigzag(n) = (n &lt;&lt; 1) ^ (n &gt;&gt; 63), &nbsp;n</span><span>为<span>sint64</span>时</span></p>
<p><span>下表是一个比较直观的映射表，这样映射后再进行编码的好处就是绝对值比较小的负数序列化后的结果占的<span>Bytes</span>数也会比较少。</span></p>
<p>
<table border=1 cellSpacing=0 cellPadding=0 width="50%">
    <tbody>
        <tr>
            <td vAlign=top width="50%">
            <p><strong><span>Signed Original</span></strong></p>
            </td>
            <td vAlign=top>
            <p><strong><span>Encoded As</span></strong></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>0</span></p>
            </td>
            <td vAlign=top>
            <p><span>0</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>-1</span></p>
            </td>
            <td vAlign=top>
            <p><span>1</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>1</span></p>
            </td>
            <td vAlign=top>
            <p><span>2</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>-2</span></p>
            </td>
            <td vAlign=top>
            <p><span>3</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>2</span></p>
            </td>
            <td vAlign=top>
            <p><span>4</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>-3</span></p>
            </td>
            <td vAlign=top>
            <p><span>5</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>&#8230;</span></p>
            </td>
            <td vAlign=top>
            <p><span>&#8230;</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>2147483647</span></p>
            </td>
            <td vAlign=top>
            <p><span>4294967294</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p><span>-2147483648</span></p>
            </td>
            <td vAlign=top>
            <p><span>4294967295</span></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><span>（<span>f</span>）<span>64-bit(wire_type=1)</span>和<span>32-bit(wire_type=5)</span>的编码方式就比较简单了，直接在<span>key</span>后面跟上<span>64bits</span>或<span>32bits</span>，采用<span>Little-Endian(</span>小端<span>)</span>字节序。</span></p>
<p><span>（<span>g</span>）<span>length-delimited(wire_type=2)</span>的编码方式：<span>key+length+content, key</span>的编码方式是统一的，<span>length</span>采用<span>varints</span>编码方式，<span>content</span>就是由<span>length</span>指定的长度的<span>Bytes</span>。</span></p>
<p><span>（<span>h</span>）<span>wire_type=3</span>和<span>4</span>的现在已经不推荐使用了，因此这里也不再做介绍。</span></p>
<p><span>3</span><span>）<span>ProtoBuf</span>编解码中字段顺序<span>(Field order)</span>的问题：</span></p>
<p><span>(a) </span><span>编码<span>/</span>解码与字段顺序无关，这一点由<span>key-value</span>机制就能保证</span></p>
<p><span>(b)</span><span>对于未知的字段，编码的时候会把它写在序列化完的已知字段后面。</span></p>
<h3><span><span>3、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span>&nbsp;<a name=_Toc266438139><span>工作流程</span></a></h3>
<p><span>首先需要在一个<span> .proto </span>文件中定义你需要做串行化的数据结构信息。每个<span>ProtocolBuffer</span>信息是一小段逻辑记录，包含一系列的键值对。这里有个非常简单的<span> .proto </span>文件定义了个人信息<span>:<br>message Person {<br>&nbsp; &nbsp; required string name=1;<br>&nbsp; &nbsp; required int32 id=2;<br>&nbsp; &nbsp; optional string email=3;<br><br>&nbsp; &nbsp; enum PhoneType {<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;MOBILE=0;<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;HOME=1;<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;WORK=2;<br>&nbsp; &nbsp; }<br><br>&nbsp; &nbsp; message PhoneNumber {<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;required string number=1;<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;optional PhoneType type=2 [default=HOME];<br>&nbsp; &nbsp; }<br><br>&nbsp; &nbsp; repeated PhoneNumber phone=4;<br>}</span></span></p>
<p><span>有如你所见，消息格式很简单，每个消息类型拥有一个或多个特定的数字字段，每个字段拥有一个名字和一个值类型。值类型可以是数字<span>(</span>整数或浮点<span>)</span>、布尔型、字符串、原始字节或者其他<span>ProtocolBuffer</span>类型，还允许数据结构的分级。你可以指定可选字段，必选字段和重复字段。你可以在<span>( </span></span><span><a href="http://code.google.com/apis/protocolbuffers/docs/proto.html" target=_blank><span>http://code.google.com/apis/protocolbuffers/docs/proto.html</span></a></span><span> )</span><span>找到更多关于如何编写<span> .proto </span>文件的信息。<span><br></span>一旦你定义了自己的报文格式<span>(message)</span>，你就可以运行<span>ProtocolBuffer</span>编译器，将你的<span> .proto </span>文件编译成特定语言的类。这些类提供了简单的方法访问每个字段<span>(</span>像是<span> query() </span>和<span> set_query() )</span>，像是访问类的方法一样将结构串行化或反串行化。例如你可以选择<span>C++</span>语言，运行编译如上的协议文件生成类叫做<span> Person </span>。随后你就可以在应用中使用这个类来串行化的读取报文信息。你可以这么写代码<span>:<br>Person person;<br>person.set_name("John Doe");<br>person.set_id(1234);<br>person.set_email("jdoe@example.com");<br>fstream.output("myfile",ios:out | ios::binary);<br>person.SerializeToOstream(&amp;output);</span></span></p>
<p><span>然后，你可以读取报文中的数据<span>:<br>fstream input("myfile",ios::in | ios:binary);<br>Person person;<br>person.ParseFromIstream(&amp;input);<br>cout &lt;&lt; "Name: " &lt;&lt; person.name() &lt;&lt; endl;<br>cout &lt;&lt; "E-mail: " &lt;&lt; person.email() &lt;&lt; endl;</span></span></p>
<p><span>你可以在不影响向后兼容的情况下随意给数据结构增加字段，旧有的数据会忽略新的字段。所以如果使用<span>ProtocolBuffer</span>作为通信协议，你可以无须担心破坏现有代码的情况下扩展协议。<span><br></span>你可以在<span>API</span>参考<span>( </span></span><span><a href="http://code.google.com/apis/protocolbuffers/docs/reference/overview.html" target=_blank><span>http://code.google.com/apis/prot ... rence/overview.html</span></a></span><span> )</span><span>中找到完整的参考，而关于<span>ProtocolBuffer</span>的报文格式编码则可以在<span>( </span></span><span><a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html" target=_blank><span>http://code.google.com/apis/protocolbuffers/docs/encoding.html</span></a></span><span> )</span><span>中找到。</span></p>
<p><span>参考：</span></p>
<p><span><span>1、</span></span><span><a href="http://code.google.com/p/protobuf/"><span>http://code.google.com/p/protobuf/</span></a></span><span> </span></p>
<p><span><span>2、</span></span><span><a href="http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/tutorials.html"><span>http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/tutorials.html</span></a></span><span> </span></p>
<p><span><span>3、</span></span><span><a href="http://www.wuzesheng.com/?p=1258"><span>http://www.wuzesheng.com/?p=1258</span></a></span><span> </span></p>
<p>&nbsp;</p>
<h2><a name=_Toc266438140><span><span>四、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>json</span></a><span><span>和</span><span>protobuf</span></span><span><span>的性能对比</span></span></h2>
<h3><a name=_Toc266438141><span><span>1、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>占用空间比较</span></a></h3>
<p><span>json</span><span>一种文本格式，带有字符串标签，对人阅读比较友好，但是<span>json</span>方式比较占空间。</span></p>
<p><span>protobuf</span><span>的每条消息<span>(message)</span>都是有一系列的<span>key-value</span>对组成的，<span>key</span>和<span>value</span>分别采用不同的编码方式，编码后占用的空间比较小。</span></p>
<h3><a name=_Toc266438142><span><span>2、<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>性能测试</span></a></h3>
<p><span>测试程序是定义<span>Person</span>消息，先序列化到一个<span>string</span>里，然后又从<span>string</span>里反序列化出来，如此重复<span>100000</span>，计算总共花费的时间。</span></p>
<p><span>测试结果：使用<span>protobuf</span>大概需要<span>10</span>秒，使用<span>json</span>大概需要<span>58</span>秒。</span></p>
<p><span>从结果可以看出，<span>protobuf</span>的序列化、反序列化是比<span>json</span>更快的。</span></p>
<p><br><br></span><br>&nbsp;</p>
<img src ="http://www.cppblog.com/toMyself/aggbug/128125.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-09-30 14:16 <a href="http://www.cppblog.com/toMyself/archive/2010/09/30/128125.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll</title><link>http://www.cppblog.com/toMyself/archive/2010/09/16/126767.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Thu, 16 Sep 2010 07:20:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/09/16/126767.html</guid><description><![CDATA[<strong>服务器代码：<br></strong>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><strong><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdlib.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">errno.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">types.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">netinet</span><span style="COLOR: #000000">/</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">socket.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">wait.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">unistd.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">arpa</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">inet.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">fcntl.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">epoll.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">time.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">resource.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br></span><strong><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;MAXBUF&nbsp;1024</span></strong><span style="COLOR: #000000"><br></span><strong><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;MAXEPOLLSIZE&nbsp;10000</span></strong><span style="COLOR: #000000"><br><br></span><span style="COLOR: #008000"><strong>/*</strong></span><span style="COLOR: #008000"><br><strong>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setnonblocking&nbsp;-&nbsp;设置句柄为非阻塞方式<br>&nbsp;</strong></span><span style="COLOR: #008000"><strong>*/</strong></span><span style="COLOR: #000000"><br></span><strong><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;setnonblocking(</span><span style="COLOR: #0000ff">int</span></strong><strong><span style="COLOR: #000000">&nbsp;sockfd)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(fcntl(sockfd,&nbsp;F_SETFL,&nbsp;fcntl(sockfd,&nbsp;F_GETFD,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">O_NONBLOCK)&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span></strong><strong><span style="COLOR: #000000">;<br>}<br><br></span><span style="COLOR: #008000">/*</span></strong><span style="COLOR: #008000"><br><strong>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handle_message&nbsp;-&nbsp;处理每个&nbsp;socket&nbsp;上的消息收发<br>&nbsp;</strong></span><span style="COLOR: #008000"><strong>*/</strong></span><span style="COLOR: #000000"><br></span><strong><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;handle_message(</span><span style="COLOR: #0000ff">int</span></strong><strong><span style="COLOR: #000000">&nbsp;new_fd)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;buf[MAXBUF&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">];<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span></strong><strong><span style="COLOR: #000000">&nbsp;len;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;开始处理每个新连接上的数据收发&nbsp;</span><span style="COLOR: #008000">*/</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;bzero(buf,&nbsp;MAXBUF&nbsp;</strong></span><strong><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;接收客户端的消息&nbsp;</span><span style="COLOR: #008000">*/</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;len&nbsp;</strong></span><strong><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;recv(new_fd,&nbsp;buf,&nbsp;MAXBUF,&nbsp;</span><span style="COLOR: #000000">0</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(len&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span></strong><strong><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%d接收消息成功:'%s'，共%d个字节的数据\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_fd,&nbsp;buf,&nbsp;len);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</strong></span><strong><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(len&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span></strong><strong><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">消息接收失败！错误代码是%d，错误信息是'%s'\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errno,&nbsp;strerror(errno));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(new_fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;处理每个新连接上的数据收发结束&nbsp;</span><span style="COLOR: #008000">*/</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;</strong></span><span style="COLOR: #0000ff"><strong>return</strong></span><strong><span style="COLOR: #000000">&nbsp;len;<br>}<br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;argc,&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">**</span></strong><strong><span style="COLOR: #000000">argv)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span></strong><strong><span style="COLOR: #000000">&nbsp;listener,&nbsp;new_fd,&nbsp;kdpfd,&nbsp;nfds,&nbsp;n,&nbsp;ret,&nbsp;curfds;<br>&nbsp;&nbsp;&nbsp;&nbsp;socklen_t&nbsp;len;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span></strong><strong><span style="COLOR: #000000">&nbsp;sockaddr_in&nbsp;my_addr,&nbsp;their_addr;<br>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span></strong><strong><span style="COLOR: #000000">&nbsp;myport,&nbsp;lisnum;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span></strong><strong><span style="COLOR: #000000">&nbsp;epoll_event&nbsp;ev;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span></strong><strong><span style="COLOR: #000000">&nbsp;epoll_event&nbsp;events[MAXEPOLLSIZE];<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span></strong><strong><span style="COLOR: #000000">&nbsp;rlimit&nbsp;rt;<br>&nbsp;&nbsp;&nbsp;&nbsp;myport&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">5000</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;lisnum&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span></strong><strong><span style="COLOR: #000000">;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;设置每个进程允许打开的最大文件数&nbsp;</span><span style="COLOR: #008000">*/</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;rt.rlim_max&nbsp;</strong></span><strong><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;rt.rlim_cur&nbsp;</span><span style="COLOR: #000000">=</span></strong><strong><span style="COLOR: #000000">&nbsp;MAXEPOLLSIZE;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(setrlimit(RLIMIT_NOFILE,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">rt)&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">setrlimit</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">设置系统资源参数成功！\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;开启&nbsp;socket&nbsp;监听&nbsp;</span><span style="COLOR: #008000">*/</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;</strong></span><strong><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((listener&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;socket(PF_INET,&nbsp;SOCK_STREAM,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">))&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">socket</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</strong></span><strong><span style="COLOR: #000000">"</span><span style="COLOR: #000000">socket&nbsp;创建成功！\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;setnonblocking(listener);<br>&nbsp;&nbsp;&nbsp;&nbsp;bzero(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">my_addr,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span></strong><strong><span style="COLOR: #000000">(my_addr));<br>&nbsp;&nbsp;&nbsp;&nbsp;my_addr.sin_family&nbsp;</span><span style="COLOR: #000000">=</span></strong><strong><span style="COLOR: #000000">&nbsp;PF_INET;<br>&nbsp;&nbsp;&nbsp;&nbsp;my_addr.sin_port&nbsp;</span><span style="COLOR: #000000">=</span></strong><strong><span style="COLOR: #000000">&nbsp;htons(myport);<br>&nbsp;&nbsp;&nbsp;&nbsp;my_addr.sin_addr.s_addr&nbsp;</span><span style="COLOR: #000000">=</span></strong><strong><span style="COLOR: #000000">&nbsp;INADDR_ANY;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(bind(listener,&nbsp;(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">my_addr,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr))&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">bind</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</strong></span><strong><span style="COLOR: #000000">"</span><span style="COLOR: #000000">IP&nbsp;地址和端口绑定成功\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(listen(listener,&nbsp;lisnum)&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">listen</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</strong></span><strong><span style="COLOR: #000000">"</span><span style="COLOR: #000000">开启服务成功！\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;创建&nbsp;epoll&nbsp;句柄，把监听&nbsp;socket&nbsp;加入到&nbsp;epoll&nbsp;集合里&nbsp;</span><span style="COLOR: #008000">*/</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;kdpfd&nbsp;</strong></span><span style="COLOR: #000000"><strong>=</strong></span><strong><span style="COLOR: #000000">&nbsp;epoll_create(MAXEPOLLSIZE);<br>&nbsp;&nbsp;&nbsp;&nbsp;len&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">struct</span></strong><strong><span style="COLOR: #000000">&nbsp;sockaddr_in);<br>&nbsp;&nbsp;&nbsp;&nbsp;ev.events&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;EPOLLIN&nbsp;</span><span style="COLOR: #000000">|</span></strong><strong><span style="COLOR: #000000">&nbsp;EPOLLET;<br>&nbsp;&nbsp;&nbsp;&nbsp;ev.data.fd&nbsp;</span><span style="COLOR: #000000">=</span></strong><strong><span style="COLOR: #000000">&nbsp;listener;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(epoll_ctl(kdpfd,&nbsp;EPOLL_CTL_ADD,&nbsp;listener,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ev)&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">epoll&nbsp;set&nbsp;insertion&nbsp;error:&nbsp;fd=%d\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">,&nbsp;listener);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</strong></span><strong><span style="COLOR: #000000">"</span><span style="COLOR: #000000">监听&nbsp;socket&nbsp;加入&nbsp;epoll&nbsp;成功！\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;curfds&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;等待有事件发生&nbsp;</span><span style="COLOR: #008000">*/</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nfds&nbsp;</strong></span><strong><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;epoll_wait(kdpfd,&nbsp;events,&nbsp;curfds,&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(nfds&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">epoll_wait</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;处理所有事件&nbsp;</span><span style="COLOR: #008000">*/</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</strong></span><strong><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(n&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;n&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;nfds;&nbsp;</span><span style="COLOR: #000000">++</span></strong><strong><span style="COLOR: #000000">n)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(events[n].data.fd&nbsp;</span><span style="COLOR: #000000">==</span></strong><strong><span style="COLOR: #000000">&nbsp;listener)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_fd&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;accept(listener,&nbsp;(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">their_addr,</span><span style="COLOR: #000000">&amp;</span></strong><strong><span style="COLOR: #000000">len);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(new_fd&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">accept</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">continue</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</strong></span><strong><span style="COLOR: #000000">"</span><span style="COLOR: #000000">有连接来自于：&nbsp;%d:%d，&nbsp;分配的&nbsp;socket&nbsp;为:%d\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">,<br>&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;inet_ntoa(their_addr.sin_addr),&nbsp;ntohs(their_addr.sin_port),&nbsp;new_fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setnonblocking(new_fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ev.events&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;EPOLLIN&nbsp;</span><span style="COLOR: #000000">|</span></strong><strong><span style="COLOR: #000000">&nbsp;EPOLLET;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ev.data.fd&nbsp;</span><span style="COLOR: #000000">=</span></strong><strong><span style="COLOR: #000000">&nbsp;new_fd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(epoll_ctl(kdpfd,&nbsp;EPOLL_CTL_ADD,&nbsp;new_fd,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ev)&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span></strong><strong><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">把&nbsp;socket&nbsp;'%d'&nbsp;加入&nbsp;epoll&nbsp;失败！%s\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">,<br>&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;new_fd,&nbsp;strerror(errno));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curfds</span><span style="COLOR: #000000">++</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span></strong><span style="COLOR: #000000"><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</strong></span><span style="COLOR: #000000"><strong>=</strong></span><strong><span style="COLOR: #000000">&nbsp;handle_message(events[n].data.fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ret&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;errno&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">11</span></strong><strong><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;epoll_ctl(kdpfd,&nbsp;EPOLL_CTL_DEL,&nbsp;events[n].data.fd,</span><span style="COLOR: #000000">&amp;</span></strong><strong><span style="COLOR: #000000">ev);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curfds</span><span style="COLOR: #000000">--</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;close(listener);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span></strong><span style="COLOR: #000000"><strong>;<br>}<br></strong></span></div>
<strong>客户端代码：</strong>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><strong><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">&gt;</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdlib.h</span><span style="COLOR: #000000">&gt;</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">errno.h</span><span style="COLOR: #000000">&gt;</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">.h</span><span style="COLOR: #000000">&gt;</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">unistd.h</span><span style="COLOR: #000000">&gt;</span></strong><span style="COLOR: #000000"><br><strong>#include&nbsp;</strong></span><strong><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">netdb.h</span><span style="COLOR: #000000">&gt;</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">types.h</span><span style="COLOR: #000000">&gt;</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">netinet</span><span style="COLOR: #000000">/</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">.h</span><span style="COLOR: #000000">&gt;</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">socket.h</span><span style="COLOR: #000000">&gt;</span></strong><strong><span style="COLOR: #000000">&nbsp;<br></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PORT&nbsp;5000&nbsp;/*&nbsp;&nbsp;Server的端口&nbsp;*/&nbsp;</span></strong><span style="COLOR: #000000"><br></span><strong><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;MAXDATASIZE&nbsp;100&nbsp;/*&nbsp;一次可以读的最大字节数&nbsp;*/&nbsp;</span></strong><span style="COLOR: #000000"><br><br></span><strong><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;argc,&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span></strong><strong><span style="COLOR: #000000">argv[])&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span></strong><strong><span style="COLOR: #000000">&nbsp;sockfd,&nbsp;numbytes;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;hostent&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">he;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;主机信息&nbsp;</span><span style="COLOR: #008000">*/</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr_in&nbsp;their_addr;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;对方地址信息&nbsp;</span><span style="COLOR: #008000">*/</span></strong><strong><span style="COLOR: #000000">&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(argc&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">usage:&nbsp;client&nbsp;hostname\n</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;get&nbsp;the&nbsp;host&nbsp;info&nbsp;</span><span style="COLOR: #008000">*/</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((he&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;gethostbyname(argv[</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]))&nbsp;</span><span style="COLOR: #000000">==</span></strong><strong><span style="COLOR: #000000">&nbsp;NULL)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;注意：获取DNS信息时，显示出错需要用herror而不是perror&nbsp;</span><span style="COLOR: #008000">*/</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;herror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">gethostbyname</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((sockfd&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;socket(AF_INET,SOCK_STREAM,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">))&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">socket</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;their_addr.sin_family&nbsp;</span><span style="COLOR: #000000">=</span></strong><strong><span style="COLOR: #000000">&nbsp;AF_INET;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;their_addr.sin_port&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;htons(PORT);&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;short,&nbsp;NBO&nbsp;</span><span style="COLOR: #008000">*/</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;their_addr.sin_addr&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">((</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)he</span><span style="COLOR: #000000">-&gt;</span></strong><strong><span style="COLOR: #000000">h_addr);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;bzero(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">(their_addr.sin_zero),&nbsp;</span><span style="COLOR: #000000">8</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;&nbsp;其余部分设成0&nbsp;</span><span style="COLOR: #008000">*/</span></strong><strong><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(connect(sockfd,&nbsp;(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">their_addr,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr))&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">connect</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pMsg</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello,world!</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((numbytes&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;send(sockfd,&nbsp;pMsg,&nbsp;strlen(pMsg),&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">))&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">send</span><span style="COLOR: #000000">"</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">1</span></strong><strong><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;close(sockfd);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span></strong><span style="COLOR: #000000"><strong>;&nbsp;<br>}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></strong></span></div>
<img src ="http://www.cppblog.com/toMyself/aggbug/126767.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-09-16 15:20 <a href="http://www.cppblog.com/toMyself/archive/2010/09/16/126767.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络爬虫--larbin</title><link>http://www.cppblog.com/toMyself/archive/2010/08/28/125073.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Sat, 28 Aug 2010 12:38:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/28/125073.html</guid><description><![CDATA[<p>larbin分析、源码分析、结构分析<br><br>&nbsp;<img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/larbin.JPG" width=1248 height=527></p>
<p>单线程非阻塞。这是目前使用的比较多的一种做法，无论在client还是server都有着广泛的应用。在一个线程内打开多个非阻塞的连接，通过poll/epoll/select对连接状态进行判断，在第一时间响应请求，不但充分利用了网络资源，同时也将本机CPU资源的消耗降至最低。这种方法需要对dns请求，连接，读写操作都采用异步非阻塞操作，其中第一种比较复杂，可以采用adns作为解决方案，后面三个操作相对简单可以直接在程序内实现。</p>
<p>效率问题解决后就需要考虑具体的设计问题了。</p>
<p>url肯定需要一个单独的类进行处理，包括显示，分析url，得到主机，端口，文件数据。</p>
<p>然后需要对url进行排重，需要一个比较大的url Hash表。</p>
<p>如果还要对网页内容进行排重，则还需要一个Document Hash表。</p>
<p>爬过的url需要记录下来，由于量比较大，我们将它写到磁盘上，所以还需要一个FIFO的类(记作urlsDisk)。</p>
<p>现在需要爬的url同样需要一个FIFO类来处理，重新开始时，url会从定时从爬过的url FIFO里取出来，写到这个FIFO里。正在运行的爬虫需要从这个FIFO里读数据出来，加入到主机类的url列表里。当然，也会从前一个FIFO里直接读url出来，不过优先级应该比这个里面出来的url低，毕竟是已经爬过的。</p>
<p>爬虫一般是对多个网站进行爬取，但在同时站点内dns的请求可以只做一次，这就需要将主机名独立于url，单独有一个类进行处理。</p>
<p>主机名解析完成后需要有一个解析完成的IP类与之应用，用于connect的时候使用。</p>
<p>HTML文档的解析类也要有一个，用来分析网页，取出里面的url，加入到urlsDisk。</p>
<p>再加上一些字符串，调度类，一个简单的爬虫基本上就完成了。</p>
<p>以上基本上是Larbin的设计思路，Larbin在具体实现上还有一些特殊的处理，例如带了一个webserver，以及对特殊文件的处理。 Larbin有一点设计不不太好，就是慢的访问会越来越多，占用大量的连接，需要改进，另外如果对于大规模的爬虫，这仅仅实现了抓取的部分，要分布式的扩展还需要增加url的集中管理与调度以及前台spider的分布式算法。<br><br></p>
<p><font face=宋体>说的简单易懂一些，网络爬虫跟你使用的〖离线阅读〗工具差不多。说离线，其实还是要跟网络联结，否则怎么抓东西下来？</font></p>
<p><font face=宋体>那么不同的地方在哪里？</font></p>
<p><font face=宋体><span lang=EN-US>1</span>】 网络爬虫高度可配置性。</font><font face=宋体><span lang=EN-US> <br>2</span>】 网络爬虫可以解析抓到的网页里的链接</font><font face=宋体><span lang=EN-US> <br>3</span>】 网络爬虫有简单的存储配置</font><font face=宋体><span lang=EN-US> <br>4</span>】 网络爬虫拥有智能的根据网页更新分析功能</font><font face=宋体><span lang=EN-US> <br>5</span>】 网络爬虫的效率相当的高</font></p>
<p><font face=宋体>那么依据特征，其实也就是要求了，如何设计爬虫呢？要注意哪些步骤呢？</font></p>
<p><font face=宋体><span lang=EN-US>1</span>】<span lang=EN-US> url </span>的遍历和纪录</font><font face=宋体><span lang=EN-US> <br></span>这点<span lang=EN-US> larbin </span>做得非常的好，其实对于<span lang=EN-US>url</span>的遍历是很简单的，例如：</font><font face=宋体><span lang=EN-US> <br>cat [what you got]| tr " \n | gawk &amp;apos;{print $2}&amp;apos; | pcregrep ^ http:// <br></span>就可以得到一个所由的<span lang=EN-US> url </span>列表</font></p>
<p><font face=宋体><span lang=EN-US>2</span>】多进程<span lang=EN-US> VS </span>多线程</font><font face=宋体><span lang=EN-US> <br></span>各有优点了，现在一台普通的<span lang=EN-US>PC </span>例如<span lang=EN-US> booso.com </span>一天可以轻松爬下<span lang=EN-US>5</span>个<span lang=EN-US>G</span>的数据。大约<span lang=EN-US>20</span>万网页。</font></p>
<p><font face=宋体><span lang=EN-US>3</span>】时间更新控制</font><font face=宋体><span lang=EN-US> <br></span>最傻的做法是没有时间更新权重，一通的爬，回头再一通的爬。</font><font face=宋体><span lang=EN-US> <br></span>通常在下一次爬的的数据要跟上一次进行比较，如果连续<span lang=EN-US>5</span>次都没有变化，那么将爬这个网页的时间间隔扩大<span lang=EN-US>1</span>倍。</font></p>
<p><font face=宋体>如果一个网页在连续<span lang=EN-US>5</span>次爬取的时候都有更新，那么将设置的爬取时间缩短为原来的<span lang=EN-US>1</span>／<span lang=EN-US>2</span>。</font></p>
<p><font face=宋体>注意，效率是取胜的关键之一。</font></p>
<p><font face=宋体><span lang=EN-US>4</span>】爬的深度是多少呢？</font><font face=宋体><span lang=EN-US> <br></span>看情况了。如果你比较牛，有几万台服务器做网络爬虫，我劝您跳过这一点。</font><font face=宋体><span lang=EN-US> <br></span>如果你同我一样只有一台服务器做网络爬虫，那么这样一个统计您应该知道：</font></p>
<p><font face=宋体>网页深度：网页个数：网页重要程度</font><font face=宋体><span lang=EN-US> <br>0 : 1 : : 10 <br>1 :20 : :8 <br>2: :600: :5 <br>3: :2000: :2 <br>4 above: 6000: </span>一般无法计算</font></p>
<p><font face=宋体>好了，爬到三级就差不多了，再深入一是数据量扩大了<span lang=EN-US>3</span>／<span lang=EN-US>4</span>倍，二是重要度确下降了许多，这叫做<span lang=EN-US>&#8220;</span>种下的是龙种，收获的是跳蚤。<span lang=EN-US>&#8221;</span></font></p>
<p><font face=宋体><span lang=EN-US>5</span>】爬虫一般不直接爬对方的网页，一般是通过一个<span lang=EN-US>Proxy</span>出去，这个<span lang=EN-US>proxy</span>有缓解压力的功能，因为当对方的网页没有更新的时候，只要拿到<span lang=EN-US> header </span>的<span lang=EN-US> tag</span>就可以了，没有必要全部传输一次了，可以大大节约网络带宽。</font></p>
<p><font face=宋体><span lang=EN-US>apache webserver</span>里面纪录的<span lang=EN-US> 304 </span>一般就是被<span lang=EN-US>cache</span>的了。</font></p>
<p><font face=宋体><span lang=EN-US>6</span>】请有空的时候照看一下<span lang=EN-US>robots.txt</span></font></p>
<p><font face=宋体><span lang=EN-US>7</span>】存储结构。</font><font face=宋体><span lang=EN-US> <br></span>这个人人见智，<span lang=EN-US>google </span>用<span lang=EN-US> gfs </span>系统，如果你有<span lang=EN-US>7</span>／<span lang=EN-US>8</span>台服务器，我劝你用<span lang=EN-US>NFS</span>系统，要是你有<span lang=EN-US>70</span>／<span lang=EN-US>80</span>个服务器的话我建议你用<span lang=EN-US>afs </span>系统，要是你只有一台服务器，那么随便。</font></p>
<p><font face=宋体>给一个代码片断，是我写的新闻搜索引擎是如何进行数据存储的：</font></p>
<p><span lang=EN-US><font face=宋体>NAME=`echo $URL |perl -p -e &amp;apos;s/([^w-.@])/$1 eq " " ? " ":sprintf("%%%2.2x",ord($1))/eg&amp;apos;` <br>mkdir -p $AUTHOR <br>newscrawl.pl $URL --user-agent="news.booso.com+(+ </font><a href="http://booso.com/"><u><font color=#0000ff face=宋体>http://booso.com</font></u></a><font face=宋体>)" -outfile=$AUTHOR/$NAME</font></span></p>
<p><br>larbin官方地址：<a href="http://larbin.sourceforge.net/index-eng.html">http://larbin.sourceforge.net/index-eng.html</a><br><br><br>larbin，a input system。<br>定制larbin： <a href="http://larbin.sourceforge.net/custom-eng.html">http://larbin.sourceforge.net/custom-eng.html</a> <br>1、对爬来的网页定制自己的输出。<br>目前系统已经定义了4中输出，参见src/interf/useroutput.cc。自己的输出可以参考它们写。<br>2、修改larbin.conf，options.h进行简单定制。<br><br><br><br>&nbsp; </p>
<p>&nbsp;</p>
<p><span>&nbsp;Labin</span><span>、</span><span>OpenSpider</span><span>、天网</span> <span>三款爬虫对比分析</span> </p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Labin</span><span>、</span><span>OpenSpider</span><span>、天网</span> <span>是三款比较著名的网络爬虫，其中天网现在已经做成了分布式爬虫，据称天网在</span><span>ftp</span><span>搜索方面水平比较高。这三款爬虫本人都接触过，对于</span><span>Labin</span><span>和天网的源代码也研究过一段时间。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Larbin:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>首先，</span><span>Labin</span><span>采用的</span><span>socket</span><span>方式是</span> <span>单线程非阻塞式的爬取。具体的技术实现采用</span><span> linux/unix</span><span>的</span><span>poll</span><span>轮询接口。当</span><span>Larbin</span><span>读取种子网站以后，会解析出网页中的</span><span>url.</span><span>从源代码中来看，</span><span>Larbin</span><span>提取</span><span>url</span><span>的技术水平并不高，只是采取简单的字符串操作。然而</span><span>url</span><span>是各种各样的，字符串操作能否提取</span><span>90%</span><span>以上</span><span>html</span><span>网页中的</span><span>url</span><span>，还值得怀疑。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>当被</span><span>urls</span><span>被提取以后，</span><span>Labin</span><span>建立了四个队列存取之：两个优先权队列、两个普通队列。为什么要这么做呢？我想做着可能这样想：优先权队列用作采集优先权高的</span><span>url</span><span>，而这个优先权怎么计算？这个要根据系统的实际情况了！如果是垂直搜索引擎，可能会根据倾向性、内容相关性来打分，分数高则优先级高。甚至会用分类、聚类或者各种评判手段，甚至</span><span>pagerank/hits </span><span>算法来预先计算</span><span>url</span><span>的权重。如果是普通搜索引擎，那么可以这样计算打分：要不根据爬取的网页深度，要不根据是否</span><span>location</span><span>字段等等。</span><span>Larbin</span><span>中优先权是根据什么来计算的，我想读了上面的东西已经不重要了。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>既然提取了</span><span>urls,</span><span>就要对</span><span>url</span><span>进行域名解析。</span><span>Larbin</span><span>自建立了一个</span><span>DNS</span><span>服务器。关于</span><span>DNS</span><span>服务器的运作，鲜有说明。我自己是这样认为的：首先，本地（</span><span>Larbin)</span><span>的</span><span>dns</span><span>与外网的</span><span>DNS</span><span>进行交互，将外网的</span><span>DNS</span><span>的相关内容读取进来。然后，就可以响应</span><span>larbin</span><span>的解析请求了。所以当开启</span><span>DNS</span><span>服务器的时候，应该会有一个大量读取外网</span><span>DNS</span><span>内容的过程。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Larbin</span><span>似乎是将解析完毕的</span><span>DNS</span><span>存入一个管道之中，一头进，一头出。出的一头就是继续重复爬取了。另外</span><span>Larbin</span><span>的</span><span>url</span><span>消重算法是采用的是普通的</span><span>bloom filter</span><span>算法。不在累赘了。</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>总结：从上面看来，</span><span>Larbin</span><span>只是一个小型架构的爬虫，并不适合大型分布式爬取。其架构针对小型爬虫来说，轻装上阵，非常合理。但是对于大型爬虫则是非常的不合理了。</span></p>
<p><span><span>&nbsp;&nbsp; </span>OpenSpider:</span></p>
<p><span><span>&nbsp;&nbsp; </span>OpenSpider</span><span>我并没细看，大体上也就是那么个流程。不过值得一提的是，</span><span>OpenSpider</span><span>提取</span><span>urls</span><span>采用的是正则表达式的方法。这个是值得大书特书的。</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>关于正则表达式的具体设计，需要丰富的工程项目经验。其设计非常复杂。</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>天网：</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>天网的初期版本在这三个爬虫之中是最差劲的。几乎毫无架构可言。不过凡事都是从低级做起，还是要大书一番。勇气可嘉么。</span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp; </span></span><span>看得出来，天网模仿了很多开源系统。譬如一个</span><span>http</span><span>的简单读取器，还有</span><span>Larbin</span><span>似乎也看到了模仿的影子。低等科技国家只能够从模仿开始。一味鼓吹创新只能是自欺欺人。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>天网开了几个线程，并行进行爬取。爬取流程大约如下：</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/larbin2.jpg" width=453 height=573></p>
<p align=left>&nbsp;<span>足见，天网将爬来的并解析的</span><span>url</span><span>直接放在了</span><span>map,vector</span><span>等容器中，消重也是简单的</span><span>MD5</span><span>匹配。同时也设置了一个已爬取的容器。</span><span>socket</span><span>采用的是非阻塞的</span><span>select</span><span>。但是我怀疑他们的</span><span>select</span><span>没有起作用。用法等于没有用。</span></p>
<p align=left><span>&nbsp;&nbsp; </span><span>以上是我对三款爬虫的简要分析。因为没有必要进行非常细致的抠，所以很多地方都是一笔带过。谬误之处肯定很多的，希望大家多指教。</span></p>
<p><span>&nbsp;本文来自</span><span>CSDN</span><span>博客，转载请标明出处：</span><span><a href="http://blog.csdn.net/liwenjia1981/archive/2009/11/21/4846668.aspx">http://blog.csdn.net/liwenjia1981/archive/2009/11/21/4846668.aspx</a></span></p>
<img src ="http://www.cppblog.com/toMyself/aggbug/125073.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-28 20:38 <a href="http://www.cppblog.com/toMyself/archive/2010/08/28/125073.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学习《TCP/IP详解》</title><link>http://www.cppblog.com/toMyself/archive/2010/08/19/123932.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Thu, 19 Aug 2010 02:27:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/19/123932.html</guid><description><![CDATA[<p>&nbsp; <span>TCP/IP</span><span>详解</span></p>
<p><span>（在第<span>21</span>章我们将看到<span>TCP</span>的超时和重发算法的细节）</span></p>
<p><span>本书第<span>17</span>～<span>22</span>章将详细讨论<span>TCP</span>的内部操作细节。然后，我们将介绍一些<span>TCP</span>的应用，如第<span>26</span>章中的<span>Telnet</span>和<span>Rlogin</span>、第<span>27</span>章中的<span>FTP</span>以及第<span>28</span>章中的<span>SMTP</span>等。这些应用通常都是用户进程。</span></p>
<p><span>本书第<span>11</span>章将讨论<span>UDP</span>，然后在第<span>14</span>章（<span>DNS:</span>域名系统），第<span>15</span>章（<span>TFTP</span>：简单文件传送协议），以及第<span>16</span>章（<span>BOOTP</span>：引导程序协议）介绍使用<span>UDP</span>的应用程序。<span>SNMP</span>也使用了<span>UDP</span>协议，但是由于它还要处理许多其他的协议，因此本书把它留到第<span>25</span>章再进行讨论。</span></p>
<p><span>2010-6-8&nbsp;</span><span>概述、链路层、<span>IP</span>、<span>ARP</span>、<span>RARP</span></span></p>
<p><span>分层：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/protocols.JPG" width=734 height=755></span></p>
<p align=left><span><span>1)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>链路层，有时也称作数据链路层或网络接口层，通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆（或其他任何传输媒介）的物理接口细节。</span></p>
<p align=left><span><span>2)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>网络层，有时也称作互联网层，处理<span>分组在网络中的活动，例如分组的选路</span>。在<span>TCP/IP</span>协议族中，网络层协议包括<span>IP</span>协议（网际协议），<span>ICMP</span>协议（<span>Internet</span>互联网控制报文协议），以及<span>IGMP</span>协议（<span>Internet</span>组管理协议）。</span></p>
<p align=left><span><span>3)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>运输层主要为两台主机上的应用程序提供端到端的通信。在<span>TCP/IP</span>协议族中，有两个互不相同的传输协议：<span> TCP</span>（传输控制协议）和<span>UDP</span>（用户数据报协议）。</span></p>
<p align=left><span>TCP</span><span>为两台主机提供高可靠性的数据通信。它所做的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层，确认接收到的分组，设置发送最后确认分组的超时时钟等。由于运输层提供了高可靠性的端到端的通信，因此应用层可以忽略所有这些细节。</span></p>
<p align=left><span>而另一方面，<span> UDP</span>则为应用层提供一种非常简单的服务。它只是把称作数据报的分组从一台主机发送到另一台主机，但并不保证该数据报能到达另一端。任何必需的可靠性必须由应用层来提供。</span></p>
<p align=left><span><span>4)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>应用层负责处理特定的应用程序细节。</span><span>Telnet</span><span>、<span>FTP</span>、<span>SMTP</span>、<span>SNMP</span>。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>以太网驱动程序、令牌环驱动程序</span></p>
<p align=left><span>网桥是在链路层上对网络进行互连，而路由器则是在网络层上对网络进行互连。网桥使得多个局域网（<span>LAN</span>）组合在一起，这样对上层来说就好像是一个局域网。</span></p>
<p align=left><span>有三类</span><span>I P</span><span>地址：单播地址（目的为单个主机）、广播地址（目的端为给定网络上的所有主机）以及多播地址（目的端为<span>同一组</span>内的所有主机）。</span></p>
<p align=left><span>典型以太网帧首部的字节长度：（</span><span>UDP</span><span>的首部长为</span><span>8</span><span>字节）</span></p>
<p align=left><span><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/ethernet.JPG" width=558 height=130><br>由于<span>TCP</span>、<span>UDP</span>、<span>ICMP</span>和<span>IGMP</span>都要向<span>IP</span>传送数据，因此<span>IP</span>必须在生成的<span>IP</span>首部中加入某种标识，以表明数据属于哪一层。</span></p>
<p align=left><span>网络接口分别要发送和接收<span>IP</span>、<span>ARP</span>和<span>RARP</span>数据，因此也必须在以太网的帧首部中加入某种形式的标识，以指明生成数据的网络层协议。为此，以太网的帧首部也有一个<span>16 bit</span>的帧类型域。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>链路层：</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>TCP/IP</span><span>支持多种不同的链路层协议，这取决于网络所使用的硬件，如以太网、令牌环网、<span>FDDI</span>（光纤分布式数据接口）及<span>RS-232</span>串行线路等。</span></p>
<p align=left><span>以太网链路层协议，两个串行接口链路层协议（<span>SLIP</span>和<span>PPP</span>），以及大多数实现都包含的环回（<span>loopback</span>）驱动程序。</span></p>
<p align=left><span>以太网：</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>它是当今<span>TCP/IP</span>采用的主要的局域网技术。它采用一种称作<span>CSMA/CD</span>的媒体接入方法，其意思是<span>带冲突检测的载波侦听多路接入</span>（<span>Carrier Sense, Multiple Access with Collision Detection</span>）。它的速率为<span>10 Mb/s</span>，地址为<span>48 bit</span>。</span></p>
<p align=left><strong><span>以太网首部：</span></strong></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/eth_hdr.JPG" width=230 height=60></span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>这里的目的地址和源地址都是<span>Mac</span>地址。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>类型：对于<span>IP</span>数据，该值为<span>0800</span>；对于<span>ARP</span>数据，该值为<span>0806</span>；对于<span>RARP</span>数据，该值为<span>8035</span></span></p>
<p align=left><span>以太网尾部：</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>为<span>CRC</span>字段，用于帧内后续字节差错的循环冗余码检验（检验和）（它也被称为<span>FCS</span>或帧检验序列）。</span></p>
<p align=left><span>SLIP:</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>全称是<span>Serial Line IP</span>。它是一种在串行线路上对<span>IP</span>数据报进行封装的简单形式<span>, SLIP</span>适用于家庭中每台计算机几乎都有的<span>RS-232</span>串行端口和高速调制解调器接入<span>Internet</span>。</span></p>
<p align=left><span>PPP:<span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/ppp.JPG" width=560 height=80><br></span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>协议字段<span>: </span>当它的值为<span>0x0021</span>时，表示信息字段是一个<span>IP</span>数据报；值为<span>0xc021</span>时，表示信息字段是链路控制数据；值为<span>0x8021</span>时，表示信息字段是网络控制数据。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>由于标志字符的值是<span>0x7e</span>，因此当该字符出现在信息字段中时，<span> PPP</span>需要对它进行转义。在异步链路中，特殊字符<span>0x7d</span>用作转义字符。当它出现在<span>PPP</span>数据帧中时，那么紧接着的字符的第<span>6</span>个比特要取其补码。</span></p>
<p align=left><span>MTU:</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>不同类型的网络会有不同的<span>MTU</span>值。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>路径<span>MTU</span>的发现机制，即在任何时候确定路径<span>MTU</span>的方法。</span></p>
<p align=left><span>IP:</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/ip.JPG" width=560 height=354>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">定义IP首部</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">typedef&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;_iphdr{<br>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;h_lenver;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">4&nbsp;位IP版本号+4位首部长度</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;tos;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">8&nbsp;位服务类型TOS</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;total_len;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">16&nbsp;位IP包总长度（字节）</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;ident;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">1&nbsp;6位标识,&nbsp;用于辅助IP包的拆装,本实验不用,置零</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;frag_and_flags;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">3&nbsp;位标志位+13位偏移位,&nbsp;也是用于IP包的拆装,本实验不用,置零</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;ttl;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">8&nbsp;位IP包生存时间&nbsp;TTL</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;proto;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">8&nbsp;位协议&nbsp;(TCP,&nbsp;UDP&nbsp;或其他),&nbsp;本实验置ICMP,置为1</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;checksum;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">16&nbsp;位IP首部校验和,最初置零,等所有包头都填写正确后,计算并替换.</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;sourceIP;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">32&nbsp;位源IP地址</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;destIP;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">32&nbsp;位目的IP地址</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}IP_HEADER;</span></div>
<p align=left></span></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>总长度字段是指整个<span>IP</span>数据报的长度，以字节为单位。利用首部长度字段和总长度字段，就可以知道<span>IP</span>数据报中数据内容的起始位置和长度。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>为了计算一份数据报的<span>IP</span>检验和，首先把检验和字段置为<span>0</span>。然后，对首部中每个<span>16 bit</span>进行二进制反码求和（整个首部看成是由一串<span>16 bit</span>的字组成），结果存在检验和字段中。当收到一份<span>IP</span>数据报后，同样对首部中每个<span>16 bit</span>进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和，因此，如果首部在传输过程中没有发生任何差错，那么接收方计算的结果应该为全<span>1</span>。如果结果不是全<span>1</span>（即检验和错误），那么<span>IP</span>就丢弃收到的数据报。但是不生成差错报文，由上层去发现丢失的数据报并进行重传。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>以太网地址一般通过<span>ARP</span>获得。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>子网对于子网内部的路由器是不透明的。</span></p>
<p align=left><span>ARP:</span></p>
<p align=left><span>地址解析为这两种不同的地址形式提供映射：<span>32bit</span>的<span>IP</span>地址和数据链路层使用的任何类型的地址。</span></p>
<p align=left><span>ARP</span><span>发送一份称作<span>ARP</span>请求的以太网数据帧给以太网上的每个主机。<span>ARP</span>请求数据帧中包含目的主机的<span>IP</span>地址，其意思是&#8220;如果你是这个<span>IP</span>地址的拥有者，请回答你的硬件地址。&#8221; 目的主机的<span>ARP</span>层收到这份广播报文后，识别出这是发送端在寻问它的<span>IP</span>地址，于是发送一个<span>ARP</span>应答。这个<span>ARP</span>应答包含<span>IP</span>地址及对应的硬件地址。</span></p>
<p align=left><span>ARP</span><span>的功能是在<span>32 bit</span>的<span>IP</span>地址和采用不同网络技术的硬件地址之间提供动态映射。</span></p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/arp.JPG"></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>帧类型：对于<span>ARP</span>请求或应答来说，该字段的值为<span>0x0806</span>。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>硬件类型：它的值为<span>1</span>即表示以太网地址。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>协议类型：它的值为<span>0x0800</span>即表示<span>IP</span>地址。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>硬件地址长度：对于以太网上<span>IP</span>地址的<span>ARP</span>请求或应答来说，它的值为<span>6</span>。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>协议地址长度：对于以太网上<span>IP</span>地址的<span>ARP</span>请求或应答来说，它的值为<span>4</span>。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>操作<span>(op)</span>：<span>ARP</span>请求（值为<span>1</span>）、<span>ARP</span>应答（值为<span>2</span>）、<span>RARP</span>请求（值为<span>3</span>）和<span>RARP</span>应答（值为<span>4</span>）。</span></p>
<p align=left><span>RARP:</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>RARP</span><span>协议是许多无盘系统在引导时用来获取<span>IP</span>地址的。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>RARP</span><span>请求或应答的帧类型代码为<span>0x8035</span>。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>RARP</span><span>服务程序。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>RARP</span><span>服务器的设计与系统相关而且比较复杂。<span>RARP</span>服务器的功能就由用户进程来提供，</span></p>
<p align=left><span>而不是作为内核的<span>TCP/IP</span>实现的一部分。</span></p>
<p align=left><span>ARP</span><span>服务器很简单，通常是<span>TCP/IP</span>在内核中实现的一部分。由于内核知道<span>I P</span>地址和硬件地址，因此当它收到一个询问<span>I P</span>地址的<span>ARP</span>请求时，只需用相应的硬件地址来提供应答就可以了。</span></p>
<p align=left><span>ICMP:</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/icmp1.JPG" width=400 height=113><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/icmp2.JPG" width=557 height=189></span></span><span><span></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">定义ICMP首部</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">typedef&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;_icmphdr{<br>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;i_type;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">8&nbsp;位类型,&nbsp;本实验用&nbsp;8:&nbsp;ECHO&nbsp;0:ECHO&nbsp;REPLY</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;i_code;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">8&nbsp;位代码,&nbsp;本实验置零</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;i_cksum;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">16位校验和,&nbsp;从TYPE开始,直到最后一位用户数据,如果为字节数为奇数则补充一位</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;i_id&nbsp;;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;识别号（一般用进程号作为识别号）,&nbsp;用于匹配ECHO和ECHO&nbsp;REPLY包</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;i_seq&nbsp;;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;报文序列号,&nbsp;用于标记ECHO报文顺序</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i_data;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">不同类型和代码有不同的内容:&nbsp;type==0时为时间戳timestamp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}ICMP_HEADER;</span></div>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>不同类型由报文中的类型字段和代码字段来共同决定。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ICMP</span><span>地址掩码请求用于无盘系统在引导过程中获取自己的子网掩码：</span></p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/icmp3.JPG" width=557 height=156></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ICMP</span><span>时间戳请求允许系统向另一个系统查询当前的时间。返回的建议值是自午夜开始计算的毫秒数，协调的统一时间（<span>UTC</span>）：<span>(</span>这种<span>ICMP</span>报文的好处是它提供了毫秒级的分辨率）</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/icmp4.JPG" width=557 height=238></span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>发起时间戳（<span>orig</span>）、接收时间戳（<span>recv</span>）以及发送时间戳（<span>xmit</span>），往返时间（<span>rtt</span>）。</span></p>
<p align=left><span>ICMP</span><span>不可达报文：</span></p>
<p align=left><span>UDP</span><span>的规则之一是，如果收到一份<span>UDP</span>数据报而目的端口与某个正在使用的进程不相符，那么<span>UDP</span>返回一个<span>ICMP</span>不可达报文。</span></p>
<p align=left><span>ICMP</span><span>的一个规则是，<span> ICMP</span>差错报文必须包括生成该差错报文的数据报<span>IP</span>首部（包含任何选项），还必须至少包括跟在该<span>IP</span>首部后面的前<span>8</span>个字节。</span></p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/icmp5.JPG" width=557 height=194></p>
<p align=left><span>采用一种指数退避方法来设置超时值，分别在<span>0</span>、<span>5</span>、<span>15</span>和<span>35</span>秒时重发报文，这正是所推荐的方法。</span></p>
<p align=left><span>netstat -s </span><span>是查看每个协议统计数据的常用方法。</span></p>
<p align=left><span>Ping</span><span>程序<span>:</span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>几年前我们还可以作出这样没有限定的断言，如果不能<span>Ping</span>到某台主机，那么就不能<span>Telne t</span>或<span>FTP</span>到那台主机。随着<span>Internet</span>安全意识的增强，出现了提供访问控制清单的路由器和防火墙，那么像这样没有限定的断言就不再成立了。一台主机的可达性可能不只取决于<span>IP</span>层是否可达，还取决于使用何种协议以及端口号。<span>Ping</span>程序的运行结果可能显示某台主机不可达，但我们可以用<span>Telnet</span>远程登录到该台主机的<span>25</span>号端口（邮件服务器）。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>大多数的<span>TCP/IP</span>实现都在内核中直接支持<span>Ping</span>服务器—这种服务器不是一个用户进程。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>地址掩码和时间戳请求，也都是直接在内核中进行处理的。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>IP</span><span>记录路由选项。<span>IP</span>时间戳选项。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>提供<span>-r</span>选项，以提供记录路由的功能。它使得<span>ping</span>程序在发送出去的<span>IP</span>数据报中设置<span>IP RR</span>（记录路由）选项（该<span>IP</span>数据报包含<span>ICMP</span>回显请求报文）。这样，每个处理该数据报的路由器都把它的<span>IP</span>地址放入选项字段中。当数据报到达目的端时，<span>IP</span>地址清单应该复制到<span>ICMP</span>回显应答中，这样返回途中所经过的路由器地址也被加入清单中。当<span>ping</span>程序收到回显应答时，它就打印出这份<span>IP</span>地址清单。（</span><span>RFC 791</span><span>指定路由器记录出口</span><span>IP</span><span>地址。</span><span>）</span></p>
<p align=left><span>Traceroute</span><span>程序<span>:</span>（<span>Windows</span>下为<span>tracert</span>）</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>开始时发送一个<span>TTL</span>字段为<span>1</span>的<span>UDP</span>数据报，然后将<span>TTL</span>字段每次加<span>1</span>，以确定路径中的每个路由器。每个路由器在丢弃<span>UDP</span>数据报时都返回一个<span>ICMP</span>超时报文<span>2</span>，而最终目的主机则产生一个<span>ICMP</span>端口不可达的报文。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>Traceroute</span><span>程序使用<span>ICMP</span>报文和<span>IP</span>首部中的<span>TTL</span>字段（生存周期）。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>每个处理数据报的路由器都需要把<span>TTL</span>的值减<span>1</span>。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>Traceroute</span><span>程序发送一份<span>UDP</span>数据报给目的主机，但它选择一个不可能的值作为<span>UDP</span>端口号（大于<span>30000</span>），使目的主机的任何一个应用程序都不可能使用该端口。因为，当该数据报到达时，将使目的主机的<span>UDP</span>模块产生一份&#8220;端口不可达&#8221;错误的<span>ICMP</span>报文。这样，<span>Traceroute</span>程序所要做的就是区分接收到的<span>ICMP</span>报文是超时还是端口不可达，以判断什么时候结束。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>40</span><span>字节的数据报包含<span>20</span>字节<span>IP</span>首部、<span>8</span>字节的<span>UDP</span>首部和<span>12</span>字节的用户数据（<span>12</span>字节的用户数据包含每发一个数据报就加<span>1</span>的序列号，送出<span>TTL</span>的副本以及发送数据报的时间）。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于每个<span>TTL</span>值，发送<span>3</span>份数据报。每接收到一份<span>ICMP</span>报文，就计算并打印出往返时间。如果在<span>5</span>秒种内仍未收到<span>3</span>份数据报的任意一份的响应，则打印一个星号，并发送下一份数据报。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>每个打印出来的<span>RTT</span>值是从发送主机到路由器的总时间。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>目的主机<span>UDP</span>端口号最开始设置为<span>33435</span>，且每发送一个数据报加<span>1</span>。可以通过命令行选项来改变开始的端口号。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>返回的<span>ICMP</span>报文中的信源<span>IP</span>地址是<span>UDP</span>数据报到达的路由器接口的<span>IP</span>地址。这与<span>IP</span>记录路由选项不同，记录的<span>IP</span>地址指的是发送接口地址。<br><br></p>
<p align=left><span>TCP:<br><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>第<span>17</span>章<span><span>&nbsp;&nbsp; </span>TCP</span>：传输控制协议</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对</span><span>TCP</span><span>的介绍将由本章开始，并一直包括随后的</span><span>7</span><span>章。第</span><span>18</span><span>章描述如何建立和终止一个</span><span>TCP</span><span>连接，第</span><span>19</span><span>和第</span><span>20</span><span>章将了解正常的数据传输过程，包括交互使用（远程登录）和批量数据传送（文件传输）。第</span><span>21</span><span>章提供</span><span>TCP</span><span>超时及重传的技术细节，第</span><span>22</span><span>和第</span><span>23</span><span>章将介绍两种其他的定时器。最后，第</span><span>24</span><span>章概述</span><span>TCP</span><span>新的特性以及</span><span>TCP</span><span>的性能。</span></p>
<p align=left>&nbsp;&nbsp;&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/tcp_hdr.JPG" width=953 height=622><br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">定义TCP首部</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">typedef&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;tcp_hdr&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;USHORT&nbsp;th_sport;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">16位源端口</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;USHORT&nbsp;th_dport;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">16位目的端口</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;th_seq;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">32位序列号</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;th_ack;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">32位确认号</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;th_lenres;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">4位首部长度/6位保留字</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;th_flag;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">6位标志位</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;USHORT&nbsp;th_win;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">16位窗口大小</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;USHORT&nbsp;th_sum;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">16位校验和</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;USHORT&nbsp;th_urp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">16位紧急数据偏移量</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}TCP_HEADER;&nbsp;</span></div>
<p align=left><span>每个</span><span>TCP</span><span>段都包含源端和目的端的端口号，用于寻找发端和收端应用进程。这两个值加上</span><span>IP</span><span>首部中的源端</span><span>IP</span><span>地址和目的端</span><span>IP</span><span>地址唯一确定一个</span><span>TCP</span><span>连接。有时，一个</span><span>IP</span><span>地址和一个端口号也称为一个插口（</span><span>socket</span><span>）。插口对（</span><span>socket &nbsp;pair</span><span>）</span><span>(</span><span>包含客户</span><span>IP</span><span>地址、客户端口号、服务器</span><span>IP</span><span>地址和服务器端口号的四元组</span><span>)</span><span>可唯一确定互联网络中每个</span><span>TCP</span><span>连接的双方。</span></p>
<p align=left><span>URG </span><span>紧急指针（</span><span>urgent pointer</span><span>）有效。</span></p>
<p align=left><span>ACK </span><span>确认序号有效。</span></p>
<p align=left><span>PSH </span><span>接收方应该尽快将这个报文段交给应用层。</span></p>
<p align=left><span>RST </span><span>重建连接。</span></p>
<p align=left><span>SYN </span><span>同步序号用来发起一个连接。</span></p>
<p align=left><span>FIN </span><span>发端完成发送任务。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>TCP</span><span>的流量控制由连接的每一端通过声明的窗口大小来提供。</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>TCP</span><span>检验和的计算和</span><span>UDP</span><span>检验和的计算相似，使用如</span><span>11.3</span><span>节所述的一个伪首部。<span>(???)</span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>选项：</span></p>
<p align=left><span>MSS (Maximum Segment Size)</span><span>，最长报文大小，每个连接方通常都在通信的第一个报文段（为建立连接而设置</span><span>S Y N</span><span>标志的那个段）中指明这个选项。它指明本端所能接收的最大长度的报文段。</span></p>
<p><br><br><span style="FONT-SIZE: 18pt"><u>IPv4头和TCP头校验和计算算法</u><br><br>IP首部校验和的计算方法：</span></p>
<p><span style="FONT-SIZE: 18pt">◆当发送IP包时，需要计算IP报头的校验和：<br>1、把校验和字段置为0；<br>2、对IP头部中的每16bit进行二进制求和；<br>3、如果和的高16bit不为0，则将和的高16bit和低16bit反复相加，直到和的高16bit为0，从而获得一个16bit的值；<br>4、将该16bit的值取反，存入校验和字段。</span></p>
<p><span style="FONT-SIZE: 18pt">◆当接收IP包时，需要对报头进行确认，检查IP头是否有误，算法同上2、3步，然后判断取反的结果是否为0，是则正确，否则有错。</span></p>
<p><span style="FONT-SIZE: 18pt">&nbsp;<br>算法：</span></span></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #000000">SHORT&nbsp;checksum(USHORT</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;buffer,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;size)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;cksum&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">step&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">&nbsp;(size&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cksum&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">buffer</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size&nbsp;</span><span style="COLOR: #000000">-=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(USHORT);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(size)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cksum&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(UCHAR</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)buffer;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">step&nbsp;3</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;cksum&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(cksum&nbsp;</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">16</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;(cksum&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0xffff</span><span style="COLOR: #000000">);&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">将高16bit与低16bit相加</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;cksum&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;(cksum&nbsp;</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">16</span><span style="COLOR: #000000">);&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">将进位到高位的16bit与低16bit&nbsp;再相加&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">step&nbsp;4</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(USHORT)(</span><span style="COLOR: #000000">~</span><span style="COLOR: #000000">cksum);<br>}</span></div>
<p>实例：</p>
<p>IP头：&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 45 00&nbsp;&nbsp;&nbsp; 00 31</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 89 F5&nbsp;&nbsp;&nbsp; 00 00</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6E 06&nbsp;&nbsp;&nbsp; 00 00（校验字段）</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DE B7&nbsp;&nbsp; 45 5D&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;&nbsp;&nbsp;&nbsp; 222.183.69.93</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C0 A8&nbsp;&nbsp; 00 DC&nbsp;&nbsp;&nbsp;&nbsp; -&gt;&nbsp;&nbsp;&nbsp; 192.168.0.220</p>
<p>计算：&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; 4500 + 0031 +89F5 + 0000 + 6e06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4</p>
<p>&nbsp;&nbsp;&nbsp; 0003 + 22C4 = 22C7</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; ~22C7 = DD38&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;即为应填充的校验和</p>
<p>当接受到IP数据包时，要检查IP头是否正确，则对IP头进行检验，方法同上：</p>
<p>计算：</p>
<p>&nbsp;&nbsp;&nbsp; 4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC</p>
<p>&nbsp;&nbsp;&nbsp; 0003 + FFFC = FFFF</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; ~FFFF = 00000&nbsp;&nbsp;&nbsp;&nbsp; -&gt;正确</p>
<p>&nbsp;</p>
<p>TCP首部检验和与IP首部校验和的计算方法相同，在程序中使用同一个函数来计算。</p>
<p>需要注意的是，由于TCP首部中不包含源地址与目标地址等信息，为了保证TCP校验的有效性，在进行TCP校验和的计算时，需要增加一个TCP伪首部的校验和，定义如下：</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;saddr;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">源地址</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;daddr;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">目的地址</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;mbz;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">置空</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;ptcl;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">协议类型</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;tcpl;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">TCP长度</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}&nbsp;psd_header;</span></div>
<p>然后我们将这两个字段复制到同一个缓冲区SendBuf中并计算TCP校验和：</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #000000">memcpy(SendBuf,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">psd_header,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(psd_header));&nbsp;<br><br>memcpy(SendBuf&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(psd_header),&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tcp_header,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(tcp_header));<br><br>tcp_header.th_sum&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;checksum((USHORT&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)SendBuf,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(psd_header)&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(tcp_header));</span></div>
<br>
<img src ="http://www.cppblog.com/toMyself/aggbug/123932.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-19 10:27 <a href="http://www.cppblog.com/toMyself/archive/2010/08/19/123932.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数inet_addr和inet_ntoa</title><link>http://www.cppblog.com/toMyself/archive/2010/08/13/123353.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Fri, 13 Aug 2010 09:46:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/13/123353.html</guid><description><![CDATA[<p>inet_addr 将"数字+句点"的格式的IP地址转换到unsigned long中,返回值已经是按照网络字节顺序的<br>相反inet_ntoa把类型为struct in_addr的数据转化为"数字+句点"的形式的字符串<br>typedef u_int32_t in_addr_t;<br>struct in_addr<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in_addr_t s_addr;<br>};</p>
<p>本机字节顺序与网络字节顺序的转换<br>#include &lt;arpa/inet.h&gt;<br>htons&nbsp; ------"host to network short"<br>htonl&nbsp;&nbsp; -------"host to network long"<br>ntohs&nbsp; -------"network to host short"<br>ntohl&nbsp;&nbsp; -------"network to host long"<br>*注意:在你的数据放到网络上的时候，确信它是网络字节顺序<br>网络字节顺序(大端字节)和x86机器字节顺序(小端字节)<br>eg:0X3132&nbsp; 在x86上显示21&nbsp; 在网络传输中为12</p>
<p>inet_addr返回的整数形式是网络字节序，而inet_network返回的整数形式是主机字节序。他俩都有一个小缺陷，<br>那就是当IP是255.255.255.255时，这两个函数会认为这是个无效的IP地址，这是历史遗留问题，其实在目前大部<br>分的路由器上，这个255.255.255.255的IP都是有效的。<br>inet_aton函数和上面这俩个函数的区别就是在于他认为255.255.255.255是有效的，他不会冤枉这个看似特殊的IP地址。对了，inet_aton函数返回的是网络字节序的IP地址。</p>
<p>综上所述，应该使用inet_aton和inet_ntoa这一对函数。<br><br><br>资料：</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">socket.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">netinet</span><span style="COLOR: #000000">/</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">arpa</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">inet.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><br>typedef&nbsp;uint32_t&nbsp;in_addr_t;<br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;inet_aton(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cp,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">inp);<br>in_addr_t&nbsp;inet_addr(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cp);<br>in_addr_t&nbsp;inet_network(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cp);<br></span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">inet_ntoa(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;inet_makeaddr(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;net,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;host);<br>in_addr_t&nbsp;inet_lnaof(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">);<br>in_addr_t&nbsp;inet_netof(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">);</span></div>
<p><br>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Internet&nbsp;address.</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;union&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;{&nbsp;u_char&nbsp;s_b1,s_b2,s_b3,s_b4;&nbsp;}&nbsp;S_un_b;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;{&nbsp;u_short&nbsp;s_w1,s_w2;&nbsp;}&nbsp;S_un_w;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u_long&nbsp;S_addr;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;port&nbsp;in&nbsp;network&nbsp;byte&nbsp;order&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;S_un;<br></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;s_addr&nbsp;&nbsp;S_un.S_addr</span><span style="COLOR: #000000"><br>};<br></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Socket&nbsp;address,&nbsp;internet&nbsp;style.</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr_in&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;struct&nbsp;sockaddr的一种特殊形式</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sin_family;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;address&nbsp;family:&nbsp;AF_INET&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u_short&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sin_port;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;port&nbsp;in&nbsp;network&nbsp;byte&nbsp;order&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;sin_addr;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;port&nbsp;in&nbsp;network&nbsp;byte&nbsp;order&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sin_zero[</span><span style="COLOR: #000000">8</span><span style="COLOR: #000000">];&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;8&nbsp;byte&nbsp;pad&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>};<br></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Structure&nbsp;used&nbsp;by&nbsp;kernel&nbsp;to&nbsp;store&nbsp;most&nbsp;addresses.</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u_short&nbsp;sa_family;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;address&nbsp;family&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;sa_data[</span><span style="COLOR: #000000">14</span><span style="COLOR: #000000">];&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;up&nbsp;to&nbsp;14&nbsp;bytes&nbsp;of&nbsp;direct&nbsp;address&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>};<br><br></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;in_addr&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;s_addr;<br>}<br></span></div>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/toMyself/aggbug/123353.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-13 17:46 <a href="http://www.cppblog.com/toMyself/archive/2010/08/13/123353.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ioctl</title><link>http://www.cppblog.com/toMyself/archive/2010/08/13/123302.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Fri, 13 Aug 2010 02:48:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/13/123302.html</guid><description><![CDATA[<div style="MARGIN: 15px" id=art width="560">
<div>
<p align=left><strong>ioctl</strong> 函数 </p>
<p align=left></p>
<p align=left>本函数影响由fd 参数引用的一个打开的文件。 </p>
<p align=left>&nbsp; </p>
<p align=left>#include&lt;unistd.h&gt; </p>
<p align=left>int <strong>ioctl</strong>( int fd, int <a name=baidusnap1></a><strong>request</strong>, .../* void *arg */ ); </p>
<p align=left>返回0 ：成功&nbsp;&nbsp;&nbsp; -1 ：出错 </p>
<p align=left>&nbsp; </p>
<p align=left>第三个参数总是一个指针，但指针的类型依赖于<strong>request</strong> 参数。 </p>
<p align=left>我们可以把和网络相关的请求划分为6 类： </p>
<p align=left>套接口操作 </p>
<p align=left>文件操作 </p>
<p align=left>接口操作 </p>
<p align=left>ARP 高速缓存操作 </p>
<p align=left>路由表操作 </p>
<p align=left>流系统 </p>
<p align=left>下表列出了网络相关<strong>ioctl</strong> 请求的<strong>request</strong> 参数以及arg 地址必须指向的数据类型： </p>
<p align=left>&nbsp; </p>
<table border=0 cellSpacing=0 cellPadding=0 width=576>
    <tbody>
        <tr>
            <td vAlign=top width=60>
            <p align=center>类别 </p>
            </td>
            <td vAlign=top width=144>
            <p align=center><strong>Request</strong> </p>
            </td>
            <td vAlign=top width=264>
            <p align=center>说明 </p>
            </td>
            <td vAlign=top width=108>
            <p align=center>数据类型 </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p align=left><strong>套 </strong></p>
            <p align=left><strong>接 </strong></p>
            <p align=left><strong>口 </strong></p>
            </td>
            <td vAlign=top width=144>
            <p align=left>SIOCATMARK </p>
            <p align=left>SIOCSPGRP </p>
            <p align=left>SIOCGPGRP </p>
            </td>
            <td vAlign=top width=264>
            <p align=left>是否位于带外标记 </p>
            <p align=left>设置套接口的进程ID 或进程组ID </p>
            <p align=left>获取套接口的进程ID 或进程组ID </p>
            </td>
            <td vAlign=top width=108>
            <p align=left>int </p>
            <p align=left>int </p>
            <p align=left>int </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p align=left>&nbsp; </p>
            <p align=left><strong>文 </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>件 </strong></p>
            <p align=left>&nbsp; </p>
            <p align=left>&nbsp; </p>
            </td>
            <td vAlign=top width=144>
            <p align=left>FIONBIN </p>
            <p align=left>FIOASYNC </p>
            <p align=left>FIONREAD </p>
            <p align=left>FIOSETOWN </p>
            <p align=left>FIOGETOWN </p>
            <p align=left>&nbsp; </p>
            </td>
            <td vAlign=top width=264>
            <p align=left>设置/ 清除非阻塞I/O 标志 </p>
            <p align=left>设置/ 清除信号驱动异步I/O 标志 </p>
            <p align=left>获取接收缓存区中的字节数 </p>
            <p align=left>设置文件的进程ID 或进程组ID </p>
            <p align=left>获取文件的进程ID 或进程组ID </p>
            </td>
            <td vAlign=top width=108>
            <p align=left>int </p>
            <p align=left>int </p>
            <p align=left>int </p>
            <p align=left>int </p>
            <p align=left>int </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p align=left>&nbsp; </p>
            <p align=left>&nbsp; </p>
            <p align=left>&nbsp; </p>
            <p align=left>&nbsp; </p>
            <p align=left><strong>接 </strong></p>
            <p align=left><strong>口 </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left><strong>&nbsp; </strong></p>
            <p align=left>&nbsp; </p>
            </td>
            <td vAlign=top width=144>
            <p align=left>SIOCGIFCONF </p>
            <p align=left>SIOCSIFADDR </p>
            <p align=left>SIOCGIFADDR </p>
            <p align=left>SIOCSIFFLAGS </p>
            <p align=left>SIOCGIFFLAGS </p>
            <p align=left>SIOCSIFDSTADDR </p>
            <p align=left>SIOCGIFDSTADDR </p>
            <p align=left>SIOCGIFBRDADDR </p>
            <p align=left>SIOCSIFBRDADDR </p>
            <p align=left>SIOCGIFNETMASK </p>
            <p align=left>SIOCSIFNETMASK </p>
            <p align=left>SIOCGIFMETRIC </p>
            <p align=left>SIOCSIFMETRIC </p>
            <p align=left>SIOCGIFMTU </p>
            <p align=left>SIOCxxx </p>
            </td>
            <td vAlign=top width=264>
            <p align=left>获取所有接口的清单 </p>
            <p align=left>设置接口地址 </p>
            <p align=left>获取接口地址 </p>
            <p align=left>设置接口标志 </p>
            <p align=left>获取接口标志 </p>
            <p align=left>设置点到点地址 </p>
            <p align=left>获取点到点地址 </p>
            <p align=left>获取广播地址 </p>
            <p align=left>设置广播地址 </p>
            <p align=left>获取子网掩码 </p>
            <p align=left>设置子网掩码 </p>
            <p align=left>获取接口的测度 </p>
            <p align=left>设置接口的测度 </p>
            <p align=left>获取接口MTU </p>
            <p align=left>（还有很多取决于系统的实现） </p>
            </td>
            <td vAlign=top width=108>
            <p align=left>struct ifconf </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            <p align=left>struct ifreq </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p align=left>&nbsp; </p>
            <p align=left><strong>ARP </strong></p>
            </td>
            <td vAlign=top width=144>
            <p align=left>SIOCSARP </p>
            <p align=left>SIOCGARP </p>
            <p align=left>SIOCDARP </p>
            </td>
            <td vAlign=top width=264>
            <p align=left>创建/ 修改ARP 表项 </p>
            <p align=left>获取ARP 表项 </p>
            <p align=left>删除ARP 表项 </p>
            </td>
            <td vAlign=top width=108>
            <p align=left>struct arpreq </p>
            <p align=left>struct arpreq </p>
            <p align=left>struct arpreq </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p align=left><strong>路 </strong></p>
            <p align=left><strong>由 </strong></p>
            </td>
            <td vAlign=top width=144>
            <p align=left>SIOCADDRT </p>
            <p align=left>SIOCDELRT </p>
            </td>
            <td vAlign=top width=264>
            <p align=left>增加路径 </p>
            <p align=left>删除路径 </p>
            </td>
            <td vAlign=top width=108>
            <p align=left>struct rtentry </p>
            <p align=left>struct rtentry </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>
            <p align=left><strong>流 </strong></p>
            </td>
            <td vAlign=top width=144>
            <p align=left>I_xxx </p>
            </td>
            <td vAlign=top width=264>
            <p align=left>&nbsp; </p>
            </td>
            <td vAlign=top width=108>
            <p align=left>&nbsp; </p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left>&nbsp; </p>
<p align=left>&nbsp; </p>
<p align=left><strong>套接口操作： </strong></p>
<p align=left>明确用于套接口操作的<strong>ioctl</strong> 请求有三个, 它们都要求<strong>ioctl</strong> 的第三个参数是指向某个整数的一个指针。 </p>
<p align=left>&nbsp; </p>
<p align=left>SIOCATMARK:&nbsp;&nbsp;&nbsp; 如果本套接口的的度指针当前位于带外标记，那就通过由第三个参数指向的整数返回一个非0 值；否则返回一个0 值。POSIX 以函数sockatmark 替换本请求。 </p>
<p align=left>SIOCGPGRP ：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过第三个参数指向的整数返回本套接口的进程ID 或进程组ID ，该ID 指定针对本套接口的SIGIO 或SIGURG 信号的接收进程。本请求和fcntl 的F_GETOWN 命令等效，POSIX 标准化的是fcntl 函数。 </p>
<p align=left>SIOCSPGRP ：&nbsp;&nbsp;&nbsp;&nbsp; 把本套接口的进程ID 或者进程组ID 设置成第三个参数指向的整数，该ID 指定针对本套接口的SIGIO 或SIGURG 信号的接收进程，本请求和fcntl 的F_SETOWN 命令等效，POSIX 标准化的是fcntl 操作。 </p>
<p align=left>&nbsp; </p>
<p align=left><strong>文件操作： </strong></p>
<p align=left>以下5 个请求都要求<strong>ioctl</strong> 的第三个参数指向一个整数。 </p>
<p align=left>&nbsp; </p>
<p align=left>FIONBIO ：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据<strong>ioctl</strong> 的第三个参数指向一个0 或非0 值分别清除或设置本套接口的非阻塞标志。本请求和O_NONBLOCK 文件状态标志等效，而该标志通过fcntl 的F_SETFL 命令清除或设置。 </p>
<p align=left>&nbsp; </p>
<p align=left>FIOASYNC ：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据iocl 的第三个参数指向一个0 值或非0 值分别清除或设置针对本套接口的信号驱动异步I/O 标志，它决定是否收取针对本套接口的异步I/O 信号（SIGIO ）。本请求和O_ASYNC 文件状态标志等效，而该标志可以通过fcntl 的F_SETFL 命令清除或设置。 </p>
<p align=left>&nbsp; </p>
<p align=left>FIONREAD ：&nbsp;&nbsp;&nbsp;&nbsp; 通过由<strong>ioctl</strong> 的第三个参数指向的整数返回当前在本套接口接收缓冲区中的字节数。本特性同样适用于文件，管道和终端。 </p>
<p align=left>&nbsp; </p>
<p align=left>FIOSETOWN ：&nbsp;&nbsp;&nbsp; 对于套接口和SIOCSPGRP 等效。 </p>
<p align=left>FIOGETOWN ：&nbsp;&nbsp;&nbsp; 对于套接口和SIOCGPGRP 等效。 </p>
<p align=left>&nbsp; </p>
<p align=left>接口配置： </p>
<p align=left>得到系统中所有接口由SIOCGIFCONF 请求完成，该请求使用ifconf 结构，ifconf 又使用ifreq </p>
<p align=left>结构，如下所示： </p>
<p align=left>&nbsp; </p>
<p align=left>Struct ifconf{ </p>
<p align=left>&nbsp;&nbsp;&nbsp; int ifc_len;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 缓冲区的大小 </p>
<p align=left>&nbsp;&nbsp;&nbsp; union{ </p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; caddr_t ifcu_buf;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // input from user-&gt;kernel </p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ifreq *ifcu_req;&nbsp;&nbsp;&nbsp; // return of structures returned </p>
<p align=left>&nbsp;&nbsp;&nbsp; }ifc_ifcu; </p>
<p align=left>}; </p>
<p align=left>&nbsp; </p>
<p align=left>#define&nbsp; ifc_buf&nbsp; ifc_ifcu.ifcu_buf&nbsp;&nbsp;&nbsp; //buffer address </p>
<p align=left>#define&nbsp; ifc_req&nbsp; ifc_ifcu.ifcu_req&nbsp;&nbsp;&nbsp; //array of structures returned </p>
<p align=left>&nbsp; </p>
<p align=left>#define&nbsp; IFNAMSIZ&nbsp; 16 </p>
<p align=left>&nbsp; </p>
<p align=left>struct ifreq{ </p>
<p align=left>&nbsp;&nbsp;&nbsp; char ifr_name[IFNAMSIZ];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // interface name, e.g., &#8220;le0&#8221; </p>
<p align=left>&nbsp;&nbsp; &nbsp;union{ </p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct sockaddr ifru_addr; </p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct sockaddr ifru_dstaddr; </p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct sockaddr ifru_broadaddr; </p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; short ifru_flags; </p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int ifru_metric; </p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; caddr_t ifru_data; </p>
<p align=left>&nbsp;&nbsp;&nbsp; }ifr_ifru; </p>
<p align=left>}; </p>
<p align=left>&nbsp; </p>
<p align=left>#define ifr_addr&nbsp;&nbsp;&nbsp;&nbsp; ifr_ifru.ifru_addr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // address </p>
<p align=left>#define ifr_dstaddr&nbsp;&nbsp; ifr_ifru.ifru_dstaddr &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // otner end of p-to-p link </p>
<p align=left>#define ifr_broadaddr ifr_ifru.ifru_broadaddr&nbsp;&nbsp;&nbsp; // broadcast address </p>
<p align=left>#define ifr_flags&nbsp;&nbsp;&nbsp;&nbsp; ifr_ifru.ifru_flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // flags </p>
<p align=left>#define ifr_metric&nbsp;&nbsp;&nbsp; ifr_ifru.ifru_metric&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // metric </p>
<p align=left>#define ifr_data&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ifr_ifru.ifru_data&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // for use by interface </p>
<p align=left>&nbsp; </p>
<p align=left>再调用<strong>ioctl</strong> 前我们必须先分撇一个缓冲区和一个ifconf 结构，然后才初始化后者。如下图 </p>
<p align=left>展示了一个ifconf 结构的初始化结构，其中缓冲区的大小为1024 ，<strong>ioctl</strong> 的第三个参数指向 </p>
<p align=left>这样一个ifconf 结构。 </p>
<table border=0 cellSpacing=0 cellPadding=0 align=left>
    <tbody>
        <tr>
            <td vAlign=top width=83>
            <p align=left>ifc_len </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=83>
            <p align=left>&nbsp;Ifc_buf </p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left>1024 </p>
<p align=left>---------------------&gt; 缓存 </p>
<p align=left>&nbsp; </p>
<p align=left>&nbsp; </p>
<p align=left>假设内核返回2 个ifreq 结构，<strong>ioctl</strong> 返回时通过同一个ifconf 结构缓冲区填入了那2 个ifreq 结构，ifconf 结构的ifc_len 成员也被更新，以反映存放在缓冲区中的信息量 </p>
</div>
<div>一般来讲<strong>ioctl</strong>在用户程序中的调用是： </div>
<div><strong>ioctl</strong>(int fd,int command, (char*)argstruct) </div>
<div><strong>ioctl</strong>调用与网络编程有关（本文只讨论这一点），文件描述符fd实际上是由socket()系统调用返回的。参数command的取值由/usr/include/linux/sockios.h 所规定。这些command的由于功能的不同，可分为以下几个小类：<br>&#8226; 改变路由表 (例如 SIOCADDRT, SIOCDELRT), <br>&#8226; 读/更新 ARP/RARP 缓存(如：SIOCDARP, SIOCSRARP), <br>&#8226; 一般的与网络接口有关的(例如 SIOCGIFNAME, SIOCSIFADDR 等等) <br>在 Gooodies目录下有很多样例程序展示了如何使用<strong>ioctl</strong>。当你看这些程序时，注意参数argstruct是与参数command相关的。例如，与路由表相关的<strong>ioctl</strong>使用rtentry这种结构，rtentry定义在/usr/include/linux/route.h（参见例子 adddefault.c）。与ARP有关的<strong>ioctl</strong>调用使用arpreq结构，arpreq定义在/usr/include/linux /if_arp.h（参见例子arpread.c） </div>
<div>与网络接口有关的<strong>ioctl</strong>调用使用的command参数通常看起来像 SIOCxIFyyyy的形式，这里x要么是S（设定set，写write），要么是G（得到get，读read）。在getifinfo.c程序中就使用了这种形式的command参数来读 IP地址，硬件地址，广播地址和得到与网络接口有关的一些标志（flag）。在这些<strong>ioctl</strong>调用中，第三个参数是ifreq结构，它在/usr /include/linux/if.h中定义。在某些情况下， ioctrl调用可能会使用到在sockios.h之外的新的定义，例如，WaveLAN无线网络卡会保存有关无线网络信号强度的信息，这对用户的程序可 能有用。但用户怎么得到这种信息呢？我们的第一个本能是在sockios.h中定义新的ioctl命令，例如SIOCGIFWVLNSS（它的英文缩写表 示WaveLAN的信号强度）。但不幸的是，这种命令不是对所有其他的网络接口（例如：loopback环回接口）有意义，而且不应当允许对于 WAVLAN卡以外的网络接口使用ioctl命令。那么，我们需要的是这样一种机制：它能够定义一种与网络接口相关的ioctl命令。幸运的是，在 Linux操作系统中已经为实现这个目的内建了一种挂钩（hook）机制。当你再次看sockios.h文件时，你将发现每一种设备已经预先定义了 SIOCDEVPRIVATE的ioctl命令。而它的实现将留给开发相应驱动程序的人去完成。<br>通常，一个用户程序使用ioctl (sockid,SIOCDEVPRIVATE,(char*)&amp;ifr)来调用与某种设备（指像WaveLAN那样的特殊设备）相关的 ioctl命令，这里ifr是struct ifreq ifr形式的变量。用户程序应当在ifr.ifr_name中填充与这个设备相关的名字，例如，假设WaveLAN使用的接口号为eth1。一般的，一个 用户程序还需要与内核互相交换ioctl的command参数和结果，这可以通过ifr.ifr_data这个变量来实现，例如，想得到WaveLAN中 表示信号强度的信息时，可以通过返回这个变量来实现。Linux的源代码已经包括了两种设备de4x5和ewrk3，它们定义并且实现了特定的ioctl 调用。这两个设备的源代码在de4x5.h,de4x5.c,ewrk3.h,ewrk3.c中（在 /usr/src/linux/drivers/net/目录中）。这两种设备都定义了它们特有的结构（struct ewrk3_ioctl 和 struct de4x5_ioctl）来方便用户程序和设备驱动之间交换信息。每次调用ioctl前，用户程序应当在相应的结构变量中设定合适的初值，并且将 ifr.ifr_data指向该值。<br>在我们进一步讨论ewrk3和de4x5的代码前，让我们仔细看看ioctl调用是如何一步步地实现的。所有的和接口相关的ioctl请求 (SIOCxIFyyyy 和 SIOCDEVPRIVATE)将会调用dev_ioctl()（在/usr/src/linux/net/core/dev.c中）。但这只是一个包装 器（wrapper），实际的动作将由dev_ifsioc()（也在dev.c中）来实现。差不多dev_ioctl()这个函数所做的所有工作只是检 查这个调用是否已经有了正当的权限（例如，改变路由表需要有root的权限）。而dev_ifsioc()这个函数首先要做的一些事情包括得到与 ifr.ifr_name相匹配的设备的结构（在/usr/include/linux/netdevice.h中定义）。但这是在实现特定的接口命令 （例如：SIOCGIFADDR）之后。这些特定的接口命令被放置到一个巨大的switch语句之中。其中SIOCDEVPRIVATE命令和其他的在 0x89F0到0x89FF之间的代码将出现在switch语句中的一个分支——default语句中。内核会检查表示设备的结构变量中，是否已经定义了 一个与设备相关的ioctl句柄（handler）。这里的句柄是一个函数指针，它在表示设备的结构变量中do_ioctl部分。如果已经设置了这个句 柄，那么内核将会执行它。<br>所以，如果要实现一个与设备相关的ioctl命令，所要做的只是编写一个与这个设备相关的ioctl句柄，并且将表示这 个设备的结构变量中do_ioctl部分指向这个句柄。对于ewrk3这个设备，它的句柄是ewrk3_ioctl()（在ewrk3.c里面）并且相应 的表示该设备的结构变量由ewrk3_init()来初始化。在ewrk3_ioctl()的代码中清晰的指出ifr.ifr_data是用作设备驱动程 序和用户程序之间交换信息的。注意，这部分的内存可以双向的交流信息。例如，在ewrk3的驱动程序代码中，if.ifr_data的头两个字节是用来表 示特殊的动作（例如，EWRK3_SET_PROM,EWRK3_CLR_PROM），而这个动作是符合使用者（驱动程序实现了多个与设备相关的、由 SIOCDEVPRIVATE调用的命令）的要求的。另外，ifr.ifr_data中第5个字节指向的缓冲区(buffer)被用来交换其他的信息 （如：当使用EWRK3_SET_HWADDR和EWRK3_GET_HWADDR时为硬件地址）<br>在你深入ewrk3_ioctl()时，请注意一般情况下一个用户进程不能直接访问内核所在的内存。为此，驱动开发者可以使用两个特殊的函数 memcpy_tofs()和memcpy_fromfs()。内核函数memcpy_tofs(arg1, arg2, arg3) 从地址arg2（用户空间）向地址arg1(内核空间)拷贝arg3个字节。类似的，memcpy_fromfs(arg1,arg2,arg3)从地址 arg2（用户空间）向地址arg1（内核空间）拷贝arg3个字节。在这些调用之前，verify_area()将会检查这个进程是否拥有合适的访问权 限。另外，注意使用printk()函数可以输出debug信息。这个函数与printf()函数类似，但不能处理浮点类型的数。内核代码不能够使用 printf()函数。printk()函数产生的结果将记录在/usr/adm/messages里。如果想知道更多的关于这些函数的或者与它们相关的 信息，可以参考《Linux Kernel Hacker&#8217;s Guide》（在Linux文档网站的首页） 这本书中Supporting Functions部分。<br><br><br>使用ioctl与内核交换数据
<p>1. 前言 <br><br>使用ioctl系统调用是用户空间向内核交换数据的常用方法之一，从ioctl这个名称上看，本意是针对I/O设备进行的控制操作，但实际并不限制是真正的I/O设备，可以是任何一个内核设备即可。</p>
<p>2. 基本过程<br><br>在内核空间中ioctl是很多内核操作结构的一个成员函数，如文件操作结构struct file_operations(include/linux/fs.h)、协议操作结构struct proto_ops(include/linux/net.h)等、tty操作结构struct tty_driver(include/linux/tty_driver.h)等，而这些操作结构分别对应各种内核设备，只要在用户空间打开这些设备， 如I/O设备可用open(2)打开，网络协议可用socket(2)打开等，获取一个文件描述符后，就可以在这个描述符上调用ioctl(2)来向内核 交换数据。<br><br>3. ioctl(2)<br><br>ioctl(2)函数的基本使用格式为：<br>int ioctl(int fd, int cmd, void *data)<br>第一个参数是文件描述符；cmd是操作命令，一般分为GET、SET以及其他类型命令，GET 是用户空间进程从内核读数据，SET是用户空间进程向内核写数据，cmd虽然是一个整数，但是有一定的参数格式的，下面再详细说明；第三个参数是数据起始位置指针，<br>cmd命令参数是个32位整数，分为四部分：<br>dir(2b) size(14b) type(8b) nr(8b)<br>详细定义cmd要包括这4个部分时可使用宏_IOC(dir,type,nr,size)来定义，而最简单情况下使用_IO(type, nr)来定义就可以了，这些宏都在include/asm/ioctl.h中定义<br>本文cmd定义为：<br>#define NEWCHAR_IOC_MAGIC&nbsp;&nbsp; 'M'<br>#define NEWCHAR_SET&nbsp;&nbsp;&nbsp; _IO(NEWCHAR_IOC_MAGIC, 0)<br>#define NEWCHAR_GET&nbsp;&nbsp;&nbsp; _IO(NEWCHAR_IOC_MAGIC, 1)<br>#define NEWCHAR_IOC_MAXNR&nbsp;&nbsp; 1</p>
<p>要定义自己的ioctl操作，可以有两个方式，一种是在现有的内核代码中直接添加相关代码进行支持，比如想通过socket描述符进行 ioctl操作，可在net/ipv4/af_inet.c中的inet_ioctl()函数中添加自己定义的命令和相关的处理函数，重新编译内核即可， 不过这种方法一般不推荐；第二种方法是定义自己的内核设备，通过设备的ioctl()来操作，可以编成模块，这样不影响原有的内核，这是最通常的做法。<br><br>4. 内核设备<br><br>为进行ioctl操作最通常是使用字符设备来进行，当然定义其他类型的设备也可以。在用户空间，可使用mknod命令建立一个字符类型设备文件，假设该设备的主设备号为123，次设备号为0：<br>mknode /dev/newchar c 123 0<br>如果是编程的话，可以用mknode(2)函数来建立设备文件。<br><br>建立设备文件后再将该设备的内核模块文件插入内核，就可以使用open(2)打开 /dev/newchar文件，然后调用ioctl(2)来传递数据，最后用close(2)关闭设备。而如果内核中还没有插入该设备的模块，open(2)时就会失败。<br><br>由于内核内存空间和用户内存空间不同，要将内核数据拷贝到用户空间，要使用专用拷贝函数 copy_to_user()；要将用户空间数据拷贝到内核，要使用copy_from_user()。<br>要最简单实现以上功能，内核模块只需要实现设备的open, ioctl和release三个函数即可，<br>下面介绍程序片断：<br>static int newchar_ioctl(struct inode *inode, struct file *filep, <br>&nbsp;&nbsp; unsigned int cmd, unsigned long arg);<br>static int newchar_open(struct inode *inode, struct file *filep);<br>static int newchar_release(struct inode *inode, struct file *filep);<br>// 定义文件操作结构，结构中其他元素为空<br>struct file_operations newchar_fops = <br>{<br>owner: THIS_MODULE,<br>ioctl: newchar_ioctl,<br>open: newchar_open,<br>release: newchar_release,<br>};<br>// 定义要传输的数据块结构<br>struct newchar{<br>int a;<br>int b;<br>};<br>#define MAJOR_DEV_NUM 123<br>#define DEVICE_NAME "newchar"<br><br>打开设备，非常简单，就是增加模块计数器，防止在打开设备的情况下删除模块,<br>当然想搞得复杂的话可进行各种限制检查，如只允许指定的用户打开等：<br>static int newchar_open(struct inode *inode, struct file *filep)<br>{<br>MOD_INC_USE_COUNT;<br>return 0;<br>}</p>
<p>关闭设备，也很简单，减模块计数器：<br>static int newchar_release(struct inode *inode, struct file *filep)<br>{<br>MOD_DEC_USE_COUNT;<br>return 0;<br>}</p>
<p>进行ioctl调用的基本处理函数<br>static int newchar_ioctl(struct inode *inode, struct file *filep, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int cmd, unsigned long arg)<br>{<br>int ret;<br>// 首先检查cmd是否合法<br>if (_IOC_TYPE(cmd) != NEWCHAR_IOC_MAGIC) return -EINVAL;<br>if (_IOC_NR(cmd) &gt; NEWCHAR_IOC_MAXNR) return -EINVAL;<br>// 错误情况下的缺省返回值<br>ret = EINVAL;<br>switch(cmd)<br>{<br>case KNEWCHAR_SET:<br>// 设置操作，将数据从用户空间拷贝到内核空间<br>{<br>&nbsp;&nbsp; struct newchar nc;<br>&nbsp;&nbsp; if(copy_from_user(&amp;nc, (const char*)arg, sizeof(nc)) != 0)<br>&nbsp;&nbsp;&nbsp; return -EFAULT;<br>&nbsp;&nbsp; ret = do_set_newchar(&amp;nc);<br>}<br>break;<br>case KNEWCHAR_GET:<br>// GET操作通常会在数据缓冲区中先传递部分初始值作为数据查找条件，获取全部<br>// 数据后重新写回缓冲区<br>// 当然也可以根据具体情况什么也不传入直接向内核获取数据<br>{<br>&nbsp;&nbsp; struct newchar nc;<br>&nbsp;&nbsp; if(copy_from_user(&amp;nc, (const char*)arg, sizeof(nc)) != 0)<br>&nbsp;&nbsp;&nbsp; return -EFAULT;<br>&nbsp;&nbsp; ret = do_get_newchar(&amp;nc);<br>&nbsp;&nbsp; if(ret == 0){<br>&nbsp;&nbsp;&nbsp; if(copy_to_user((unsigned char *)arg, &amp;nc, sizeof(nc))!=0)<br>&nbsp;&nbsp;&nbsp;&nbsp; return -EFAULT;<br>&nbsp;&nbsp; }<br>}<br>break;<br>}<br>return ret;<br>}<br>模块初始化函数，登记字符设备<br>static int __init _init(void)<br>{<br>int result;<br>// 登记该字符设备，这是2.4以前的基本方法，到2.6后有了些变化，<br>// 是使用MKDEV和cdev_init()来进行，本文还是按老方法<br>result = register_chrdev(MAJOR_DEV_NUM, DEVICE_NAME, &amp;newchar_fops);<br>if (result &lt; 0) {<br>printk(KERN_WARNING __FUNCTION__ ": failed register character device for /dev/newchar\n");<br>return result;<br>}<br>return 0;<br>}</p>
模块退出函数，登出字符设备<br>static void __exit _cleanup(void)<br>{<br>int result;<br>result = unregister_chrdev(MAJOR_DEV_NUM, DEVICE_NAME);<br>if (result &lt; 0)<br>printk(__FUNCTION__ ": failed unregister character device for /dev/newchar\n");<br>return;<br>} <br>module_init(_init);<br>module_exit(_cleanup);<br><br>5. 结论<br><br>用ioctl()在用户空间和内核空间传递数据是最常用方法之一，比较简单方便，而且可以在同一个ioctl中对不同的命令传送不同的数据结构，本文只是为描述方便而在不同命令中使用了相同的数据结构。</div>
</div>
<img src ="http://www.cppblog.com/toMyself/aggbug/123302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-13 10:48 <a href="http://www.cppblog.com/toMyself/archive/2010/08/13/123302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>setsockopt</title><link>http://www.cppblog.com/toMyself/archive/2010/08/13/123300.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Fri, 13 Aug 2010 02:39:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/13/123300.html</guid><description><![CDATA[<p><strong>一、设置 SO_LINGER 选项</strong></p>
<p>&nbsp;</p>
<p>此选项指定函数close对面向连接的协议如何操作（如TCP）。内核缺省close操作是立即返回，如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。</p>
<p>&nbsp;</p>
<p>SO_LINGER选项用来改变此缺省设置。使用如下结构：</p>
<p>struct linger {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; int l_onoff; /* 0 = off, nozero = on */</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; int l_linger; /* linger time */</p>
<p>};</p>
<p>&nbsp;</p>
<p>有下列三种情况：</p>
<p>1、设置 l_onoff为0，则该选项关闭，l_linger的值被忽略，等于内核缺省情况，close调用会立即返回给调用者，如果可能将会传输任何未发送的数据；</p>
<p>&nbsp;</p>
<p>2、设置 l_onoff为非0，l_linger为0，则套接口关闭时TCP夭折连接，TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方，而不是通常的四分组终止序列，这避免了TIME_WAIT状态；</p>
<p>&nbsp;</p>
<p>3、设置 l_onoff 为非0，l_linger为非0，当套接口关闭时内核将拖延一段时间（由l_linger决定）。如果套接口缓冲区中仍残留数据，进程将处于睡眠状态，直 到（a）所有数据发送完且被对方确认，之后进行正常的终止序列（描述字访问计数为0）或（b）延迟时间到。此种情况下，应用程序检查close的返回值是非常重要的，如果在数据发送完并被确认前时间到，close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。close的成功返回仅告诉我们发送的数据（和FIN）已由对方TCP确认，它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的，它将不等待close完成。 </p>
<p>&nbsp;</p>
<p>注释：l_linger的单位依赖于实现: 4.4BSD假设其单位是时钟滴答（百分之一秒），但Posix.1g规定单位为秒。</p>
<p>&nbsp;</p>
<p>下面的代码是一个使用SO_LINGER选项的例子，使用30秒的超时时限：<br>#define TRUE&nbsp;&nbsp;&nbsp;&nbsp; 1<br>#define FALSE&nbsp;&nbsp;&nbsp; 0<br>int z; /* Status code<br>*/ int s;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Socket s */<br>struct linger so_linger;<br>...<br>so_linger.l_onoff = TRUE;<br>so_linger.l_linger = 30;<br>z = setsockopt(s,<br>&nbsp;&nbsp;&nbsp; SOL_SOCKET,<br>&nbsp;&nbsp;&nbsp; SO_LINGER,<br>&nbsp;&nbsp;&nbsp; &amp;so_linger,<br>&nbsp;&nbsp;&nbsp; sizeof so_linger);<br>if ( z )<br>&nbsp;&nbsp; perror("setsockopt(2)");</p>
<p>下面的例子显示了如何设置SO_LINGER的值来中止套接口s上的当前连接：<br>#define TRUE&nbsp;&nbsp;&nbsp;&nbsp; 1<br>#define FALSE&nbsp;&nbsp;&nbsp; 0<br>int z; /* Status code */<br>int s;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Socket s */<br>struct linger so_linger;<br>...<br>so_linger.l_onoff = TRUE;<br>so_linger.l_linger = 0;<br>z = setsockopt(s,<br>&nbsp;&nbsp;&nbsp; SOL_SOCKET,<br>&nbsp;&nbsp;&nbsp; SO_LINGER,<br>&nbsp;&nbsp;&nbsp; &amp;so_linger,<br>&nbsp;&nbsp;&nbsp; sizeof so_linger);<br>if ( z )<br>&nbsp;&nbsp;&nbsp; perror("setsockopt(2)");<br>&nbsp;&nbsp;&nbsp; close(s); /* Abort connection */</p>
<p>在上面的这个例子中，当调用close函数时，套接口s会立即中止。中止的语义是通过将超时值设置为0来实现的。</p>
<p>&nbsp;</p>
<p>/********** WINDOWS **********/</p>
<p>&nbsp;<br>/* 当连接中断时，需要延迟关闭(linger)以保证所有数据都被传输，所以需要打开SO_LINGER这个选项；&nbsp; <br>&nbsp;* //注：大致意思就是说SO_LINGER选项用来设置当调用closesocket时是否马上关闭socket； <br>&nbsp;* linger的结构在/usr/include/linux/socket.h中定义：//注：这个结构就是SetSocketOpt中的Data的数据结构 <br>&nbsp;*　 struct linger <br>&nbsp;*　 { <br>&nbsp;*　　 int l_onoff;　 /* Linger active */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //低字节，0和非0，用来表示是否延时关闭socket<br>&nbsp;*　　 int l_linger; /* How long to linger */&nbsp;&nbsp; //高字节,延时的时间数，单位为秒<br>&nbsp;*　 }; <br>&nbsp;*　 如果l_onoff为0，则延迟关闭特性就被取消。</p>
<p>&nbsp;*&nbsp;&nbsp; 如果非零，则允许套接口延迟关闭； l_linger字段则指明延迟关闭的时间 <br>&nbsp;*/ </p>
<p><br>更具体的描述如下：<br>1、若设置了SO_LINGER（亦即linger结构中的l_onoff域设为非零），并设置了零超时间隔，则closesocket()不被阻塞立即执行，不论是否有排队数据未发送或未被确认。这种关闭方式称为&#8220;强制&#8221;或&#8220;失效&#8221;关闭，因为套接口的虚电路立即被复位，且丢失了未发送的数据。在远端的recv()调用将以WSAECONNRESET出错。</p>
<p>2、若设置了SO_LINGER并确定了非零的超时间隔，则closesocket()调用阻塞进程，直到所剩数据发送完毕或超时。这种关闭称为&#8220;优雅&#8221;或&#8220;从容&#8221;关闭。请注意如果套接口置为非阻塞且SO_LINGER设为非零超时，则closesocket()调用将以WSAEWOULDBLOCK错误返回。</p>
<p>3、若在一个流类套接口上设置了SO_DONTLINGER（也就是说将linger结构的l_onoff域设为零），则closesocket()调用立即返回。但是，如果可能，排队的数据将在套接口关闭前发送。请注意，在这种情况下WINDOWS套接口实现将在一段不确定的时间内保留套接口以及其他资源，这对于想用所以套接口的应用程序来说有一定影响。<br>&nbsp;</p>
<p>SO_DONTLINGER 若为真，则SO_LINGER选项被禁止。<br>SO_LINGER延迟关闭连接 struct linger上面这两个选项影响close行为；</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp; 选项&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 间隔&nbsp;&nbsp;&nbsp; 关闭方式&nbsp; 等待关闭与否<br>&nbsp; SO_DONTLINGER&nbsp;&nbsp; 不关心&nbsp;&nbsp;&nbsp;&nbsp; 优雅&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 否<br>&nbsp; SO_LINGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 零&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 强制&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 否<br>&nbsp; SO_LINGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 非零&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 优雅&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是<br></p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/factor2000/archive/2009/02/23/3929816.aspx">http://blog.csdn.net/factor2000/archive/2009/02/23/3929816.aspx</a><br><br><br><br><strong>二、设置 SO_KEEPALIVE 选项<br><br></strong>SO_KEEPALIVE 保持连接检测对方主机是否崩溃，避免（服务器）永远阻塞于TCP连接的输入。设置该选项后，如果2小时内在此套接口的任一方向都没有数据交换，TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导致以下三种情况：对方接收一切正常：以期望的ACK响应。2小时后，TCP将发出另一个探测分节。对方已崩溃且已重新启动：以RST响应。套接口的待处理错误被置为ECONNRESET，套接 口本身则被关闭。对方无任何响应：源自berkeley的TCP发送另外8个探测分节，相隔75秒一个，试图得到一个响应。在发出第一个探测分节11分钟15秒后若仍无响应就放弃。套接口的待处理错误被置为ETIMEOUT，套接口本身则被关闭。如ICMP错误是&#8220;host unreachable(主机不可达)&#8221;，说明对方主机并没有崩溃，但是不可达，这种情况下待处理错误被置为 EHOSTUNREACH。 在该书的第158页有更详细的描述。 根据上面的介绍我们可以知道对端以一种非优雅的方式断开连接的时候，我们可以设置SO_KEEPALIVE属性使得我们在2小时以后发现对方的TCP连接是否依然存在。 keepAlive = 1； Setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&amp;keepAlive, sizeof(keepAlive)); 如果我们不能接受如此之长的等待时间，从TCP-Keepalive-HOWTO上可以知道一共有两种方式可以设置，一种是修改内核关于网络方面的配置参数，另外一种就是SOL_TCP字段的TCP_KEEPIDLE， TCP_KEEPINTVL， TCP_KEEPCNT三个选项。 <br><br><br><strong>三、设置 SO_REUSEADDR 选项<br></strong><br>SO_REUSEADDR 套接字选项:使两个server socket可以监听同一个端口 默认情况下，套接字不同一个正在使用的本地地址绑定到一起。但在少数情况下，仍有必要以这种方式，来实现对一个地址的重复利用。通过第7章的学习，大家已经知道，每个连接都是通过它的本地及远程地址的组合，&#8220;独一无二&#8221;地标识出来的。针对我们想要连接的地址，只要能用极其细微的差异（比如T C P / I P中采用不同的端口号），来维持这种&#8220;独一无二&#8221;或者&#8220;唯一&#8221;的特点，绑定便是允许的。唯一例外的是监听套接字。两个独立的套接字不可与同一个本地接口（在T C P / I P的情况下，则是端口）绑定到一起，以等待进入的连接通知。 假定两个套接字都在同一个端口上进行监听，那么到底由谁来接收一个进入连接通知呢？对于这个问题，目前尚无一种正式规范提出了解决方案。在T C P的环境下，假如服务器关闭，或异常退出，造成本地地址和端口均进入T I M E _ WA I T状态，那么S O _ R E U S E A D D R 这个套接字选项便显得非常有用。在T I M E _ WA I T状态下，其他任何套接字都不能与那个端口绑定到一起。但假若设置了该选项，服务器便可在重新启动之后，在相同的本地接口及端口上进行监听。 将服务端socket套接字设置上SO_REUSEADDR属性,代码如下: //设置socket套接字的属性:允许多个设备在同一个本地网中监听同一下端口 int one = 1; if (sock, SOL_SOCKET, SO_REUSEADDR, &amp;one, sizeof(one))) { printf("setsockopt SO_REUSEADDR error\n"); close(sock); return; } 这么设置套接字的作用是:允许两个server端的套接字在同一个本地网段中监听同一个端口.<br><br><strong>四、设置 TCP_NODELA 选项</strong><br></p>
TCP_NODELAY和TCP_CORK基本上控制了包的&#8220;Nagle化&#8221;，Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。John Nagle是Nagle算法的发明人，后者就是用他的名字来命名的，他在1984年首次用这种方法来尝试解决福特汽车公司的网络拥塞问题（欲了解详情请参看IETF RFC 896）。他解决的问题就是所谓的silly window syndrome ，中文称&#8220;愚蠢窗口症候群&#8221;，具体含义是，因为普遍终端应用程序每产生一次击键操作就会发送一个包，而典型情况下一个包会拥有一个字节的数据载荷以及40个字节长的包头，于是产生4000%的过载，很轻易地就能令网络发生拥塞,。 Nagle化后来成了一种标准并且立即在因特网上得以实现。它现在已经成为缺省配置了，但在我们看来，有些场合下把这一选项关掉也是合乎需要的。 现在让我们假设某个应用程序发出了一个请求，希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。如果我们马上发送数据，那么交互性的以及客户/服务器型的应用程序将极大地受益。例如，当我们正在发送一个较短的请求并且等候较大的响应时，相关过载与传输的数据总量相比就会比较低，而且，如果请求立即发出那么响应时间也会快一些。以上操作可以通过设置套接字的TCP_NODELAY选项来完成，这样就禁用了Nagle算法。 另外一种情况则需要我们等到数据量达到最大时才通过网络一次发送全部数据，这种数据传输方式有益于大量数据的通信性能，典型的应用就是文件服务器。应用Nagle算法在这种情况下就会产生问题。但是，如果你正在发送大量数据，你可以设置TCP_CORK选项禁用Nagle化，其方式正好同TCP_NODELAY相反（TCP_CORK 和 TCP_NODELAY 是互相排斥的）。下面就让我们仔细分析下其工作原理。 假设应用程序使用sendfile()函数来转移大量数据。应用协议通常要求发送某些信息来预先解释数据，这些信息其实就是报头内容。典型情况下报头很小，而且套接字上设置了TCP_NODELAY。有报头的包将被立即传输，在某些情况下（取决于内部的包计数器），因为这个包成功地被对方收到后需要请求对方确认。这样，大量数据的传输就会被推迟而且产生了不必要的网络流量交换。 但是，如果我们在套接字上设置了TCP_CORK（可以比喻为在管道上插入&#8220;塞子&#8221;）选项，具有报头的包就会填补大量的数据，所有的数据都根据大小自动地通过包传输出去。当数据传输完成时，最好取消TCP_CORK 选项设置给连接&#8220;拔去塞子&#8221;以便任一部分的帧都能发送出去。这同&#8220;塞住&#8221;网络连接同等重要。 总而言之，如果你肯定能一起发送多个数据集合（例如HTTP响应的头和正文），那么我们建议你设置TCP_CORK选项，这样在这些数据之间不存在延迟。能极大地有益于WWW、FTP以及文件服务器的性能，同时也简化了你的工作。 
<img src ="http://www.cppblog.com/toMyself/aggbug/123300.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-13 10:39 <a href="http://www.cppblog.com/toMyself/archive/2010/08/13/123300.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何划分子网</title><link>http://www.cppblog.com/toMyself/archive/2010/08/12/123155.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Thu, 12 Aug 2010 02:58:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/12/123155.html</guid><description><![CDATA[<p>网络ID为&#8220;192.168.0.0/24&#8221;，这个网络ID中的IP地址是一个C类网段，其中&#8220;24&#8221;表示在二进制形式的子网掩码中&#8220;1&#8221;的个数是24个。其实这是&#8220;255.255.255.0&#8221;的另外一种表示方法，每个&#8220;255&#8221;表示二进制的8个&#8220;1&#8221;，最后的&#8220;0&#8221;表示二进制的8个&#8220;0&#8221;，用二进制表示为&#8220;11111111.11111111.11111111.00000000&#8221;。</p>
<p>　　果冻提示：可别小看这个&#8220;0&#8221;，根据&#8220;0&#8221;的个数可以计算出该子网能够容纳的客户机的数量。</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/toMyself/aggbug/123155.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-12 10:58 <a href="http://www.cppblog.com/toMyself/archive/2010/08/12/123155.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络地址转换（NAT）</title><link>http://www.cppblog.com/toMyself/archive/2010/08/12/123150.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Thu, 12 Aug 2010 02:40:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/12/123150.html</guid><description><![CDATA[<div class=summary sizcache="2" sizset="36"><strong sizcache="3" sizset="3">摘要</strong>
<p>网络地址转换(NAT,Network Address Translation)属接入广域网(WAN)技术,是一种将私有(保留)地址转化为合法IP地址的转换技术,它被广泛应用于各种类型Internet接入方式和各种类型的网络中。原因很简单，NAT不仅完美地解决了lP地址不足的问题，而且还能够有效地避免来自网络外部的攻击，隐藏并保护网络内部的计算机。</p>
</div>
<p><br>网络地址转换(NAT,NetworkAddressTranslation)被广泛应用于各种类型<a class=innerlink title=Internet href="http://www.hudong.com/wiki/Internet" jQuery1281580885625="3"><font color=#0268cd>Internet</font></a>接入方式和备种类型的网络中。原因很简单，NAT不仅完美地解决了lP地址不足的问题，而且还能够有效地避免来自网络外部的攻击，隐藏并保护网络内部的计算机。虽然NAT可以借助于某些代理服务器来实现，但考虑到运算成本和网络性能，很多时候都是在路由器上来实现的。随着接入Internet的计算机数量的不断猛增，IP地址资源也就愈加显得捉襟见肘。事实上，除了中国教育和<a class=link_red title=科研计算机网 href="javascript:linkredwin('科研计算机网');" target=""><font color=#ff0000>科研计算机网</font></a>(CERNET)外，一般用户几乎申请不到整段的C类IP地址。在其他ISP那里，即使是拥有几百台计算机的大型局域网用户，当他们申请IP地址时，所分配的地址也不过只有几个或十几个IP地址。显然，这样少的IP地址根本无法满足网络用户的需求，于是也就产生了NAT技术。 </p>
<p>&nbsp;</p>
<div align=left sizcache="2" sizset="41">
<div class=content_h2 sizcache="2" sizset="41">
<h2 sizcache="3" sizset="4">网络地址转换-基本简介</h2>
<span sizcache="3" sizset="4"><a name=1>&nbsp;</a><a id=bjbd href="http://www.hudong.com/editsectionauth/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2/1" target=_self>&nbsp;</a><a id=tiwen class=quizhref href="javascript:void(0)" jQuery1281580885625="58">&nbsp;</a><a id=hml href="http://www.hudong.com/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2#catalog">&nbsp;</a></span> </div>
</div>
<p align=left></p>
<div class="img img_r" sizcache="2" sizset="45"><a title=点击查看原图 href="http://tupian.hudong.com/a0_44_20_01300000256992122667206119212_gif.html" target=_blank></a><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/nat1.gif" width=528 height=289>网络地址转换</div>
<p>借助于NAT，私有(保留)地址的"内部"网络通过路由器发送数据包时，私有地址被转换成合法的IP地址，一个局域网只需使用少量IP地址(甚至是1个)即可实现私有地址网络内所有计算机与Internet的通信需求。 </p>
<p>&nbsp;</p>
<p align=left>NAT将自动修改IP报文头申的源IP地址和目的IP地址，Ip地址校验则在NAT处理过程中自动完成。有些应用程序将源IP地址嵌入到IP报文的数据部分中，所以还需要同时对报文进行修改，以匹配IP头中已经修改过的源IP地址。否则，在报文数据都分别嵌入IP地址的应用程序就不能正常工作。</p>
<div align=left sizcache="2" sizset="46">
<div class=content_h2 sizcache="2" sizset="46">
<h2 sizcache="3" sizset="5">网络地址转换-实现方式</h2>
<span sizcache="3" sizset="5"><a name=3>&nbsp;</a><a id=bjbd href="http://www.hudong.com/editsectionauth/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2/3" target=_self>&nbsp;</a><a id=tiwen class=quizhref href="javascript:void(0)" jQuery1281580885625="59">&nbsp;</a><a id=hml href="http://www.hudong.com/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2#catalog">&nbsp;</a></span> </div>
</div>
<p align=left></p>
<div class="img img_r" sizcache="2" sizset="50"><a title=点击查看原图 href="http://tupian.hudong.com/a4_12_24_01300000256992122667241901883_jpg.html" target=_blank></a><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/nat2.jpg" width=512 height=384>NAT静态转换</div>
<p>NAT的实现方式有三种，即静态转换StaticNat、动态转换DynamicNat和端口多路复用OverLoad。 </p>
<p>&nbsp;</p>
<p align=left sizcache="2" sizset="51"><a class=link_red title=静态转换 href="javascript:linkredwin('静态转换');" target=""><font color=#ff0000>静态转换</font></a>是指将内部网络的私有IP地址转换为公有IP地址，IP地址对是一对一的，是一成不变的，某个私有IP地址只转换为某个公有IP地址。借助于静态转换，可以实现外部网络对内部网络中某些特定设备(如服务器)的访问。</p>
<p align=left sizcache="2" sizset="52"><a class=link_red title=动态转换 href="javascript:linkredwin('动态转换');" target=""><font color=#ff0000>动态转换</font></a>是指将内部网络的私有IP地址转换为公用IP地址时，IP地址对是不确定的，而是随机的，所有被授权访问上Internet的私有IP地址可随机转换为任何指定的合法IP地址。也就是说，只要指定哪些内部地址可以进行转换，以及用哪些合法地址作为外部地址时，就可以进行动态转换。动态转换可以使用多个合法外部地址集。当ISP提供的合法IP地址略少于网络内部的计算机数量时。可以采用动态转换的方式。</p>
<p align=left sizcache="2" sizset="53"><a class=link_red title=端口多路复用 href="javascript:linkredwin('端口多路复用');" target=""><font color=#ff0000>端口多路复用</font></a>是指改变外出数据包的源端口并进行端口转换，即端口地址转换(PAT，PortAddressTranslation).采用端口多路复用方式。内部网络的所有主机均可共享一个合法外部IP地址实现对Internet的访问，从而可以最大限度地节约IP地址资源。同时，又可隐藏网络内部的所有<a class=innerlink title=主机 href="http://www.hudong.com/wiki/%E4%B8%BB%E6%9C%BA" jQuery1281580885625="4"><font color=#0268cd>主机</font></a>，有效避免来自internet的攻击。因此，目前网络中应用最多的就是端口多路复用方式。</p>
<p><br><br><br><br>NAT英文全称是&#8220;Network Address Transl<a class=pk href="http://corp.it168.com/corp/730_index.shtml" target=_blank><u><font color=#0000ff>ati</font></u></a>on&#8221;，中文意思是&#8220;网络地址转换&#8221;，它是一个IETF(Internet Engineering Task Force, Internet工程任务组)标准，允许一个整体机构以一个公用IP（Internet Protocol）地址出现在Internet上。顾名思义，它是一种把内部私有网络地址（IP地址）翻译成合法网络IP地址的技术。如图<br><font color=red></p>
<p align=center><img border=1 align=center src="http://publish.it168.com/cWord/images/137946.jpg"></p>
<p></font><br>&nbsp;&nbsp;&nbsp; 简单的说，NAT就是在局域网内部网络中使用内部地址，而当内部节点要与外部网络进行通讯时，就在网关（可以理解为出口，打个比方就像院子的门一样）处，将内部地址替换成公用地址，从而在外部公网（internet）上正常使用，NAT可以使多台计算机共享Internet连接，这一功能很好地解决了公共IP地址紧缺的问题。通过这种方法，您可以只申请一个合法IP地址，就把整个局域网中的计算机接入Internet中。这时，NAT屏蔽了内部网络，所有内部网计算机对于公共网络来说是不可见的，而内部网计算机用户通常不会意识到NAT的存在。如图2所示。这里提到的内部地址，是指在内部网络中分配给节点的私有IP地址，这个地址只能在内部网络中使用，不能被路由（一种网络技术，可以实现不同路径转发）。虽然内部地址可以随机挑选，但是通常使用的是下面的地址：10.0.0.0~10.255.255.255，172.16.0.0~172.16.255.255，192.168.0.0~192.168.255.255。NAT将这些无法在互联网上使用的保留IP地址翻译成可以在互联网上使用的合法IP地址。而全局地址，是指合法的IP地址，它是由NIC（网络信息中心）或者ISP(网络服务提供商)分配的地址，对外代表一个或多个内部局部地址，是全球统一的可寻址的地址。<br><font color=red></p>
<p align=center><img border=1 align=center src="http://publish.it168.com/cWord/images/137947.jpg"></p>
<p></font><br>&nbsp;&nbsp;&nbsp; NAT功能通常被集成到<a class=pk href="http://product.it168.com/files/0409search.shtml" target=_blank><u><font color=#0000ff>路由器</font></u></a>、<a class=pk href="http://product.it168.com/files/0418search.shtml" target=_blank><u><font color=#0000ff>防火墙</font></u></a>、ISDN路由器或者单独的NAT设备中。比如Cisco路由器中已经加入这一功能，网络管理员只需在路由器的IOS中设置NAT功能，就可以实现对内部网络的屏蔽。再比如防火墙将WEB Server的内部地址192.168.1.1映射为外部地址202.96.23.11，外部访问202.96.23.11地址实际上就是访问访问192.168.1.1。另外资金有限的小型企业来说，现在通过软件也可以实现这一功能。Windows 98 SE、Windows 2000 都包含了这一功能。 </p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; <strong>NAT技术类型</strong><br>&nbsp;&nbsp;&nbsp;<strong> NAT有三种类型：静态NAT(Static NAT)、动态地址NAT(Pooled NAT)、网络地址端口转换NAPT（Port－Level NAT）。</strong><br>&nbsp;&nbsp;&nbsp; 其中静态NAT设置起来最为简单和最容易实现的一种，内部网络中的每个主机都被永久映射成外部网络中的某个合法的地址。而动态地址NAT则是在外部网络中定义了一系列的合法地址，采用动态分配的方法映射到内部网络。NAPT则是把内部地址映射到外部网络的一个IP地址的不同端口上。根据不同的需要，三种NAT方案各有利弊。 <br>&nbsp;&nbsp;&nbsp; 动态地址NAT只是转换IP地址，它为每一个内部的IP地址分配一个临时的外部IP地址，主要应用于拨号，对于频繁的远程联接也可以采用动态NAT。当远程用户联接上之后，动态地址NAT就会分配给他一个IP地址，用户断开时，这个IP地址就会被释放而留待以后使用。 <br>&nbsp;&nbsp;&nbsp; 网络地址端口转换NAPT（Network Address Port Translation）是人们比较熟悉的一种转换方式。NAPT普遍应用于接入设备中，它可以将中小型的网络隐藏在一个合法的IP地址后面。NAPT与动态地址NAT不同，它将内部连接映射到外部网络中的一个单独的IP地址上，同时在该地址上加上一个由NAT设备选定的TCP端口号。 <br>&nbsp;&nbsp;&nbsp; 在Internet中使用NAPT时，所有不同的信息流看起来好像来源于同一个IP地址。这个优点在小型办公室内非常实用，通过从ISP处申请的一个IP地址，将多个连接通过NAPT接入Internet。实际上，许多SOHO远程访问设备支持基于PPP的动态IP地址。这样，ISP甚至不需要支持NAPT，就可以做到多个内部IP地址共用一个外部IP地址上Internet，虽然这样会导致信道的一定拥塞，但考虑到节省的ISP上网费用和易管理的特点，用NAPT还是很值得的。</p>
<p><br><br><br>&nbsp;</p>
<br><br><br>参考资料：<br>1、互动百科：<a href="http://www.hudong.com/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2">http://www.hudong.com/wiki/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2</a>&nbsp;<br>2、IT168：<a href="http://publish.it168.com/cword/514.shtml">http://publish.it168.com/cword/514.shtml</a>&nbsp; 
<img src ="http://www.cppblog.com/toMyself/aggbug/123150.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-12 10:40 <a href="http://www.cppblog.com/toMyself/archive/2010/08/12/123150.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Tcp 断开连接</title><link>http://www.cppblog.com/toMyself/archive/2010/08/11/123072.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Wed, 11 Aug 2010 07:34:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/11/123072.html</guid><description><![CDATA[<div>TCP状态转移要点<br>&nbsp;&nbsp;&nbsp; TCP协议规定，对于已经建立的连接，网络双方要进行四次握手才能成功断开连接，如果缺少了其中某个步骤，将会使连接处于假死状态，连接本身占用的资源不会被释放。网络服务器程序要同时管理大量连接，所以很有必要保证无用连接完全断开，否则大量僵死的连接会浪费许多服务器资源。在众多TCP状态中，最值得注意的状态有两个：CLOSE_WAIT和TIME_WAIT。 </div>
<div>1、LISTENING状态<br>　　FTP服务启动后首先处于侦听（LISTENING）状态。</div>
<div>2、ESTABLISHED状态<br>　　ESTABLISHED的意思是建立连接。表示两台机器正在通信。</div>
<div>3、CLOSE_WAIT</div>
<div>&nbsp;&nbsp;&nbsp; 对方主动关闭连接或者网络异常导致连接中断，这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭</div>
<div>4、TIME_WAIT</div>
<div>&nbsp;&nbsp;&nbsp; 我方主动调用close()断开连接，收到对方确认后状态变为TIME_WAIT。TCP协议规定TIME_WAIT状态会一直持续2MSL(即两倍的分段最大生存期)，以此来确保旧的连接状态不会对新连接产生影响。处于TIME_WAIT状态的连接占用的资源不会被内核释放，所以作为服务器，在可能的情况下，尽量不要主动断开连接，以减少TIME_WAIT状态造成的资源浪费。</div>
<div>&nbsp;&nbsp;&nbsp; 目前有一种避免TIME_WAIT资源浪费的方法，就是关闭socket的LINGER选项。但这种做法是TCP协议不推荐使用的，在某些情况下这个操作可能会带来错误。</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>在连接撤销过程中，有如下四个过程:&nbsp;&nbsp;&nbsp;</div>
<img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/disconnect.png" width=554 height=360><br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HOST1上的应用程序关闭己方的连接导致TCP发送一个FIN消息给HOST2。<br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HOST2发送一个确认消息给HOST1,并且HOST2把FIN作为EOF递交给HOST2上的应用程序。<br>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一段时间过后，HOST2上的应用程序关闭它那边的连接，引发一个FIN消息给HOST1。<br>4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HOST1给HOST2发送一个确认消息，然后HOST2关闭连接并释放资源，然而，HOST1却没有关闭连接，而是进入了TIME_WAIT状态，并为两个最大段生存时间(2MSL)保留在此状态.
<div><br>为什么需要time_wait?<br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因为在第四步的时候，HOST1发送的ACK可能丢失并导致HOST2重新发送FIN消息，TIME_WAIT维护连接状态.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果执行主动关闭的一方HOST1 不进入到TIME_WAIT状态就关闭连接那会发生什么呢？当重传的FIN消息到达时，因为TCP已经不再有连接的信息了，所以就用RST(重新启动)消息应答，导致HOST2进入错误的状态而不是有序终止状态，如果发送最后ACK消息的一方处于TIME_WAIT状态并仍然记录着连接的信息，它就可以正确的响应对等方HOST2的FIN消息了.<br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TIME_WAIT为连接中&#8221;离群的段&#8221;提供从网络中消失的时间.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 考虑一下，如果延迟或者重传段在连接关闭后到达时会发生什么呢？通常情况下，因为TCP仅仅丢弃该数据并响应RST消息，所以这不会造成任何问题。当RST消息到达发出延时段的主机时，因为该主机也没有记录连接的任何信息，所以它也丢弃该段。然而，如果两个相同主机之间又建立了一个具有相同端口号的新连接，那么离群的段就可能被看成是新连接的，如果离群的段中数据的任何序列号恰恰在新连接的当前接收窗口中，数据就会被重新接收，其结果就是破坏新连接。</div>
<div><br>&nbsp;</div>
<div><u><font color=#3300ff></font></u>&nbsp;</div>
<div><u><font color=#3300ff></font></u>&nbsp;</div>
<div>&nbsp;</div>
<div>一、TCP连接关闭的几种方式：</div>
<div>1、&#8220;正常&#8221;关闭：调用close()关闭socket、没close但进程正常结束(当然这是不应该的做法)、进程core掉、在shell命令行中kill掉进程，都可抽象成&#8220;正常&#8221;关闭。因为即使core掉，内核也会马上帮应用程序回收(close)socket文件描述符。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp; &#8220;正常&#8221;关闭，默认情况下(非默认即设置Linger下面会介绍)，关闭端即客户端TCP层会发FIN包，对端即服务器TCP层收到后，回ACK，客户端进入FIN_WAIT2状态。此时，TCP终止连接的4个分组中服务器应该发的第3个分组FIN包，其TCP层是不会主动发的，只有服务器端socket&#8220;正常&#8221;关闭，才会发出这个FIN包。至此，客户端进入TIME_WAIT状态。</div>
<div>2、&#8220;非&#8221;正常关闭：客户端崩溃了，此时肯定发不出FIN包了(当然啦，内核都没机会帮应用程序回收资源了)。这种情况，服务器端有如下两种情况：</div>
<div>&nbsp;&nbsp;&nbsp; A、服务器send数据，因为客户端已经崩溃，服务器收不到ACK自然会不停的重传。源自</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Berkeley的重传机制，重传8次，相对第一次传的15分钟后仍没收到ACK，则返回</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ETIMEDOUT或EHOSTUNREAC错误。如果服务器不理会这个错误，再次调用send，则</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 立马返回Broken Pipe错误。&nbsp;&nbsp;&nbsp;&nbsp; </div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注：15分钟超时可以在 /proc/sys/net/ipv4/tcp_retries2 中修改</div>
<div>&nbsp;&nbsp; B、 服务器不发任何数据了，那只有靠应用层心跳检测机制或Keepalive，来发觉TCP断连了。</div>
<div>二、SO_LINGER套接口选项</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A、l_onoff设置为0，这也是默认情况，函数close()是立即返回的，然后TCP连接双方是通过</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FIN、ACK4分组来终止TCP连接的。当然,发送缓冲区还有数据的话，系统将试着将这些数据</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 发送到对方。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B、l_onoff非0，l_linger设置0，函数close()立即返回，并发送RST终止连接，发送缓冲区的数据丢弃。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C、l_onoff非0，l_linger非0，函数close()不立即返回,而是在(a)发送缓冲区数据发送完并得到确认</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (b)l_linger延迟时间到,l_linger时间单位为微妙。两者之一成立时返回。如果在发送缓冲区数据发送</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 完并被确认前延迟时间到的话，close返回EWOULDBLOCK(或EAGAIN)错误。</div>
<div>三、客户端TCP连接&#8220;正常&#8221;关闭，服务器的几种情况：</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 情形 客户端l_onoff设置为0， <br>&#8220;正常&#8221;关闭 客户端l_onoff非0，l_linger设置0,&#8220;正常&#8221;关闭 <br>服务器阻塞模式send，正阻塞在send函数未返回 客户端TCP发送FIN，服务器send函数返回成功(返回字节数是实际拷贝到发送缓冲区的字节数)。客户端发送RST。如果服务器再次调用send,将返回errno[32]:Broken pipe 客户端TCP发送RST，服务器函数返回成功(返回字节数是实际拷贝到发送缓冲区的字节数)。若服务器再次调用send，则返回-1，errno[104]:Connection reset by peer。若再次调用send，则返回-1，errno[32]:Broken pipe <br>服务器空闲 客户端TCP发送FIN，若服务器没理会而调用send，客户端发送RST，send返回-1，errno[32]:Broken pipe 客户端TCP发送RST,若服务器没理会而调用send，send返回-1，errno[104]:Connection reset by peer。若再次调用send，则返回-1，errno[32]:Broken pipe </div>
<div>总之，1、收到对端RST后，仍然调入send()，则返回Connection reset by peer,再次调用send()，则返回Broken pipe</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、收到对端FIN后，仍然调研哪个send(),直接返回Broken pipe</div>
<img src ="http://www.cppblog.com/toMyself/aggbug/123072.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-11 15:34 <a href="http://www.cppblog.com/toMyself/archive/2010/08/11/123072.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP: SYN ACK FIN RST PSH URG 详解</title><link>http://www.cppblog.com/toMyself/archive/2010/08/11/123068.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Wed, 11 Aug 2010 07:08:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/11/123068.html</guid><description><![CDATA[<img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/tomyself/connect.JPG" width=490 height=300><br>
<div>三次握手Three-way Handshake</div>
<div>一个虚拟连接的建立是通过三次握手来实现的</div>
<div><span id=more -2429></span></div>
<div>1. (B) &#8211;&gt; [SYN] &#8211;&gt; (A)</div>
<div>假如服务器A和客户机B通讯. 当A要和B通信时，A首先向B发一个SYN (Synchronize) 标记的包，告诉A请求建立连接.</div>
<div>注意: 一个 SYN包就是仅SYN标记设为1的TCP包(参见TCP包头Resources). 认识到这点很重要，只有当A受到B发来的SYN包，才可建立连接，除此之外别无他法。因此，如果你的防火墙丢弃所有的发往外网接口的SYN包，那么你将不能让外部任何主机主动建立连接。</div>
<div>2. (A) &lt;&#8211; [SYN/ACK] &lt;&#8211;(B)</div>
<div>接着，B收到后会发一个对SYN包的确认包(SYN/ACK)回去，表示对第一个SYN包的确认，并继续握手操作.</div>
<div>注意: SYN/ACK包是仅SYN 和 ACK 标记为1的包.</div>
<div>3. (A) &#8211;&gt; [ACK] &#8211;&gt; (B)</div>
<div>A收到SYN/ACK 包,A发一个确认包(ACK)，通知A连接已建立。至此，三次握手完成，一个TCP连接完成</div>
<div>Note: ACK包就是仅ACK 标记设为1的TCP包. 需要注意的是当三此握手完成、连接建立以后，TCP连接的每个包都会设置ACK位</div>
<div>这就是为何连接跟踪很重要的原因了. 没有连接跟踪,防火墙将无法判断收到的ACK包是否属于一个已经建立的连接.一般的包过滤(Ipchains)收到ACK包时,会让它通过(这绝对不是个好主意). 而当状态型防火墙收到此种包时，它会先在连接表中查找是否属于哪个已建连接，否则丢弃该包</div>
<div>四次握手Four-way Handshake</div>
<div>四次握手用来关闭已建立的TCP连接</div>
<div>1. (B) &#8211;&gt; ACK/FIN &#8211;&gt; (A)</div>
<div>2. (B) &lt;&#8211; ACK &lt;&#8211; (A)</div>
<div>3. (B) &lt;&#8211; ACK/FIN &lt;&#8211; (A)</div>
<div>4. (B) &#8211;&gt; ACK &#8211;&gt; (A)</div>
<div>注意: 由于TCP连接是双向连接, 因此关闭连接需要在两个方向上做。ACK/FIN 包(ACK 和FIN 标记设为1)通常被认为是FIN(终结)包.然而, 由于连接还没有关闭, FIN包总是打上ACK标记. 没有ACK标记而仅有FIN标记的包不是合法的包，并且通常被认为是恶意的</div>
<div>连接复位Resetting a connection</div>
<div>四次握手不是关闭TCP连接的唯一方法. 有时,如果主机需要尽快关闭连接(或连接超时,端口或主机不可达),RST (Reset)包将被发送. 注意在，由于RST包不是TCP连接中的必须部分, 可以只发送RST包(即不带ACK标记). 但在正常的TCP连接中RST包可以带ACK确认标记</div>
<div>请注意RST包是可以不要收到方确认的?</div>
<div>无效的TCP标记Invalid TCP Flags</div>
<div>到目前为止，你已经看到了 SYN, ACK, FIN, 和RST 标记. 另外，还有PSH (Push) 和URG (Urgent)标记.</div>
<div>最常见的非法组合是SYN/FIN 包. 注意:由于 SYN包是用来初始化连接的, 它不可能和 FIN和RST标记一起出现. 这也是一个恶意攻击.</div>
<div>由于现在大多数防火墙已知 SYN/FIN 包, 别的一些组合,例如SYN/FIN/PSH, SYN/FIN/RST, SYN/FIN/RST/PSH。很明显，当网络中出现这种包时，很你的网络肯定受到攻击了。</div>
<div>别的已知的非法包有FIN (无ACK标记)和&#8221;NULL&#8221;包。如同早先讨论的，由于ACK/FIN包的出现是为了关闭一个TCP连接，那么正常的FIN包总是带有 ACK 标记。&#8221;NULL&#8221;包就是没有任何TCP标记的包(URG,ACK,PSH,RST,SYN,FIN都为0)。</div>
<div>到目前为止，正常的网络活动下，TCP协议栈不可能产生带有上面提到的任何一种标记组合的TCP包。当你发现这些不正常的包时，肯定有人对你的网络不怀好意。</div>
<div>UDP (用户数据包协议User Datagram Protocol)<br>TCP是面向连接的，而UDP是非连接的协议。UDP没有对接受进行确认的标记和确认机制。对丢包的处理是在应用层来完成的。(or accidental arrival).</div>
<div>此处需要重点注意的事情是：在正常情况下，当UDP包到达一个关闭的端口时，会返回一个UDP复位包。由于UDP是非面向连接的, 因此没有任何确认信息来确认包是否正确到达目的地。因此如果你的防火墙丢弃UDP包，它会开放所有的UDP端口(?)。</div>
<div>由于Internet上正常情况下一些包将被丢弃，甚至某些发往已关闭端口(非防火墙的)的UDP包将不会到达目的，它们将返回一个复位UDP包。</div>
<div>因为这个原因，UDP端口扫描总是不精确、不可靠的。</div>
<div>看起来大UDP包的碎片是常见的DOS (Denial of Service)攻击的常见形式 (这里有个DOS攻击的例子，<a href="http://grc.com/dos/grcdos.htm" target=_blank><font color=#000000><u>http://grc.com/dos/grcdos.htm</u></font></a> ).</div>
<div>ICMP (网间控制消息协议Internet Control Message Protocol)<br>如同名字一样， ICMP用来在主机/路由器之间传递控制信息的协议。 ICMP包可以包含诊断信息(ping, traceroute &#8211; 注意目前unix系统中的traceroute用UDP包而不是ICMP)，错误信息(网络/主机/端口 不可达 network/host/port unreachable), 信息(时间戳timestamp, 地址掩码address mask request, etc.)，或控制信息 (source quench, redirect, etc.) 。</div>
<div>你可以在<a href="http://www.iana.org/assignments/icmp-parameters" target=_blank><font color=#000000><u>http://www.iana.org/assignments/icmp-parameters</u></font></a>中找到ICMP包的类型。</div>
<div>尽管ICMP通常是无害的，还是有些类型的ICMP信息需要丢弃。</div>
<div>Redirect (5), Alternate Host Address (6), Router Advertisement (9) 能用来转发通讯。</div>
<div>Echo (8), Timestamp (13) and Address Mask Request (17) 能用来分别判断主机是否起来，本地时间和地址掩码。注意它们是和返回的信息类别有关的。它们自己本身是不能被利用的，但它们泄露出的信息对攻击者是有用的。</div>
<div>ICMP消息有时也被用来作为DOS攻击的一部分(例如：洪水ping flood ping,死 ping ?呵呵，有趣 ping of death)?/p&gt;</div>
<div>包碎片注意A Note About Packet Fragmentation</div>
<div>如果一个包的大小超过了TCP的最大段长度MSS (Maximum Segment Size) 或MTU (Maximum Transmission Unit)，能够把此包发往目的的唯一方法是把此包分片。由于包分片是正常的，它可以被利用来做恶意的攻击。</div>
<div>因为分片的包的第一个分片包含一个包头，若没有包分片的重组功能，包过滤器不可能检测附加的包分片。典型的攻击Typical attacks involve in overlapping the packet data in which packet header is 典型的攻击Typical attacks involve in overlapping the packet data in which packet header isnormal until is it overwritten with different destination IP (or port) thereby bypassing firewall rules。包分片能作为 DOS 攻击的一部分，它可以crash older IP stacks 或涨死CPU连接能力。</div>
<div>Netfilter/Iptables中的连接跟踪代码能自动做分片重组。它仍有弱点，可能受到饱和连接攻击，可以把CPU资源耗光。</div>
<div>握手阶段：<br>序号 方向 seq ack<br>1　　A-&gt;B 10000 0<br>2 B-&gt;A 20000 10000+1=10001<br>3 A-&gt;B 10001 20000+1=20001<br>解释：<br>1：A向B发起连接请求，以一个随机数初始化A的seq,这里假设为10000，此时ACK＝0</div>
<div>2：B收到A的连接请求后，也以一个随机数初始化B的seq，这里假设为20000，意思是：你的请求我已收到，我这方的数据流就从这个数开始。B的ACK是A的seq加1，即10000＋1＝10001</div>
<div>3：A收到B的回复后，它的seq是它的上个请求的seq加1，即10000＋1＝10001，意思也是：你的回复我收到了，我这方的数据流就从这个数开始。A此时的ACK是B的seq加1，即20000+1=20001</div>
<div>数据传输阶段：<br>序号　　方向　　　　　　seq ack size<br>23 A-&gt;B 40000 70000 1514<br>24 B-&gt;A 70000 40000+1514-54=41460 54<br>25 A-&gt;B 41460 70000+54-54=70000 1514<br>26 B-&gt;A 70000 41460+1514-54=42920 54<br>解释：<br>23:B接收到A发来的seq=40000,ack=70000,size=1514的数据包<br>24:于是B向A也发一个数据包，告诉B，你的上个包我收到了。B的seq就以它收到的数据包的ACK填充，ACK是它收到的数据包的SEQ加上数据包的大小(不包括以太网协议头，IP头，TCP头)，以证实B发过来的数据全收到了。<br>25:A在收到B发过来的ack为41460的数据包时，一看到41460，正好是它的上个数据包的seq加上包的大小，就明白，上次发送的数据包已安全到达。于是它再发一个数据包给B。这个正在发送的数据包的seq也以它收到的数据包的ACK填充，ACK就以它收到的数据包的seq(70000)加上包的size(54)填充,即ack=70000+54-54(全是头长，没数据项)。</div>
<div>其实在握手和结束时确认号应该是对方序列号加1,传输数据时则是对方序列号加上对方携带应用层数据的长度.如果从以太网包返回来计算所加的长度,就嫌走弯路了.<br>另外,如果对方没有数据过来,则自己的确认号不变,序列号为上次的序列号加上本次应用层数据发送长度.</div>
<img src ="http://www.cppblog.com/toMyself/aggbug/123068.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-11 15:08 <a href="http://www.cppblog.com/toMyself/archive/2010/08/11/123068.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>连接状态</title><link>http://www.cppblog.com/toMyself/archive/2010/08/03/122071.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Tue, 03 Aug 2010 08:06:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2010/08/03/122071.html</guid><description><![CDATA[连接状态 State <br>　　ESTABLISHED 有一个确定的连接 <br>　　SYN_SENT 活动socket尝试建立连接 <br>　　SYN_RECV 连接请求已经收到 <br>　　FIN_WAIT1 socket已经关闭，链接已经断开。 <br>　　FIN_WAIT2 socket已经关闭，链接在最后断开。 <br>　　TIME_WAIT The socket is waiting after close to handle packets still in the network. <br>　　CLOSED The socket is not being used. <br>　　CLOSE_WAIT The remote end has shut down, waiting for the socket to close. <br>　　LAST_ACK The remote end has shut down, and the socket is closed. Waiting for acknowledgement. <br>　　LISTEN The socket is listening for incoming connections. <br>　　CLOSING Both sockets are shut down but we still donot have all our data sent. <br>　　UNKNOWN The state of the socket is unknown.
<img src ="http://www.cppblog.com/toMyself/aggbug/122071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2010-08-03 16:06 <a href="http://www.cppblog.com/toMyself/archive/2010/08/03/122071.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络编程时的注意点</title><link>http://www.cppblog.com/toMyself/archive/2009/09/15/96257.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Tue, 15 Sep 2009 10:13:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2009/09/15/96257.html</guid><description><![CDATA[在网络上发送、接收数据时，要注意字节序的问题！！！！！！！！！！！！<br><br><br>从网络上来的整形数据，若使用string来接收，则可能因为整形数据中含有字符0而将整形数据截断了。<br><br><br>考虑问题(目前定位)要从系统API层来考虑，所有的在系统API上的库都是垃圾。<br><br> <img src ="http://www.cppblog.com/toMyself/aggbug/96257.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2009-09-15 18:13 <a href="http://www.cppblog.com/toMyself/archive/2009/09/15/96257.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>建立连接</title><link>http://www.cppblog.com/toMyself/archive/2009/03/05/75638.html</link><dc:creator>kongkongzi</dc:creator><author>kongkongzi</author><pubDate>Thu, 05 Mar 2009 07:37:00 GMT</pubDate><guid>http://www.cppblog.com/toMyself/archive/2009/03/05/75638.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 三次握手的基本原则是防止老的重复连接发起导致的混乱。为了处理这个，一条特殊的控制信息，reset，被提了出来。如果正在接收的TCP正处于一个非同步状态（比如，SYN-SENT,SYN-RECEIVED）,它在接收到一个可以接受的reset后返回继续监听。如果TCP处于同步状态（ESTABLISHED，FIN-WAIT-1,FIN-WAIT2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT），它放弃了连接且通知用户。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一条已经建立的连接当TCP的一方已经在自己一端关闭或者中断连接且不知道另一端的情况，或者当连接双方由于崩溃导致失忆变成不同步，就称为&#8220;半打开&#8221;的连接。如果尝试在该连接的任何一个方向上发送数据，这个连接就会自动变成reset。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果在A端连接不再存在，则用户在B端在该连接发送任何数据会导致B端的TCP接收到一个reset控制信息。该信息指示B端TCP连接发生了错误，且它被期望终止连接。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rest产生：作为一条基本规则，reset（RST）在一个分片到达且明显不是当前连接的分片的任何时候必须被发送。如果还不清楚，reset不能被发送<br>
 <img src ="http://www.cppblog.com/toMyself/aggbug/75638.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/toMyself/" target="_blank">kongkongzi</a> 2009-03-05 15:37 <a href="http://www.cppblog.com/toMyself/archive/2009/03/05/75638.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>