﻿<?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++博客- 攀升·Uranus-随笔分类-Linux</title><link>http://www.cppblog.com/iuranus/category/4279.html</link><description>&lt;br&gt;&lt;font color="#ADFF2F"&gt;Something Different，Something New&lt;/font&gt;</description><language>zh-cn</language><lastBuildDate>Tue, 07 Aug 2012 08:08:22 GMT</lastBuildDate><pubDate>Tue, 07 Aug 2012 08:08:22 GMT</pubDate><ttl>60</ttl><item><title>(转)MongoDB与内存</title><link>http://www.cppblog.com/iuranus/archive/2012/08/06/186446.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Mon, 06 Aug 2012 05:36:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2012/08/06/186446.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/186446.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2012/08/06/186446.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/186446.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/186446.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: @import url(http://www.cppblog.com/cutesoft_client/cuteeditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);MongoDB与内存Posted on&nbsp;2011-08-19但凡初次接触M...&nbsp;&nbsp;<a href='http://www.cppblog.com/iuranus/archive/2012/08/06/186446.html'>阅读全文</a><img src ="http://www.cppblog.com/iuranus/aggbug/186446.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2012-08-06 13:36 <a href="http://www.cppblog.com/iuranus/archive/2012/08/06/186446.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>./configure -build,-host,-target设置</title><link>http://www.cppblog.com/iuranus/archive/2011/07/22/151615.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Fri, 22 Jul 2011 03:10:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2011/07/22/151615.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/151615.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2011/07/22/151615.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/151615.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/151615.html</trackback:ping><description><![CDATA[<meta charset="utf-8" /><span  style="font-family: Arial; line-height: 20px; font-size: 12px; color: #666666; "><font size="3" style="line-height: normal; ">build:执行代码编译的主机，正常的话就是你的主机系统。这个参数一般由config.guess来猜就可以。当然自己指定也可以。<br style="line-height: normal; " />
host:编译出来的二进制程序所执行的主机，因为绝大多数是如果本机编译，本机执行。所以这个值就等于build。只有交叉编译的时候（也就是本机编译，其他系统机器执行）才会build和host不同。用host指定运行主机。<br style="line-height: normal; " />
target:这个选项只有在建立交叉编译环境的时候用到，正常编译和交叉编译都不会用到。他用build主机上的编译器，编译一个新的编译器（</font><font size="3" style="line-height: normal; ">binutils, gcc,gdb等</font><font size="3" style="line-height: normal; ">），这个新的编译器将来编译出来的其他程序将运行在</font><font size="3" style="line-height: normal; ">target指定的系统上。<br style="line-height: normal; " />
让我们以编译binutils为例：<br style="line-height: normal; " />
</font><font size="3" style="line-height: normal; ">1. `./configure --build=mipsel-linux --host=mipsel-linux　--target=mipsel-linux'&nbsp;<br style="line-height: normal; " />
说明我们利用</font><font size="3" style="line-height: normal; ">mipsel-linux的编译器对</font><font size="3" style="line-height: normal; ">binutils进行编译，编译出来的</font><font size="3" style="line-height: normal; ">binutils运行在</font><font size="3" style="line-height: normal; ">mipsel-linux</font><font size="3" style="line-height: normal; ">，这个</font><font size="3" style="line-height: normal; ">binutils用来编译能够在</font><font size="3" style="line-height: normal; ">mipsel-linux运行的代码。&#8220;当然没有人会用这个选项来编译</font><font size="3" style="line-height: normal; ">binutils&#8221;</font>
<div style="font-family: Arial; word-wrap: break-word; word-break: break-all; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: normal; "></div>
<div style="font-family: Arial; word-wrap: break-word; word-break: break-all; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: normal; "><font size="3" style="line-height: normal; ">2. `./configure --build=i386-linux --host=mipsel-linux<br style="line-height: normal; " />
--target=mipsel-linux' will cross-build native mipsel-linux binutils on<wbr style="line-height: normal; ">i386-linux.<br style="line-height: normal; " />
<br style="line-height: normal; " />
</font></div>
<div style="font-family: Arial; word-wrap: break-word; word-break: break-all; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: normal; "><font size="3" style="line-height: normal; ">说明我们利用</font><font size="3" style="line-height: normal; ">i386-linux</font><font size="3" style="line-height: normal; ">的编译器对</font><font size="3" style="line-height: normal; ">binutils进行编译，编译出来的</font><font size="3" style="line-height: normal; ">binutils运行在</font><font size="3" style="line-height: normal; ">mipsel-linux</font><font size="3" style="line-height: normal; ">，这个</font><font size="3" style="line-height: normal; ">binutils用来编译能够在</font><font size="3" style="line-height: normal; ">mipsel-linux运行的代码。&#8220;这个选项可以用来为其他的机器编译它的编译器</font><font size="3" style="line-height: normal; ">&#8221;。<br style="line-height: normal; " />
<br style="line-height: normal; " />
</font></div>
<div style="font-family: Arial; word-wrap: break-word; word-break: break-all; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: normal; "><font size="3" style="line-height: normal; ">3. `./configure --build=i386-linux --host=i386-linux<br style="line-height: normal; " />
--target=mipsel-linux' will build mipsel-linux cross-binutils on　i386-linux.</font></div>
<div style="font-family: Arial; word-wrap: break-word; word-break: break-all; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: normal; "><font size="3" style="line-height: normal; ">说明我们利用</font><font size="3" style="line-height: normal; ">i386-linux</font><font size="3" style="line-height: normal; ">的编译器对</font><font size="3" style="line-height: normal; ">binutils进行编译，编译出来的</font><font size="3" style="line-height: normal; ">binutils运行在</font><font size="3" style="line-height: normal; ">i386-linux</font><font size="3" style="line-height: normal; ">，这个</font><font size="3" style="line-height: normal; ">binutils用来编译能够在</font><font size="3" style="line-height: normal; ">mipsel-linux运行的代码。&#8220;这个选项用来在i386主机上建立一个</font><font size="3" style="line-height: normal; ">mipsel-linux的</font><font size="3" style="line-height: normal; ">交叉编译环境</font><font size="3" style="line-height: normal; ">&#8221;。<br style="line-height: normal; " />
<br style="line-height: normal; " />
</font></div>
<p style="line-height: normal; "><font size="3" style="line-height: normal; ">4. `./configure --build=mipsel-linux --host=i386-linux<br style="line-height: normal; " />
--target=mipsel-linux' will cross-build mipsel-linux cross-binutils for<br style="line-height: normal; " />
i386-linux on mipsel-linux.<br style="line-height: normal; " />
</font><font size="3" style="line-height: normal; ">说明我们利用</font><font size="3" style="line-height: normal; ">mipsel-linux</font><font size="3" style="line-height: normal; ">的编译器对</font><font size="3" style="line-height: normal; ">binutils进行编译，编译出来的</font><font size="3" style="line-height: normal; ">binutils运行在</font><font size="3" style="line-height: normal; ">i386-linux</font><font size="3" style="line-height: normal; ">，这个</font><font size="3" style="line-height: normal; ">binutils用来编译能够在</font><font size="3" style="line-height: normal; ">mipsel-linux运行的代码。&#8220;这个选项可以用来在i386主机上建立一个</font><font size="3" style="line-height: normal; ">mipsel-linux的</font><font size="3" style="line-height: normal; ">交叉编译环境</font><font size="3" style="line-height: normal; ">，但是交叉编译环境在</font><font size="3" style="line-height: normal; ">mipsel-linux 编译出来，安装到i386-linux主机上，估计没有多少人会这么用吧</font><font size="3" style="line-height: normal; ">&#8221;<br style="line-height: normal; " />
<br style="line-height: normal; " />
总的来说，只有host !=build的时候编译才是交叉编译。否则就是正常编译。</font></p>
</span><img src ="http://www.cppblog.com/iuranus/aggbug/151615.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2011-07-22 11:10 <a href="http://www.cppblog.com/iuranus/archive/2011/07/22/151615.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> UDP"打洞"原理</title><link>http://www.cppblog.com/iuranus/archive/2011/06/08/148303.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Wed, 08 Jun 2011 15:09:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2011/06/08/148303.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/148303.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2011/06/08/148303.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/148303.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/148303.html</trackback:ping><description><![CDATA[<meta charset="utf-8" /><span  style="font-family: Verdana, Arial, Helvetica, sans-serif; line-height: 16px; font-size: 12px; color: #ff0000; ">
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAT分类</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">根据Stun协议(RFC3489),NAT大致分为下面四类</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><a href="http://blog.csdn.net/ronmy/category/819006.aspx">http://blog.csdn.net/ronmy/category/819006.aspx</a></font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><br />
</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Full Cone</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">这种NAT内部的机器A连接过外网机器C后,NAT会打开一个端口.然后外网的任何发到这个打开的端口的UDP数据报都可以到达A.不管是不是C发过来的.</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">A(192.168.8.100:5000) -&gt; NAT(202.100.100.100 : 8000) -&gt; C(292.88.88.88:2000)</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">任何发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">2)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Restricted Cone</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">这种NAT内部的机器A连接过外网的机器C后,NAT打开一个端口.然后C可以用任何端口和A通信.其他的外网机器不行.</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">A(192.168.8.100:5000) -&gt; NAT(202.100.100.100 : 8000) -&gt; C(292.88.88.88:2000)</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">任何从C发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">&nbsp;</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Port Restricted Cone</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">这种NAT内部的机器A连接过外网的机器C后,NAT打开一个端口.然后C可以用原来的端口和A通信.其他的外网机器不行.</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">A(192.168.8.100:5000) -&gt; NAT(202.100.100.100 : 8000) -&gt; C(292.88.88.88:2000)</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">C(202.88.88.88:2000)发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">&nbsp;</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">以上三种NAT通称Cone NAT.我们只能用这种NAT进行UDP打洞.</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Symmetic</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">对于这种NAT.连接不同的外部目标.原来NAT打开的端口会变化.而Cone NAT不会.虽然可以用端口猜测.但是成功的概率很小.因此放弃这种NAT的UDP打洞.</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UDP hole punching</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">对于Cone NAT.要采用UDP打洞.需要一个公网机器C来充当&#8221;介绍人&#8221;.内网的A,B先分别和C通信.打开各自的NAT端口.C这个时候知道A,B的公网IP: Port. 现在A和B想直接连接.比如A给B发.除非B是Full Cone.否则不能通信.反之亦然.但是我们可以这样.</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">A要连接B.A给B发一个UDP包.同时.A让那个介绍人给B发一个命令,让B同时给A发一个UDP包.这样双方的NAT都会记录对方的IP,然后就会允许互相通信.</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 同一个NAT后面的情况</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">如果A,B在同一个NAT后面.如果用上面的技术来进行互连.那么如果NAT支持loopback(就是本地到本地的转换),A,B可以连接,但是比较浪费带宽和NAT.有一种办法是,A,B和介绍人通信的时候,同时把自己的local IP也告诉服务器.A,B通信的时候,同时发local ip和公网IP.谁先到就用哪个IP.但是local ip就有可能不知道发到什么地方去了.比如A,B在不同的NAT后面但是他们各自的local ip段一样.A给B的local IP发的UDP就可能发给自己内部网里面的某某某了.</font></p>
<p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 10px; margin-right: auto; margin-bottom: 10px; margin-left: auto; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; "><font color="#000000" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; ">还有一个办法是服务器来判断A,B是否在一个NAT后面.(网络拓朴不同会不会有问题?)</font></p>
</span><img src ="http://www.cppblog.com/iuranus/aggbug/148303.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2011-06-08 23:09 <a href="http://www.cppblog.com/iuranus/archive/2011/06/08/148303.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)P2P之UDP穿透NAT的原理与实现（附源代码）</title><link>http://www.cppblog.com/iuranus/archive/2011/05/19/146728.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Thu, 19 May 2011 00:37:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2011/05/19/146728.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/146728.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2011/05/19/146728.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/146728.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/146728.html</trackback:ping><description><![CDATA[<span style="font-family: verdana, sans-serif; line-height: 21px; font-size: 14px; ">
<div style="word-break: break-all; ">P2P 之 UDP穿透NAT的原理与实现（附源代码）<br />
原创：shootingstars<br />
参考：<a href="http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt" style="text-decoration: none; color: #336699; ">http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt</a></div>
<div style="word-break: break-all; ">论坛上经常有对P2P原理的讨论，但是讨论归讨论，很少有实质的东西产生（源代码）。呵呵，在这里我就用自己实现的一个源代码来说明UDP穿越NAT的原理。</div>
<div style="word-break: break-all; ">首先先介绍一些基本概念：<br />
&nbsp;&nbsp;&nbsp; NAT(Network Address Translators)，网络地址转换：网络地址转换是在IP地址日益缺乏的情况下产生的，它的主要目的就是为了能够地址重用。NAT分为两大类，基本的NAT和NAPT(Network Address/Port Translator)。<br />
&nbsp;&nbsp;&nbsp; 最开始NAT是运行在路由器上的一个功能模块。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 最先提出的是基本的NAT，它的产生基于如下事实：一个私有网络（域）中的节点中只有很少的节点需要与外网连接（呵呵，这是在上世纪90年代中期提出的）。那么这个子网中其实只有少数的节点需要全球唯一的IP地址，其他的节点的IP地址应该是可以重用的。<br />
&nbsp;&nbsp;&nbsp; 因此，基本的NAT实现的功能很简单，在子网内使用一个保留的IP子网段，这些IP对外是不可见的。子网内只有少数一些IP地址可以对应到真正全球唯一的IP地址。如果这些节点需要访问外部网络，那么基本NAT就负责将这个节点的子网内IP转化为一个全球唯一的IP然后发送出去。(基本的NAT会改变IP包中的原IP地址，但是不会改变IP包中的端口)<br />
&nbsp;&nbsp;&nbsp; 关于基本的NAT可以参看RFC 1631<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 另外一种NAT叫做NAPT，从名称上我们也可以看得出，NAPT不但会改变经过这个NAT设备的IP数据报的IP地址，还会改变IP数据报的TCP/UDP端口。基本NAT的设备可能我们见的不多（呵呵，我没有见到过），NAPT才是我们真正讨论的主角。看下图：<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;&nbsp;&nbsp;&nbsp; Server S1&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;<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; 18.181.0.31:1235&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;<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp; Session 1 (A-S1)&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; 18.181.0.31:1235&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v 155.99.25.11:62000 v&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAT<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;&nbsp;&nbsp;&nbsp;&nbsp; 155.99.25.11<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp; Session 1 (A-S1)&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; 18.181.0.31:1235&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp; 10.0.0.1:1234&nbsp;&nbsp;&nbsp; v&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Client A<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;&nbsp;&nbsp;&nbsp; 10.0.0.1:1234<br />
&nbsp;&nbsp;&nbsp; 有一个私有网络10.*.*.*，Client A是其中的一台计算机，这个网络的网关（一个NAT设备）的外网IP是155.99.25.11(应该还有一个内网的IP地址，比如10.0.0.10)。如果Client A中的某个进程（这个进程创建了一个UDP Socket,这个Socket绑定1234端口）想访问外网主机18.181.0.31的1235端口，那么当数据包通过NAT时会发生什么事情呢？<br />
&nbsp;&nbsp;&nbsp; 首先NAT会改变这个数据包的原IP地址，改为155.99.25.11。接着NAT会为这个传输创建一个Session（Session是一个抽象的概念，如果是TCP，也许Session是由一个SYN包开始，以一个FIN包结束。而UDP呢，以这个IP的这个端口的第一个UDP开始，结束呢，呵呵，也许是几分钟，也许是几小时，这要看具体的实现了）并且给这个Session分配一个端口，比如62000，然后改变这个数据包的源端口为62000。所以本来是（10.0.0.1:1234-&gt;18.181.0.31:1235）的数据包到了互联网上变为了（155.99.25.11:62000-&gt;18.181.0.31:1235）。<br />
&nbsp;&nbsp;&nbsp; 一旦NAT创建了一个Session后，NAT会记住62000端口对应的是10.0.0.1的1234端口，以后从18.181.0.31发送到62000端口的数据会被NAT自动的转发到10.0.0.1上。（注意：这里是说18.181.0.31发送到62000端口的数据会被转发，其他的IP发送到这个端口的数据将被NAT抛弃）这样Client A就与Server S1建立以了一个连接。</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp; 呵呵，上面的基础知识可能很多人都知道了，那么下面是关键的部分了。<br />
&nbsp;&nbsp;&nbsp; 看看下面的情况：<br />
&nbsp;&nbsp;&nbsp; Server S1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server S2<br />
&nbsp;18.181.0.31:1235&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 138.76.29.7:1235<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp; ^&nbsp; Session 1 (A-S1)&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp; Session 2 (A-S2)&nbsp; ^<br />
&nbsp;&nbsp; |&nbsp; 18.181.0.31:1235&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; 138.76.29.7:1235&nbsp; |<br />
&nbsp;&nbsp; v 155.99.25.11:62000 v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v 155.99.25.11:62000 v<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;&nbsp;&nbsp; |<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; Cone NAT<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; 155.99.25.11<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;&nbsp;&nbsp; |<br />
&nbsp;&nbsp; ^&nbsp; Session 1 (A-S1)&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp; Session 2 (A-S2)&nbsp; ^<br />
&nbsp;&nbsp; |&nbsp; 18.181.0.31:1235&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; 138.76.29.7:1235&nbsp; |<br />
&nbsp;&nbsp; v&nbsp;&nbsp; 10.0.0.1:1234&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp; 10.0.0.1:1234&nbsp;&nbsp;&nbsp; v<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;&nbsp;&nbsp; |<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; Client A<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; 10.0.0.1:1234<br />
&nbsp;&nbsp;&nbsp; 接上面的例子，如果Client A的原来那个Socket(绑定了1234端口的那个UDP Socket)又接着向另外一个Server S2发送了一个UDP包，那么这个UDP包在通过NAT时会怎么样呢？<br />
&nbsp;&nbsp;&nbsp; 这时可能会有两种情况发生，一种是NAT再次创建一个Session，并且再次为这个Session分配一个端口号（比如：62001）。另外一种是NAT再次创建一个Session，但是不会新分配一个端口号，而是用原来分配的端口号62000。前一种NAT叫做Symmetric NAT，后一种叫做Cone NAT。我们期望我们的NAT是第二种，呵呵，如果你的NAT刚好是第一种，那么很可能会有很多P2P软件失灵。（可以庆幸的是，现在绝大多数的NAT属于后者，即Cone NAT）<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 好了，我们看到，通过NAT,子网内的计算机向外连结是很容易的（NAT相当于透明的，子网内的和外网的计算机不用知道NAT的情况）。<br />
&nbsp;&nbsp;&nbsp; 但是如果外部的计算机想访问子网内的计算机就比较困难了（而这正是P2P所需要的）。<br />
&nbsp;&nbsp;&nbsp; 那么我们如果想从外部发送一个数据报给内网的计算机有什么办法呢？首先，我们必须在内网的NAT上打上一个&#8220;洞&#8221;（也就是前面我们说的在NAT上建立一个Session），这个洞不能由外部来打，只能由内网内的主机来打。而且这个洞是有方向的，比如从内部某台主机（比如：192.168.0.10）向外部的某个IP(比如：219.237.60.1)发送一个UDP包，那么就在这个内网的NAT设备上打了一个方向为219.237.60.1的&#8220;洞&#8221;，（这就是称为UDP Hole Punching的技术）以后219.237.60.1就可以通过这个洞与内网的192.168.0.10联系了。（但是其他的IP不能利用这个洞）。</div>
<div style="word-break: break-all; ">呵呵，现在该轮到我们的正题P2P了。有了上面的理论，实现两个内网的主机通讯就差最后一步了：那就是鸡生蛋还是蛋生鸡的问题了，两边都无法主动发出连接请求，谁也不知道谁的公网地址，那我们如何来打这个洞呢？我们需要一个中间人来联系这两个内网主机。<br />
&nbsp;&nbsp;&nbsp; 现在我们来看看一个P2P软件的流程，以下图为例：</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server S （219.237.60.1）<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; |<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; |<br />
&nbsp;&nbsp; +----------------------+----------------------+<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;NAT A (外网IP:202.187.45.3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAT B (外网IP:187.34.1.56)<br />
&nbsp;&nbsp; |&nbsp;&nbsp; (内网IP:192.168.0.1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | (内网IP:192.168.0.1)<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
Client A&nbsp; (192.168.0.20:4000)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Client B (192.168.0.10:40000)</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp; 首先，Client A登录服务器，NAT A为这次的Session分配了一个端口60000，那么Server S收到的Client A的地址是202.187.45.3:60000，这就是Client A的外网地址了。同样，Client B登录Server S，NAT B给此次Session分配的端口是40000，那么Server S收到的B的地址是187.34.1.56:40000。<br />
&nbsp;&nbsp;&nbsp; 此时，Client A与Client B都可以与Server S通信了。如果Client A此时想直接发送信息给Client B，那么他可以从Server S那儿获得B的公网地址187.34.1.56:40000，是不是Client A向这个地址发送信息Client B就能收到了呢？答案是不行，因为如果这样发送信息，NAT B会将这个信息丢弃（因为这样的信息是不请自来的，为了安全，大多数NAT都会执行丢弃动作）。现在我们需要的是在NAT B上打一个方向为202.187.45.3（即Client A的外网地址）的洞，那么Client A发送到187.34.1.56:40000的信息,Client B就能收到了。这个打洞命令由谁来发呢，呵呵，当然是Server S。<br />
&nbsp;&nbsp;&nbsp; 总结一下这个过程：如果Client A想向Client B发送信息，那么Client A发送命令给Server S，请求Server S命令Client B向Client A方向打洞。呵呵，是不是很绕口，不过没关系，想一想就很清楚了，何况还有源代码呢（侯老师说过：在源代码面前没有秘密 8）），然后Client A就可以通过Client B的外网地址与Client B通信了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 注意：以上过程只适合于Cone NAT的情况，如果是Symmetric NAT，那么当Client B向Client A打洞的端口已经重新分配了，Client B将无法知道这个端口（如果Symmetric NAT的端口是顺序分配的，那么我们或许可以猜测这个端口号，可是由于可能导致失败的因素太多，我们不推荐这种猜测端口的方法）。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 下面是一个模拟P2P聊天的过程的源代码，过程很简单，P2PServer运行在一个拥有公网IP的计算机上，P2PClient运行在两个不同的NAT后（注意，如果两个客户端运行在一个NAT后，本程序很可能不能运行正常，这取决于你的NAT是否支持loopback translation，详见<a href="http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt" style="text-decoration: none; color: #336699; ">http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt</a>，当然，此问题可以通过双方先尝试连接对方的内网IP来解决，但是这个代码只是为了验证原理，并没有处理这些问题），后登录的计算机可以获得先登录计算机的用户名，后登录的计算机通过send username message的格式来发送消息。如果发送成功，说明你已取得了直接与对方连接的成功。<br />
&nbsp;&nbsp;&nbsp; 程序现在支持三个命令：send , getu , exit<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; send格式：send username message<br />
&nbsp;&nbsp;&nbsp; 功能：发送信息给username<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; getu格式：getu<br />
&nbsp;&nbsp;&nbsp; 功能：获得当前服务器用户列表<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; exit格式：exit<br />
&nbsp;&nbsp;&nbsp; 功能：注销与服务器的连接（服务器不会自动监测客户是否吊线）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 代码很短，相信很容易懂，如果有什么问题，可以给我发邮件<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>&nbsp; 或者在CSDN上发送短消息。同时，欢迎转发此文，但希望保留作者版权8-）。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 最后感谢CSDN网友 PiggyXP 和 Seilfer的测试帮助</div>
<div style="word-break: break-all; ">P2PServer.c</div>
<div style="word-break: break-all; ">/* P2P 程序服务端<br />
&nbsp;*&nbsp;<br />
&nbsp;* 文件名：P2PServer.c<br />
&nbsp;*<br />
&nbsp;* 日期：2004-5-21<br />
&nbsp;*<br />
&nbsp;* 作者：shootingstars(<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>)<br />
&nbsp;*<br />
&nbsp;*/<br />
#pragma comment(lib, "ws2_32.lib")</div>
<div style="word-break: break-all; ">#include "windows.h"<br />
#include "..\proto.h"<br />
#include "..\Exception.h"</div>
<div style="word-break: break-all; ">UserList ClientList;</div>
<div style="word-break: break-all; ">void InitWinSock()<br />
{<br />
&nbsp;WSADATA wsaData;</div>
<div style="word-break: break-all; ">&nbsp;if (WSAStartup(MAKEWORD(2, 2), &amp;wsaData) != 0)<br />
&nbsp;{<br />
&nbsp;&nbsp;printf("Windows sockets 2.2 startup");<br />
&nbsp;&nbsp;throw Exception("");<br />
&nbsp;}<br />
&nbsp;else{<br />
&nbsp;&nbsp;printf("Using %s (Status: %s)\n",<br />
&nbsp;&nbsp;&nbsp;wsaData.szDescription, wsaData.szSystemStatus);<br />
&nbsp;&nbsp;printf("with API versions %d.%d to %d.%d\n\n",<br />
&nbsp;&nbsp;&nbsp;LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion),<br />
&nbsp;&nbsp;&nbsp;LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));<br />
&nbsp;&nbsp;<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; ">SOCKET mksock(int type)<br />
{<br />
&nbsp;SOCKET sock = socket(AF_INET, type, 0);<br />
&nbsp;if (sock &lt; 0)<br />
&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("create socket error");<br />
&nbsp;&nbsp;throw Exception("");<br />
&nbsp;}<br />
&nbsp;return sock;<br />
}</div>
<div style="word-break: break-all; ">stUserListNode GetUser(char *username)<br />
{<br />
&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;{<br />
&nbsp;&nbsp;if( strcmp( ((*UserIterator)-&gt;userName), username) == 0 )<br />
&nbsp;&nbsp;&nbsp;return *(*UserIterator);<br />
&nbsp;}<br />
&nbsp;throw Exception("not find this user");<br />
}</div>
<div style="word-break: break-all; ">int main(int argc, char* argv[])<br />
{<br />
&nbsp;try{<br />
&nbsp;&nbsp;InitWinSock();<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;SOCKET PrimaryUDP;<br />
&nbsp;&nbsp;PrimaryUDP = mksock(SOCK_DGRAM);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sockaddr_in local;<br />
&nbsp;&nbsp;local.sin_family=AF_INET;<br />
&nbsp;&nbsp;local.sin_port= htons(SERVER_PORT);&nbsp;<br />
&nbsp;&nbsp;local.sin_addr.s_addr = htonl(INADDR_ANY);<br />
&nbsp;&nbsp;int nResult=bind(PrimaryUDP,(sockaddr*)&amp;local,sizeof(sockaddr));<br />
&nbsp;&nbsp;if(nResult==SOCKET_ERROR)<br />
&nbsp;&nbsp;&nbsp;throw Exception("bind error");</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sockaddr_in sender;<br />
&nbsp;&nbsp;stMessage recvbuf;<br />
&nbsp;&nbsp;memset(&amp;recvbuf,0,sizeof(stMessage));</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;// 开始主循环.<br />
&nbsp;&nbsp;// 主循环负责下面几件事情:<br />
&nbsp;&nbsp;// 一:读取客户端登陆和登出消息,记录客户列表<br />
&nbsp;&nbsp;// 二:转发客户p2p请求<br />
&nbsp;&nbsp;for(;;)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;int dwSender = sizeof(sender);<br />
&nbsp;&nbsp;&nbsp;int ret = recvfrom(PrimaryUDP, (char *)&amp;recvbuf, sizeof(stMessage), 0, (sockaddr *)&amp;sender, &amp;dwSender);<br />
&nbsp;&nbsp;&nbsp;if(ret &lt;= 0)<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("recv error");<br />
&nbsp;&nbsp;&nbsp;&nbsp;continue;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;int messageType = recvbuf.iMessageType;<br />
&nbsp;&nbsp;&nbsp;&nbsp;switch(messageType){<br />
&nbsp;&nbsp;&nbsp;&nbsp;case LOGIN:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp; 将这个用户的信息记录到用户列表中<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("has a user login : %s\n", recvbuf.message.loginmember.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUserListNode *currentuser = new stUserListNode();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(currentuser-&gt;userName, recvbuf.message.loginmember.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentuser-&gt;ip = ntohl(sender.sin_addr.S_un.S_addr);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentuser-&gt;port = ntohs(sender.sin_port);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClientList.push_back(currentuser);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 发送已经登陆的客户信息<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int nodecount = (int)ClientList.size();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;nodecount, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&amp;sender, sizeof(sender));&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;case LOGOUT:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 将此客户信息删除<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("has a user logout : %s\n", recvbuf.message.logoutmember.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserList::iterator removeiterator = NULL;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( strcmp( ((*UserIterator)-&gt;userName), recvbuf.message.logoutmember.userName) == 0 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;removeiterator = UserIterator;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(removeiterator != NULL)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClientList.remove(*removeiterator);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;case P2PTRANS:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 某个客户希望服务端向另外一个客户发送一个打洞消息<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%s wants to p2p %s\n",inet_ntoa(sender.sin_addr),recvbuf.message.translatemessage.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUserListNode node = GetUser(recvbuf.message.translatemessage.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sockaddr_in remote;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_family=AF_INET;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_port= htons(node.port);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_addr.s_addr = htonl(node.ip);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in_addr tmp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp.S_un.S_addr = htonl(node.ip);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("the address is %s,and port is %d\n",inet_ntoa(tmp), node.port);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stP2PMessage transMessage;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transMessage.iMessageType = P2PSOMEONEWANTTOCALLYOU;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transMessage.iStringLen = ntohl(sender.sin_addr.S_un.S_addr);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transMessage.Port = ntohs(sender.sin_port);<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;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP,(const char*)&amp;transMessage, sizeof(transMessage), 0, (const sockaddr *)&amp;remote, sizeof(remote));</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;case GETALLUSER:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int command = GETALLUSER;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;command, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int nodecount = (int)ClientList.size();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;nodecount, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&amp;sender, sizeof(sender));&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;}<br />
&nbsp;catch(Exception &amp;e)<br />
&nbsp;{<br />
&nbsp;&nbsp;printf(e.GetMessage());<br />
&nbsp;&nbsp;return 1;<br />
&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;return 0;<br />
}</div>
<div style="word-break: break-all; ">/* P2P 程序客户端<br />
&nbsp;*&nbsp;<br />
&nbsp;* 文件名：P2PClient.c<br />
&nbsp;*<br />
&nbsp;* 日期：2004-5-21<br />
&nbsp;*<br />
&nbsp;* 作者：shootingstars(<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>)<br />
&nbsp;*<br />
&nbsp;*/</div>
<div style="word-break: break-all; ">#pragma comment(lib,"ws2_32.lib")</div>
<div style="word-break: break-all; ">#include "windows.h"<br />
#include "..\proto.h"<br />
#include "..\Exception.h"<br />
#include &lt;iostream&gt;<br />
using namespace std;</div>
<div style="word-break: break-all; ">UserList ClientList;</div>
<div style="word-break: break-all; ">&nbsp;</div>
<div style="word-break: break-all; ">#define COMMANDMAXC 256<br />
#define MAXRETRY&nbsp;&nbsp;&nbsp; 5</div>
<div style="word-break: break-all; ">SOCKET PrimaryUDP;<br />
char UserName[10];<br />
char ServerIP[20];</div>
<div style="word-break: break-all; ">bool RecvedACK;</div>
<div style="word-break: break-all; ">void InitWinSock()<br />
{<br />
&nbsp;WSADATA wsaData;</div>
<div style="word-break: break-all; ">&nbsp;if (WSAStartup(MAKEWORD(2, 2), &amp;wsaData) != 0)<br />
&nbsp;{<br />
&nbsp;&nbsp;printf("Windows sockets 2.2 startup");<br />
&nbsp;&nbsp;throw Exception("");<br />
&nbsp;}<br />
&nbsp;else{<br />
&nbsp;&nbsp;printf("Using %s (Status: %s)\n",<br />
&nbsp;&nbsp;&nbsp;wsaData.szDescription, wsaData.szSystemStatus);<br />
&nbsp;&nbsp;printf("with API versions %d.%d to %d.%d\n\n",<br />
&nbsp;&nbsp;&nbsp;LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion),<br />
&nbsp;&nbsp;&nbsp;LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; ">SOCKET mksock(int type)<br />
{<br />
&nbsp;SOCKET sock = socket(AF_INET, type, 0);<br />
&nbsp;if (sock &lt; 0)<br />
&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("create socket error");<br />
&nbsp;&nbsp;throw Exception("");<br />
&nbsp;}<br />
&nbsp;return sock;<br />
}</div>
<div style="word-break: break-all; ">stUserListNode GetUser(char *username)<br />
{<br />
&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;{<br />
&nbsp;&nbsp;if( strcmp( ((*UserIterator)-&gt;userName), username) == 0 )<br />
&nbsp;&nbsp;&nbsp;return *(*UserIterator);<br />
&nbsp;}<br />
&nbsp;throw Exception("not find this user");<br />
}</div>
<div style="word-break: break-all; ">void BindSock(SOCKET sock)<br />
{<br />
&nbsp;sockaddr_in sin;<br />
&nbsp;sin.sin_addr.S_un.S_addr = INADDR_ANY;<br />
&nbsp;sin.sin_family = AF_INET;<br />
&nbsp;sin.sin_port = 0;<br />
&nbsp;<br />
&nbsp;if (bind(sock, (struct sockaddr*)&amp;sin, sizeof(sin)) &lt; 0)<br />
&nbsp;&nbsp;throw Exception("bind error");<br />
}</div>
<div style="word-break: break-all; ">void ConnectToServer(SOCKET sock,char *username, char *serverip)<br />
{<br />
&nbsp;sockaddr_in remote;<br />
&nbsp;remote.sin_addr.S_un.S_addr = inet_addr(serverip);<br />
&nbsp;remote.sin_family = AF_INET;<br />
&nbsp;remote.sin_port = htons(SERVER_PORT);<br />
&nbsp;<br />
&nbsp;stMessage sendbuf;<br />
&nbsp;sendbuf.iMessageType = LOGIN;<br />
&nbsp;strncpy(sendbuf.message.loginmember.userName, username, 10);</div>
<div style="word-break: break-all; ">&nbsp;sendto(sock, (const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&amp;remote,sizeof(remote));</div>
<div style="word-break: break-all; ">&nbsp;int usercount;<br />
&nbsp;int fromlen = sizeof(remote);<br />
&nbsp;int iread = recvfrom(sock, (char *)&amp;usercount, sizeof(int), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />
&nbsp;if(iread&lt;=0)<br />
&nbsp;{<br />
&nbsp;&nbsp;throw Exception("Login error\n");<br />
&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;// 登录到服务端后，接收服务端发来的已经登录的用户的信息<br />
&nbsp;cout&lt;&lt;"Have "&lt;&lt;usercount&lt;&lt;" users logined server:"&lt;&lt;endl;<br />
&nbsp;for(int i = 0;i&lt;usercount;i++)<br />
&nbsp;{<br />
&nbsp;&nbsp;stUserListNode *node = new stUserListNode;<br />
&nbsp;&nbsp;recvfrom(sock, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />
&nbsp;&nbsp;ClientList.push_back(node);<br />
&nbsp;&nbsp;cout&lt;&lt;"Username:"&lt;&lt;node-&gt;userName&lt;&lt;endl;<br />
&nbsp;&nbsp;in_addr tmp;<br />
&nbsp;&nbsp;tmp.S_un.S_addr = htonl(node-&gt;ip);<br />
&nbsp;&nbsp;cout&lt;&lt;"UserIP:"&lt;&lt;inet_ntoa(tmp)&lt;&lt;endl;<br />
&nbsp;&nbsp;cout&lt;&lt;"UserPort:"&lt;&lt;node-&gt;port&lt;&lt;endl;<br />
&nbsp;&nbsp;cout&lt;&lt;""&lt;&lt;endl;<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; ">void OutputUsage()<br />
{<br />
&nbsp;cout&lt;&lt;"You can input you command:\n"<br />
&nbsp;&nbsp;&lt;&lt;"Command Type:\"send\",\"exit\",\"getu\"\n"<br />
&nbsp;&nbsp;&lt;&lt;"Example : send Username Message\n"<br />
&nbsp;&nbsp;&lt;&lt;"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit\n"<br />
&nbsp;&nbsp;&lt;&lt;"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getu\n"<br />
&nbsp;&nbsp;&lt;&lt;endl;<br />
}</div>
<div style="word-break: break-all; ">/* 这是主要的函数：发送一个消息给某个用户(C)<br />
&nbsp;*流程：直接向某个用户的外网IP发送消息，如果此前没有联系过<br />
&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么此消息将无法发送，发送端等待超时。<br />
&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 超时后，发送端将发送一个请求信息到服务端，<br />
&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要求服务端发送给客户C一个请求，请求C给本机发送打洞消息<br />
&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以上流程将重复MAXRETRY次<br />
&nbsp;*/<br />
bool SendMessageTo(char *UserName, char *Message)<br />
{<br />
&nbsp;char realmessage[256];<br />
&nbsp;unsigned int UserIP;<br />
&nbsp;unsigned short UserPort;<br />
&nbsp;bool FindUser = false;<br />
&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;{<br />
&nbsp;&nbsp;if( strcmp( ((*UserIterator)-&gt;userName), UserName) == 0 )<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;UserIP = (*UserIterator)-&gt;ip;<br />
&nbsp;&nbsp;&nbsp;UserPort = (*UserIterator)-&gt;port;<br />
&nbsp;&nbsp;&nbsp;FindUser = true;<br />
&nbsp;&nbsp;}<br />
&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;if(!FindUser)<br />
&nbsp;&nbsp;return false;</div>
<div style="word-break: break-all; ">&nbsp;strcpy(realmessage, Message);<br />
&nbsp;for(int i=0;i&lt;MAXRETRY;i++)<br />
&nbsp;{<br />
&nbsp;&nbsp;RecvedACK = false;</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sockaddr_in remote;<br />
&nbsp;&nbsp;remote.sin_addr.S_un.S_addr = htonl(UserIP);<br />
&nbsp;&nbsp;remote.sin_family = AF_INET;<br />
&nbsp;&nbsp;remote.sin_port = htons(UserPort);<br />
&nbsp;&nbsp;stP2PMessage MessageHead;<br />
&nbsp;&nbsp;MessageHead.iMessageType = P2PMESSAGE;<br />
&nbsp;&nbsp;MessageHead.iStringLen = (int)strlen(realmessage)+1;<br />
&nbsp;&nbsp;int isend = sendto(PrimaryUDP, (const char *)&amp;MessageHead, sizeof(MessageHead), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />
&nbsp;&nbsp;isend = sendto(PrimaryUDP, (const char *)&amp;realmessage, MessageHead.iStringLen, 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;// 等待接收线程将此标记修改<br />
&nbsp;&nbsp;for(int j=0;j&lt;10;j++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;if(RecvedACK)<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;Sleep(300);<br />
&nbsp;&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;// 没有接收到目标主机的回应，认为目标主机的端口映射没有<br />
&nbsp;&nbsp;// 打开，那么发送请求信息给服务器，要服务器告诉目标主机<br />
&nbsp;&nbsp;// 打开映射端口（UDP打洞）<br />
&nbsp;&nbsp;sockaddr_in server;<br />
&nbsp;&nbsp;server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />
&nbsp;&nbsp;server.sin_family = AF_INET;<br />
&nbsp;&nbsp;server.sin_port = htons(SERVER_PORT);<br />
&nbsp;<br />
&nbsp;&nbsp;stMessage transMessage;<br />
&nbsp;&nbsp;transMessage.iMessageType = P2PTRANS;<br />
&nbsp;&nbsp;strcpy(transMessage.message.translatemessage.userName, UserName);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;transMessage, sizeof(transMessage), 0, (const sockaddr*)&amp;server, sizeof(server));<br />
&nbsp;&nbsp;Sleep(100);// 等待对方先发送信息。<br />
&nbsp;}<br />
&nbsp;return false;<br />
}</div>
<div style="word-break: break-all; ">// 解析命令，暂时只有exit和send命令<br />
// 新增getu命令，获取当前服务器的所有用户<br />
void ParseCommand(char * CommandLine)<br />
{<br />
&nbsp;if(strlen(CommandLine)&lt;4)<br />
&nbsp;&nbsp;return;<br />
&nbsp;char Command[10];<br />
&nbsp;strncpy(Command, CommandLine, 4);<br />
&nbsp;Command[4]='\0';</div>
<div style="word-break: break-all; ">&nbsp;if(strcmp(Command,"exit")==0)<br />
&nbsp;{<br />
&nbsp;&nbsp;stMessage sendbuf;<br />
&nbsp;&nbsp;sendbuf.iMessageType = LOGOUT;<br />
&nbsp;&nbsp;strncpy(sendbuf.message.logoutmember.userName, UserName, 10);<br />
&nbsp;&nbsp;sockaddr_in server;<br />
&nbsp;&nbsp;server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />
&nbsp;&nbsp;server.sin_family = AF_INET;<br />
&nbsp;&nbsp;server.sin_port = htons(SERVER_PORT);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sendto(PrimaryUDP,(const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr *)&amp;server, sizeof(server));<br />
&nbsp;&nbsp;shutdown(PrimaryUDP, 2);<br />
&nbsp;&nbsp;closesocket(PrimaryUDP);<br />
&nbsp;&nbsp;exit(0);<br />
&nbsp;}<br />
&nbsp;else if(strcmp(Command,"send")==0)<br />
&nbsp;{<br />
&nbsp;&nbsp;char sendname[20];<br />
&nbsp;&nbsp;char message[COMMANDMAXC];<br />
&nbsp;&nbsp;int i;<br />
&nbsp;&nbsp;for(i=5;;i++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;if(CommandLine[i]!=' ')<br />
&nbsp;&nbsp;&nbsp;&nbsp;sendname[i-5]=CommandLine[i];<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;sendname[i-5]='\0';<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;strcpy(message, &amp;(CommandLine[i+1]));<br />
&nbsp;&nbsp;if(SendMessageTo(sendname, message))<br />
&nbsp;&nbsp;&nbsp;printf("Send OK!\n");<br />
&nbsp;&nbsp;else&nbsp;<br />
&nbsp;&nbsp;&nbsp;printf("Send Failure!\n");<br />
&nbsp;}<br />
&nbsp;else if(strcmp(Command,"getu")==0)<br />
&nbsp;{<br />
&nbsp;&nbsp;int command = GETALLUSER;<br />
&nbsp;&nbsp;sockaddr_in server;<br />
&nbsp;&nbsp;server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />
&nbsp;&nbsp;server.sin_family = AF_INET;<br />
&nbsp;&nbsp;server.sin_port = htons(SERVER_PORT);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sendto(PrimaryUDP,(const char*)&amp;command, sizeof(command), 0, (const sockaddr *)&amp;server, sizeof(server));<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; ">// 接受消息线程<br />
DWORD WINAPI RecvThreadProc(LPVOID lpParameter)<br />
{<br />
&nbsp;sockaddr_in remote;<br />
&nbsp;int sinlen = sizeof(remote);<br />
&nbsp;stP2PMessage recvbuf;<br />
&nbsp;for(;;)<br />
&nbsp;{<br />
&nbsp;&nbsp;int iread = recvfrom(PrimaryUDP, (char *)&amp;recvbuf, sizeof(recvbuf), 0, (sockaddr *)&amp;remote, &amp;sinlen);<br />
&nbsp;&nbsp;if(iread&lt;=0)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;printf("recv error\n");<br />
&nbsp;&nbsp;&nbsp;continue;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;switch(recvbuf.iMessageType)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;case P2PMESSAGE:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 接收到P2P的消息<br />
&nbsp;&nbsp;&nbsp;&nbsp;char *comemessage= new char[recvbuf.iStringLen];<br />
&nbsp;&nbsp;&nbsp;&nbsp;int iread1 = recvfrom(PrimaryUDP, comemessage, 256, 0, (sockaddr *)&amp;remote, &amp;sinlen);<br />
&nbsp;&nbsp;&nbsp;&nbsp;comemessage[iread1-1] = '\0';<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(iread1&lt;=0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw Exception("Recv Message Error\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Recv a Message:%s\n",comemessage);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stP2PMessage sendbuf;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendbuf.iMessageType = P2PMESSAGEACK;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;delete []comemessage;<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;case P2PSOMEONEWANTTOCALLYOU:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 接收到打洞命令，向指定的IP地址打洞<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("Recv p2someonewanttocallyou data\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;sockaddr_in remote;<br />
&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_addr.S_un.S_addr = htonl(recvbuf.iStringLen);<br />
&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_family = AF_INET;<br />
&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_port = htons(recvbuf.Port);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;// UDP hole punching<br />
&nbsp;&nbsp;&nbsp;&nbsp;stP2PMessage message;<br />
&nbsp;&nbsp;&nbsp;&nbsp;message.iMessageType = P2PTRASH;<br />
&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char *)&amp;message, sizeof(message), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;case P2PMESSAGEACK:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 发送消息的应答<br />
&nbsp;&nbsp;&nbsp;&nbsp;RecvedACK = true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;case P2PTRASH:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 对方发送的打洞消息，忽略掉。<br />
&nbsp;&nbsp;&nbsp;&nbsp;//do nothing ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("Recv p2ptrash data\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;case GETALLUSER:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;int usercount;<br />
&nbsp;&nbsp;&nbsp;&nbsp;int fromlen = sizeof(remote);<br />
&nbsp;&nbsp;&nbsp;&nbsp;int iread = recvfrom(PrimaryUDP, (char *)&amp;usercount, sizeof(int), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(iread&lt;=0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw Exception("Login error\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;ClientList.clear();</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"Have "&lt;&lt;usercount&lt;&lt;" users logined server:"&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;for(int i = 0;i&lt;usercount;i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUserListNode *node = new stUserListNode;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recvfrom(PrimaryUDP, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClientList.push_back(node);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"Username:"&lt;&lt;node-&gt;userName&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in_addr tmp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp.S_un.S_addr = htonl(node-&gt;ip);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"UserIP:"&lt;&lt;inet_ntoa(tmp)&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"UserPort:"&lt;&lt;node-&gt;port&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;""&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; "><br />
int main(int argc, char* argv[])<br />
{<br />
&nbsp;try<br />
&nbsp;{<br />
&nbsp;&nbsp;InitWinSock();<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;PrimaryUDP = mksock(SOCK_DGRAM);<br />
&nbsp;&nbsp;BindSock(PrimaryUDP);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;cout&lt;&lt;"Please input server ip:";<br />
&nbsp;&nbsp;cin&gt;&gt;ServerIP;</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;cout&lt;&lt;"Please input your name:";<br />
&nbsp;&nbsp;cin&gt;&gt;UserName;</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;ConnectToServer(PrimaryUDP, UserName, ServerIP);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;HANDLE threadhandle = CreateThread(NULL, 0, RecvThreadProc, NULL, NULL, NULL);<br />
&nbsp;&nbsp;CloseHandle(threadhandle);<br />
&nbsp;&nbsp;OutputUsage();</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;for(;;)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;char Command[COMMANDMAXC];<br />
&nbsp;&nbsp;&nbsp;gets(Command);<br />
&nbsp;&nbsp;&nbsp;ParseCommand(Command);<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
&nbsp;catch(Exception &amp;e)<br />
&nbsp;{<br />
&nbsp;&nbsp;printf(e.GetMessage());<br />
&nbsp;&nbsp;return 1;<br />
&nbsp;}<br />
&nbsp;return 0;<br />
}</div>
<div style="word-break: break-all; ">/* 异常类<br />
&nbsp;*<br />
&nbsp;* 文件名：Exception.h<br />
&nbsp;*<br />
&nbsp;* 日期：2004.5.5<br />
&nbsp;*<br />
&nbsp;* 作者：shootingstars(<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>)<br />
&nbsp;*/</div>
<div style="word-break: break-all; ">#ifndef __HZH_Exception__<br />
#define __HZH_Exception__</div>
<div style="word-break: break-all; ">#define EXCEPTION_MESSAGE_MAXLEN 256<br />
#include "string.h"</div>
<div style="word-break: break-all; ">class Exception<br />
{<br />
private:<br />
&nbsp;char m_ExceptionMessage[EXCEPTION_MESSAGE_MAXLEN];<br />
public:<br />
&nbsp;Exception(char *msg)<br />
&nbsp;{<br />
&nbsp;&nbsp;strncpy(m_ExceptionMessage, msg, EXCEPTION_MESSAGE_MAXLEN);<br />
&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;char *GetMessage()<br />
&nbsp;{<br />
&nbsp;&nbsp;return m_ExceptionMessage;<br />
&nbsp;}<br />
};</div>
<div style="word-break: break-all; ">#endif</div>
<div style="word-break: break-all; ">/* P2P 程序传输协议<br />
&nbsp;*&nbsp;<br />
&nbsp;* 日期：2004-5-21<br />
&nbsp;*<br />
&nbsp;* 作者：shootingstars(<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>)<br />
&nbsp;*<br />
&nbsp;*/</div>
<div style="word-break: break-all; ">#pragma once<br />
#include &lt;list&gt;</div>
<div style="word-break: break-all; ">// 定义iMessageType的值<br />
#define LOGIN 1<br />
#define LOGOUT 2<br />
#define P2PTRANS 3<br />
#define GETALLUSER&nbsp; 4</div>
<div style="word-break: break-all; ">// 服务器端口<br />
#define SERVER_PORT 2280</div>
<div style="word-break: break-all; ">// Client登录时向服务器发送的消息<br />
struct stLoginMessage<br />
{<br />
&nbsp;char userName[10];<br />
&nbsp;char password[10];<br />
};</div>
<div style="word-break: break-all; ">// Client注销时发送的消息<br />
struct stLogoutMessage<br />
{<br />
&nbsp;char userName[10];<br />
};</div>
<div style="word-break: break-all; ">// Client向服务器请求另外一个Client(userName)向自己方向发送UDP打洞消息<br />
struct stP2PTranslate<br />
{<br />
&nbsp;char userName[10];<br />
};</div>
<div style="word-break: break-all; ">// Client向服务器发送的消息格式<br />
struct stMessage<br />
{<br />
&nbsp;int iMessageType;<br />
&nbsp;union _message<br />
&nbsp;{<br />
&nbsp;&nbsp;stLoginMessage loginmember;<br />
&nbsp;&nbsp;stLogoutMessage logoutmember;<br />
&nbsp;&nbsp;stP2PTranslate translatemessage;<br />
&nbsp;}message;<br />
};</div>
<div style="word-break: break-all; ">// 客户节点信息<br />
struct stUserListNode<br />
{<br />
&nbsp;char userName[10];<br />
&nbsp;unsigned int ip;<br />
&nbsp;unsigned short port;<br />
};</div>
<div style="word-break: break-all; ">// Server向Client发送的消息<br />
struct stServerToClient<br />
{<br />
&nbsp;int iMessageType;<br />
&nbsp;union _message<br />
&nbsp;{<br />
&nbsp;&nbsp;stUserListNode user;<br />
&nbsp;}message;</div>
<div style="word-break: break-all; ">};</div>
<div style="word-break: break-all; ">//======================================<br />
// 下面的协议用于客户端之间的通信<br />
//======================================<br />
#define P2PMESSAGE 100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 发送消息<br />
#define P2PMESSAGEACK 101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 收到消息的应答<br />
#define P2PSOMEONEWANTTOCALLYOU 102&nbsp; // 服务器向客户端发送的消息<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 希望此客户端发送一个UDP打洞包<br />
#define P2PTRASH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 103&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 客户端发送的打洞包，接收端应该忽略此消息</div>
<div style="word-break: break-all; ">// 客户端之间发送消息格式<br />
struct stP2PMessage<br />
{<br />
&nbsp;int iMessageType;<br />
&nbsp;int iStringLen;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // or IP address<br />
&nbsp;unsigned short Port;&nbsp;<br />
};</div>
<div style="word-break: break-all; ">using namespace std;<br />
typedef list&lt;stUserListNode *&gt; UserList;</div>
</span><img src ="http://www.cppblog.com/iuranus/aggbug/146728.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2011-05-19 08:37 <a href="http://www.cppblog.com/iuranus/archive/2011/05/19/146728.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)P2P之UDP穿透NAT的原理与实现（附源代码）</title><link>http://www.cppblog.com/iuranus/archive/2011/05/19/146727.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Thu, 19 May 2011 00:35:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2011/05/19/146727.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/146727.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2011/05/19/146727.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/146727.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/146727.html</trackback:ping><description><![CDATA[<span style="font-family: verdana, sans-serif; line-height: 21px; font-size: 14px; ">
<div style="word-break: break-all; ">P2P 之 UDP穿透NAT的原理与实现（附源代码）<br />
原创：shootingstars<br />
参考：<a href="http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt" style="text-decoration: none; color: #336699; ">http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt</a></div>
<div style="word-break: break-all; ">论坛上经常有对P2P原理的讨论，但是讨论归讨论，很少有实质的东西产生（源代码）。呵呵，在这里我就用自己实现的一个源代码来说明UDP穿越NAT的原理。</div>
<div style="word-break: break-all; ">首先先介绍一些基本概念：<br />
&nbsp;&nbsp;&nbsp; NAT(Network Address Translators)，网络地址转换：网络地址转换是在IP地址日益缺乏的情况下产生的，它的主要目的就是为了能够地址重用。NAT分为两大类，基本的NAT和NAPT(Network Address/Port Translator)。<br />
&nbsp;&nbsp;&nbsp; 最开始NAT是运行在路由器上的一个功能模块。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 最先提出的是基本的NAT，它的产生基于如下事实：一个私有网络（域）中的节点中只有很少的节点需要与外网连接（呵呵，这是在上世纪90年代中期提出的）。那么这个子网中其实只有少数的节点需要全球唯一的IP地址，其他的节点的IP地址应该是可以重用的。<br />
&nbsp;&nbsp;&nbsp; 因此，基本的NAT实现的功能很简单，在子网内使用一个保留的IP子网段，这些IP对外是不可见的。子网内只有少数一些IP地址可以对应到真正全球唯一的IP地址。如果这些节点需要访问外部网络，那么基本NAT就负责将这个节点的子网内IP转化为一个全球唯一的IP然后发送出去。(基本的NAT会改变IP包中的原IP地址，但是不会改变IP包中的端口)<br />
&nbsp;&nbsp;&nbsp; 关于基本的NAT可以参看RFC 1631<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 另外一种NAT叫做NAPT，从名称上我们也可以看得出，NAPT不但会改变经过这个NAT设备的IP数据报的IP地址，还会改变IP数据报的TCP/UDP端口。基本NAT的设备可能我们见的不多（呵呵，我没有见到过），NAPT才是我们真正讨论的主角。看下图：<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;&nbsp;&nbsp;&nbsp; Server S1&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;<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; 18.181.0.31:1235&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;<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp; Session 1 (A-S1)&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; 18.181.0.31:1235&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v 155.99.25.11:62000 v&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAT<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;&nbsp;&nbsp;&nbsp;&nbsp; 155.99.25.11<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp; Session 1 (A-S1)&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; 18.181.0.31:1235&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp; 10.0.0.1:1234&nbsp;&nbsp;&nbsp; v&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Client A<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;&nbsp;&nbsp;&nbsp; 10.0.0.1:1234<br />
&nbsp;&nbsp;&nbsp; 有一个私有网络10.*.*.*，Client A是其中的一台计算机，这个网络的网关（一个NAT设备）的外网IP是155.99.25.11(应该还有一个内网的IP地址，比如10.0.0.10)。如果Client A中的某个进程（这个进程创建了一个UDP Socket,这个Socket绑定1234端口）想访问外网主机18.181.0.31的1235端口，那么当数据包通过NAT时会发生什么事情呢？<br />
&nbsp;&nbsp;&nbsp; 首先NAT会改变这个数据包的原IP地址，改为155.99.25.11。接着NAT会为这个传输创建一个Session（Session是一个抽象的概念，如果是TCP，也许Session是由一个SYN包开始，以一个FIN包结束。而UDP呢，以这个IP的这个端口的第一个UDP开始，结束呢，呵呵，也许是几分钟，也许是几小时，这要看具体的实现了）并且给这个Session分配一个端口，比如62000，然后改变这个数据包的源端口为62000。所以本来是（10.0.0.1:1234-&gt;18.181.0.31:1235）的数据包到了互联网上变为了（155.99.25.11:62000-&gt;18.181.0.31:1235）。<br />
&nbsp;&nbsp;&nbsp; 一旦NAT创建了一个Session后，NAT会记住62000端口对应的是10.0.0.1的1234端口，以后从18.181.0.31发送到62000端口的数据会被NAT自动的转发到10.0.0.1上。（注意：这里是说18.181.0.31发送到62000端口的数据会被转发，其他的IP发送到这个端口的数据将被NAT抛弃）这样Client A就与Server S1建立以了一个连接。</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp; 呵呵，上面的基础知识可能很多人都知道了，那么下面是关键的部分了。<br />
&nbsp;&nbsp;&nbsp; 看看下面的情况：<br />
&nbsp;&nbsp;&nbsp; Server S1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server S2<br />
&nbsp;18.181.0.31:1235&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 138.76.29.7:1235<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp; ^&nbsp; Session 1 (A-S1)&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp; Session 2 (A-S2)&nbsp; ^<br />
&nbsp;&nbsp; |&nbsp; 18.181.0.31:1235&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; 138.76.29.7:1235&nbsp; |<br />
&nbsp;&nbsp; v 155.99.25.11:62000 v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v 155.99.25.11:62000 v<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;&nbsp;&nbsp; |<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; Cone NAT<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; 155.99.25.11<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;&nbsp;&nbsp; |<br />
&nbsp;&nbsp; ^&nbsp; Session 1 (A-S1)&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp; Session 2 (A-S2)&nbsp; ^<br />
&nbsp;&nbsp; |&nbsp; 18.181.0.31:1235&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; 138.76.29.7:1235&nbsp; |<br />
&nbsp;&nbsp; v&nbsp;&nbsp; 10.0.0.1:1234&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp; 10.0.0.1:1234&nbsp;&nbsp;&nbsp; v<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;&nbsp;&nbsp; |<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; Client A<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; 10.0.0.1:1234<br />
&nbsp;&nbsp;&nbsp; 接上面的例子，如果Client A的原来那个Socket(绑定了1234端口的那个UDP Socket)又接着向另外一个Server S2发送了一个UDP包，那么这个UDP包在通过NAT时会怎么样呢？<br />
&nbsp;&nbsp;&nbsp; 这时可能会有两种情况发生，一种是NAT再次创建一个Session，并且再次为这个Session分配一个端口号（比如：62001）。另外一种是NAT再次创建一个Session，但是不会新分配一个端口号，而是用原来分配的端口号62000。前一种NAT叫做Symmetric NAT，后一种叫做Cone NAT。我们期望我们的NAT是第二种，呵呵，如果你的NAT刚好是第一种，那么很可能会有很多P2P软件失灵。（可以庆幸的是，现在绝大多数的NAT属于后者，即Cone NAT）<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 好了，我们看到，通过NAT,子网内的计算机向外连结是很容易的（NAT相当于透明的，子网内的和外网的计算机不用知道NAT的情况）。<br />
&nbsp;&nbsp;&nbsp; 但是如果外部的计算机想访问子网内的计算机就比较困难了（而这正是P2P所需要的）。<br />
&nbsp;&nbsp;&nbsp; 那么我们如果想从外部发送一个数据报给内网的计算机有什么办法呢？首先，我们必须在内网的NAT上打上一个&#8220;洞&#8221;（也就是前面我们说的在NAT上建立一个Session），这个洞不能由外部来打，只能由内网内的主机来打。而且这个洞是有方向的，比如从内部某台主机（比如：192.168.0.10）向外部的某个IP(比如：219.237.60.1)发送一个UDP包，那么就在这个内网的NAT设备上打了一个方向为219.237.60.1的&#8220;洞&#8221;，（这就是称为UDP Hole Punching的技术）以后219.237.60.1就可以通过这个洞与内网的192.168.0.10联系了。（但是其他的IP不能利用这个洞）。</div>
<div style="word-break: break-all; ">呵呵，现在该轮到我们的正题P2P了。有了上面的理论，实现两个内网的主机通讯就差最后一步了：那就是鸡生蛋还是蛋生鸡的问题了，两边都无法主动发出连接请求，谁也不知道谁的公网地址，那我们如何来打这个洞呢？我们需要一个中间人来联系这两个内网主机。<br />
&nbsp;&nbsp;&nbsp; 现在我们来看看一个P2P软件的流程，以下图为例：</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server S （219.237.60.1）<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; |<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; |<br />
&nbsp;&nbsp; +----------------------+----------------------+<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;NAT A (外网IP:202.187.45.3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAT B (外网IP:187.34.1.56)<br />
&nbsp;&nbsp; |&nbsp;&nbsp; (内网IP:192.168.0.1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | (内网IP:192.168.0.1)<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
Client A&nbsp; (192.168.0.20:4000)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Client B (192.168.0.10:40000)</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp; 首先，Client A登录服务器，NAT A为这次的Session分配了一个端口60000，那么Server S收到的Client A的地址是202.187.45.3:60000，这就是Client A的外网地址了。同样，Client B登录Server S，NAT B给此次Session分配的端口是40000，那么Server S收到的B的地址是187.34.1.56:40000。<br />
&nbsp;&nbsp;&nbsp; 此时，Client A与Client B都可以与Server S通信了。如果Client A此时想直接发送信息给Client B，那么他可以从Server S那儿获得B的公网地址187.34.1.56:40000，是不是Client A向这个地址发送信息Client B就能收到了呢？答案是不行，因为如果这样发送信息，NAT B会将这个信息丢弃（因为这样的信息是不请自来的，为了安全，大多数NAT都会执行丢弃动作）。现在我们需要的是在NAT B上打一个方向为202.187.45.3（即Client A的外网地址）的洞，那么Client A发送到187.34.1.56:40000的信息,Client B就能收到了。这个打洞命令由谁来发呢，呵呵，当然是Server S。<br />
&nbsp;&nbsp;&nbsp; 总结一下这个过程：如果Client A想向Client B发送信息，那么Client A发送命令给Server S，请求Server S命令Client B向Client A方向打洞。呵呵，是不是很绕口，不过没关系，想一想就很清楚了，何况还有源代码呢（侯老师说过：在源代码面前没有秘密 8）），然后Client A就可以通过Client B的外网地址与Client B通信了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 注意：以上过程只适合于Cone NAT的情况，如果是Symmetric NAT，那么当Client B向Client A打洞的端口已经重新分配了，Client B将无法知道这个端口（如果Symmetric NAT的端口是顺序分配的，那么我们或许可以猜测这个端口号，可是由于可能导致失败的因素太多，我们不推荐这种猜测端口的方法）。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 下面是一个模拟P2P聊天的过程的源代码，过程很简单，P2PServer运行在一个拥有公网IP的计算机上，P2PClient运行在两个不同的NAT后（注意，如果两个客户端运行在一个NAT后，本程序很可能不能运行正常，这取决于你的NAT是否支持loopback translation，详见<a href="http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt" style="text-decoration: none; color: #336699; ">http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt</a>，当然，此问题可以通过双方先尝试连接对方的内网IP来解决，但是这个代码只是为了验证原理，并没有处理这些问题），后登录的计算机可以获得先登录计算机的用户名，后登录的计算机通过send username message的格式来发送消息。如果发送成功，说明你已取得了直接与对方连接的成功。<br />
&nbsp;&nbsp;&nbsp; 程序现在支持三个命令：send , getu , exit<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; send格式：send username message<br />
&nbsp;&nbsp;&nbsp; 功能：发送信息给username<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; getu格式：getu<br />
&nbsp;&nbsp;&nbsp; 功能：获得当前服务器用户列表<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; exit格式：exit<br />
&nbsp;&nbsp;&nbsp; 功能：注销与服务器的连接（服务器不会自动监测客户是否吊线）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 代码很短，相信很容易懂，如果有什么问题，可以给我发邮件<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>&nbsp; 或者在CSDN上发送短消息。同时，欢迎转发此文，但希望保留作者版权8-）。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; 最后感谢CSDN网友 PiggyXP 和 Seilfer的测试帮助</div>
<div style="word-break: break-all; ">P2PServer.c</div>
<div style="word-break: break-all; ">/* P2P 程序服务端<br />
&nbsp;*&nbsp;<br />
&nbsp;* 文件名：P2PServer.c<br />
&nbsp;*<br />
&nbsp;* 日期：2004-5-21<br />
&nbsp;*<br />
&nbsp;* 作者：shootingstars(<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>)<br />
&nbsp;*<br />
&nbsp;*/<br />
#pragma comment(lib, "ws2_32.lib")</div>
<div style="word-break: break-all; ">#include "windows.h"<br />
#include "..\proto.h"<br />
#include "..\Exception.h"</div>
<div style="word-break: break-all; ">UserList ClientList;</div>
<div style="word-break: break-all; ">void InitWinSock()<br />
{<br />
&nbsp;WSADATA wsaData;</div>
<div style="word-break: break-all; ">&nbsp;if (WSAStartup(MAKEWORD(2, 2), &amp;wsaData) != 0)<br />
&nbsp;{<br />
&nbsp;&nbsp;printf("Windows sockets 2.2 startup");<br />
&nbsp;&nbsp;throw Exception("");<br />
&nbsp;}<br />
&nbsp;else{<br />
&nbsp;&nbsp;printf("Using %s (Status: %s)\n",<br />
&nbsp;&nbsp;&nbsp;wsaData.szDescription, wsaData.szSystemStatus);<br />
&nbsp;&nbsp;printf("with API versions %d.%d to %d.%d\n\n",<br />
&nbsp;&nbsp;&nbsp;LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion),<br />
&nbsp;&nbsp;&nbsp;LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));<br />
&nbsp;&nbsp;<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; ">SOCKET mksock(int type)<br />
{<br />
&nbsp;SOCKET sock = socket(AF_INET, type, 0);<br />
&nbsp;if (sock &lt; 0)<br />
&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("create socket error");<br />
&nbsp;&nbsp;throw Exception("");<br />
&nbsp;}<br />
&nbsp;return sock;<br />
}</div>
<div style="word-break: break-all; ">stUserListNode GetUser(char *username)<br />
{<br />
&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;{<br />
&nbsp;&nbsp;if( strcmp( ((*UserIterator)-&gt;userName), username) == 0 )<br />
&nbsp;&nbsp;&nbsp;return *(*UserIterator);<br />
&nbsp;}<br />
&nbsp;throw Exception("not find this user");<br />
}</div>
<div style="word-break: break-all; ">int main(int argc, char* argv[])<br />
{<br />
&nbsp;try{<br />
&nbsp;&nbsp;InitWinSock();<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;SOCKET PrimaryUDP;<br />
&nbsp;&nbsp;PrimaryUDP = mksock(SOCK_DGRAM);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sockaddr_in local;<br />
&nbsp;&nbsp;local.sin_family=AF_INET;<br />
&nbsp;&nbsp;local.sin_port= htons(SERVER_PORT);&nbsp;<br />
&nbsp;&nbsp;local.sin_addr.s_addr = htonl(INADDR_ANY);<br />
&nbsp;&nbsp;int nResult=bind(PrimaryUDP,(sockaddr*)&amp;local,sizeof(sockaddr));<br />
&nbsp;&nbsp;if(nResult==SOCKET_ERROR)<br />
&nbsp;&nbsp;&nbsp;throw Exception("bind error");</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sockaddr_in sender;<br />
&nbsp;&nbsp;stMessage recvbuf;<br />
&nbsp;&nbsp;memset(&amp;recvbuf,0,sizeof(stMessage));</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;// 开始主循环.<br />
&nbsp;&nbsp;// 主循环负责下面几件事情:<br />
&nbsp;&nbsp;// 一:读取客户端登陆和登出消息,记录客户列表<br />
&nbsp;&nbsp;// 二:转发客户p2p请求<br />
&nbsp;&nbsp;for(;;)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;int dwSender = sizeof(sender);<br />
&nbsp;&nbsp;&nbsp;int ret = recvfrom(PrimaryUDP, (char *)&amp;recvbuf, sizeof(stMessage), 0, (sockaddr *)&amp;sender, &amp;dwSender);<br />
&nbsp;&nbsp;&nbsp;if(ret &lt;= 0)<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("recv error");<br />
&nbsp;&nbsp;&nbsp;&nbsp;continue;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;int messageType = recvbuf.iMessageType;<br />
&nbsp;&nbsp;&nbsp;&nbsp;switch(messageType){<br />
&nbsp;&nbsp;&nbsp;&nbsp;case LOGIN:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp; 将这个用户的信息记录到用户列表中<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("has a user login : %s\n", recvbuf.message.loginmember.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUserListNode *currentuser = new stUserListNode();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(currentuser-&gt;userName, recvbuf.message.loginmember.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentuser-&gt;ip = ntohl(sender.sin_addr.S_un.S_addr);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentuser-&gt;port = ntohs(sender.sin_port);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClientList.push_back(currentuser);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 发送已经登陆的客户信息<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int nodecount = (int)ClientList.size();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;nodecount, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&amp;sender, sizeof(sender));&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;case LOGOUT:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 将此客户信息删除<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("has a user logout : %s\n", recvbuf.message.logoutmember.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserList::iterator removeiterator = NULL;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( strcmp( ((*UserIterator)-&gt;userName), recvbuf.message.logoutmember.userName) == 0 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;removeiterator = UserIterator;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(removeiterator != NULL)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClientList.remove(*removeiterator);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;case P2PTRANS:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 某个客户希望服务端向另外一个客户发送一个打洞消息<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%s wants to p2p %s\n",inet_ntoa(sender.sin_addr),recvbuf.message.translatemessage.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUserListNode node = GetUser(recvbuf.message.translatemessage.userName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sockaddr_in remote;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_family=AF_INET;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_port= htons(node.port);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_addr.s_addr = htonl(node.ip);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in_addr tmp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp.S_un.S_addr = htonl(node.ip);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("the address is %s,and port is %d\n",inet_ntoa(tmp), node.port);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stP2PMessage transMessage;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transMessage.iMessageType = P2PSOMEONEWANTTOCALLYOU;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transMessage.iStringLen = ntohl(sender.sin_addr.S_un.S_addr);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transMessage.Port = ntohs(sender.sin_port);<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;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP,(const char*)&amp;transMessage, sizeof(transMessage), 0, (const sockaddr *)&amp;remote, sizeof(remote));</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;case GETALLUSER:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int command = GETALLUSER;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;command, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int nodecount = (int)ClientList.size();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;nodecount, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&amp;sender, sizeof(sender));&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;}<br />
&nbsp;catch(Exception &amp;e)<br />
&nbsp;{<br />
&nbsp;&nbsp;printf(e.GetMessage());<br />
&nbsp;&nbsp;return 1;<br />
&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;return 0;<br />
}</div>
<div style="word-break: break-all; ">/* P2P 程序客户端<br />
&nbsp;*&nbsp;<br />
&nbsp;* 文件名：P2PClient.c<br />
&nbsp;*<br />
&nbsp;* 日期：2004-5-21<br />
&nbsp;*<br />
&nbsp;* 作者：shootingstars(<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>)<br />
&nbsp;*<br />
&nbsp;*/</div>
<div style="word-break: break-all; ">#pragma comment(lib,"ws2_32.lib")</div>
<div style="word-break: break-all; ">#include "windows.h"<br />
#include "..\proto.h"<br />
#include "..\Exception.h"<br />
#include &lt;iostream&gt;<br />
using namespace std;</div>
<div style="word-break: break-all; ">UserList ClientList;</div>
<div style="word-break: break-all; ">&nbsp;</div>
<div style="word-break: break-all; ">#define COMMANDMAXC 256<br />
#define MAXRETRY&nbsp;&nbsp;&nbsp; 5</div>
<div style="word-break: break-all; ">SOCKET PrimaryUDP;<br />
char UserName[10];<br />
char ServerIP[20];</div>
<div style="word-break: break-all; ">bool RecvedACK;</div>
<div style="word-break: break-all; ">void InitWinSock()<br />
{<br />
&nbsp;WSADATA wsaData;</div>
<div style="word-break: break-all; ">&nbsp;if (WSAStartup(MAKEWORD(2, 2), &amp;wsaData) != 0)<br />
&nbsp;{<br />
&nbsp;&nbsp;printf("Windows sockets 2.2 startup");<br />
&nbsp;&nbsp;throw Exception("");<br />
&nbsp;}<br />
&nbsp;else{<br />
&nbsp;&nbsp;printf("Using %s (Status: %s)\n",<br />
&nbsp;&nbsp;&nbsp;wsaData.szDescription, wsaData.szSystemStatus);<br />
&nbsp;&nbsp;printf("with API versions %d.%d to %d.%d\n\n",<br />
&nbsp;&nbsp;&nbsp;LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion),<br />
&nbsp;&nbsp;&nbsp;LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; ">SOCKET mksock(int type)<br />
{<br />
&nbsp;SOCKET sock = socket(AF_INET, type, 0);<br />
&nbsp;if (sock &lt; 0)<br />
&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("create socket error");<br />
&nbsp;&nbsp;throw Exception("");<br />
&nbsp;}<br />
&nbsp;return sock;<br />
}</div>
<div style="word-break: break-all; ">stUserListNode GetUser(char *username)<br />
{<br />
&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;{<br />
&nbsp;&nbsp;if( strcmp( ((*UserIterator)-&gt;userName), username) == 0 )<br />
&nbsp;&nbsp;&nbsp;return *(*UserIterator);<br />
&nbsp;}<br />
&nbsp;throw Exception("not find this user");<br />
}</div>
<div style="word-break: break-all; ">void BindSock(SOCKET sock)<br />
{<br />
&nbsp;sockaddr_in sin;<br />
&nbsp;sin.sin_addr.S_un.S_addr = INADDR_ANY;<br />
&nbsp;sin.sin_family = AF_INET;<br />
&nbsp;sin.sin_port = 0;<br />
&nbsp;<br />
&nbsp;if (bind(sock, (struct sockaddr*)&amp;sin, sizeof(sin)) &lt; 0)<br />
&nbsp;&nbsp;throw Exception("bind error");<br />
}</div>
<div style="word-break: break-all; ">void ConnectToServer(SOCKET sock,char *username, char *serverip)<br />
{<br />
&nbsp;sockaddr_in remote;<br />
&nbsp;remote.sin_addr.S_un.S_addr = inet_addr(serverip);<br />
&nbsp;remote.sin_family = AF_INET;<br />
&nbsp;remote.sin_port = htons(SERVER_PORT);<br />
&nbsp;<br />
&nbsp;stMessage sendbuf;<br />
&nbsp;sendbuf.iMessageType = LOGIN;<br />
&nbsp;strncpy(sendbuf.message.loginmember.userName, username, 10);</div>
<div style="word-break: break-all; ">&nbsp;sendto(sock, (const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&amp;remote,sizeof(remote));</div>
<div style="word-break: break-all; ">&nbsp;int usercount;<br />
&nbsp;int fromlen = sizeof(remote);<br />
&nbsp;int iread = recvfrom(sock, (char *)&amp;usercount, sizeof(int), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />
&nbsp;if(iread&lt;=0)<br />
&nbsp;{<br />
&nbsp;&nbsp;throw Exception("Login error\n");<br />
&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;// 登录到服务端后，接收服务端发来的已经登录的用户的信息<br />
&nbsp;cout&lt;&lt;"Have "&lt;&lt;usercount&lt;&lt;" users logined server:"&lt;&lt;endl;<br />
&nbsp;for(int i = 0;i&lt;usercount;i++)<br />
&nbsp;{<br />
&nbsp;&nbsp;stUserListNode *node = new stUserListNode;<br />
&nbsp;&nbsp;recvfrom(sock, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />
&nbsp;&nbsp;ClientList.push_back(node);<br />
&nbsp;&nbsp;cout&lt;&lt;"Username:"&lt;&lt;node-&gt;userName&lt;&lt;endl;<br />
&nbsp;&nbsp;in_addr tmp;<br />
&nbsp;&nbsp;tmp.S_un.S_addr = htonl(node-&gt;ip);<br />
&nbsp;&nbsp;cout&lt;&lt;"UserIP:"&lt;&lt;inet_ntoa(tmp)&lt;&lt;endl;<br />
&nbsp;&nbsp;cout&lt;&lt;"UserPort:"&lt;&lt;node-&gt;port&lt;&lt;endl;<br />
&nbsp;&nbsp;cout&lt;&lt;""&lt;&lt;endl;<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; ">void OutputUsage()<br />
{<br />
&nbsp;cout&lt;&lt;"You can input you command:\n"<br />
&nbsp;&nbsp;&lt;&lt;"Command Type:\"send\",\"exit\",\"getu\"\n"<br />
&nbsp;&nbsp;&lt;&lt;"Example : send Username Message\n"<br />
&nbsp;&nbsp;&lt;&lt;"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit\n"<br />
&nbsp;&nbsp;&lt;&lt;"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getu\n"<br />
&nbsp;&nbsp;&lt;&lt;endl;<br />
}</div>
<div style="word-break: break-all; ">/* 这是主要的函数：发送一个消息给某个用户(C)<br />
&nbsp;*流程：直接向某个用户的外网IP发送消息，如果此前没有联系过<br />
&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么此消息将无法发送，发送端等待超时。<br />
&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 超时后，发送端将发送一个请求信息到服务端，<br />
&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要求服务端发送给客户C一个请求，请求C给本机发送打洞消息<br />
&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以上流程将重复MAXRETRY次<br />
&nbsp;*/<br />
bool SendMessageTo(char *UserName, char *Message)<br />
{<br />
&nbsp;char realmessage[256];<br />
&nbsp;unsigned int UserIP;<br />
&nbsp;unsigned short UserPort;<br />
&nbsp;bool FindUser = false;<br />
&nbsp;for(UserList::iterator UserIterator=ClientList.begin();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserIterator!=ClientList.end();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++UserIterator)<br />
&nbsp;{<br />
&nbsp;&nbsp;if( strcmp( ((*UserIterator)-&gt;userName), UserName) == 0 )<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;UserIP = (*UserIterator)-&gt;ip;<br />
&nbsp;&nbsp;&nbsp;UserPort = (*UserIterator)-&gt;port;<br />
&nbsp;&nbsp;&nbsp;FindUser = true;<br />
&nbsp;&nbsp;}<br />
&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;if(!FindUser)<br />
&nbsp;&nbsp;return false;</div>
<div style="word-break: break-all; ">&nbsp;strcpy(realmessage, Message);<br />
&nbsp;for(int i=0;i&lt;MAXRETRY;i++)<br />
&nbsp;{<br />
&nbsp;&nbsp;RecvedACK = false;</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sockaddr_in remote;<br />
&nbsp;&nbsp;remote.sin_addr.S_un.S_addr = htonl(UserIP);<br />
&nbsp;&nbsp;remote.sin_family = AF_INET;<br />
&nbsp;&nbsp;remote.sin_port = htons(UserPort);<br />
&nbsp;&nbsp;stP2PMessage MessageHead;<br />
&nbsp;&nbsp;MessageHead.iMessageType = P2PMESSAGE;<br />
&nbsp;&nbsp;MessageHead.iStringLen = (int)strlen(realmessage)+1;<br />
&nbsp;&nbsp;int isend = sendto(PrimaryUDP, (const char *)&amp;MessageHead, sizeof(MessageHead), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />
&nbsp;&nbsp;isend = sendto(PrimaryUDP, (const char *)&amp;realmessage, MessageHead.iStringLen, 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;// 等待接收线程将此标记修改<br />
&nbsp;&nbsp;for(int j=0;j&lt;10;j++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;if(RecvedACK)<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;Sleep(300);<br />
&nbsp;&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;// 没有接收到目标主机的回应，认为目标主机的端口映射没有<br />
&nbsp;&nbsp;// 打开，那么发送请求信息给服务器，要服务器告诉目标主机<br />
&nbsp;&nbsp;// 打开映射端口（UDP打洞）<br />
&nbsp;&nbsp;sockaddr_in server;<br />
&nbsp;&nbsp;server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />
&nbsp;&nbsp;server.sin_family = AF_INET;<br />
&nbsp;&nbsp;server.sin_port = htons(SERVER_PORT);<br />
&nbsp;<br />
&nbsp;&nbsp;stMessage transMessage;<br />
&nbsp;&nbsp;transMessage.iMessageType = P2PTRANS;<br />
&nbsp;&nbsp;strcpy(transMessage.message.translatemessage.userName, UserName);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;transMessage, sizeof(transMessage), 0, (const sockaddr*)&amp;server, sizeof(server));<br />
&nbsp;&nbsp;Sleep(100);// 等待对方先发送信息。<br />
&nbsp;}<br />
&nbsp;return false;<br />
}</div>
<div style="word-break: break-all; ">// 解析命令，暂时只有exit和send命令<br />
// 新增getu命令，获取当前服务器的所有用户<br />
void ParseCommand(char * CommandLine)<br />
{<br />
&nbsp;if(strlen(CommandLine)&lt;4)<br />
&nbsp;&nbsp;return;<br />
&nbsp;char Command[10];<br />
&nbsp;strncpy(Command, CommandLine, 4);<br />
&nbsp;Command[4]='\0';</div>
<div style="word-break: break-all; ">&nbsp;if(strcmp(Command,"exit")==0)<br />
&nbsp;{<br />
&nbsp;&nbsp;stMessage sendbuf;<br />
&nbsp;&nbsp;sendbuf.iMessageType = LOGOUT;<br />
&nbsp;&nbsp;strncpy(sendbuf.message.logoutmember.userName, UserName, 10);<br />
&nbsp;&nbsp;sockaddr_in server;<br />
&nbsp;&nbsp;server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />
&nbsp;&nbsp;server.sin_family = AF_INET;<br />
&nbsp;&nbsp;server.sin_port = htons(SERVER_PORT);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sendto(PrimaryUDP,(const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr *)&amp;server, sizeof(server));<br />
&nbsp;&nbsp;shutdown(PrimaryUDP, 2);<br />
&nbsp;&nbsp;closesocket(PrimaryUDP);<br />
&nbsp;&nbsp;exit(0);<br />
&nbsp;}<br />
&nbsp;else if(strcmp(Command,"send")==0)<br />
&nbsp;{<br />
&nbsp;&nbsp;char sendname[20];<br />
&nbsp;&nbsp;char message[COMMANDMAXC];<br />
&nbsp;&nbsp;int i;<br />
&nbsp;&nbsp;for(i=5;;i++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;if(CommandLine[i]!=' ')<br />
&nbsp;&nbsp;&nbsp;&nbsp;sendname[i-5]=CommandLine[i];<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;sendname[i-5]='\0';<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;strcpy(message, &amp;(CommandLine[i+1]));<br />
&nbsp;&nbsp;if(SendMessageTo(sendname, message))<br />
&nbsp;&nbsp;&nbsp;printf("Send OK!\n");<br />
&nbsp;&nbsp;else&nbsp;<br />
&nbsp;&nbsp;&nbsp;printf("Send Failure!\n");<br />
&nbsp;}<br />
&nbsp;else if(strcmp(Command,"getu")==0)<br />
&nbsp;{<br />
&nbsp;&nbsp;int command = GETALLUSER;<br />
&nbsp;&nbsp;sockaddr_in server;<br />
&nbsp;&nbsp;server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />
&nbsp;&nbsp;server.sin_family = AF_INET;<br />
&nbsp;&nbsp;server.sin_port = htons(SERVER_PORT);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;sendto(PrimaryUDP,(const char*)&amp;command, sizeof(command), 0, (const sockaddr *)&amp;server, sizeof(server));<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; ">// 接受消息线程<br />
DWORD WINAPI RecvThreadProc(LPVOID lpParameter)<br />
{<br />
&nbsp;sockaddr_in remote;<br />
&nbsp;int sinlen = sizeof(remote);<br />
&nbsp;stP2PMessage recvbuf;<br />
&nbsp;for(;;)<br />
&nbsp;{<br />
&nbsp;&nbsp;int iread = recvfrom(PrimaryUDP, (char *)&amp;recvbuf, sizeof(recvbuf), 0, (sockaddr *)&amp;remote, &amp;sinlen);<br />
&nbsp;&nbsp;if(iread&lt;=0)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;printf("recv error\n");<br />
&nbsp;&nbsp;&nbsp;continue;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;switch(recvbuf.iMessageType)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;case P2PMESSAGE:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 接收到P2P的消息<br />
&nbsp;&nbsp;&nbsp;&nbsp;char *comemessage= new char[recvbuf.iStringLen];<br />
&nbsp;&nbsp;&nbsp;&nbsp;int iread1 = recvfrom(PrimaryUDP, comemessage, 256, 0, (sockaddr *)&amp;remote, &amp;sinlen);<br />
&nbsp;&nbsp;&nbsp;&nbsp;comemessage[iread1-1] = '\0';<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(iread1&lt;=0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw Exception("Recv Message Error\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Recv a Message:%s\n",comemessage);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stP2PMessage sendbuf;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendbuf.iMessageType = P2PMESSAGEACK;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;delete []comemessage;<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;case P2PSOMEONEWANTTOCALLYOU:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 接收到打洞命令，向指定的IP地址打洞<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("Recv p2someonewanttocallyou data\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;sockaddr_in remote;<br />
&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_addr.S_un.S_addr = htonl(recvbuf.iStringLen);<br />
&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_family = AF_INET;<br />
&nbsp;&nbsp;&nbsp;&nbsp;remote.sin_port = htons(recvbuf.Port);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;// UDP hole punching<br />
&nbsp;&nbsp;&nbsp;&nbsp;stP2PMessage message;<br />
&nbsp;&nbsp;&nbsp;&nbsp;message.iMessageType = P2PTRASH;<br />
&nbsp;&nbsp;&nbsp;&nbsp;sendto(PrimaryUDP, (const char *)&amp;message, sizeof(message), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;case P2PMESSAGEACK:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 发送消息的应答<br />
&nbsp;&nbsp;&nbsp;&nbsp;RecvedACK = true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;case P2PTRASH:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 对方发送的打洞消息，忽略掉。<br />
&nbsp;&nbsp;&nbsp;&nbsp;//do nothing ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf("Recv p2ptrash data\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;case GETALLUSER:<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;int usercount;<br />
&nbsp;&nbsp;&nbsp;&nbsp;int fromlen = sizeof(remote);<br />
&nbsp;&nbsp;&nbsp;&nbsp;int iread = recvfrom(PrimaryUDP, (char *)&amp;usercount, sizeof(int), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(iread&lt;=0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw Exception("Login error\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;ClientList.clear();</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"Have "&lt;&lt;usercount&lt;&lt;" users logined server:"&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;for(int i = 0;i&lt;usercount;i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUserListNode *node = new stUserListNode;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recvfrom(PrimaryUDP, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClientList.push_back(node);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"Username:"&lt;&lt;node-&gt;userName&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in_addr tmp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmp.S_un.S_addr = htonl(node-&gt;ip);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"UserIP:"&lt;&lt;inet_ntoa(tmp)&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"UserPort:"&lt;&lt;node-&gt;port&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;""&lt;&lt;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
}</div>
<div style="word-break: break-all; "><br />
int main(int argc, char* argv[])<br />
{<br />
&nbsp;try<br />
&nbsp;{<br />
&nbsp;&nbsp;InitWinSock();<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;PrimaryUDP = mksock(SOCK_DGRAM);<br />
&nbsp;&nbsp;BindSock(PrimaryUDP);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;cout&lt;&lt;"Please input server ip:";<br />
&nbsp;&nbsp;cin&gt;&gt;ServerIP;</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;cout&lt;&lt;"Please input your name:";<br />
&nbsp;&nbsp;cin&gt;&gt;UserName;</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;ConnectToServer(PrimaryUDP, UserName, ServerIP);</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;HANDLE threadhandle = CreateThread(NULL, 0, RecvThreadProc, NULL, NULL, NULL);<br />
&nbsp;&nbsp;CloseHandle(threadhandle);<br />
&nbsp;&nbsp;OutputUsage();</div>
<div style="word-break: break-all; ">&nbsp;&nbsp;for(;;)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;char Command[COMMANDMAXC];<br />
&nbsp;&nbsp;&nbsp;gets(Command);<br />
&nbsp;&nbsp;&nbsp;ParseCommand(Command);<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
&nbsp;catch(Exception &amp;e)<br />
&nbsp;{<br />
&nbsp;&nbsp;printf(e.GetMessage());<br />
&nbsp;&nbsp;return 1;<br />
&nbsp;}<br />
&nbsp;return 0;<br />
}</div>
<div style="word-break: break-all; ">/* 异常类<br />
&nbsp;*<br />
&nbsp;* 文件名：Exception.h<br />
&nbsp;*<br />
&nbsp;* 日期：2004.5.5<br />
&nbsp;*<br />
&nbsp;* 作者：shootingstars(<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>)<br />
&nbsp;*/</div>
<div style="word-break: break-all; ">#ifndef __HZH_Exception__<br />
#define __HZH_Exception__</div>
<div style="word-break: break-all; ">#define EXCEPTION_MESSAGE_MAXLEN 256<br />
#include "string.h"</div>
<div style="word-break: break-all; ">class Exception<br />
{<br />
private:<br />
&nbsp;char m_ExceptionMessage[EXCEPTION_MESSAGE_MAXLEN];<br />
public:<br />
&nbsp;Exception(char *msg)<br />
&nbsp;{<br />
&nbsp;&nbsp;strncpy(m_ExceptionMessage, msg, EXCEPTION_MESSAGE_MAXLEN);<br />
&nbsp;}</div>
<div style="word-break: break-all; ">&nbsp;char *GetMessage()<br />
&nbsp;{<br />
&nbsp;&nbsp;return m_ExceptionMessage;<br />
&nbsp;}<br />
};</div>
<div style="word-break: break-all; ">#endif</div>
<div style="word-break: break-all; ">/* P2P 程序传输协议<br />
&nbsp;*&nbsp;<br />
&nbsp;* 日期：2004-5-21<br />
&nbsp;*<br />
&nbsp;* 作者：shootingstars(<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#104;&#117;&#105;&#115;&#50;&#50;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #336699; ">zhouhuis22@sina.com</a>)<br />
&nbsp;*<br />
&nbsp;*/</div>
<div style="word-break: break-all; ">#pragma once<br />
#include &lt;list&gt;</div>
<div style="word-break: break-all; ">// 定义iMessageType的值<br />
#define LOGIN 1<br />
#define LOGOUT 2<br />
#define P2PTRANS 3<br />
#define GETALLUSER&nbsp; 4</div>
<div style="word-break: break-all; ">// 服务器端口<br />
#define SERVER_PORT 2280</div>
<div style="word-break: break-all; ">// Client登录时向服务器发送的消息<br />
struct stLoginMessage<br />
{<br />
&nbsp;char userName[10];<br />
&nbsp;char password[10];<br />
};</div>
<div style="word-break: break-all; ">// Client注销时发送的消息<br />
struct stLogoutMessage<br />
{<br />
&nbsp;char userName[10];<br />
};</div>
<div style="word-break: break-all; ">// Client向服务器请求另外一个Client(userName)向自己方向发送UDP打洞消息<br />
struct stP2PTranslate<br />
{<br />
&nbsp;char userName[10];<br />
};</div>
<div style="word-break: break-all; ">// Client向服务器发送的消息格式<br />
struct stMessage<br />
{<br />
&nbsp;int iMessageType;<br />
&nbsp;union _message<br />
&nbsp;{<br />
&nbsp;&nbsp;stLoginMessage loginmember;<br />
&nbsp;&nbsp;stLogoutMessage logoutmember;<br />
&nbsp;&nbsp;stP2PTranslate translatemessage;<br />
&nbsp;}message;<br />
};</div>
<div style="word-break: break-all; ">// 客户节点信息<br />
struct stUserListNode<br />
{<br />
&nbsp;char userName[10];<br />
&nbsp;unsigned int ip;<br />
&nbsp;unsigned short port;<br />
};</div>
<div style="word-break: break-all; ">// Server向Client发送的消息<br />
struct stServerToClient<br />
{<br />
&nbsp;int iMessageType;<br />
&nbsp;union _message<br />
&nbsp;{<br />
&nbsp;&nbsp;stUserListNode user;<br />
&nbsp;}message;</div>
<div style="word-break: break-all; ">};</div>
<div style="word-break: break-all; ">//======================================<br />
// 下面的协议用于客户端之间的通信<br />
//======================================<br />
#define P2PMESSAGE 100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 发送消息<br />
#define P2PMESSAGEACK 101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 收到消息的应答<br />
#define P2PSOMEONEWANTTOCALLYOU 102&nbsp; // 服务器向客户端发送的消息<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 希望此客户端发送一个UDP打洞包<br />
#define P2PTRASH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 103&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 客户端发送的打洞包，接收端应该忽略此消息</div>
<div style="word-break: break-all; ">// 客户端之间发送消息格式<br />
struct stP2PMessage<br />
{<br />
&nbsp;int iMessageType;<br />
&nbsp;int iStringLen;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // or IP address<br />
&nbsp;unsigned short Port;&nbsp;<br />
};</div>
<div style="word-break: break-all; ">using namespace std;<br />
typedef list&lt;stUserListNode *&gt; UserList;</div>
</span><img src ="http://www.cppblog.com/iuranus/aggbug/146727.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2011-05-19 08:35 <a href="http://www.cppblog.com/iuranus/archive/2011/05/19/146727.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）UNIX IO---再谈文件描述符 </title><link>http://www.cppblog.com/iuranus/archive/2009/12/22/103681.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Tue, 22 Dec 2009 03:20:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/12/22/103681.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/103681.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/12/22/103681.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/103681.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/103681.html</trackback:ping><description><![CDATA[<div>在C程序中，文件由文件指针或者文件描述符表示。ISO C的标准I/0库函数（fopen, fclose, fread,
fwrite, fscanf, fprintf等）使用文件指针，UNIX的I/O函数（open, close, read, write,
ioctl）使用文件描述符。下面重点来说下，文件描述符是如何工作的。</div>
<div>&nbsp;</div>
<div>文件描述符相当于一个逻辑句柄，而open,close等函数则是将文件或者物理设备与句柄相关联。句柄是一个整数，可以理解为进程特定的文件
描述符表的索引。先介绍下面三个概念，后面讲下open、close等操作以后，文件和文件描述符产生什么关系，以及fork后文件描述符的继承等问题。</div>
<div>&nbsp;</div>
<div><strong> 文件描述符表</strong> ：用户区的一部分，除非通过使用文件描述符的函数，否则程序无法对其进行访问。对进程中每个打开的文件，文件描述符表都包含一个条目。</div>
<div>&nbsp;</div>
<div><strong> 系统文件表</strong> ：为系统中所有的进程共享。对每个活动的open, 它都包含一个条目。每个系统文件表的条目都包含文件偏移量、访问模式（读、写、or 读-写）以及指向它的文件描述符表的条目计数。</div>
<div>&nbsp;</div>
<div><strong> 内存索引节点表:</strong>  对系统中的每个活动的文件（被某个进程打开了），内存中索引节点表都包含一个条目。几个系统文件表条目可能对应于同一个内存索引节点表（不同进程打开同一个文件）。</div>
<div>&nbsp;</div>
<div>1、举例: 执行myfd = open( "/home/lucy/my.dat", O_RDONLY); 以后，上述3个表的关系原理图如下：</div>
<div><img onclick="'window.open("http://blog.51cto.com/viewpic.php?refimg="" + this.src)' alt="http://keren.blog.51cto.com/" src="http://img1.51cto.com/attachment/200906/200906291246256898312.bmp" border="0"></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图1</div>
<div>&nbsp;</div>
<div>系统文件表包含一个偏移量，给出了文件当前的位置。若2个进程同时打开一个文件（如上图A,B）做读操作，每个进程都有自己相对于文件的偏移
量，而且读入整个文件是独立于另一个进程的；如果2个进程打开同一个文件做写操作，写操作是相互独立的，每个进程都可以重写另一个进程写入的内容。</div>
<div>&nbsp;</div>
<div>如果上面进程在open以后又执行了close（）函数，操作系统会删除文件描述符表的第四个条目，和系统文件表的对应条目（若指向它的描述符
表唯一），并对内存索引节点表条目中的计数减1，如果自减以后变为0，说明没有其他进程链接此文件，将索引节点表条目也删除，而这里进程B也在open这
个文件，所以索引节点表条目保留。</div>
<div>&nbsp;</div>
<div>2、文件描述符的继承</div>
<div>通过fork（）创建子进程时，子进程继承父进程环境和上下文的大部分内容的拷贝，其中就包括文件描述符表。</div>
<div>&nbsp;</div>
<div>（1）对于父进程在fork（）之前打开的文件来说，子进程都会继承，与父进程共享相同的文件偏移量。如下图所示（0-1-2 表示 标准输入-输出-错误）：</div>
<div><img onclick="'window.open("http://blog.51cto.com/viewpic.php?refimg="" + this.src)' alt="" src="http://img1.51cto.com/attachment/200906/200906291246258303796.bmp" border="0" width="675" height="447"></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图2 fork()之前打开my.dat</div>
<div>&nbsp;</div>
<div>系统文件表位于系统空间中，不会被fork()复制，但是系统文件表中的条目会保存指向它的文件描述符表的计数，fork()时需要对这个计数
进行维护，以体现子进程对应的新的文件描述符表也指向它。程序关闭文件时，也是将系统文件表条目内部的计数减一，当计数值减为0时，才将其删除。</div>
<div>&nbsp;</div>
<div>（2）相反，如果父进程先进程fork，再打开my.dat，这时父子进程关于my.dat
的文件描述符表指向不同的系统文件表条目，也不再共享文件偏移量（fork以后2个进程分别open，在系统文件表中创建2个条目）；但是关于标准输入，
标准输出，标准错误，父子进程还是共享的。</div>
<div><img onclick="'window.open("http://blog.51cto.com/viewpic.php?refimg="" + this.src)' alt="" src="http://img1.51cto.com/attachment/200906/200906291246259052281.bmp" border="0"></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图3&nbsp;&nbsp; fork()以后打开my.dat</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<p>本文出自 &#8220;<a href="http://keren.blog.51cto.com/">淡泊明志，宁静致远</a>&#8221; 博客，请务必保留此出处<a href="http://keren.blog.51cto.com/720558/170822">http://keren.blog.51cto.com/720558/170822</a></p>
<br><img src ="http://www.cppblog.com/iuranus/aggbug/103681.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-12-22 11:20 <a href="http://www.cppblog.com/iuranus/archive/2009/12/22/103681.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>dup与赋值语句用于文件描述符的区别（聚合）</title><link>http://www.cppblog.com/iuranus/archive/2009/12/22/103672.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Tue, 22 Dec 2009 02:10:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/12/22/103672.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/103672.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/12/22/103672.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/103672.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/103672.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; dup/dup2的使用请参考其他资料，个人只是想了解dup后文件描述符，进程表项，文件表的关系。<br><br>&nbsp;&nbsp;&nbsp; 进程要对文件进行操作，一般使用open调用打开一个文件进行访问，每个进程都有一个文件描述符表，该表中存放打开的文件描述符。用户使用open等调用得到的文件描述符其实是文件描述符在该表中的索引号，该表项的内容是一个指向文件表的指针。应用程序只要使用该描述符就可以对指定文件进行操作。
<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 为了了解dup与赋值语句用于文件描述符的区别，请看如下程序。
<p>程序描述：</p>
<p>&nbsp;&nbsp;&nbsp; 打开一个文件描述符，分别适用dup和赋值语句进行复制，复制之后，打印原始和被复制的文件描述符id，看看是否具有相同的值，然后关闭文件，测试关闭是否成功。</p>
<p>程序示例：</p>
<p>#include &lt;stdio.h&gt;</p>
<p>#include &lt;stdlib.h&gt;</p>
<p>#include &lt;fcntl.h&gt;</p>
<p>#include &lt;unistd.h&gt;</p>
<p>&nbsp;</p>
<p>int sys_err(char *str)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; puts(str);</p>
<p>&nbsp;&nbsp;&nbsp; exit(0);</p>
<p>}</p>
<p>&nbsp;</p>
<p>int main(void)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int p,q;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; if((p=open("c_fid.c", O_RDONLY)) == -1)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sys_err("open error");</p>
<p>&nbsp;&nbsp;&nbsp; q = dup(p);</p>
<p>&nbsp;&nbsp;&nbsp; puts("dup:");</p>
<p>&nbsp;&nbsp;&nbsp; printf("file p,q fd is:%d %d\n", q, p);</p>
<p>&nbsp;&nbsp;&nbsp; printf("close file p ok?: %d\n", close(p));</p>
<p>&nbsp;&nbsp;&nbsp; printf("close file q ok?: %d\n", close(q));</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; if((p=open("c_fid.c", O_RDONLY)) == -1)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sys_err("open error");</p>
<p>&nbsp;&nbsp;&nbsp; q = p;</p>
<p>&nbsp;&nbsp;&nbsp; puts("=:");</p>
<p>&nbsp;&nbsp;&nbsp; printf("file p,q fd is:%d %d\n", q, p);</p>
<p>&nbsp;&nbsp;&nbsp; printf("close file p ok?: %d\n", close(p));</p>
<p>&nbsp;&nbsp;&nbsp; printf("close file q ok?: %d\n", close(q));</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; return 0;</p>
<p>}</p>
<p>&nbsp;</p>
<p>程序运行结果：</p>
<p>dup:</p>
<p>file p,q fd is:4 3&nbsp;&nbsp; //文件p,q使用不同的文件描述符</p>
<p>close file p ok?: 0</p>
<p>close file q ok?: 0 //文件关闭成功</p>
<p>=:</p>
<p>file p,q fd is:3 3 //简单复制</p>
<p>close file p ok?: 0</p>
<p>close file q ok?: -1//关闭失败，原因是此描述符已经被关闭了</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 由此证明，dup是产生一个新的文件描述符id和指针在进程表项中，但是他们共用文件表，这时，关闭一个文件描述符，另外一个仍旧可用，文件表并不会被释放。而赋值语句不同，它只是简单的在另外一个变量中记录原始文件指针等，2个变量的文件描述符相同，进程表项中并不产生新的项目。</p>
<p>关于socket的文件描述符<br></p>
<p>&nbsp;&nbsp;&nbsp; socket接口增加了网络通信操作的抽象定义，与文件操作一样，每个打开的socket都对应一个整数，我们称它为socket描述符，该整数也是socket描述符在文件描述符表中的索引值。但socket描述符在描述符表中的表项并不指向文件表，而是指向一个与该socket有关的数据结构。BSD UNIX中新增加了一个socket调用，应用程序可以调用它来新建一个socket描述符，注意进程用open只能产生文件描述符，而不能产生socket描述符。socket调用只能完成建立通信的部分工作，一旦建立了一个socket，应用程序可以使用其他特定的调用来为它添加其他详细信息，以完成建立通信的过程。
<br></p><img src ="http://www.cppblog.com/iuranus/aggbug/103672.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-12-22 10:10 <a href="http://www.cppblog.com/iuranus/archive/2009/12/22/103672.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux进程通信:管道要点</title><link>http://www.cppblog.com/iuranus/archive/2009/08/09/92717.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Sun, 09 Aug 2009 09:24:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/08/09/92717.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/92717.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/08/09/92717.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/92717.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/92717.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 管道的认识从command1 | command2 认识开始，到现在做A2DP升华，写一些使用FIFO的要点下来。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;管道一般用于进程间通信，把一个进程的输出通过管道送给另一个进程。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;可以通过popen，pclose尝试实现command1 | command2 。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File *popen(const char * command, const char *open_mode);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; open_mode: r or w<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File a =popen("uname -a", "r");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fread(buffer, 1, BUFSIZE, a);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%s", buffer);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&gt;&gt; Linux Ubuntu&nbsp;8.09..................<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp;&nbsp;&nbsp;pipe创建管道<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;unistd.h&gt;</p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int pipe(int file_description[2]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pipe的参数是由两个文件描述符组成的数组。file_description[0] 用于读管道， file_description[1] 用来写管道。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;命名管道：mkfifo<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/types.h&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/stat.h&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int mkfifo(const char&nbsp;* filename, mode_t mode);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mode: O_RDONLY, O_WRONLY, O_NONBLOCK.<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; O_RDONLY:阻塞读方式打开，除非有进程以写方式打开，不然阻塞。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; O_RDONLY|O_NONBLOCK:&nbsp; 不论怎样，立即返回，总是成功<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;O_WRONLY: 阻塞写方式打开，直到有人来读，不然阻塞<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; O_WRONLY|O_NONBLOCK: 立即返回，但如果没人以读方式打开，返回-1错误<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FIFO SIZE：#include &lt;limites.h&gt;, PIPE_BUF,&nbsp;default 4096<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;多个进程可以写同一个管道。 
<img src ="http://www.cppblog.com/iuranus/aggbug/92717.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-08-09 17:24 <a href="http://www.cppblog.com/iuranus/archive/2009/08/09/92717.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>firmware是什么</title><link>http://www.cppblog.com/iuranus/archive/2009/07/19/90536.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Sun, 19 Jul 2009 11:54:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/07/19/90536.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/90536.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/07/19/90536.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/90536.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/90536.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 先声明下，我是怀着被拍的思想写firmware的定义的，还请来往的各位大侠不吝"回复"&nbsp;&nbsp;<a href='http://www.cppblog.com/iuranus/archive/2009/07/19/90536.html'>阅读全文</a><img src ="http://www.cppblog.com/iuranus/aggbug/90536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-07-19 19:54 <a href="http://www.cppblog.com/iuranus/archive/2009/07/19/90536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>glibc: getopt()</title><link>http://www.cppblog.com/iuranus/archive/2009/07/17/90337.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Fri, 17 Jul 2009 06:29:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/07/17/90337.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/90337.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/07/17/90337.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/90337.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/90337.html</trackback:ping><description><![CDATA[这个东西比较有用，<br>以后可以用它来直接读了。<br><br>#include &lt;unistd.h&gt;<br>#include &lt;stdio.h&gt;<br>int main(int argc, char * argv[])<br>{<br>&nbsp;&nbsp;&nbsp; int aflag=0, bflag=0, cflag=0;<br>&nbsp;&nbsp;&nbsp; int ch;<br>&nbsp;&nbsp;&nbsp; while ((ch = getopt(argc, argv, "ab:c")) != -1)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("optind: %d\n", optind);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch (ch) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 'a':<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("HAVE option: -a\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aflag = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 'b':<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("HAVE option: -b\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bflag = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("The argument of -b is %s\n", optarg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 'c':<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("HAVE option: -c");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cflag = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case '?':<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Unknown option: %c\n",(char)optopt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>}<br><br>int getopt( int argc, char *const argv[], const char *optstring );<br><br>
<p>给定了命令参数的数量 (<code>argc</code>)、指向这些参数的数组 (<code>argv</code>) 和选项字符串 (<code>optstring</code>) 后，<code>getopt()</code> 将返回第一个选项，并设置一些全局变量。使用相同的参数再次调用该函数时，它将返回下一个选项，并设置相应的全局变量。如果不再有识别到的选项，将返回 <code>-1</code>，此任务就完成了。</p>
<p><code>getopt()</code> 所设置的全局变量包括：</p>
<ul>
    <li><code>optarg</code>——指向当前选项参数（如果有）的指针。</li>
    <li><code>optind</code>——再次调用 <code>getopt()</code> 时的下一个 argv 指针的索引。</li>
    <li><code>optopt</code>——最后一个已知选项。</li>
</ul>
<p>&nbsp;./getopt -a -d -b foo<br>optind: 2<br>HAVE option: -a<br>./getopt: invalid option -- d<br>optind: 3<br>Unknown option: d<br>optind: 5<br>HAVE option: -b<br>The argument of -b is foo</p>
<img src ="http://www.cppblog.com/iuranus/aggbug/90337.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-07-17 14:29 <a href="http://www.cppblog.com/iuranus/archive/2009/07/17/90337.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>FileSystem: Initialization</title><link>http://www.cppblog.com/iuranus/archive/2009/07/12/89898.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Sun, 12 Jul 2009 14:50:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/07/12/89898.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/89898.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/07/12/89898.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/89898.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/89898.html</trackback:ping><description><![CDATA[<p dir=ltr style="MARGIN-RIGHT: 0px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;After the kernel started up，following with file system.<br><font face="Courier New"><br>&nbsp;&nbsp;&nbsp;init</font> is the first user space process spawned by the kernel after completion of the boot process. And <font face="Courier New">init</font> can exist in a single runlevel(7 levels) at any given time.&nbsp; Init invokes&nbsp;some scripts under a directory called /etc/rc.d/init.d that includes every service script like nfs,syslog. Init also reads the system configuration file <tt>/etc/inittab. Refer to <span class=docEmphStrong><tt>man init</tt></span><font face="Times New Roman"> and </font><span class=docEmphStrong><tt>man inittab if want details.<br><br>&nbsp;&nbsp;&nbsp; The initrd is a temporary filesystem commonly used in the boot process of the Linux kernel. It is typically used for making preparations before the real root file can be mounted.<br></tt></span></tt></p>
<img src ="http://www.cppblog.com/iuranus/aggbug/89898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-07-12 22:50 <a href="http://www.cppblog.com/iuranus/archive/2009/07/12/89898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux Kernel Image: initialize</title><link>http://www.cppblog.com/iuranus/archive/2009/07/11/89806.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Sat, 11 Jul 2009 09:40:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/07/11/89806.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/89806.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/07/11/89806.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/89806.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/89806.html</trackback:ping><description><![CDATA[<p>1. 编译kernel&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编译linux时其实会又很多产物，因为android让我接触了linux 内核的一些资料。<br><br>贴最后一段make的Log<br>$ make ARCH=arm CROSS_COMPILE=/dev/.....<br>...&nbsp;&nbsp; <span class=docEmphasis>&lt; many build steps omitted for clarity&gt;</span><br>&nbsp; LD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vmlinux&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Kernel proper，EFL format，最原始的kernel<br>&nbsp; SYSMAP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.map&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp; OBJCOPY&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/Image&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;去掉 symbols, notes, and comments.<br>&nbsp; Kernel:&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/Image is ready&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="Courier New">objcopy</font> to generate a binary file, Image&nbsp;&nbsp;&nbsp;<br>&nbsp; AS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/compressed/head.o&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ARM-specific startup code generic to ARM processors,与arm相关的一些重要的启动时要用<br>&nbsp; GZIP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/compressed/piggy.gz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gzip打包<br>&nbsp; AS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/compressed/piggy.o&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;加载<font face="Courier New">piggy.S，</font><font face="Times New Roman">initializes the processor,required memory regions<br>&nbsp; CC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/compressed/misc.o&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Routines used for decompressing the kernel image&nbsp;<br>&nbsp; AS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/compressed/head-xscale.o&nbsp;&nbsp;&nbsp;<br>&nbsp; AS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/compressed/big-endian.o<br>&nbsp; LD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/compressed/vmlinux&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这容易搞乱，这个vmlinux和第一个Kernel proper是不一样的。<br>&nbsp; OBJCOPY&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/zImage&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Final composite kernel image,loaded by&nbsp;bootload.<br>&nbsp; Kernel:&nbsp;&nbsp;&nbsp;&nbsp; arch/arm/boot/zImage is ready<br>boot-strap Loader ：misc.o head-xscale.o big-endian.o</font></p>
2. Initialization<br>&nbsp;&nbsp;&nbsp; Power on-&gt; bootloader -&gt;head-xscale.o(boot-strap )-&gt; head.o(vmlinux)-&gt;main.o(kernel)<br><br>3. start_kernel();<br>&nbsp;&nbsp; 启动Init()process.init初始化之前注册的函数，最后释放资源。内核级别启动常报的错就是&#8220;No init found.&nbsp; Try passing init= option to kernel&#8221;<br>这主要是因为通过run_init_process执行系统级别的/init时失败，返回。如果成功，该函数不会返回。<br>
<pre>if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s.  Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found.  Try passing init= option to kernel.");</pre>
<br>PS:<br><font face="Courier New">start_kernel(): .../init/main.c</font>&nbsp;<br>Most of the Linux kernel initialization takes place in this routine<br>setup_arch(&amp;command_line):&nbsp; <tt>start_kernel()</tt> function is the call to <tt>setup_arch(), .../arch/arm/kernel/setup.c<br><br></tt>
<img src ="http://www.cppblog.com/iuranus/aggbug/89806.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-07-11 17:40 <a href="http://www.cppblog.com/iuranus/archive/2009/07/11/89806.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)SUSE 10 添加 telnet 和 samba 用户</title><link>http://www.cppblog.com/iuranus/archive/2009/06/05/86868.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Fri, 05 Jun 2009 12:02:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/06/05/86868.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/86868.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/06/05/86868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/86868.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/86868.html</trackback:ping><description><![CDATA[<p>su - &lt;-- 切换到admin账户</p>
<p>mkdir /newroot/nfsroot/xxx&nbsp;&nbsp; &lt;- 建立用户的home 路径<br>adduser xxx -d /newroot/nfsroot/xxx/ -g pnd&nbsp; &lt;- 添加用户到组<br>passwd wyn&nbsp;&nbsp; &lt;- 设置用户密码</p>
<p>chown xxx:pnd /newroot/nfsroot/xxx/&nbsp; &lt;- 更改home路径拥有者和组</p>
<p>echo xxx&nbsp;&nbsp;&nbsp;&nbsp; ALL=(ALL) NOPASSWD:NOPASSWD:ALL&nbsp;&nbsp; &gt;&gt; /etc/sudoers&nbsp; &lt;- 添加sudo权限</p>
<p>&nbsp;</p>
<p>smbpasswd -a xxx&nbsp; &lt;- 添加samba 用户</p>
<p>&nbsp;</p>
<p>ps: /etc/samba/smb.conf 的内容:<br>[global]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; workgroup = TUX-NET<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printing = cups<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printcap name = cups<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printcap cache time = 750<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cups options = raw<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map to guest = Bad User<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; include = /etc/samba/dhcp.conf<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logon path = <a href="file://%25L/profiles/.msprofile">\\%L\profiles\.msprofile</a><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logon home = <a href="file://%25L/%25U/.9xprofile">\\%L\%U\.9xprofile</a><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logon drive = P:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; usershare allow guests = Yes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add machine script = /usr/sbin/useradd&nbsp; -c Machine -d /var/lib/nobody -s /bin/false %m$<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; domain logons = Yes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; domain master = Yes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; local master = Yes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; os level = 65<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; preferred master = Yes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; security = user<br>[homes]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; comment = Home Directories<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; valid users = %S, %D%w%S<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; browseable = No<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read only = No<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inherit acls = Yes</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/stevenliyong/archive/2009/03/12/3984926.aspx">http://blog.csdn.net/stevenliyong/archive/2009/03/12/3984926.aspx</a></p>
<img src ="http://www.cppblog.com/iuranus/aggbug/86868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-06-05 20:02 <a href="http://www.cppblog.com/iuranus/archive/2009/06/05/86868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>修改Grub启动</title><link>http://www.cppblog.com/iuranus/archive/2009/03/29/78286.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Sun, 29 Mar 2009 11:31:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/03/29/78286.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/78286.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/03/29/78286.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/78286.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/78286.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;之前装了双系统，默认是linux启动，作开发用，后来GF霸占了，但是每次启动时都要手动调整到windows，今天我又换回来了，也就是通过修改/boot/grub/grub.conf(/boot/<font color=#cc0033>grub</font>/<font color=#cc0033>menu.list同一个文件</font>).<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;修改default, default值以0、1、2&#8230;表示默认启动随后的第1、2或第3个操作系统，以前默认是0，因为第0个title就是我的Linux，第1个是Windows，所以吧default改为1.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reboot，OK</p>
<img src ="http://www.cppblog.com/iuranus/aggbug/78286.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-03-29 19:31 <a href="http://www.cppblog.com/iuranus/archive/2009/03/29/78286.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)Linux时间函数</title><link>http://www.cppblog.com/iuranus/archive/2009/03/01/75232.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Sun, 01 Mar 2009 03:37:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2009/03/01/75232.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/75232.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2009/03/01/75232.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/75232.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/75232.html</trackback:ping><description><![CDATA[<p>
<table style="WIDTH: 659px; HEIGHT: 4222px">
    <tbody>
        <tr>
            <td vAlign=top><font face=宋体 size=2><br>asctime（将时间和日期以字符串格式表示） </font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            <p><font face=宋体 size=2>相关函数 </font></p>
            </div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time，ctime，gmtime，localtime<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>表头文件 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>定义函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>char * asctime(const struct tm * timeptr);<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>函数说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>asctime()将参数timeptr所指的tm结构中的信息转换成真实世界所使用的时间日期表示方法，然后将结果以字符串形态返回。此函数已经由时区转换成当地时间，字符串格式为:&#8220;Wed Jun 30 21:49:08 1993\n&#8221;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>返回值 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>若再调用相关的时间日期函数，此字符串可能会被破坏。此函数与ctime不同处在于传入的参数是不同的结构。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>附加说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>返回一字符串表示目前当地的时间日期。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>范例 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include &lt;time.h&gt;<br>main()<br>{<br>time_t timep;<br>time (&amp;timep);<br>printf(&#8220;%s&#8221;,asctime(gmtime(&amp;timep)));<br>}<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>执行 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>Sat Oct 28 02:10:06 2000<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>　
            <div align=right><br><font face=宋体 size=2><a name=linuxc33></a></font></div>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2><br></font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2><br>ctime（将时间和日期以字符串格式表示） </font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>相关函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time，asctime，gmtime，localtime<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>表头文件 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>定义函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>char *ctime(const time_t *timep);<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>函数说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>ctime()将参数timep所指的time_t结构中的信息转换成真实世界所使用的时间日期表示方法，然后将结果以字符串形态返回。此函数已经由时区转换成当地时间，字符串格式为&#8220;Wed Jun 30 21 :49 :08 1993\n&#8221;。若再调用相关的时间日期函数，此字符串可能会被破坏。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>返回值 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>返回一字符串表示目前当地的时间日期。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>范例 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br>main()<br>{<br>time_t timep;<br>time (&amp;timep);<br>printf(&#8220;%s&#8221;,ctime(&amp;timep));<br>}<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>执行 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>Sat Oct 28 10 : 12 : 05 2000<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>　
            <div align=right><br><font face=宋体 size=2><a name=linuxc34></a></font></div>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2><br></font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2><br>gettimeofday（取得目前的时间） </font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>相关函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time，ctime，ftime，settimeofday<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>表头文件 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include &lt;sys/time.h&gt;<br>#include &lt;unistd.h&gt;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>定义函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>int gettimeofday ( struct timeval * tv , struct timezone * tz )<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>函数说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>gettimeofday()会把目前的时间有tv所指的结构返回，当地时区的信息则放到tz所指的结构中。<br>timeval结构定义为:<br>struct timeval{<br>long tv_sec; /*秒*/<br>long tv_usec; /*微秒*/<br>};<br>timezone 结构定义为:<br>struct timezone{<br>int tz_minuteswest; /*和Greenwich 时间差了多少分钟*/<br>int tz_dsttime; /*日光节约时间的状态*/<br>};<br>上述两个结构都定义在/usr/include/sys/time.h。tz_dsttime 所代表的状态如下<br>DST_NONE /*不使用*/<br>DST_USA /*美国*/<br>DST_AUST /*澳洲*/<br>DST_WET /*西欧*/<br>DST_MET /*中欧*/<br>DST_EET /*东欧*/<br>DST_CAN /*加拿大*/<br>DST_GB /*大不列颠*/<br>DST_RUM /*罗马尼亚*/<br>DST_TUR /*土耳其*/<br>DST_AUSTALT /*澳洲（1986年以后）*/<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>返回值 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>成功则返回0，失败返回－1，错误代码存于errno。附加说明EFAULT指针tv和tz所指的内存空间超出存取权限。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>范例 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;sys/time.h&gt;<br>#include&lt;unistd.h&gt;<br>main(){<br>struct timeval tv;<br>struct timezone tz;<br>gettimeofday (&amp;tv , &amp;tz);<br>printf(&#8220;tv_sec; %d\n&#8221;, tv,.tv_sec) ;<br>printf(&#8220;tv_usec; %d\n&#8221;,tv.tv_usec);<br>printf(&#8220;tz_minuteswest; %d\n&#8221;, tz.tz_minuteswest);<br>printf(&#8220;tz_dsttime, %d\n&#8221;,tz.tz_dsttime);<br>}<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>执行 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>tv_sec: 974857339<br>tv_usec:136996<br>tz_minuteswest:-540<br>tz_dsttime:0<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>　
            <div align=right><br><font face=宋体 size=2><a name=linuxc35></a></font></div>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2><br></font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2><br>gmtime（取得目前时间和日期） </font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>相关函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time,asctime,ctime,localtime<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>表头文件 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>定义函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>struct tm*gmtime(const time_t*timep);<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>函数说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>gmtime()将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法，然后将结果由结构tm返回。<br>结构tm的定义为<br>struct tm<br>{<br>int tm_sec;<br>int tm_min;<br>int tm_hour;<br>int tm_mday;<br>int tm_mon;<br>int tm_year;<br>int tm_wday;<br>int tm_yday;<br>int tm_isdst;<br>};<br>int tm_sec 代表目前秒数，正常范围为0-59，但允许至61秒<br>int tm_min 代表目前分数，范围0-59<br>int tm_hour 从午夜算起的时数，范围为0-23<br>int tm_mday 目前月份的日数，范围01-31<br>int tm_mon 代表目前月份，从一月算起，范围从0-11<br>int tm_year 从1900 年算起至今的年数<br>int tm_wday 一星期的日数，从星期一算起，范围为0-6<br>int tm_yday 从今年1月1日算起至今的天数，范围为0-365<br>int tm_isdst 日光节约时间的旗标<br>此函数返回的时间日期未经时区转换，而是UTC时间。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>返回值 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>返回结构tm代表目前UTC 时间<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>范例 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include &lt;time.h&gt;<br>main(){<br>char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};<br>time_t timep;<br>struct tm *p;<br>time(&amp;timep);<br>p=gmtime(&amp;timep);<br>printf(&#8220;%d%d%d&#8221;,(1900+p-&gt;tm_year), (1+p-&gt;tm_mon),p-&gt;tm_mday);<br>printf(&#8220;%s%d;%d;%d\n&#8221;, wday[p-&gt;tm_wday], p-&gt;tm_hour, p-&gt;tm_min, p-&gt;tm_sec);<br>}<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>执行 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>2000/10/28 Sat 8:15:38<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>　
            <div align=right><br><font face=宋体 size=2><a name=linuxc36></a></font></div>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2><br></font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2><br>localtime（取得当地目前时间和日期） </font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>相关函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time, asctime, ctime, gmtime<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>表头文件 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>定义函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>struct tm *localtime(const time_t * timep);<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>函数说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>localtime()将参数timep所指的time_t结构中的信息转换成真实世界所使用的时间日期表示方法，然后将结果由结构tm返回。结构tm的定义请参考gmtime()。此函数返回的时间日期已经转换成当地时区。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>返回值 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>返回结构tm代表目前的当地时间。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>范例 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br>main(){<br>char *wday[]={&#8220;Sun&#8221;,&#8221;Mon&#8221;,&#8221;Tue&#8221;,&#8221;Wed&#8221;,&#8221;Thu&#8221;,&#8221;Fri&#8221;,&#8221;Sat&#8221;};<br>time_t timep;<br>struct tm *p;<br>time(&amp;timep);<br>p=localtime(&amp;timep); /*取得当地时间*/<br>printf (&#8220;%d%d%d &#8221;, (1900+p-&gt;tm_year),( l+p-&gt;tm_mon), p-&gt;tm_mday);<br>printf(&#8220;%s%d:%d:%d\n&#8221;, wday[p-&gt;tm_wday],p-&gt;tm_hour, p-&gt;tm_min, p-&gt;tm_sec);<br>}<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>执行 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>2000/10/28 Sat 11:12:22<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>　
            <div align=right><br><font face=宋体 size=2><a name=linuxc37></a></font></div>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2><br></font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2><br>mktime（将时间结构数据转换成经过的秒数） </font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>相关函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time，asctime，gmtime，localtime<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>表头文件 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>定义函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time_t mktime(strcut tm * timeptr);<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>函数说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>mktime()用来将参数timeptr所指的tm结构数据转换成从公元1970年1月1日0时0分0 秒算起至今的UTC时间所经过的秒数。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>返回值 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>返回经过的秒数。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>范例 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>/* 用time()取得时间（秒数），利用localtime()<br>转换成struct tm 再利用mktine（）将struct tm转换成原来的秒数*/<br>#include&lt;time.h&gt;<br>main()<br>{<br>time_t timep;<br>strcut tm *p;<br>time(&amp;timep);<br>printf(&#8220;time() : %d \n&#8221;,timep);<br>p=localtime(&amp;timep);<br>timep = mktime(p);<br>printf(&#8220;time()-&gt;localtime()-&gt;mktime():%d\n&#8221;,timep);<br>}<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>执行 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time():974943297<br>time()-&gt;localtime()-&gt;mktime():974943297<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>　
            <div align=right><br><font face=宋体 size=2><a name=linuxc38></a></font></div>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2><br></font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2><br>settimeofday（设置目前时间） </font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>相关函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time，ctime，ftime，gettimeofday<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>表头文件 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;sys/time.h&gt;<br>#include&lt;unistd.h&gt;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>定义函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>int settimeofday ( const struct timeval *tv,const struct timezone *tz);<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>函数说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>settimeofday()会把目前时间设成由tv所指的结构信息，当地时区信息则设成tz所指的结构。详细的说明请参考gettimeofday()。注意，只有root权限才能使用此函数修改时间。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>返回值 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>成功则返回0，失败返回－1，错误代码存于errno。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>错误代码 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>EPERM 并非由root权限调用settimeofday（），权限不够。<br>EINVAL 时区或某个数据是不正确的，无法正确设置时间。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>　
            <div align=right><br><font face=宋体 size=2><a name=linuxc39></a></font></div>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2><br></font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2><br>time（取得目前的时间） </font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>相关函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>ctime，ftime，gettimeofday<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>表头文件 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>定义函数 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>time_t time(time_t *t);<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>函数说明 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>此函数会返回从公元1970年1月1日的UTC时间从0时0分0秒算起到现在所经过的秒数。如果t 并非空指针的话，此函数也会将返回值存到t指针所指的内存。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>返回值 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>成功则返回秒数，失败则返回((time_t)-1)值，错误原因存于errno中。<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>范例 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>#include&lt;time.h&gt;<br>mian()<br>{<br>int seconds= time((time_t*)NULL);<br>printf(&#8220;%d\n&#8221;,seconds);<br>}<br></font></td>
        </tr>
        <tr>
            <td vAlign=top width=80>
            <div align=right><font face=宋体 size=2>执行 </font></div>
            </td>
            <td vAlign=top><font face=宋体 size=2>9.73E+08</font></td>
        </tr>
    </tbody>
</table>
<br>原文地址: <a href="http://hi.baidu.com/peruke/blog/item/753192a88ee685b7cb130cf5.html">http://hi.baidu.com/peruke/blog/item/753192a88ee685b7cb130cf5.html</a><br><br>在这里我再补充一个:<br>函数说明：ftime()将目前日期由tp所指的结构返回。tp结构定义：<br><br>struct&nbsp;&nbsp;timeb{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; time_t&nbsp;&nbsp;time;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 为1970-01-01至今的秒数*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned&nbsp;&nbsp;short&nbsp;&nbsp;millitm;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;千分之一秒 */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; short&nbsp;&nbsp;timezonel;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* 为目前时区和Greenwich相差的时间，单位为分钟 */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; short&nbsp;&nbsp;dstflag;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* 为日光节约时间的修正状态，如果为非0代表启用日光节约时间修正 */<br>};<br><br>返回值 ：无论成功或失败都返回0<br><br>范例：<br>#include &lt;sys/timeb.h&gt;<br>main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;&nbsp;timeb&nbsp;&nbsp;tp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftime(&amp;tp);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%d\n", tp.time);<br>}</p>
<img src ="http://www.cppblog.com/iuranus/aggbug/75232.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2009-03-01 11:37 <a href="http://www.cppblog.com/iuranus/archive/2009/03/01/75232.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>fopen打开文件方式</title><link>http://www.cppblog.com/iuranus/archive/2008/12/25/70315.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Thu, 25 Dec 2008 04:40:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2008/12/25/70315.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/70315.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2008/12/25/70315.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/70315.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/70315.html</trackback:ping><description><![CDATA[<font style="COLOR: #000000; FONT-FAMILY: 宋体" face=SimHei color=#009900 size=4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最近写一个文件操作类，fopen的参数着实让我搞了半天，因为以前就是固定的方式读写文件的，现在要做灵活了，所以就有些参数理解不够准确。以下是关于mode参数的定义。<br><br><span style="COLOR: #000000">'r' 只读方式打开，将文件指针指向文件头，如果文件不存在，则File返回空。<br>'r+' 读写方式打开，将文件指针指向文件头，如果文件不存在，则File返回空。 <br>'w' 写入方式打开，将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。 <br>'w+' 读写方式打开，将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。 <br>'a' 写入方式打开，将文件指针指向文件末尾。如果文件不存在则尝试创建之。 <br>'a+' 读写方式打开，将文件指针指向文件末尾。如果文件不存在则尝试创建之。 <br>'x' 创建并以写入方式打开，将文件指针指向文件头。如果文件已存在，则 fopen() 调用失败并返回 FALSE。 <br>'x' 创建并以写入方式打开，将文件指针指向文件头。如果文件已存在，则 fopen() 调用失败并返回 FALSE。<br>'b' 使用字符b作为文件类型的判断，是否是binary文件。<br><br>还有在读文件时最好先判断下该文件是否存在<br>bool ClassA::IsFileExisted(const char* filePath)<br>{<br>&nbsp;&nbsp;&nbsp;struct stat info;<br>&nbsp;&nbsp;&nbsp;if(stat(filePath, &amp;info) != 0)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;<br>}<br><br></span></font>
<img src ="http://www.cppblog.com/iuranus/aggbug/70315.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2008-12-25 12:40 <a href="http://www.cppblog.com/iuranus/archive/2008/12/25/70315.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DBus 介绍</title><link>http://www.cppblog.com/iuranus/archive/2008/12/20/69894.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Sat, 20 Dec 2008 04:23:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2008/12/20/69894.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/69894.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2008/12/20/69894.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/69894.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/69894.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dbus是freedesktop下开源的Linux IPC通信机制，本身Linux 的IPC通信机制包括，管道（fifo），共享内存，信号量，消息队列，Socket等。 像现在流行的moblin平台就使用了DBUS通信，还有我最近看的bluez 4 也是通过DBUS来交互的。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;它是个3层架构的进程间通信系统，包括：&nbsp;&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;&nbsp;&nbsp;函数库<em>libdbus</em>，用于两个应用程序呼叫联系和交互消息。 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.&nbsp;&nbsp;&nbsp;Message bus daemon，总线守护进程可同时与多个应用程序相连，并能把来自一个应用程序的消息路由到0或者多个其他程序。 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.&nbsp;&nbsp;&nbsp;一系列基于特定应用程序框架的Wrapper库。 比如libdbus-glib, libdbus-python.<br><br></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;参看图1-1,&nbsp; Bus Daemon Process就是运行在linux的daemon(dbus-daemon, 用户可以在/etc/init.d/dbus 操作，stop, start等等),&nbsp; dbus-daemon运行时会调用libdus的库。 在Application Process1里面就是应用层的东西了，应用程序调用特定的应用程序框架的Wrapper库与dbus-daemon进行通信。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我前段时间就是用Python写程序与dbus-daemon通信，所以就需要libdbus-python，后来又用c写程序，又装了libdus-glib。实质上在dbus主页上(<a href="http://www.freedesktop.org/wiki/Software/dbus">http://www.freedesktop.org/wiki/Software/dbus</a>)提供了很多Wrapper库, for QT4, JAVA, Perl, C++, Pascal, QT3, .NET, Ruby等等。这个Wrapper库呢其实就是对dbus下层调用做了封装，给上层暴露一个友好的接口。dbus的底层其实也是通过socket通信的<br><img style="WIDTH: 766px; HEIGHT: 542px" height=542 alt="" src="http://dbus.freedesktop.org/doc/diagram.png" width=766 border=0><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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图 1－1&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我再给一张bluez的例子让大家更理解dbus; 有四个应用想与bluz的damon通信，bluez注册到dbus中，其它的应用只需要向dbus要bluez的数据，<br>dbus负责再和bluez沟通了，但是bluez一定要把接口告诉其它应用。<br><br></p>
<div style="WIDTH: 393px; HEIGHT: 205px" align=center src_cetemp="http://wiki.bluez.org/attachment/wiki/Architecture/Desktops.png?format=raw"><img style="WIDTH: 393px; HEIGHT: 205px" height=205 alt="" src="http://wiki.bluez.org/attachment/wiki/Architecture/Desktops.png?format=raw" width=393 border=0></div>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;理解有限，先说到这。
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/iuranus/aggbug/69894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2008-12-20 12:23 <a href="http://www.cppblog.com/iuranus/archive/2008/12/20/69894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转载)linux select使用 </title><link>http://www.cppblog.com/iuranus/archive/2008/12/18/69718.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Thu, 18 Dec 2008 02:44:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2008/12/18/69718.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/69718.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2008/12/18/69718.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/69718.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/69718.html</trackback:ping><description><![CDATA[<div class=postBody>
<p>select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待，直到被监视的文件句柄有某一个或多个发生了状态改变。 文件在句柄在Linux里很多，如果你man某个函数，在函数返回值部分说到成功后有一个文件句柄被创建的都是的，如man socket可以看到&#8220;On success, a file descriptor for the new socket is returned.&#8221;而man 2 open可以看到&#8220;open() and creat() return the new file descriptor&#8221;，其实文件句柄就是一个整数，看socket函数的声明就明白了：</p>
<p><span style="FONT-SIZE: medium"><font size=3>int socket(int domain, int type, int protocol);</font></span></p>
<p>当然，我们最熟悉的句柄是0、1、2三个，0是标准输入，1是标准输出，2是标准错误输出。0、1、2是整数表示的，对应的FILE *结构的表示就是stdin、stdout、stderr，0就是stdin，1就是stdout，2就是stderr。比如下面这两段代码都是从标准输入读入9个字节字符：</p>
<p>
<table style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
    <tbody>
        <tr>
            <td>
            <p>#include &lt;stdio.h&gt;</p>
            <p>#include &lt;unistd.h&gt;</p>
            <p>#include &lt;string.h&gt;</p>
            <p>int main(int argc, char ** argv)</p>
            <p>{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>&nbsp;&nbsp;&nbsp; char buf[10] = ""; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>&nbsp;&nbsp;&nbsp; read(0, buf, 9); /* 从标准输入 0 读入字符 */ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stdout, "%s\n", buf); /* 向标准输出 stdout 写字符 */ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>&nbsp;</p>
            <p>&nbsp;&nbsp;&nbsp; return 0;</p>
            <p>}</p>
            <p>/* **上面和下面的代码都可以用来从标准输入读用户输入的9个字符** */</p>
            <p>#include &lt;stdio.h&gt;</p>
            <p>#include &lt;unistd.h&gt;</p>
            <p>#include &lt;string.h&gt;</p>
            <p>int main(int argc, char ** argv)</p>
            <p>{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>char buf[10] = ""; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>fread(buf, 9, 1, stdin); /* 从标准输入 stdin 读入字符 */ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>write(1, buf, strlen(buf)); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>&nbsp;</p>
            <p>return 0;</p>
            <p>}</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>继续上面说的select，就是用来监视某个或某些句柄的状态变化的。</p>
<p>select函数原型如下：</p>
<p><span style="FONT-SIZE: small"><font size=2>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);</font></span></p>
<p>函 数的最后一个参数timeout显然是一个超时时间值，其类型是struct timeval *，即一个struct timeval结构的变量的指针，所以我们在程序里要申明一个struct timeval tv;然后把变量tv的地址&amp;tv传递给select函数。struct timeval结构如下：</p>
<p>
<table style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
    <tbody>
        <tr>
            <td>
            <p>struct timeval</p>
            <p>{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>long&nbsp;&nbsp;&nbsp; tv_sec;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* seconds */ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>long&nbsp;&nbsp;&nbsp; tv_usec;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* microseconds */ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>};</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>第2、 3、4三个参数是一样的类型： fd_set *，即我们在程序里要申明几个fd_set类型的变量，比如rdfds, wtfds, exfds，然后把这个变量的地址&amp;rdfds, &amp;wtfds, &amp;exfds 传递给select函数。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这三个参数都是一个句柄的集合，第一个rdfds是用来保存这样的句柄的：当句柄的状态变成可读的时系统就会告诉select函数返回，同理第二个wtfds是指有句柄状态变成可写的时系统就会告诉select函数返回，同理第三个参数exfds是特殊情况，即句柄上有特殊情况发生时系统会告诉select函数返回。特殊情况比如对方通过一个socket句柄发来了紧急数据。如果我们程序里只想检测某个socket是否有数据可读，我们可以这样：</p>
<p>
<table style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
    <tbody>
        <tr>
            <td>
            <p>fd_set rdfds; /* 先申明一个 fd_set 集合来保存我们要检测的 socket句柄 */</p>
            <p>struct timeval tv; /* 申明一个时间变量来保存时间 */</p>
            <p>int ret; /* 保存返回值 */</p>
            <p>FD_ZERO(&amp;rdfds); /* 用select函数之前先把集合清零 */</p>
            <p>FD_SET(socket, &amp;rdfds); /* 把要检测的句柄socket加入到集合里 */</p>
            <p>&nbsp;</p>
            <p><span style="COLOR: #ff0000">tv.tv_sec = 1; </span></p>
            <p><span style="COLOR: #ff0000">tv.tv_usec = 500; /* 设置select等待的最大时间为1秒加500微秒 */ </span></p>
            <p>&nbsp;</p>
            <p>ret = select(socket + 1, &amp;rdfds, NULL, NULL, &amp;tv); /* 检测我们上面设置到集合rdfds里的句柄是否有可读信息 */</p>
            <p>if(ret &lt; 0)</p>
            <p>&nbsp;&nbsp;&nbsp; perror("select");/* 这说明select函数出错 */</p>
            <p>else if(ret == 0)</p>
            <p>&nbsp;&nbsp; printf("超时\n"); /* 说明在我们设定的时间值1秒加500毫秒的时间内，socket的状态没有发生变化 */</p>
            <p>else</p>
            <p>{ /* 说明等待时间还未到1秒加500毫秒，socket的状态发生了变化 */ &nbsp;&nbsp;&nbsp;</p>
            <p>&nbsp;&nbsp;&nbsp; printf("ret=%d\n", ret); /* ret这个返回值记录了发生状态变化的句柄的数目，由于我们只监视了socket这一个句柄，所以这里一定ret=1，如果同时有多个句柄发生变化返回的就是句柄的总和了 */</p>
            <p>&nbsp;&nbsp;&nbsp; /* 这里我们就应该从socket这个句柄里读取数据了，因为select函数已经告诉我们这个句柄里有数据可读 */ &nbsp;&nbsp;</p>
            <p>if(FD_ISSET(socket, &amp;rdfds)) { /* 先判断一下socket这外被监视的句柄是否真的变成可读的了 */ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>&nbsp;&nbsp; /* 读取socket句柄里的数据 */ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>&nbsp;&nbsp;&nbsp; recv(...); &nbsp;&nbsp;&nbsp;</p>
            <p>}</p>
            <p>}</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>注意select函数的第一个参数，是所有加入集合的句柄值的最大那个值还要加1。比如我们创建了3个句柄：</p>
<p>&nbsp;</p>
<p>
<table style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
    <tbody>
        <tr>
            <td>
            <p>int sa, sb, sc;</p>
            <p>sa = socket(...); /* 分别创建3个句柄并连接到服务器上 */</p>
            <p>connect(sa,...);</p>
            <p>sb = socket(...);</p>
            <p>connect(sb,...);</p>
            <p>sc = socket(...);</p>
            <p>connect(sc,...);</p>
            <p>FD_SET(sa, &amp;rdfds);/* 分别把3个句柄加入读监视集合里去 */</p>
            <p>FD_SET(sb, &amp;rdfds);</p>
            <p>FD_SET(sc, &amp;rdfds);</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>在使用select函数之前，一定要找到3个句柄中的最大值是哪个，我们一般定义一个变量来保存最大值，取得最大socket值如下：</p>
<p>
<table style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
    <tbody>
        <tr>
            <td>
            <p>int maxfd = 0;</p>
            <p>if(sa &gt; maxfd)</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp; maxfd = sa;</p>
            <p>if(sb &gt; maxfd)</p>
            <p>&nbsp;&nbsp;&nbsp; maxfd = sb;</p>
            <p>if(sc &gt; maxfd)</p>
            <p>&nbsp;&nbsp;&nbsp; maxfd = sc;</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>然后调用select函数：</p>
<p>
<table style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
    <tbody>
        <tr>
            <td>ret = select(maxfd + 1, &amp;rdfds, NULL, NULL, &amp;tv); /* 注意是最大值还要加1 */</td>
        </tr>
    </tbody>
</table>
</p>
<p>同样的道理，如果我们要检测用户是否按了键盘进行输入，我们就应该把标准输入0这个句柄放到select里来检测，如下：</p>
<p>FD_ZERO(&amp;rdfds);</p>
<p>FD_SET(0, &amp;rdfds);</p>
<p>tv.tv_sec = 1;</p>
<p>tv.tv_usec = 0;</p>
<p>ret = select(1, &amp;rdfds, NULL, NULL, &amp;tv); /* 注意是最大值还要加1 */</p>
<p>if(ret &lt; 0)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; perror("select");/* 出错 */</p>
<p>else if(ret == 0)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; printf("超时\n"); /* 在我们设定的时间tv内，用户没有按键盘 */</p>
<p>else { /* 用户有按键盘，要读取用户的输入 */ &nbsp;&nbsp;</p>
<p>scanf("%s", buf); }</p>
</div>
<img src ="http://www.cppblog.com/iuranus/aggbug/69718.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2008-12-18 10:44 <a href="http://www.cppblog.com/iuranus/archive/2008/12/18/69718.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转载)Linux操作系统的Configure参数解释说明</title><link>http://www.cppblog.com/iuranus/archive/2008/12/17/69689.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Wed, 17 Dec 2008 14:30:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2008/12/17/69689.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/69689.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2008/12/17/69689.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/69689.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/69689.html</trackback:ping><description><![CDATA[<div class=content id=textbody>
<p>Linux环境下的软件安装，并不是一件容易的事情；如果通过源代码编译后在安装，当然事情就更为复杂一些；现在安装各种软件的教程都非常普遍；但万变不离其中，对基础知识的扎实掌握，安装各种软件的问题就迎刃而解了。Configure脚本配置工具就是基础之一，它是autoconf的工具的基本应用。 </p>
<p><br>与一些技巧相比，Configure显得基础一些，当然使用和学习起来就显得枯燥乏味一些，当然要成为高手，对基础的熟悉不能超越哦。 </p>
<p><br>为此我转载了一篇关于Configure选项配置的详细介绍。供大家参考 </p>
<p><br>'configure'脚本有大量的命令行选项。对不同的软件包来说，这些选项可能会有变化，但是许多基本的选项是不会改变的。带上'--help'选项执行'configure'脚本可以看到可用的所有选项。尽管许多选项是很少用到的，但是当你为了特殊的需求而configure一个包时，知道他们的存在是很有益处的。下面对每一个选项进行简略的介绍： </p>
<p>--cache-file=FILE </p>
<p>'configure'会在你的系统上测试存在的特性(或者bug!)。为了加速随后进行的配置，测试的结果会存储在一个cache file里。当configure一个每个子树里都有'configure'脚本的复杂的源码树时，一个很好的cache file的存在会有很大帮助。</p>
<p>--help </p>
<p>输出帮助信息。即使是有经验的用户也偶尔需要使用使用'--help'选项，因为一个复杂的项目会包含附加的选项。例如，GCC包里的'configure'脚本就包含了允许你控制是否生成和在GCC中使用GNU汇编器的选项。 </p>
<p><br>--no-create </p>
<p><br>'configure'中的一个主要函数会制作输出文件。此选项阻止'configure'生成这个文件。你可以认为这是一种演习(dry run)，尽管缓存(cache)仍然被改写了。 </p>
<p><br>--quiet </p>
<p>--silent </p>
<p><br>当'configure'进行他的测试时，会输出简要的信息来告诉用户正在作什么。这样作是因为'configure'可能会比较慢，没有这种输出的话用户将会被扔在一旁疑惑正在发生什么，使用这两个选项中的任何一个都会把你扔到一旁。(译注：这两句话比较有意思，原文是这样的：If there was no such output, the user would be left wondering what is happening. By using this option, you too can be left wondering!) </p>
<p><br>--version </p>
<p><br>打印用来产生'configure'脚本的Autoconf的版本号。</p>
<p><br>--prefix=PEWFIX </p>
<p><br>'--prefix'是最常用的选项。制作出的'Makefile'会查看随此选项传递的参数，当一个包在安装时可以彻底的重新安置他的结构独立部分。举一个例子，当安装一个包，例如说Emacs，下面的命令将会使Emacs Lisp file被安装到"/opt/gnu/share"：</p>
<p>$ ./configure --prefix=/opt/gnu </p>
<p><br>--exec-prefix=EPREFIX </p>
<p><br>与'--prefix'选项类似，但是他是用来设置结构倚赖的文件的安装位置，编译好的'emacs'二进制文件就是这样一个问件。如果没有设置这个选项的话，默认使用的选项值将被设为和'--prefix'选项值一样。 </p>
<p><br>--bindir=DIR </p>
<p><br>指定二进制文件的安装位置，这里的二进制文件定义为可以被用户直接执行的程序。</p>
<p><br>--sbindir=DIR </p>
<p><br>指定超级二进制文件的安装位置。这是一些通常只能由超级用户执行的程序。 </p>
<p><br>--libexecdir=DIR </p>
<p><br>指定可执行支持文件的安装位置。与二进制文件相反，这些文件从来不直接由用户执行，但是可以被上面提到的二进制文件所执行。</p>
<p><br>--datadir=DIR </p>
<p><br>指定通用数据文件的安装位置。 </p>
<p><br>--sysconfdir=DIR </p>
<p><br>指定在单个机器上使用的只读数据的安装位置。 </p>
<p><br>--sharedstatedir=DIR </p>
<p>指定可以在多个机器上共享的可写数据的安装位置。</p>
<p><br>--localstatedir=DIR </p>
<p>指定只能单机使用的可写数据的安装位置。</p>
<p>--libdir=DIR </p>
<p>指定库文件的安装位置。</p>
<p><br>--includedir=DIR </p>
<p>指定C头文件的安装位置。其他语言如C++的头文件也可以使用此选项。</p>
<p><br>--oldincludedir=DIR </p>
<p>指定为除GCC外编译器安装的C头文件的安装位置。 </p>
<p><br>--infodir=DIR </p>
<p>指定Info格式文档的安装位置.Info是被GNU工程所使用的文档格式。</p>
<p><br>--mandir=DIR </p>
<p>指定手册页的安装位置。</p>
<p><br>--srcdir=DIR </p>
<p>这个选项对安装没有作用，他会告诉'configure'源码的位置。一般来说不用指定此选项，因为'configure'脚本一般和源码文件在同一个目录下。</p>
<p><br>--program-prefix=PREFIX </p>
<p>指定将被加到所安装程序的名字上的前缀。例如，使用'--program-prefix=g'来configure一个名为'tar'的程序将会使安装的程序被命名为'gtar'。当和其他的安装选项一起使用时，这个选项只有当他被`Makefile.in'文件使用时才会工作。</p>
<p><br>--program-suffix=SUFFIX </p>
<p>指定将被加到所安装程序的名字上的后缀。</p>
<p><br>--program-transform-name=PROGRAM </p>
<p>这里的PROGRAM是一个sed脚本。当一个程序被安装时，他的名字将经过`sed -e PROGRAM'来产生安装的名字。</p>
<p><br>--build=BUILD </p>
<p>指定软件包安装的系统平台。如果没有指定，默认值将是'--host'选项的值。 </p>
<p><br>--host=HOST </p>
<p>指定软件运行的系统平台。如果没有指定。将会运行`config.guess'来检测。</p>
<p><br>--target=GARGET </p>
<p>指定软件面向(target to)的系统平台。这主要在程序语言工具如编译器和汇编器上下文中起作用。如果没有指定，默认将使用'--host'选项的值。 </p>
<p><br>--disable-FEATURE </p>
<p>一些软件包可以选择这个选项来提供为大型选项的编译时配置，例如使用Kerberos认证系统或者一个实验性的编译器最优配置。如果默认是提供这些特性，可以使用'--disable-FEATURE'来禁用它，这里'FEATURE'是特性的名字，例如：</p>
<p>$ ./configure --disable-gui </p>
<p><br>-enable-FEATURE[=ARG] </p>
<p>相反的，一些软件包可能提供了一些默认被禁止的特性,可以使用'--enable-FEATURE'来起用它。这里'FEATURE'是特性的名字。一个特性可能会接受一个可选的参数。例如：</p>
<p>$ ./configure --enable-buffers=128 </p>
<p>`--enable-FEATURE=no'与上面提到的'--disable-FEATURE'是同义的。</p>
<p><br>--with-PACKAGE[=ARG] </p>
<p>在自由软件社区里，有使用已有软件包和库的优秀传统。当用'configure'来配置一个源码树时，可以提供其他已经安装的软件包的信息。例如，倚赖于Tcl和Tk的BLT器件工具包。要配置BLT，可能需要给'configure'提供一些关于我们把Tcl和Tk装的何处的信息：</p>
<p>$ ./configure --with-tcl=/usr/local --with-tk=/usr/local </p>
<p>'--with-PACKAGE=no'与下面将提到的'--without-PACKAGE'是同义的。 </p>
<p><br>--without-PACKAGE </p>
<p>有时候你可能不想让你的软件包与系统已有的软件包交互。例如，你可能不想让你的新编译器使用GNU ld。通过使用这个选项可以做到这一点：</p>
<p>$ ./configure --without-gnu-ld </p>
<p><br>--x-includes=DIR </p>
<p>这个选项是'--with-PACKAGE'选项的一个特例。在Autoconf最初被开发出来时，流行使用'configure'来作为Imake的一个变通方法来制作运行于X的软件。'--x-includes'选项提供了向'configure'脚本指明包含X11头文件的目录的方法。</p>
<p><br>--x-libraries=DIR </p>
<p>类似的，'--x-libraries'选项提供了向'configure'脚本指明包含X11库的目录的方法。</p>
<p><br>在源码树中运行'configure'是不必要的同时也是不好的。一个由'configure'产生的良好的'Makefile'可以构筑源码属于另一棵树的软件包。在一个独立于源码的树中构筑派生的文件的好处是很明显的：派生的文件，如目标文件，会凌乱的散布于源码树。这也使在另一个不同的系统或用不同的配置选项构筑同样的目标文件非常困难。建议使用三棵树：一棵源码树(source tree)，一棵构筑树(build tree)，一棵安装树(install tree)。这里有一个很接近的例子，是使用这种方法来构筑GNU malloc包： </p>
<p>$ gtar zxf mmalloc-1.0.tar.gz </p>
<p>$ mkdir build &amp;&amp; cd build </p>
<p>$ ../mmalloc-1.0/configure </p>
<p>creating cache ./config.cache </p>
<p>checking for gcc... gcc </p>
<p>checking whether the C compiler (gcc ) works... yes </p>
<p>checking whether the C compiler (gcc ) is a cross-compiler... no </p>
<p>checking whether we are using GNU C... yes </p>
<p>checking whether gcc accepts -g... yes </p>
<p>checking for a BSD compatible install... /usr/bin/install -c </p>
<p>checking host system type... i586-pc-linux-gnu </p>
<p>checking build system type... i586-pc-linux-gnu </p>
<p>checking for ar... ar </p>
<p>checking for ranlib... ranlib </p>
<p>checking how to run the C preprocessor... gcc -E </p>
<p>checking for unistd.h... yes </p>
<p>checking for getpagesize... yes </p>
<p>checking for working mmap... yes </p>
<p>checking for limits.h... yes </p>
<p>checking for stddef.h... yes </p>
<p>updating cache ../config.cache </p>
<p>creating ./config.status </p>
<p>这样这棵构筑树就被配置了，下面可以继续构筑和安装这个包到默认的位置'/usr/local'： </p>
<p>$ make all &amp;&amp; make install</p>
</div>
<img src ="http://www.cppblog.com/iuranus/aggbug/69689.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2008-12-17 22:30 <a href="http://www.cppblog.com/iuranus/archive/2008/12/17/69689.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转载)GUN gcc 中文手册</title><link>http://www.cppblog.com/iuranus/archive/2008/12/17/69687.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Wed, 17 Dec 2008 14:09:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2008/12/17/69687.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/69687.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2008/12/17/69687.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/69687.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/69687.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: NAMEgcc,g++-GNU工程的C和C++编译器(egcs-1.1.2) 总览(SYNOPSIS)gcc[option|filename ]... g++[option|filename ]... 警告(WARNING)本手册页内容摘自GNU C编译器的完整文档,仅限于解释选项的含义. 除非有人自愿维护,否则本手册页不再更新.如果发现手册页和软件之间有所矛盾,请查对In...&nbsp;&nbsp;<a href='http://www.cppblog.com/iuranus/archive/2008/12/17/69687.html'>阅读全文</a><img src ="http://www.cppblog.com/iuranus/aggbug/69687.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2008-12-17 22:09 <a href="http://www.cppblog.com/iuranus/archive/2008/12/17/69687.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简述蓝牙协议栈-完整版</title><link>http://www.cppblog.com/iuranus/archive/2008/12/14/69391.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Sun, 14 Dec 2008 03:50:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2008/12/14/69391.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/69391.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2008/12/14/69391.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/69391.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/69391.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;项目刚好做到蓝牙了，也不是很忙，讲讲自己最近一段时间做的东西。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;提到协议栈，都会想到与<a title=开放式系统互联 href="http://wiki.ccw.com.cn/%E5%BC%80%E6%94%BE%E5%BC%8F%E7%B3%BB%E7%BB%9F%E4%BA%92%E8%81%94"><u><font color=#800080>开放式系统互联</font></u></a>(<a title=OSI href="http://wiki.ccw.com.cn/OSI"><u><font color=#0000ff>OSI</font></u></a>)协议栈的&nbsp;，OSI协议栈定义了厂商们如何才能生产可以与其它厂商的产品一起工作的产品。协议栈是指一组协议的集合，举个例子，把大象装到冰箱里，总共要3步。每步就是一个协议，3步组成一个协议栈。把应用层数据包发出去，也要好几步，TCP/UDP头，IP头，ether头，每步也是一个协议。另外每层都有一些特殊的协议。所有这些统称协议栈。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;简单的来说，蓝牙协议栈就是SIG(Special Intersted Group)定义的一组协议的规范，目标是允许遵循规范的蓝牙应用应用能够进行相互间操作，图1-1就是完整的蓝牙协议栈和部分profile：<br><img height=439 alt="" src="http://www.cppblog.com/images/cppblog_com/iuranus/Bluetooth_Stack.jpg" width=477 border=0></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;图1－1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;接着介绍下蓝牙里面profile的定义，profile既是配置文件，配置文件定义了可能的应用，<em>蓝牙</em>配置文件表达了一般行为，<em>蓝牙</em>设备可以通过这些行为与其它设备进行通信。<em>蓝牙</em>技术定义了广泛的配置文件，描述了许多不同类型的使用案例。按照<em>蓝牙</em>规格中提供的指导，开发商可以创建应用程序以与其它符合<em>蓝牙</em>规格的设备协同工作。&nbsp;到目前为止，蓝牙一共有22个profile，在这里我就不详细介绍图1-1的协议和每个Profile了，在<a href="http://www.bluetooth.com/">www.bluetooth.com</a>上有详细的文档说明。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这里我想详细介绍下已经实现了r的协议栈。</p>
<ol>
    <li><span class=mw-headline>Widcomm:&nbsp; 第一个windows上的协议栈，由Widcomm公司开发，也就是现在的<a class=mw-redirect title="Broadcom Corporation" href="http://en.wikipedia.org/wiki/Broadcom_Corporation"><font color=#002bb8>Broadcom </font></a>.</span>
    <li><span class=mw-headline><span class=mw-headline>Microsoft Windows stack: Windows XP SP2中包括了这个内建的协议栈，开发者也可以调用其API开发第三方软件。</span></span>
    <li><span class=mw-headline><span class=mw-headline></span></span><span class=mw-headline>Toshiba stack: 它也是基于Windows的，不支持第三方开发，但它把协议栈授权给一些laptop商(sony, asus等，我的本本上就是Toshiba的）。它支持的Profile有： <a title=SPP href="http://en.wikipedia.org/wiki/SPP"><font color=#002bb8>SPP</font></a>, <a class=mw-redirect title=DUN href="http://en.wikipedia.org/wiki/DUN"><font color=#002bb8>DUN</font></a>, <a class=mw-redirect title=FAX href="http://en.wikipedia.org/wiki/FAX"><font color=#002bb8>FAX</font></a>, <a class=mw-redirect title=LAP href="http://en.wikipedia.org/wiki/LAP"><font color=#002bb8>LAP</font></a>, <a title=OPP href="http://en.wikipedia.org/wiki/OPP"><font color=#002bb8>OPP</font></a>, <a class=mw-redirect title=FTP href="http://en.wikipedia.org/wiki/FTP"><font color=#002bb8>FTP</font></a>, <a title=HID href="http://en.wikipedia.org/wiki/HID"><font color=#002bb8>HID</font></a>, <a class=new title="HCRP (page does not exist)" href="http://en.wikipedia.org/w/index.php?title=HCRP&amp;action=edit&amp;redlink=1"><font color=#ba0000>HCRP</font></a>, <a class=mw-redirect title=PAN href="http://en.wikipedia.org/wiki/PAN"><font color=#002bb8>PAN</font></a>, <a title=BIP href="http://en.wikipedia.org/wiki/BIP"><font color=#002bb8>BIP</font></a>, <a title=HSP href="http://en.wikipedia.org/wiki/HSP"><font color=#002bb8>HSP</font></a>, <a title=HFP href="http://en.wikipedia.org/wiki/HFP"><font color=#002bb8>HFP</font></a> , <a class=mw-redirect title=A2DP href="http://en.wikipedia.org/wiki/A2DP"><font color=#002bb8>A2DP</font></a>, <a class=mw-redirect title=AVRCP href="http://en.wikipedia.org/wiki/AVRCP"><font color=#002bb8>AVRCP</font></a>, <font color=#002bb8>GAVDP</font>）</span>
    <li>BlueSoleil: 著名的IVT公司的产品，这个应该是个中国公司，值得自豪。该产品可以用于桌面和嵌入式，他也支持第三方开发，DUN, FAX, HFP, HSP, LAP, OBEX, OPP, PAN SPP, AV, BIP, FTP, GAP, HID, SDAP, and SYNC。
    <li>Bluez: Linux官方协议栈，该协议栈的上层用Socket封装，便于开发者使用，通过DBUS与其它应用程序通信。那么最近我的工作就是移植bluez 4.x到板子上。
    <li>&nbsp;Affix: NOKIA公司的协议栈，在Symbian系统上运行，具体的没找到资料
    <li>BlueDragon：东软公司产品，值得骄傲，好像2002年6月就通过了蓝牙的认证，支持的Profile：SDP、Serial-DevB、AVCTP、AVRCP-Controller、AVRCP-Target、Headset-AG、Headset-HS、OPP-Client、OPP-Server、CT-GW、CT-Term、Intercom、FT-Server、FT-Client、GAP、SDAP、Serial-DevA、AVDTP、GAVDP、A2DP-Source、A2DP-Sink，但到现在我没怎么听过这个协议栈的应用，难得是个烂尾楼？？
    <li><span class=mw-headline>BlueMagic：美国Open Interface 公司for portable embedded divce的协议栈，iphone(apple)，nav-u(sony)等很多电子产品都用该商业的协议栈，<span class=mw-headline>BlueMagic 3.0是第一个通过bluetooth 协议栈1.1认证的协议栈，那么我现在就在用它，那么该栈用起来简单，API清晰明了。实现了的profile有:HCI,L2CAP,RFCOMM,A/V,Remote,Control,A/V,Streaming,BIP,BPP,DUN,FAX,FTP,GAP,Hands-Free,and,Headset,HCRP,HID,OBEX,OPP,PAN,BNEP,PBAP,SAP,SPP,Synchronization,SyncML,Telephony,XML.</span></span>
    <li><span class=mw-headline><span class=mw-headline>BCHS-Bluecore Host Software: 蓝牙芯片CSR的协议栈，同时他也提供了一些上层应用的Profile的库，当然了它也是为嵌入式产品了，支持的Profile有：A2DP,AVRCP,PBAP,BIP,BPP,CTP,DUN,FAX,FM API,FTP GAP,GAVDP,GOEP,HCRP,Headset,HF1.5,HID,ICP,JSR82,LAP Message Access Profile,OPP,PAN,SAP,SDAP,SPP,SYNC,SYNC ML。</span></span>
    <li><span class=mw-headline><span class=mw-headline><span class=mw-headline>Windows CE：微软给Windows CE开发的协议栈，但是windows ce本身也支持其它的协议栈</span></span></span>
    <li><span class=mw-headline><span class=mw-headline><span class=mw-headline><span class=mw-headline>BlueLet：IVT公司for embedded product的清量级协议栈。</span></span></span></span> </li>
</ol>
<span class=mw-headline><span class=mw-headline><span class=mw-headline><span class=mw-headline></span></span></span></span><span class=mw-headline><span class=mw-headline>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们是基于BlueMagic3的，最近呢也在研究bluez 4的移植和profile工作，后面我会再针对bluez做详细介绍。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;时间有限，简单的写了下，如果各位网友知道一些协议栈的动态，或对我写的有补充，请给我留言，我会及时改正，<br></p>
<ol></span></span></ol>
<img src ="http://www.cppblog.com/iuranus/aggbug/69391.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2008-12-14 11:50 <a href="http://www.cppblog.com/iuranus/archive/2008/12/14/69391.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转载)怎么源码安装 PKG_CONFIG_PATH设置</title><link>http://www.cppblog.com/iuranus/archive/2008/12/10/69035.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Wed, 10 Dec 2008 03:15:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2008/12/10/69035.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/69035.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2008/12/10/69035.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/69035.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/69035.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在./configure时，隐藏着一些秘密&nbsp;&nbsp;<a href='http://www.cppblog.com/iuranus/archive/2008/12/10/69035.html'>阅读全文</a><img src ="http://www.cppblog.com/iuranus/aggbug/69035.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2008-12-10 11:15 <a href="http://www.cppblog.com/iuranus/archive/2008/12/10/69035.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多国语言惹得祸</title><link>http://www.cppblog.com/iuranus/archive/2007/11/09/36242.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Fri, 09 Nov 2007 10:53:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2007/11/09/36242.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/36242.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2007/11/09/36242.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/36242.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/36242.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面下指令说要做多国语言版的程序，这可是个不小的改动呀。于是我就拿当前的程序运行，一系列的问题随之出现了。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 话分两头，先说跑在Windows上的，最基本的就是读各种语言的文件名，正常英文还是正常的，但遇到俄文，法文，德文时就出现问题，正常的字符都变成了"?"，跟踪内存发现读入内存的字符已经变成了3f00，也就是"?"的unicode，可见是读入目录到内存的函数出了问题，如下代码：<br>&nbsp;&nbsp; long filehandle;<br>&nbsp;&nbsp;&nbsp; //the structure of file <br>&nbsp;&nbsp;&nbsp; struct _finddata_t entry;<br>&nbsp;&nbsp;&nbsp; //"*" means get all the file and directory<br>&nbsp;&nbsp;&nbsp; // Get the first file<br>&nbsp;&nbsp;&nbsp; if((filehandle = _findfirst( "*", &amp;entry )) != -1)&nbsp;&nbsp;&nbsp; //-1 means the directory is null<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tree* child;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(entry.attrib&amp;FILE_ATTRIBUTE_DIRECTORY){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( strcmp("..", entry.name) != 0 &amp;&amp; strcmp(".", entry.name) != 0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //printf("%*s%s\\\n", depth, "", entry.name);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(treenode)<br>&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char name[MAX_LOCALPATH_FOLDERNAME_LENGTH+1];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncpy(name,entry.name,MAX_LOCALPATH_FOLDERNAME_LENGTH-1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcat(name,"\\");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; child=new tree(name);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treenode-&gt;AddChild(child);<br>&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp; //Recursively processes directories<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //printdir(entry.name, depth + 4,child);<br>&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; } else{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //printf("%*s%s\n", depth, "", entry.name);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(treenode)<br>&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; child=new tree(entry.name);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treenode-&gt;AddChild(child);<br>&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; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }while( (_findnext(filehandle,&amp;entry)) ==0 );<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; chdir("..");<br>&nbsp;&nbsp;&nbsp; _findclose(filehandle);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过查证MSDN得知类似于_findfirst，findnext都是针对ASCII码的，要读unicode（windows默认字符集），就得用_wfindfirst，_wfindnext等读宽字符的操作函数，最终解决问题，但我没有松气，因为程序主要是运行在linux中的，Linux真不知道怎么整了。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Linux上读多国语言的文件和目录就需要对Linux系统深入了解，因为我要读的文件是usb上的文件，所以得先挂载到一个目录，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mount&nbsp;-t&nbsp;vfat&nbsp;/dev/sda1&nbsp;/mnt/usb，然后readdir读入文件，与Windows上同样的错，读入的是"?"，我想和windows一样去找一个类似wreaddir，但是没有。于是应该从挂载着手，目前在NTFS和FAT32/VFAT下的文件系统上都使用了Unicode,这就需要系统在读取这些文件名时动态将其转换为相应的语言编码，也就是说挂载的时候要把usb上的编码转化成16位的Unicode编码，改命令如下后成功。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mount -o iocharset = utf8 /dev/sda1 /mnt/usb.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Linux对iocharset的解释如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Character set to use for converting between 8 bit characters and 16 bit unicode charaters.The default is iso8859-1. Long filenames are stored on disk in Unicode format.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;至此终于解决了多国语言的问题，接着我无法想象还有什么问题会出来，但我准备好了。<br></p>
<img src ="http://www.cppblog.com/iuranus/aggbug/36242.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2007-11-09 18:53 <a href="http://www.cppblog.com/iuranus/archive/2007/11/09/36242.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>dos2unix</title><link>http://www.cppblog.com/iuranus/archive/2007/07/05/27564.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Thu, 05 Jul 2007 12:07:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2007/07/05/27564.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/27564.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2007/07/05/27564.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/27564.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/27564.html</trackback:ping><description><![CDATA[<p>&#160;&#160;&#160; 说起来都不好意思，今天搞了半天，最后发现竟然是vss上check out的文件没有dos2unix，无语，其实主要是没有认真考虑整个程序的运行全过程，仅在看代码，向锦锦一样。<br><br>&#160;&#160;&#160; 说起锦锦，他就惨了，广东话+english，我看他怎么过。呵呵，不过还好了，看这个家伙怎么适应这个生活，毕业了，大家都散了，老的走了，新的来了，我们都好好努力把。<br><br>&#160;&#160;&#160; 事情还挺多的，辜子怎么就把老板炒了，那个公司好就好在很自由，适合他的性格，看他找到下一个能不能适应了。<br><br>&#160;&#160;&#160; 好了，回家了，这两天筹划写些设计模式的东西，希望早点写出来。</p>
&#160; 
 <img src ="http://www.cppblog.com/iuranus/aggbug/27564.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2007-07-05 20:07 <a href="http://www.cppblog.com/iuranus/archive/2007/07/05/27564.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>suse尝鲜</title><link>http://www.cppblog.com/iuranus/archive/2006/11/10/17081.html</link><dc:creator>攀升</dc:creator><author>攀升</author><pubDate>Fri, 10 Nov 2006 05:15:00 GMT</pubDate><guid>http://www.cppblog.com/iuranus/archive/2006/11/10/17081.html</guid><wfw:comment>http://www.cppblog.com/iuranus/comments/17081.html</wfw:comment><comments>http://www.cppblog.com/iuranus/archive/2006/11/10/17081.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iuranus/comments/commentRss/17081.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iuranus/services/trackbacks/17081.html</trackback:ping><description><![CDATA[<table width="100%">
    <tbody>
        <tr>
            <td>
            <p>ubuntu已经被我从硬盘上擦除了，呵呵，因为一些问题，找了张suse，安装时选了KDE,现在突然感觉不错，从可用性上，界面上都不再是那个死板的Linux，而且我没装什么驱动用跑起来也没问题（对于菜鸟来说这个比较重要），然后U盘也不用挂载就可用，不会出中文问题，就感觉还可以了，先给张图片大家看看。 <br><img src="http://uranus.javaeye.com/upload/picture/pic/618/6519f3b1-64fc-48dd-9375-b9045dd1e5a5.bmp" border=0></p>
            </td>
        </tr>
    </tbody>
</table>
<br><br><a href="http://www.cppblog.com/iuranus/admin/%20%20%20%0A%20%20%20%20http://uranus.javaeye.com/blog/33510%0A%20%20%20%20"></a>
<img src ="http://www.cppblog.com/iuranus/aggbug/17081.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iuranus/" target="_blank">攀升</a> 2006-11-10 13:15 <a href="http://www.cppblog.com/iuranus/archive/2006/11/10/17081.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>