﻿<?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++博客-CPP&amp;&amp;设计模式小屋-随笔分类-Network</title><link>http://www.cppblog.com/shenhuafeng/category/2582.html</link><description>(STL,Templete,Generric Programming COM,COM+,ActiveX)---Windows &amp;&amp; Linux &amp;&amp;OpenSource
</description><language>zh-cn</language><lastBuildDate>Wed, 21 May 2008 21:14:47 GMT</lastBuildDate><pubDate>Wed, 21 May 2008 21:14:47 GMT</pubDate><ttl>60</ttl><item><title>修改tcpip.sys,破除winxp的10个网络连接限制(转)</title><link>http://www.cppblog.com/shenhuafeng/archive/2007/02/25/18963.html</link><dc:creator>CPP&amp;&amp;设计模式小屋</dc:creator><author>CPP&amp;&amp;设计模式小屋</author><pubDate>Sun, 25 Feb 2007 06:29:00 GMT</pubDate><guid>http://www.cppblog.com/shenhuafeng/archive/2007/02/25/18963.html</guid><wfw:comment>http://www.cppblog.com/shenhuafeng/comments/18963.html</wfw:comment><comments>http://www.cppblog.com/shenhuafeng/archive/2007/02/25/18963.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/shenhuafeng/comments/commentRss/18963.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shenhuafeng/services/trackbacks/18963.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 修改tcpip.sys,破除winxp的10个网络连接限制&nbsp;&nbsp;<a href='http://www.cppblog.com/shenhuafeng/archive/2007/02/25/18963.html'>阅读全文</a><img src ="http://www.cppblog.com/shenhuafeng/aggbug/18963.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shenhuafeng/" target="_blank">CPP&&设计模式小屋</a> 2007-02-25 14:29 <a href="http://www.cppblog.com/shenhuafeng/archive/2007/02/25/18963.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinSocket模型的探讨——select模型（转 收藏）</title><link>http://www.cppblog.com/shenhuafeng/archive/2007/01/19/17788.html</link><dc:creator>CPP&amp;&amp;设计模式小屋</dc:creator><author>CPP&amp;&amp;设计模式小屋</author><pubDate>Fri, 19 Jan 2007 08:44:00 GMT</pubDate><guid>http://www.cppblog.com/shenhuafeng/archive/2007/01/19/17788.html</guid><wfw:comment>http://www.cppblog.com/shenhuafeng/comments/17788.html</wfw:comment><comments>http://www.cppblog.com/shenhuafeng/archive/2007/01/19/17788.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/shenhuafeng/comments/commentRss/17788.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shenhuafeng/services/trackbacks/17788.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转贴自：http://blog.csdn.net/dylgsy/查找了很多资料都找不到select模型的详细用法，《Windows网络编程》这本书上也只是写了一个简单的回应服务器，就连writefds的用法都没讲，也不知道什么时候利用“可写”来发文件。这些都是我的疑问，相信很多研究网络编程的同路人也碰到了我的这些问题。这些疑问在这篇文章中都解决了！耗费了偶很多的精力去猜测去思考！ 		感觉一些...&nbsp;&nbsp;<a href='http://www.cppblog.com/shenhuafeng/archive/2007/01/19/17788.html'>阅读全文</a><img src ="http://www.cppblog.com/shenhuafeng/aggbug/17788.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shenhuafeng/" target="_blank">CPP&&设计模式小屋</a> 2007-01-19 16:44 <a href="http://www.cppblog.com/shenhuafeng/archive/2007/01/19/17788.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>微软ping程序源代码完整版(转)</title><link>http://www.cppblog.com/shenhuafeng/archive/2007/01/05/17312.html</link><dc:creator>CPP&amp;&amp;设计模式小屋</dc:creator><author>CPP&amp;&amp;设计模式小屋</author><pubDate>Fri, 05 Jan 2007 05:46:00 GMT</pubDate><guid>http://www.cppblog.com/shenhuafeng/archive/2007/01/05/17312.html</guid><wfw:comment>http://www.cppblog.com/shenhuafeng/comments/17312.html</wfw:comment><comments>http://www.cppblog.com/shenhuafeng/archive/2007/01/05/17312.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shenhuafeng/comments/commentRss/17312.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shenhuafeng/services/trackbacks/17312.html</trackback:ping><description><![CDATA[
		<span id="ArticleContent1_ArticleContent1_lblContent">
				<p>作者:侯志江</p>
				<p>编写自己的一个ping程序,可以说是许多人迈出网络编程的第一步吧!!这个ping程序的源代码经过我的修改和调试,基本上可以取代windows中自带的ping程序. 各个模块后都有我的详细注释和修改日志,希望能够对大家的学习有所帮助!!</p>
				<p>/*  本程序的主要源代码来自MSDN网站, 笔者只是做了一些改进和注释! 另外需要注意的是在Build之前,必须加入ws2_32.lib库文件,否则会提示"error LNK2001:"的错误！*/</p>
				<p>/******************************************************************************\ <br />| Version 1.1 修改记录:                                                                                                              | <br />|    &lt;1&gt; 解决了socket阻塞的问题,从而能够正确地处理超时的请求!                                                        | <br />|----------------------------------------------------------------------------------------------------| <br />| Version 1.2 修改记录:                                                                                                              | <br />|    &lt;1&gt; 增加了由用户控制发送ICMP包的数目的功能(即命令的第二个参数).                                              |    <br />|    &lt;2&gt; 增加了对ping结果的统计功能.                                                                                           | <br />\******************************************************************************/ </p>
				<p>#pragma pack(4) </p>
				<p>#include <br />#include <br />#include </p>
				<p>#define ICMP_ECHO 8 <br />#define ICMP_ECHOREPLY 0 </p>
				<p>#define ICMP_MIN 8 // minimum 8 byte icmp packet (just header) </p>
				<p>/* The IP header */ <br />typedef struct iphdr { <br />unsigned int h_len:4; // length of the header <br />unsigned int version:4; // Version of IP <br />unsigned char tos; // Type of service <br />unsigned short total_len; // total length of the packet <br />unsigned short ident; // unique identifier <br />unsigned short frag_and_flags; // flags <br />unsigned char ttl; <br />unsigned char proto; // protocol (TCP, UDP etc) <br />unsigned short checksum; // IP checksum </p>
				<p>unsigned int sourceIP; <br />unsigned int destIP; </p>
				<p>}IpHeader; </p>
				<p>// <br />// ICMP header <br />// <br />typedef struct icmphdr { <br />BYTE i_type; <br />BYTE i_code; /* type sub code */ <br />USHORT i_cksum; <br />USHORT i_id; <br />USHORT i_seq; <br />/* This is not the std header, but we reserve space for time */ <br />ULONG timestamp; <br />}IcmpHeader; </p>
				<p>#define STATUS_FAILED 0xFFFF <br />#define DEF_PACKET_SIZE    32 <br />#define DEF_PACKET_NUMBER  4    /* 发送数据报的个数 */ <br />#define MAX_PACKET 1024 </p>
				<p>#define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s)) <br />#define xfree(p) HeapFree (GetProcessHeap(),0,(p)) </p>
				<p>void fill_icmp_data(char *, int); <br />USHORT checksum(USHORT *, int); <br />int decode_resp(char *,int ,struct sockaddr_in *); </p>
				<p>void Usage(char *progname){ </p>
				<p>fprintf(stderr,"Usage:\n"); <br />fprintf(stderr,"%s [number of packets] [data_size]\n",progname); <br />fprintf(stderr,"datasize can be up to 1Kb\n"); <br />ExitProcess(STATUS_FAILED); </p>
				<p>} <br />int main(int argc, char **argv){ </p>
				<p>WSADATA wsaData; <br />SOCKET sockRaw; <br />struct sockaddr_in dest,from; <br />struct hostent * hp; <br />int bread,datasize,times; <br />int fromlen = sizeof(from); <br />int timeout = 1000; <br />int statistic = 0;  /* 用于统计结果 */  <br />char *dest_ip; <br />char *icmp_data; <br />char *recvbuf; <br />unsigned int addr=0; <br />USHORT seq_no = 0; </p>
				<p>if (WSAStartup(MAKEWORD(2,1),&amp;wsaData) != 0){ <br />fprintf(stderr,"WSAStartup failed: %d\n",GetLastError()); <br />ExitProcess(STATUS_FAILED); <br />} </p>
				<p>if (argc &lt;2 ) { <br />Usage(argv[0]); <br />} <br />sockRaw = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL, 0,WSA_FLAG_OVERLAPPED); </p>
				<p>// <br />//注：为了使用发送接收超时设置(即设置SO_RCVTIMEO, SO_SNDTIMEO)， <br />//    必须将标志位设为WSA_FLAG_OVERLAPPED ! <br />// </p>
				<p>if (sockRaw == INVALID_SOCKET) { <br />fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError()); <br />ExitProcess(STATUS_FAILED); <br />} <br />bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&amp;timeout, <br />sizeof(timeout)); <br />if(bread == SOCKET_ERROR) { <br />fprintf(stderr,"failed to set recv timeout: %d\n",WSAGetLastError()); <br />ExitProcess(STATUS_FAILED); <br />} <br />timeout = 1000; <br />bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&amp;timeout, <br />sizeof(timeout)); <br />if(bread == SOCKET_ERROR) { <br />fprintf(stderr,"failed to set send timeout: %d\n",WSAGetLastError()); <br />ExitProcess(STATUS_FAILED); <br />} <br />memset(&amp;dest,0,sizeof(dest)); </p>
				<p>hp = gethostbyname(argv[1]); </p>
				<p>if (!hp){ <br />addr = inet_addr(argv[1]); <br />} <br />if ((!hp) &amp;&amp; (addr == INADDR_NONE) ) { <br />fprintf(stderr,"Unable to resolve %s\n",argv[1]); <br />ExitProcess(STATUS_FAILED); <br />} </p>
				<p>if (hp != NULL) <br />memcpy(&amp;(dest.sin_addr),hp-&gt;h_addr,hp-&gt;h_length); <br />else <br />dest.sin_addr.s_addr = addr; </p>
				<p>if (hp) <br />dest.sin_family = hp-&gt;h_addrtype; <br />else <br />dest.sin_family = AF_INET; </p>
				<p>dest_ip = inet_ntoa(dest.sin_addr); </p>
				<p>// <br />//  atoi函数原型是: int atoi( const char *string ); <br />//  The return value is 0 if the input cannot be converted to an integer ! <br />// <br />if(argc&gt;2) <br />{ <br /> times=atoi(argv[2]); <br /> if(times == 0) <br />  times=DEF_PACKET_NUMBER; <br />} <br />else <br />    times=DEF_PACKET_NUMBER; </p>
				<p>if (argc &gt;3) <br />{ <br /> datasize = atoi(argv[3]); <br />    if (datasize == 0) <br />        datasize = DEF_PACKET_SIZE; <br /> if (datasize &gt;1024)   /* 用户给出的数据包大小太大 */ <br /> { <br />  fprintf(stderr,"WARNING : data_size is too large !\n"); <br />  datasize = DEF_PACKET_SIZE; <br /> } <br />} <br />else <br />    datasize = DEF_PACKET_SIZE; </p>
				<p>datasize += sizeof(IcmpHeader); </p>
				<p>icmp_data = (char*)xmalloc(MAX_PACKET); <br />recvbuf = (char*)xmalloc(MAX_PACKET); </p>
				<p>if (!icmp_data) { <br />fprintf(stderr,"HeapAlloc failed %d\n",GetLastError()); <br />ExitProcess(STATUS_FAILED); <br />} </p>
				<p>
						<br />memset(icmp_data,0,MAX_PACKET); <br />fill_icmp_data(icmp_data,datasize); </p>
				<p>// <br />//显示提示信息 <br />// <br />fprintf(stdout,"\nPinging %s ....\n\n",dest_ip); </p>
				<p>
						<br />for(int i=0;i{ <br />int bwrote; </p>
				<p>((IcmpHeader*)icmp_data)-&gt;i_cksum = 0; <br />((IcmpHeader*)icmp_data)-&gt;timestamp = GetTickCount(); </p>
				<p>((IcmpHeader*)icmp_data)-&gt;i_seq = seq_no++; <br />((IcmpHeader*)icmp_data)-&gt;i_cksum = checksum((USHORT*)icmp_data,datasize); </p>
				<p>bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&amp;dest,sizeof(dest)); <br />if (bwrote == SOCKET_ERROR){ <br />if (WSAGetLastError() == WSAETIMEDOUT) { <br />printf("Request timed out.\n"); <br />continue; <br />} <br />fprintf(stderr,"sendto failed: %d\n",WSAGetLastError()); <br />ExitProcess(STATUS_FAILED); <br />} <br />if (bwrote &lt; datasize ) { <br />fprintf(stdout,"Wrote %d bytes\n",bwrote); <br />} <br />bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&amp;from,&amp;fromlen); <br />if (bread == SOCKET_ERROR){ <br />if (WSAGetLastError() == WSAETIMEDOUT) { <br />printf("Request timed out.\n"); <br />continue; <br />} <br />fprintf(stderr,"recvfrom failed: %d\n",WSAGetLastError()); <br />ExitProcess(STATUS_FAILED); <br />} <br />if(!decode_resp(recvbuf,bread,&amp;from)) <br /> statistic++; /* 成功接收的数目++ */ <br />Sleep(1000); </p>
				<p>} <br />  <br />/* <br />Display the statistic result <br />*/ <br />fprintf(stdout,"\nPing statistics for %s \n",dest_ip); <br />fprintf(stdout,"    Packets: Sent = %d,Received = %d, Lost = %d (%2.0f%% loss)\n",times, <br />     statistic,(times-statistic),(float)(times-statistic)/times*100); </p>
				<p>
						<br />WSACleanup(); <br />return 0; </p>
				<p>} <br />/* <br />The response is an IP packet. We must decode the IP header to locate <br />the ICMP data <br />*/ <br />int decode_resp(char *buf, int bytes,struct sockaddr_in *from) { </p>
				<p>IpHeader *iphdr; <br />IcmpHeader *icmphdr; <br />unsigned short iphdrlen; </p>
				<p>iphdr = (IpHeader *)buf; </p>
				<p>iphdrlen = (iphdr-&gt;h_len) * 4 ; // number of 32-bit words *4 = bytes </p>
				<p>if (bytes &lt; iphdrlen + ICMP_MIN) { <br />printf("Too few bytes from %s\n",inet_ntoa(from-&gt;sin_addr)); <br />} </p>
				<p>icmphdr = (IcmpHeader*)(buf + iphdrlen); </p>
				<p>if (icmphdr-&gt;i_type != ICMP_ECHOREPLY) { <br />fprintf(stderr,"non-echo type %d recvd\n",icmphdr-&gt;i_type); <br />return 1; <br />} <br />if (icmphdr-&gt;i_id != (USHORT)GetCurrentProcessId()) { <br />fprintf(stderr,"someone else's packet!\n"); <br />return 1; <br />} <br />printf("%d bytes from %s:",bytes, inet_ntoa(from-&gt;sin_addr)); <br />printf(" icmp_seq = %d. ",icmphdr-&gt;i_seq); <br />printf(" time: %d ms ",GetTickCount()-icmphdr-&gt;timestamp); <br />printf("\n"); <br />return 0; </p>
				<p>} </p>
				<p>
						<br />USHORT checksum(USHORT *buffer, int size) { </p>
				<p>unsigned long cksum=0; </p>
				<p>while(size &gt;1) { <br />cksum+=*buffer++; <br />size -=sizeof(USHORT); <br />} </p>
				<p>if(size) { <br />cksum += *(UCHAR*)buffer; <br />} </p>
				<p>cksum = (cksum &gt;&gt; 16) + (cksum &amp; 0xffff); <br />cksum += (cksum &gt;&gt;16); <br />return (USHORT)(~cksum); <br />} <br />/* <br />Helper function to fill in various stuff in our ICMP request. <br />*/ <br />void fill_icmp_data(char * icmp_data, int datasize){ </p>
				<p>IcmpHeader *icmp_hdr; <br />char *datapart; </p>
				<p>icmp_hdr = (IcmpHeader*)icmp_data; </p>
				<p>icmp_hdr-&gt;i_type = ICMP_ECHO; <br />icmp_hdr-&gt;i_code = 0; <br />icmp_hdr-&gt;i_id = (USHORT)GetCurrentProcessId(); <br />icmp_hdr-&gt;i_cksum = 0; <br />icmp_hdr-&gt;i_seq = 0; </p>
				<p>datapart = icmp_data + sizeof(IcmpHeader); <br />// <br />// Place some junk in the buffer. <br />// <br />memset(datapart,'E', datasize - sizeof(IcmpHeader)); </p>
				<p>} </p>
				<p>/******************* 附: ping命令执行时显示的画面 ***************\ <br />*  C:\Documents and Settings\houzhijiang&gt;ping 236.56.54.12               * <br />*                                                                                                      * <br />*  Pinging 236.56.54.12 with 32 bytes of data:                                      * <br />*                                                                                                      * <br />*  Request timed out.                                                                         * <br />*  Request timed out.                                                                         * <br />*  Request timed out.                                                                         * <br />*  Request timed out.                                                                         * <br />*                                                                                                      * <br />*  Ping statistics for 236.56.54.12:                                                      * <br />*     Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),                  * <br />*                                                                                                      * <br />\*********************************************************/ </p>
				<p>/*********************************************************\ <br />*  C:\Documents and Settings\houzhijiang&gt;ping 127.0.0.1                    * <br />*                                                                                                     * <br />*  Pinging 127.0.0.1 with 32 bytes of data:                                          * <br />*                                                                                                     * <br />*  Reply from 127.0.0.1: bytes=32 time&lt;1ms TTL=128                        * <br />*  Reply from 127.0.0.1: bytes=32 time&lt;1ms TTL=128                        * <br />*  Reply from 127.0.0.1: bytes=32 time&lt;1ms TTL=128                        * <br />*  Reply from 127.0.0.1: bytes=32 time&lt;1ms TTL=128                        * <br />*                                                                                                     * <br />*  Ping statistics for 127.0.0.1:                                                          * <br />*     Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),                    * <br />*  Approximate round trip times in milli-seconds:                                 * <br />*     Minimum = 0ms, Maximum = 0ms, Average = 0ms                        * <br />*                                                                                                    * <br />\********************************************************/ </p>
		</span>
<img src ="http://www.cppblog.com/shenhuafeng/aggbug/17312.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shenhuafeng/" target="_blank">CPP&&设计模式小屋</a> 2007-01-05 13:46 <a href="http://www.cppblog.com/shenhuafeng/archive/2007/01/05/17312.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>P2P之UDP穿透NAT的原理与实现（附源代码）（转）</title><link>http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17215.html</link><dc:creator>CPP&amp;&amp;设计模式小屋</dc:creator><author>CPP&amp;&amp;设计模式小屋</author><pubDate>Thu, 04 Jan 2007 05:37:00 GMT</pubDate><guid>http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17215.html</guid><wfw:comment>http://www.cppblog.com/shenhuafeng/comments/17215.html</wfw:comment><comments>http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17215.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shenhuafeng/comments/commentRss/17215.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shenhuafeng/services/trackbacks/17215.html</trackback:ping><description><![CDATA[
		<div align="center">
				<h1 class="aTitle">P2P之UDP穿透NAT的原理与实现（附源代码）</h1>
		</div>
		<table width="97%" align="center">
				<tbody>
						<tr>
								<td align="middle">作者：<span id="AuthorLabel">shootingstars</span> | 日期：<span id="TimeLabel">2004-05-25</span> | 字体：<a href="javascript:ContentSize(16)">大</a><a href="javascript:ContentSize(14)">中</a><a href="javascript:ContentSize(12)">小</a></td>
						</tr>
				</tbody>
		</table>
		<div class="content" id="BodyLabel" style="PADDING-RIGHT: 10px; DISPLAY: block; PADDING-LEFT: 10px; PADDING-BOTTOM: 0px; PADDING-TOP: 0px">
				<p>P2P 之 UDP穿透NAT的原理与实现（附源代码）<br />原创：shootingstars<br />参考：<a href="http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt">http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt</a><!--IWMS_AD_BEGIN--></p>
				<div style="FLOAT: right; WIDTH: 350px">
						<script src="ads/show350_250.js" type="text/javascript">
						</script>
				</div>
				<!--IWMS_AD_END-->
				<p>
				</p>
				<p>论坛上经常有对P2P原理的讨论，但是讨论归讨论，很少有实质的东西产生（源代码）。呵呵，在这里我就用自己实现的一个源代码来说明UDP穿越NAT的原理。</p>
				<p>首先先介绍一些基本概念：<br />    NAT(Network Address Translators)，网络地址转换：网络地址转换是在IP地址日益缺乏的情况下产生的，它的主要目的就是为了能够地址重用。NAT分为两大类，基本的NAT和NAPT(Network Address/Port Translator)。<br />    最开始NAT是运行在路由器上的一个功能模块。<br />    <br />    最先提出的是基本的NAT，它的产生基于如下事实：一个私有网络（域）中的节点中只有很少的节点需要与外网连接（呵呵，这是在上世纪90年代中期提出的）。那么这个子网中其实只有少数的节点需要全球唯一的IP地址，其他的节点的IP地址应该是可以重用的。<br />    因此，基本的NAT实现的功能很简单，在子网内使用一个保留的IP子网段，这些IP对外是不可见的。子网内只有少数一些IP地址可以对应到真正全球唯一的IP地址。如果这些节点需要访问外部网络，那么基本NAT就负责将这个节点的子网内IP转化为一个全球唯一的IP然后发送出去。(基本的NAT会改变IP包中的原IP地址，但是不会改变IP包中的端口)<br />    关于基本的NAT可以参看RFC 1631<br />    <br />    另外一种NAT叫做NAPT，从名称上我们也可以看得出，NAPT不但会改变经过这个NAT设备的IP数据报的IP地址，还会改变IP数据报的TCP/UDP端口。基本NAT的设备可能我们见的不多（呵呵，我没有见到过），NAPT才是我们真正讨论的主角。看下图：<br />                                Server S1                         <br />                         18.181.0.31:1235                          <br />                                      |<br />          ^  Session 1 (A-S1)  ^      |  <br />          |  18.181.0.31:1235  |      |   <br />          v 155.99.25.11:62000 v      |    <br />                                      |<br />                                     NAT<br />                                 155.99.25.11<br />                                      |<br />          ^  Session 1 (A-S1)  ^      |  <br />          |  18.181.0.31:1235  |      |  <br />          v   10.0.0.1:1234    v      |  <br />                                      |<br />                                   Client A<br />                                10.0.0.1:1234<br />    有一个私有网络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 />    首先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 />    一旦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建立以了一个连接。</p>
				<p>    呵呵，上面的基础知识可能很多人都知道了，那么下面是关键的部分了。<br />    看看下面的情况：<br />    Server S1                                     Server S2<br /> 18.181.0.31:1235                              138.76.29.7:1235<br />        |                                             |<br />        |                                             |<br />        +----------------------+----------------------+<br />                               |<br />   ^  Session 1 (A-S1)  ^      |      ^  Session 2 (A-S2)  ^<br />   |  18.181.0.31:1235  |      |      |  138.76.29.7:1235  |<br />   v 155.99.25.11:62000 v      |      v 155.99.25.11:62000 v<br />                               |<br />                            Cone NAT<br />                          155.99.25.11<br />                               |<br />   ^  Session 1 (A-S1)  ^      |      ^  Session 2 (A-S2)  ^<br />   |  18.181.0.31:1235  |      |      |  138.76.29.7:1235  |<br />   v   10.0.0.1:1234    v      |      v   10.0.0.1:1234    v<br />                               |<br />                            Client A<br />                         10.0.0.1:1234<br />    接上面的例子，如果Client A的原来那个Socket(绑定了1234端口的那个UDP Socket)又接着向另外一个Server S2发送了一个UDP包，那么这个UDP包在通过NAT时会怎么样呢？<br />    这时可能会有两种情况发生，一种是NAT再次创建一个Session，并且再次为这个Session分配一个端口号（比如：62001）。另外一种是NAT再次创建一个Session，但是不会新分配一个端口号，而是用原来分配的端口号62000。前一种NAT叫做Symmetric NAT，后一种叫做Cone NAT。我们期望我们的NAT是第二种，呵呵，如果你的NAT刚好是第一种，那么很可能会有很多P2P软件失灵。（可以庆幸的是，现在绝大多数的NAT属于后者，即Cone NAT）<br />   <br />    好了，我们看到，通过NAT,子网内的计算机向外连结是很容易的（NAT相当于透明的，子网内的和外网的计算机不用知道NAT的情况）。<br />    但是如果外部的计算机想访问子网内的计算机就比较困难了（而这正是P2P所需要的）。<br />    那么我们如果想从外部发送一个数据报给内网的计算机有什么办法呢？首先，我们必须在内网的NAT上打上一个“洞”（也就是前面我们说的在NAT上建立一个Session），这个洞不能由外部来打，只能由内网内的主机来打。而且这个洞是有方向的，比如从内部某台主机（比如：192.168.0.10）向外部的某个IP(比如：219.237.60.1)发送一个UDP包，那么就在这个内网的NAT设备上打了一个方向为219.237.60.1的“洞”，（这就是称为UDP Hole Punching的技术）以后219.237.60.1就可以通过这个洞与内网的192.168.0.10联系了。（但是其他的IP不能利用这个洞）。</p>
				<p>呵呵，现在该轮到我们的正题P2P了。有了上面的理论，实现两个内网的主机通讯就差最后一步了：那就是鸡生蛋还是蛋生鸡的问题了，两边都无法主动发出连接请求，谁也不知道谁的公网地址，那我们如何来打这个洞呢？我们需要一个中间人来联系这两个内网主机。<br />    现在我们来看看一个P2P软件的流程，以下图为例：</p>
				<p>                       Server S （219.237.60.1）<br />                          |<br />                          |<br />   +----------------------+----------------------+<br />   |                                             |<br /> NAT A (外网IP:202.187.45.3)                 NAT B (外网IP:187.34.1.56)<br />   |   (内网IP:192.168.0.1)                      | (内网IP:192.168.0.1)<br />   |                                             |<br />Client A  (192.168.0.20:4000)             Client B (192.168.0.10:40000)</p>
				<p>    首先，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 />    此时，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 />    总结一下这个过程：如果Client A想向Client B发送信息，那么Client A发送命令给Server S，请求Server S命令Client B向Client A方向打洞。呵呵，是不是很绕口，不过没关系，想一想就很清楚了，何况还有源代码呢（侯老师说过：在源代码面前没有秘密 8）），然后Client A就可以通过Client B的外网地址与Client B通信了。<br />    <br />    注意：以上过程只适合于Cone NAT的情况，如果是Symmetric NAT，那么当Client B向Client A打洞的端口已经重新分配了，Client B将无法知道这个端口（如果Symmetric NAT的端口是顺序分配的，那么我们或许可以猜测这个端口号，可是由于可能导致失败的因素太多，我们不推荐这种猜测端口的方法）。<br />    <br />    下面是一个模拟P2P聊天的过程的源代码，过程很简单，P2PServer运行在一个拥有公网IP的计算机上，P2PClient运行在两个不同的NAT后（注意，如果两个客户端运行在一个NAT后，本程序很可能不能运行正常，这取决于你的NAT是否支持loopback translation，详见<a href="http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt">http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt</a>，当然，此问题可以通过双方先尝试连接对方的内网IP来解决，但是这个代码只是为了验证原理，并没有处理这些问题），后登录的计算机可以获得先登录计算机的用户名，后登录的计算机通过send username message的格式来发送消息。如果发送成功，说明你已取得了直接与对方连接的成功。<br />    程序现在支持三个命令：send , getu , exit<br />    <br />    send格式：send username message<br />    功能：发送信息给username<br />    <br />    getu格式：getu<br />    功能：获得当前服务器用户列表<br />    <br />    exit格式：exit<br />    功能：注销与服务器的连接（服务器不会自动监测客户是否吊线）<br />        <br />    代码很短，相信很容易懂，如果有什么问题，可以给我发邮件<a href="mailto:zhouhuis22@sina.com">zhouhuis22@sina.com</a>  或者在CSDN上发送短消息。同时，欢迎转发此文，但希望保留作者版权8-）。<br />    <br />    最后感谢CSDN网友 PiggyXP 和 Seilfer的测试帮助</p>
				<p>P2PServer.c</p>
				<p>/* P2P 程序服务端<br /> * <br /> * 文件名：P2PServer.c<br /> *<br /> * 日期：2004-5-21<br /> *<br /> * 作者：shootingstars(<a href="mailto:zhouhuis22@sina.com">zhouhuis22@sina.com</a>)<br /> *<br /> */<br />#pragma comment(lib, "ws2_32.lib")</p>
				<p>#include "windows.h"<br />#include "..\proto.h"<br />#include "..\Exception.h"</p>
				<p>UserList ClientList;</p>
				<p>void InitWinSock()<br />{<br /> WSADATA wsaData;</p>
				<p> if (WSAStartup(MAKEWORD(2, 2), &amp;wsaData) != 0)<br /> {<br />  printf("Windows sockets 2.2 startup");<br />  throw Exception("");<br /> }<br /> else{<br />  printf("Using %s (Status: %s)\n",<br />   wsaData.szDescription, wsaData.szSystemStatus);<br />  printf("with API versions %d.%d to %d.%d\n\n",<br />   LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion),<br />   LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));<br />  <br /> }<br />}</p>
				<p>SOCKET mksock(int type)<br />{<br /> SOCKET sock = socket(AF_INET, type, 0);<br /> if (sock &lt; 0)<br /> {<br />        printf("create socket error");<br />  throw Exception("");<br /> }<br /> return sock;<br />}</p>
				<p>stUserListNode GetUser(char *username)<br />{<br /> for(UserList::iterator UserIterator=ClientList.begin();<br />      UserIterator!=ClientList.end();<br />       ++UserIterator)<br /> {<br />  if( strcmp( ((*UserIterator)-&gt;userName), username) == 0 )<br />   return *(*UserIterator);<br /> }<br /> throw Exception("not find this user");<br />}</p>
				<p>int main(int argc, char* argv[])<br />{<br /> try{<br />  InitWinSock();<br />  <br />  SOCKET PrimaryUDP;<br />  PrimaryUDP = mksock(SOCK_DGRAM);</p>
				<p>  sockaddr_in local;<br />  local.sin_family=AF_INET;<br />  local.sin_port= htons(SERVER_PORT); <br />  local.sin_addr.s_addr = htonl(INADDR_ANY);<br />  int nResult=bind(PrimaryUDP,(sockaddr*)&amp;local,sizeof(sockaddr));<br />  if(nResult==SOCKET_ERROR)<br />   throw Exception("bind error");</p>
				<p>  sockaddr_in sender;<br />  stMessage recvbuf;<br />  memset(&amp;recvbuf,0,sizeof(stMessage));</p>
				<p>  // 开始主循环.<br />  // 主循环负责下面几件事情:<br />  // 一:读取客户端登陆和登出消息,记录客户列表<br />  // 二:转发客户p2p请求<br />  for(;;)<br />  {<br />   int dwSender = sizeof(sender);<br />   int ret = recvfrom(PrimaryUDP, (char *)&amp;recvbuf, sizeof(stMessage), 0, (sockaddr *)&amp;sender, &amp;dwSender);<br />   if(ret &lt;= 0)<br />   {<br />    printf("recv error");<br />    continue;<br />   }<br />   else<br />   {<br />    int messageType = recvbuf.iMessageType;<br />    switch(messageType){<br />    case LOGIN:<br />     {<br />      //  将这个用户的信息记录到用户列表中<br />      printf("has a user login : %s\n", recvbuf.message.loginmember.userName);<br />      stUserListNode *currentuser = new stUserListNode();<br />      strcpy(currentuser-&gt;userName, recvbuf.message.loginmember.userName);<br />      currentuser-&gt;ip = ntohl(sender.sin_addr.S_un.S_addr);<br />      currentuser-&gt;port = ntohs(sender.sin_port);<br />      <br />      ClientList.push_back(currentuser);</p>
				<p>      // 发送已经登陆的客户信息<br />      int nodecount = (int)ClientList.size();<br />      sendto(PrimaryUDP, (const char*)&amp;nodecount, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));<br />      for(UserList::iterator UserIterator=ClientList.begin();<br />        UserIterator!=ClientList.end();<br />        ++UserIterator)<br />      {<br />       sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&amp;sender, sizeof(sender)); <br />      }</p>
				<p>      break;<br />     }<br />    case LOGOUT:<br />     {<br />      // 将此客户信息删除<br />      printf("has a user logout : %s\n", recvbuf.message.logoutmember.userName);<br />      UserList::iterator removeiterator = NULL;<br />      for(UserList::iterator UserIterator=ClientList.begin();<br />       UserIterator!=ClientList.end();<br />       ++UserIterator)<br />      {<br />       if( strcmp( ((*UserIterator)-&gt;userName), recvbuf.message.logoutmember.userName) == 0 )<br />       {<br />        removeiterator = UserIterator;<br />        break;<br />       }<br />      }<br />      if(removeiterator != NULL)<br />       ClientList.remove(*removeiterator);<br />      break;<br />     }<br />    case P2PTRANS:<br />     {<br />      // 某个客户希望服务端向另外一个客户发送一个打洞消息<br />      printf("%s wants to p2p %s\n",inet_ntoa(sender.sin_addr),recvbuf.message.translatemessage.userName);<br />      stUserListNode node = GetUser(recvbuf.message.translatemessage.userName);<br />      sockaddr_in remote;<br />      remote.sin_family=AF_INET;<br />      remote.sin_port= htons(node.port); <br />      remote.sin_addr.s_addr = htonl(node.ip);</p>
				<p>      in_addr tmp;<br />      tmp.S_un.S_addr = htonl(node.ip);<br />      printf("the address is %s,and port is %d\n",inet_ntoa(tmp), node.port);</p>
				<p>      stP2PMessage transMessage;<br />      transMessage.iMessageType = P2PSOMEONEWANTTOCALLYOU;<br />      transMessage.iStringLen = ntohl(sender.sin_addr.S_un.S_addr);<br />      transMessage.Port = ntohs(sender.sin_port);<br />                        <br />      sendto(PrimaryUDP,(const char*)&amp;transMessage, sizeof(transMessage), 0, (const sockaddr *)&amp;remote, sizeof(remote));</p>
				<p>      break;<br />     }<br />    <br />    case GETALLUSER:<br />     {<br />      int command = GETALLUSER;<br />      sendto(PrimaryUDP, (const char*)&amp;command, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));</p>
				<p>      int nodecount = (int)ClientList.size();<br />      sendto(PrimaryUDP, (const char*)&amp;nodecount, sizeof(int), 0, (const sockaddr*)&amp;sender, sizeof(sender));</p>
				<p>      for(UserList::iterator UserIterator=ClientList.begin();<br />        UserIterator!=ClientList.end();<br />        ++UserIterator)<br />      {<br />       sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&amp;sender, sizeof(sender)); <br />      }<br />      break;<br />     }<br />    }<br />   }<br />  }</p>
				<p> }<br /> catch(Exception &amp;e)<br /> {<br />  printf(e.GetMessage());<br />  return 1;<br /> }</p>
				<p> return 0;<br />}<br /></p>
				<p>/* P2P 程序客户端<br /> * <br /> * 文件名：P2PClient.c<br /> *<br /> * 日期：2004-5-21<br /> *<br /> * 作者：shootingstars(<a href="mailto:zhouhuis22@sina.com">zhouhuis22@sina.com</a>)<br /> *<br /> */</p>
				<p>#pragma comment(lib,"ws2_32.lib")</p>
				<p>#include "windows.h"<br />#include "..\proto.h"<br />#include "..\Exception.h"<br />#include &lt;iostream&gt;<br />using namespace std;</p>
				<p>UserList ClientList;</p>
				<p> </p>
				<p>#define COMMANDMAXC 256<br />#define MAXRETRY    5</p>
				<p>SOCKET PrimaryUDP;<br />char UserName[10];<br />char ServerIP[20];</p>
				<p>bool RecvedACK;</p>
				<p>void InitWinSock()<br />{<br /> WSADATA wsaData;</p>
				<p> if (WSAStartup(MAKEWORD(2, 2), &amp;wsaData) != 0)<br /> {<br />  printf("Windows sockets 2.2 startup");<br />  throw Exception("");<br /> }<br /> else{<br />  printf("Using %s (Status: %s)\n",<br />   wsaData.szDescription, wsaData.szSystemStatus);<br />  printf("with API versions %d.%d to %d.%d\n\n",<br />   LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion),<br />   LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));<br /> }<br />}</p>
				<p>SOCKET mksock(int type)<br />{<br /> SOCKET sock = socket(AF_INET, type, 0);<br /> if (sock &lt; 0)<br /> {<br />        printf("create socket error");<br />  throw Exception("");<br /> }<br /> return sock;<br />}</p>
				<p>stUserListNode GetUser(char *username)<br />{<br /> for(UserList::iterator UserIterator=ClientList.begin();<br />      UserIterator!=ClientList.end();<br />       ++UserIterator)<br /> {<br />  if( strcmp( ((*UserIterator)-&gt;userName), username) == 0 )<br />   return *(*UserIterator);<br /> }<br /> throw Exception("not find this user");<br />}</p>
				<p>void BindSock(SOCKET sock)<br />{<br /> sockaddr_in sin;<br /> sin.sin_addr.S_un.S_addr = INADDR_ANY;<br /> sin.sin_family = AF_INET;<br /> sin.sin_port = 0;<br /> <br /> if (bind(sock, (struct sockaddr*)&amp;sin, sizeof(sin)) &lt; 0)<br />  throw Exception("bind error");<br />}</p>
				<p>void ConnectToServer(SOCKET sock,char *username, char *serverip)<br />{<br /> sockaddr_in remote;<br /> remote.sin_addr.S_un.S_addr = inet_addr(serverip);<br /> remote.sin_family = AF_INET;<br /> remote.sin_port = htons(SERVER_PORT);<br /> <br /> stMessage sendbuf;<br /> sendbuf.iMessageType = LOGIN;<br /> strncpy(sendbuf.message.loginmember.userName, username, 10);</p>
				<p> sendto(sock, (const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&amp;remote,sizeof(remote));</p>
				<p> int usercount;<br /> int fromlen = sizeof(remote);<br /> int iread = recvfrom(sock, (char *)&amp;usercount, sizeof(int), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br /> if(iread&lt;=0)<br /> {<br />  throw Exception("Login error\n");<br /> }</p>
				<p> // 登录到服务端后，接收服务端发来的已经登录的用户的信息<br /> cout&lt;&lt;"Have "&lt;&lt;usercount&lt;&lt;" users logined server:"&lt;&lt;endl;<br /> for(int i = 0;i&lt;usercount;i++)<br /> {<br />  stUserListNode *node = new stUserListNode;<br />  recvfrom(sock, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />  ClientList.push_back(node);<br />  cout&lt;&lt;"Username:"&lt;&lt;node-&gt;userName&lt;&lt;endl;<br />  in_addr tmp;<br />  tmp.S_un.S_addr = htonl(node-&gt;ip);<br />  cout&lt;&lt;"UserIP:"&lt;&lt;inet_ntoa(tmp)&lt;&lt;endl;<br />  cout&lt;&lt;"UserPort:"&lt;&lt;node-&gt;port&lt;&lt;endl;<br />  cout&lt;&lt;""&lt;&lt;endl;<br /> }<br />}</p>
				<p>void OutputUsage()<br />{<br /> cout&lt;&lt;"You can input you command:\n"<br />  &lt;&lt;"Command Type:\"send\",\"exit\",\"getu\"\n"<br />  &lt;&lt;"Example : send Username Message\n"<br />  &lt;&lt;"          exit\n"<br />  &lt;&lt;"          getu\n"<br />  &lt;&lt;endl;<br />}</p>
				<p>/* 这是主要的函数：发送一个消息给某个用户(C)<br /> *流程：直接向某个用户的外网IP发送消息，如果此前没有联系过<br /> *      那么此消息将无法发送，发送端等待超时。<br /> *      超时后，发送端将发送一个请求信息到服务端，<br /> *      要求服务端发送给客户C一个请求，请求C给本机发送打洞消息<br /> *      以上流程将重复MAXRETRY次<br /> */<br />bool SendMessageTo(char *UserName, char *Message)<br />{<br /> char realmessage[256];<br /> unsigned int UserIP;<br /> unsigned short UserPort;<br /> bool FindUser = false;<br /> for(UserList::iterator UserIterator=ClientList.begin();<br />      UserIterator!=ClientList.end();<br />      ++UserIterator)<br /> {<br />  if( strcmp( ((*UserIterator)-&gt;userName), UserName) == 0 )<br />  {<br />   UserIP = (*UserIterator)-&gt;ip;<br />   UserPort = (*UserIterator)-&gt;port;<br />   FindUser = true;<br />  }<br /> }</p>
				<p> if(!FindUser)<br />  return false;</p>
				<p> strcpy(realmessage, Message);<br /> for(int i=0;i&lt;MAXRETRY;i++)<br /> {<br />  RecvedACK = false;</p>
				<p>  sockaddr_in remote;<br />  remote.sin_addr.S_un.S_addr = htonl(UserIP);<br />  remote.sin_family = AF_INET;<br />  remote.sin_port = htons(UserPort);<br />  stP2PMessage MessageHead;<br />  MessageHead.iMessageType = P2PMESSAGE;<br />  MessageHead.iStringLen = (int)strlen(realmessage)+1;<br />  int isend = sendto(PrimaryUDP, (const char *)&amp;MessageHead, sizeof(MessageHead), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />  isend = sendto(PrimaryUDP, (const char *)&amp;realmessage, MessageHead.iStringLen, 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />  <br />  // 等待接收线程将此标记修改<br />  for(int j=0;j&lt;10;j++)<br />  {<br />   if(RecvedACK)<br />    return true;<br />   else<br />    Sleep(300);<br />  }</p>
				<p>  // 没有接收到目标主机的回应，认为目标主机的端口映射没有<br />  // 打开，那么发送请求信息给服务器，要服务器告诉目标主机<br />  // 打开映射端口（UDP打洞）<br />  sockaddr_in server;<br />  server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />  server.sin_family = AF_INET;<br />  server.sin_port = htons(SERVER_PORT);<br /> <br />  stMessage transMessage;<br />  transMessage.iMessageType = P2PTRANS;<br />  strcpy(transMessage.message.translatemessage.userName, UserName);</p>
				<p>  sendto(PrimaryUDP, (const char*)&amp;transMessage, sizeof(transMessage), 0, (const sockaddr*)&amp;server, sizeof(server));<br />  Sleep(100);// 等待对方先发送信息。<br /> }<br /> return false;<br />}<br /></p>
				<p>// 解析命令，暂时只有exit和send命令<br />// 新增getu命令，获取当前服务器的所有用户<br />void ParseCommand(char * CommandLine)<br />{<br /> if(strlen(CommandLine)&lt;4)<br />  return;<br /> char Command[10];<br /> strncpy(Command, CommandLine, 4);<br /> Command[4]='\0';</p>
				<p> if(strcmp(Command,"exit")==0)<br /> {<br />  stMessage sendbuf;<br />  sendbuf.iMessageType = LOGOUT;<br />  strncpy(sendbuf.message.logoutmember.userName, UserName, 10);<br />  sockaddr_in server;<br />  server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />  server.sin_family = AF_INET;<br />  server.sin_port = htons(SERVER_PORT);</p>
				<p>  sendto(PrimaryUDP,(const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr *)&amp;server, sizeof(server));<br />  shutdown(PrimaryUDP, 2);<br />  closesocket(PrimaryUDP);<br />  exit(0);<br /> }<br /> else if(strcmp(Command,"send")==0)<br /> {<br />  char sendname[20];<br />  char message[COMMANDMAXC];<br />  int i;<br />  for(i=5;;i++)<br />  {<br />   if(CommandLine[i]!=' ')<br />    sendname[i-5]=CommandLine[i];<br />   else<br />   {<br />    sendname[i-5]='\0';<br />    break;<br />   }<br />  }<br />  strcpy(message, &amp;(CommandLine[i+1]));<br />  if(SendMessageTo(sendname, message))<br />   printf("Send OK!\n");<br />  else <br />   printf("Send Failure!\n");<br /> }<br /> else if(strcmp(Command,"getu")==0)<br /> {<br />  int command = GETALLUSER;<br />  sockaddr_in server;<br />  server.sin_addr.S_un.S_addr = inet_addr(ServerIP);<br />  server.sin_family = AF_INET;<br />  server.sin_port = htons(SERVER_PORT);</p>
				<p>  sendto(PrimaryUDP,(const char*)&amp;command, sizeof(command), 0, (const sockaddr *)&amp;server, sizeof(server));<br /> }<br />}</p>
				<p>// 接受消息线程<br />DWORD WINAPI RecvThreadProc(LPVOID lpParameter)<br />{<br /> sockaddr_in remote;<br /> int sinlen = sizeof(remote);<br /> stP2PMessage recvbuf;<br /> for(;;)<br /> {<br />  int iread = recvfrom(PrimaryUDP, (char *)&amp;recvbuf, sizeof(recvbuf), 0, (sockaddr *)&amp;remote, &amp;sinlen);<br />  if(iread&lt;=0)<br />  {<br />   printf("recv error\n");<br />   continue;<br />  }<br />  switch(recvbuf.iMessageType)<br />  {<br />  case P2PMESSAGE:<br />   {<br />    // 接收到P2P的消息<br />    char *comemessage= new char[recvbuf.iStringLen];<br />    int iread1 = recvfrom(PrimaryUDP, comemessage, 256, 0, (sockaddr *)&amp;remote, &amp;sinlen);<br />    comemessage[iread1-1] = '\0';<br />    if(iread1&lt;=0)<br />     throw Exception("Recv Message Error\n");<br />    else<br />    {<br />     printf("Recv a Message:%s\n",comemessage);<br />     <br />     stP2PMessage sendbuf;<br />     sendbuf.iMessageType = P2PMESSAGEACK;<br />     sendto(PrimaryUDP, (const char*)&amp;sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />    }</p>
				<p>    delete []comemessage;<br />    break;</p>
				<p>   }<br />  case P2PSOMEONEWANTTOCALLYOU:<br />   {<br />    // 接收到打洞命令，向指定的IP地址打洞<br />    printf("Recv p2someonewanttocallyou data\n");<br />    sockaddr_in remote;<br />    remote.sin_addr.S_un.S_addr = htonl(recvbuf.iStringLen);<br />    remote.sin_family = AF_INET;<br />    remote.sin_port = htons(recvbuf.Port);</p>
				<p>    // UDP hole punching<br />    stP2PMessage message;<br />    message.iMessageType = P2PTRASH;<br />    sendto(PrimaryUDP, (const char *)&amp;message, sizeof(message), 0, (const sockaddr*)&amp;remote, sizeof(remote));<br />                <br />    break;<br />   }<br />  case P2PMESSAGEACK:<br />   {<br />    // 发送消息的应答<br />    RecvedACK = true;<br />    break;<br />   }<br />  case P2PTRASH:<br />   {<br />    // 对方发送的打洞消息，忽略掉。<br />    //do nothing ...<br />    printf("Recv p2ptrash data\n");<br />    break;<br />   }<br />  case GETALLUSER:<br />   {<br />    int usercount;<br />    int fromlen = sizeof(remote);<br />    int iread = recvfrom(PrimaryUDP, (char *)&amp;usercount, sizeof(int), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />    if(iread&lt;=0)<br />    {<br />     throw Exception("Login error\n");<br />    }<br />    <br />    ClientList.clear();</p>
				<p>    cout&lt;&lt;"Have "&lt;&lt;usercount&lt;&lt;" users logined server:"&lt;&lt;endl;<br />    for(int i = 0;i&lt;usercount;i++)<br />    {<br />     stUserListNode *node = new stUserListNode;<br />     recvfrom(PrimaryUDP, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&amp;remote, &amp;fromlen);<br />     ClientList.push_back(node);<br />     cout&lt;&lt;"Username:"&lt;&lt;node-&gt;userName&lt;&lt;endl;<br />     in_addr tmp;<br />     tmp.S_un.S_addr = htonl(node-&gt;ip);<br />     cout&lt;&lt;"UserIP:"&lt;&lt;inet_ntoa(tmp)&lt;&lt;endl;<br />     cout&lt;&lt;"UserPort:"&lt;&lt;node-&gt;port&lt;&lt;endl;<br />     cout&lt;&lt;""&lt;&lt;endl;<br />    }<br />    break;<br />   }<br />  }<br /> }<br />}</p>
				<p>
						<br />int main(int argc, char* argv[])<br />{<br /> try<br /> {<br />  InitWinSock();<br />  <br />  PrimaryUDP = mksock(SOCK_DGRAM);<br />  BindSock(PrimaryUDP);</p>
				<p>  cout&lt;&lt;"Please input server ip:";<br />  cin&gt;&gt;ServerIP;</p>
				<p>  cout&lt;&lt;"Please input your name:";<br />  cin&gt;&gt;UserName;</p>
				<p>  ConnectToServer(PrimaryUDP, UserName, ServerIP);</p>
				<p>  HANDLE threadhandle = CreateThread(NULL, 0, RecvThreadProc, NULL, NULL, NULL);<br />  CloseHandle(threadhandle);<br />  OutputUsage();</p>
				<p>  for(;;)<br />  {<br />   char Command[COMMANDMAXC];<br />   gets(Command);<br />   ParseCommand(Command);<br />  }<br /> }<br /> catch(Exception &amp;e)<br /> {<br />  printf(e.GetMessage());<br />  return 1;<br /> }<br /> return 0;<br />}<br /></p>
				<p>/* 异常类<br /> *<br /> * 文件名：Exception.h<br /> *<br /> * 日期：2004.5.5<br /> *<br /> * 作者：shootingstars(<a href="mailto:zhouhuis22@sina.com">zhouhuis22@sina.com</a>)<br /> */</p>
				<p>#ifndef __HZH_Exception__<br />#define __HZH_Exception__</p>
				<p>#define EXCEPTION_MESSAGE_MAXLEN 256<br />#include "string.h"</p>
				<p>class Exception<br />{<br />private:<br /> char m_ExceptionMessage[EXCEPTION_MESSAGE_MAXLEN];<br />public:<br /> Exception(char *msg)<br /> {<br />  strncpy(m_ExceptionMessage, msg, EXCEPTION_MESSAGE_MAXLEN);<br /> }</p>
				<p> char *GetMessage()<br /> {<br />  return m_ExceptionMessage;<br /> }<br />};</p>
				<p>#endif<br /></p>
				<p>/* P2P 程序传输协议<br /> * <br /> * 日期：2004-5-21<br /> *<br /> * 作者：shootingstars(<a href="mailto:zhouhuis22@sina.com">zhouhuis22@sina.com</a>)<br /> *<br /> */</p>
				<p>#pragma once<br />#include &lt;list&gt;</p>
				<p>// 定义iMessageType的值<br />#define LOGIN 1<br />#define LOGOUT 2<br />#define P2PTRANS 3<br />#define GETALLUSER  4</p>
				<p>// 服务器端口<br />#define SERVER_PORT 2280</p>
				<p>// Client登录时向服务器发送的消息<br />struct stLoginMessage<br />{<br /> char userName[10];<br /> char password[10];<br />};</p>
				<p>// Client注销时发送的消息<br />struct stLogoutMessage<br />{<br /> char userName[10];<br />};</p>
				<p>// Client向服务器请求另外一个Client(userName)向自己方向发送UDP打洞消息<br />struct stP2PTranslate<br />{<br /> char userName[10];<br />};</p>
				<p>// Client向服务器发送的消息格式<br />struct stMessage<br />{<br /> int iMessageType;<br /> union _message<br /> {<br />  stLoginMessage loginmember;<br />  stLogoutMessage logoutmember;<br />  stP2PTranslate translatemessage;<br /> }message;<br />};</p>
				<p>// 客户节点信息<br />struct stUserListNode<br />{<br /> char userName[10];<br /> unsigned int ip;<br /> unsigned short port;<br />};</p>
				<p>// Server向Client发送的消息<br />struct stServerToClient<br />{<br /> int iMessageType;<br /> union _message<br /> {<br />  stUserListNode user;<br /> }message;</p>
				<p>};</p>
				<p>//======================================<br />// 下面的协议用于客户端之间的通信<br />//======================================<br />#define P2PMESSAGE 100               // 发送消息<br />#define P2PMESSAGEACK 101            // 收到消息的应答<br />#define P2PSOMEONEWANTTOCALLYOU 102  // 服务器向客户端发送的消息<br />                                     // 希望此客户端发送一个UDP打洞包<br />#define P2PTRASH        103          // 客户端发送的打洞包，接收端应该忽略此消息</p>
				<p>// 客户端之间发送消息格式<br />struct stP2PMessage<br />{<br /> int iMessageType;<br /> int iStringLen;         // or IP address<br /> unsigned short Port; <br />};</p>
				<p>using namespace std;<br />typedef list&lt;stUserListNode *&gt; UserList;</p>
		</div>
<img src ="http://www.cppblog.com/shenhuafeng/aggbug/17215.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shenhuafeng/" target="_blank">CPP&&设计模式小屋</a> 2007-01-04 13:37 <a href="http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17215.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Peer-to-Peer (P2P) communication across middleboxes(转)</title><link>http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17214.html</link><dc:creator>CPP&amp;&amp;设计模式小屋</dc:creator><author>CPP&amp;&amp;设计模式小屋</author><pubDate>Thu, 04 Jan 2007 05:35:00 GMT</pubDate><guid>http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17214.html</guid><wfw:comment>http://www.cppblog.com/shenhuafeng/comments/17214.html</wfw:comment><comments>http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17214.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shenhuafeng/comments/commentRss/17214.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shenhuafeng/services/trackbacks/17214.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Internet Draft                                                   B. FordDocument: draft-ford-midcom-p2p-01.txt                            M.I.T.Expires: April 27, 2004                                 ...&nbsp;&nbsp;<a href='http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17214.html'>阅读全文</a><img src ="http://www.cppblog.com/shenhuafeng/aggbug/17214.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shenhuafeng/" target="_blank">CPP&&设计模式小屋</a> 2007-01-04 13:35 <a href="http://www.cppblog.com/shenhuafeng/archive/2007/01/04/17214.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>