﻿<?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++博客-时间的痕迹-文章分类-网络编程</title><link>http://www.cppblog.com/ivenher/category/1501.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 21:52:22 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 21:52:22 GMT</pubDate><ttl>60</ttl><item><title>CSocket  初始化</title><link>http://www.cppblog.com/ivenher/articles/19821.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Wed, 14 Mar 2007 08:10:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/19821.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/19821.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/19821.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/19821.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/19821.html</trackback:ping><description><![CDATA[如果在线程中使用socket，也要使用  AfxSocketInit(); 进行初始化。<img src ="http://www.cppblog.com/ivenher/aggbug/19821.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2007-03-14 16:10 <a href="http://www.cppblog.com/ivenher/articles/19821.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十九</title><link>http://www.cppblog.com/ivenher/articles/12529.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Fri, 15 Sep 2006 14:05:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12529.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12529.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12529.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12529.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12529.html</trackback:ping><description><![CDATA[
		<p>8.2.4 重叠模型<br />在Wi n s o c k中，相比我们迄今为止解释过的其他所有I / O模型，重叠I / O（Overlapped I/O）<br />模型使应用程序能达到更佳的系统性能。重叠模型的基本设计原理便是让应用程序使用一个重叠的数据结构，一次投递一个或多个Winsock I/O请求。针对那些提交的请求，在它们完成之后，应用程序可为它们提供服务。该模型适用于除Windows CE之外的各种Wi n d o w s平台。<br />模型的总体设计以Wi n 3 2重叠I / O机制为基础。那个机制可通过R e a d F i l e和Wr i t e F i l e两个函数，针对设备执行I / O操作。<br />最开始的时候，Wi n s o c k重叠I / O模型只能应用于Windows NT操作系统上运行的Wi n s o c k1 . 1应用程序。作为应用程序，它可针对一个套接字句柄，调用R e a d F i l e以及Wr i t e F i l e，同时指定一个重叠式结构（稍后详述），从而利用这个模型。自Winsock 2发布开始，重叠I / O便已集成到新的Wi n s o c k函数中，比如W S A S e n d和W S A R e c v。这样一来，重叠I / O模型便能适用于安装了Winsock 2的所有Wi n d o w s平台。</p>
		<p>注意在Winsock 2发布之后，重叠I/O仍可在Windows NT和Windows 2000这两个操作系统中，随R e a d F i l e和Wr i t e F i l e两个函数使用。但是，Windows 95和Windows 98均不具备这一功能。考虑到应用程序的跨平台兼容问题，同时考虑到性能方面的因素，应尽量避免使用Wi n 3 2的R e a d F i l e和Wr i t e F i l e函数，分别换以W S A R e c v和W S A S e n d函数。本节只打算讲述通过新的Winsock 2函数，来使用重叠I/O模型。<br />要想在一个套接字上使用重叠I / O模型，首先必须使用W S A _ F L A G _ O V E R L A P P E D这个标志，创建一个套接字。如下所示：</p>
		<p>  s = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);<br />  创建套接字的时候，假如使用的是s o c k e t函数，而非W S A S o c k e t函数，那么会默认设置W S A _ F L A G _ O V E R L A P P E D标志。成功建好一个套接字，同时将其与一个本地接口绑定到一起后，便可开始进行重叠I / O 操作，方法是调用下述的Wi n s o c k 函数，同时指定一个<br />W S A O V E R L A P P E D结构（可选）：<br />■ W S A S e n d<br />■ W S A S e n d To<br />■ W S A R e c v<br />■ W S A R e c v F r o m<br />■ W S A I o c t l<br />■ A c c e p t E x<br />■ Tr n a s m i t F i l e<br />大家现在或许已经知道，其中每个函数都与一个套接字上数据的发送、数据接收以及连接的接受有关。因此，这些活动可能会花极少的时间才能完成。这正是每个函数都可接受一个W S A O V E R L A P P E D结构作为参数的原因。若随一个W S A O V E R L A P P E D结构一起调用这些函数，函数会立即完成并返回，无论套接字是否设为锁定模式（本章开头已详细讲过了）。它<br />们依赖于W S A O V E R L A P P E D结构来返回一个I / O请求的返回。<br />主要有两个方法可用来管理一个重叠I / O请求的完成：我们的应用程序可等待“事件对象通知”，亦可通过“完成例程”，对已经完成的请求加以处理。上面列出的函数（ A c c e p t E x除外）还有另一个常用的参数：<br />l p C o m p l e t i o n R O U T I N E。该参数指定的是一个可选的指针，指向一个完成例程函数，在重叠请求完成后调用。接下去，我们将探讨事件通知方法。在本章稍后，还会介绍如何使用可选的完成例程，代替事件，对完成的重叠请求加以处理。<br />1. 事件通知<br />重叠I / O的事件通知方法要求将Wi n 3 2事件对象与W S A O V E R L A P P E D结构关联在一起。若使用一个W S A O V E R L A P P E D结构，发出像W S A S e n d和W S A R e c v这样的I / O调用，它们会立即返回。<br />通常，大家会发现这些I / O 调用会以失败告终，返回S O C K E T _ E R R O R 。使用W S A G e t L a s t E r r o r函数，便可获得与错误状态有关的一个报告。这个错误状态意味着I / O操作正在进行。稍后的某个时间，我们的应用程序需要等候与W S A O V E R L A P P E D结构对应的事件对象，了解一个重叠I / O请示何时完成。W S A O V E R L A P P E D结构在一个重叠I / O请求的初始化，及其后续的完成之间，提供了一种沟通或通信机制。下面是这个结构的定义：</p>
		<p> typedef struct WSAOVERLAPPED<br /> {<br />  DWORD Internal;<br />  DWORD InternalHigh;<br />  DWORD Offset;<br />  DWORD OffsetHigh;<br />  WSAEVENT hEvent;<br /> }WSAOVERLAPPED,FAR *LPWSAOVERLPPED;<br /> <br />其中，I n t e r n a l、I n t e r n a l H i g h、O ff s e t和O ff s e t H i g h字段均由系统在内部使用，不应由应用程序直接进行处理或使用。而另一方面， h E v e n t字段有点儿特殊，它允许应用程序将一个事件对象句柄同一个套接字关联起来。大家可能会觉得奇怪，如何将一个事件对象句柄分配给该字段呢？正如我们早先在W S A E v e n t S e l e c t模型中讲述的那样，可用W S A C r e a t e E v e n t函数来创建一个事件对象句柄。一旦创建好一个事件句柄，简单地将重叠结构的h E v e n t字段分配给事件句柄，再使用重叠结构，调用一个Wi n s o c k函数即可，比如W S A S e n d或W S A R e c v。<br />一个重叠I / O请求最终完成后，我们的应用程序要负责取回重叠I / O操作的结果。一个重叠请求操作最终完成之后，在事件通知方法中， Wi n s o c k会更改与一个W S A O V E R L A P P E D结构对应的一个事件对象的事件传信状态，将其从“未传信”变成“已传信”。由于一个事件对象已分配给W S A O V E R L A P P E D结构，所以只需简单地调用W S AWa i t F o r M u l t i p l e E v e n t s函数，从而判断出一个重叠I / O调用在什么时候完成。该函数已在我们前面讲述WSAEventSelect I/O模型时介绍过了。W S AWa i t F o r M u l t i p l e E v e n t s函数会等候一段规定的时间，等待一个或多个事件对象进入“已传信”状态。在此再次提醒大家注意： W S AWa i t F o r M u l t i p l e E v e n t s函数一次<br />最多只能等待6 4个事件对象。发现一次重叠请求完成之后，接着需要调用W S A G e t O v e r l a p p e dR e s u l t（取得重叠结构）函数，判断那个重叠调用到底是成功，还是失败。该函数的定义如下：<br /> BOOL WSAGetOverlappedResult(<br />                SOCKET s,<br />                LPWSAOVERLAPPED lpOverlapped,<br />                LPWORD lpcbTransfer,<br />                BOOL  fWait,<br />                LPWORD lpdwFlags<br />               );<br />其中， s参数用于指定在重叠操作开始的时候，与之对应的那个套接字。l p O v e r l a p p e d参数是一个指针，对应于在重叠操作开始时，指定的那个W S A O V E R L A P P E D 结构。<br />l p c b Tr a n s f e r参数也是一个指针，对应一个D W O R D（双字）变量，负责接收一次重叠发送或接收操作实际传输的字节数。f Wa i t参数用于决定函数是否应该等待一次待决（未决）的重叠操作完成。若将f Wa i t设为T R U E，那么除非操作完成，否则函数不会返回；若设为FA L S E，而且操作仍然处于“待决”状态，那么W S A G e t O v e r l a p p e d R e s u l t函数会返回FA L S E值，同时返回一个W S A _ I O _ I N C O M P L E T E（I / O操作未完成）错误。但就我们目前的情况来说，由于需要等候重叠操作的一个已传信事件完成，所以该参数无论采用什么设置，都没有任何效果。<br />最后一个参数是l p d w F l a g s，它对应于一个指针，指向一个D W O R D（双字），负责接收结果标志（假如原先的重叠调用是用W S A R e c v或W S A R e c v F r o m函数发出的）。<br />如W S A G e t O v e r l a p p e d R e s u l t函数调用成功，返回值就是T R U E。这意味着我们的重叠I / O操作已成功完成，而且由l p c b Tr a n s f e r参数指向的值已进行了更新。若返回值是FA L S E，那么可能是由下述任何一种原因造成的：<br />■ 重叠I / O操作仍处在“待决”状态。<br />■ 重叠操作已经完成，但含有错误。<br />■ 重叠操作的完成状态不可判决，因为在提供给W S A G e t O v e r l a p p e d R e s u l t函数的一个或<br />多个参数中，存在着错误。</p>
		<p>失败后，由l p c b Tr a n s f e r参数指向的值不会进行更新，而且我们的应用程序应调用W S A G e t L a s t E r r o r函数，调查到底是何种原因造成了调用失败。<br />在程序清单8 - 7中，我们向大家阐述了如何编制一个简单的服务器应用，令其在一个套接字上对重叠I / O操作进行管理，程序完全利用了前述的事件通知机制。对该程序采用的编程步骤总结如下：<br />1) 创建一个套接字，开始在指定的端口上监听连接请求。<br />2) 接受一个进入的连接请求。<br />3) 为接受的套接字新建一个W S A O V E R L A P P E D结构，并为该结构分配一个事件对象句柄。<br />也将事件对象句柄分配给一个事件数组，以便稍后由W S AWa i t F o r M u l t i p l e E v e n t s函数使用。<br />4) 在套接字上投递一个异步W S A R e c v请求，指定参数为W S A O V E R L A P P E D结构。<br />注意函数通常会以失败告终，返回S O C K E T _ E R R O R错误状态W S A _ I O _ P E N D I N G（I/O操作尚未完成）。<br />5) 使用步骤3 )的事件数组，调用W S AWa i t F o r M u l t i p l e E v e n t s函数，并等待与重叠调用关联在一起的事件进入“已传信”状态（换言之，等待那个事件的“触发”）。<br />6) WSAWa i t F o r M u l t i p l e E v e n t s函数完成后，针对事件数组，调用W S A R e s e t E v e n t（重设事件）函数，从而重设事件对象，并对完成的重叠请求进行处理。<br />7) 使用W S A G e t O v e r l a p p e d R e s u l t函数，判断重叠调用的返回状态是什么。<br />8) 在套接字上投递另一个重叠W S A R e c v请求。<br />9) 重复步骤5 ) ~ 8 )。<br />这个例子极易扩展，提供对多个套接字的支持。方法是将代码的重叠I / O处理部分移至一个独立的线程内，让主应用程序线程为附加的连接请求提供服务。<br />程序清单8-7 采用事件机制的简单重叠I / O处理示例</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12529.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-15 22:05 <a href="http://www.cppblog.com/ivenher/articles/12529.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十七</title><link>http://www.cppblog.com/ivenher/articles/12512.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Fri, 15 Sep 2006 07:43:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12512.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12512.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12512.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12512.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12512.html</trackback:ping><description><![CDATA[
		<p>8.2.3 WSAEventSelect<br />Wi n s o c k提供了另一个有用的异步I / O模型。和W S A A s y n c S e l e c t模型类似的是，它也允许应用程序在一个或多个套接字上，接收以事件为基础的网络事件通知。对于表8 - 3总结的、由W S A A s y n c S e l e c t模型采用的网络事件来说，它们均可原封不动地移植到新模型。在用新模型开发的应用程序中，也能接收和处理所有那些事件。该模型最主要的差别在于网络事件会投递至一个事件对象句柄，而非投递至一个窗口例程。<br />事件通知<br />事件通知模型要求我们的应用程序针对打算使用的每一个套接字，首先创建一个事件对象。创建方法是调用W S A C r e a t e E v e n t函数，它的定义如下：</p>
		<p>WSAEVENT WSACreateEvent(void);</p>
		<p>W S A C r e a t e E v e n t函数的返回值很简单，就是一个创建好的事件对象句柄。事件对象句柄到手后，接下来必须将其与某个套接字关联在一起，同时注册自己感兴趣的网络事件类型，如表8 - 3所示。要做到这一点，方法是调用W S A E v e n t S e l e c t函数，对它的定义如下：</p>
		<p>int WSAEventSelect(<br />          SOCKET s, <br />          WSAEVENT hEventObject,<br />          long lNetwordEvents<br />         );<br /> 其中， s参数代表自己感兴趣的套接字。h E v e n t O b j e c t参数指定要与套接字关联在一起的 事件对象—用W S A C r e a t e E v e n t取得的那一个。而最后一个参数l N e t w o r k E v e n t s，则对应一个“位掩码”，用于指定应用程序感兴趣的各种网络事件类型的一个组合（如表8 - 3所示）。要想获知对这些事件类型的详细说明，请参考早先讨论过的WSAAsyncSelect I/O模型。<br />为W S A E v e n t S e l e c t创建的事件拥有两种工作状态，以及两种工作模式。其中，两种工作状态分别是“已传信”（s i g n a l e d）和“未传信”（n o n s i g n a l e d）。工作模式则包括“人工重设”（manual reset）和“自动重设”（auto reset）。W S A C r e a t e E v e n t最开始在一种未传信的工作状态中，并用一种人工重设模式，来创建事件句柄。随着网络事件触发了与一个套接字关联在<br />一起的事件对象，工作状态便会从“未传信”转变成“已传信”。由于事件对象是在一种人工重设模式中创建的，所以在完成了一个I / O请求的处理之后，我们的应用程序需要负责将工作状态从已传信更改为未传信。要做到这一点，可调用W S A R e s e t E v e n t函数，对它的定义如下：</p>
		<p> BOOL  WSAResetEvent(WSAEVENT hEvent);<br /> <br /> 该函数唯一的参数便是一个事件句柄；基于调用是成功还是失败，会分别返回T R U E或FA L S E。应用程序完成了对一个事件对象的处理后，便应调用W S A C l o s e E v e n t函数，释放由事件句柄使用的系统资源。对W S A C l o s e E v e n t函数的定义如下：</p>
		<p> BOOL WSACloseEvent(WSAEVENT hEvent);<br /> <br /> 该函数也要拿一个事件句柄作为自己唯一的参数，并会在成功后返回T R U E，失败后返回FA L S E。<br />一个套接字同一个事件对象句柄关联在一起后，应用程序便可开始I / O处理；方法是等待网络事件触发事件对象句柄的工作状态。W S AWa i t F o r M u l t i p l e E v e n t s函数的设计宗旨便是用来等待一个或多个事件对象句柄，并在事先指定的一个或所有句柄进入“已传信”状态后，或在超过了一个规定的时间周期后，立即返回。下面是W S AWa i t F o r M u l t i p l e E v e n t s函数的定义：</p>
		<p>DWORD WSAWaitForMultipleEvents(<br />                DWORD cEvent,<br />                const WSAEVENT FAR * lphEvents,<br />                BOOL  fWaitAll,<br />                DWORD dwTimeout,<br />                BOOL  fAlertable<br />               );<br />               <br /> 其中， c E v e n t s和l p h E v e n t s参数定义了由W S A E V E N T对象构成的一个数组。在这个数组中，c E v e n t s指定的是事件对象的数量，而l p h E v e n t s对应的是一个指针，用于直接引用该数组。<br />要注意的是， W S AWa i t F o r M u l t i p l e E v e n t s只能支持由W S A _ M A X I M U M _ WA I T _ E V E N T S对象规定的一个最大值，在此定义成6 4个。因此，针对发出W S AWa i t F o r M u l t i p l e E v e n t s调用的每个线程，该I / O模型一次最多都只能支持6 4个套接字。假如想让这个模型同时管理不止6 4个套<br />接字，必须创建额外的工作者线程，以便等待更多的事件对象。f Wa i t A l l 参数指定了W S AWa i t F o r M u l t i p l e E v e n t s如何等待在事件数组中的对象。若设为T R U E，那么只有等l p h E v e n t s数组内包含的所有事件对象都已进入“已传信”状态，函数才会返回；但若设为FA L S E，任何一个事件对象进入“已传信”状态，函数就会返回。就后一种情况来说，返回值指出了到底是哪个事件对象造成了函数的返回。通常，应用程序应将该参数设为FA L S E，<br />一次只为一个套接字事件提供服务。d w Ti m e o u t参数规定了W S AWa i t F o r M u l t i p l e E v e n t s最多可等待一个网络事件发生有多长时间，以毫秒为单位，这是一项“超时”设定。超过规定的时间，函数就会立即返回，即使由f Wa i t A l l参数规定的条件尚未满足也如此。如超时值为0，函数会检测指定的事件对象的状态，并立即返回。这样一来，应用程序实际便可实现对事件对<br />象的“轮询”。但考虑到它对性能造成的影响，还是应尽量避免将超时值设为0。假如没有等待处理的事件， W S AWa i t F o r M u l t i p l e E v e n t s便会返回W S A _ WA I T _ T I M E O U T。如d w s Ti m e o u t设为W S A _ I N F I N I T E（永远等待），那么只有在一个网络事件传信了一个事件对象后，函数才<br />会返回。最后一个参数是f A l e r t a b l e，在我们使用W S A E v e n t S e l e c t模型的时候，它是可以忽略的，且应设为FA L S E。该参数主要用于在重叠式I / O模型中，在完成例程的处理过程中使用。<br />本章后面还会对此详述。</p>
		<p>若W S AWa i t F o r M u l t i p l e E v e n t s收到一个事件对象的网络事件通知，便会返回一个值，指出造成函数返回的事件对象。这样一来，我们的应用程序便可引用事件数组中已传信的事件，并检索与那个事件对应的套接字，判断到底是在哪个套接字上，发生了什么网络事件类型。<br />对事件数组中的事件进行引用时，应该用W S AWa i t F o r M u l t i p l e E v e n t s的返回值，减去预定义值W S A _ WA I T _ E V E N T _ 0，得到具体的引用值（即索引位置）。如下例所示：</p>
		<p>Index = WSAWaitForMultipleEvents(...);<br />MyEvent = EventArray[Index - WSA_WAIT_EVENT_0];<br />知道了造成网络事件的套接字后，接下来可调用W S A E n u m N e t w o r k E v e n t s函数，调查发生了什么类型的网络事件。该函数定义如下：</p>
		<p> int WSAEnumNetworkEvents(<br />              SOCKET s,<br />              WSAEVENT hEventObject,<br />              LPWSANETWORKEVENTS lpNetworkEvents<br />             );<br /> s参数对应于造成了网络事件的套接字。h E v e n t O b j e c t参数则是可选的；它指定了一个事件句柄，对应于打算重设的那个事件对象。由于我们的事件对象处在一个“已传信”状态，所以可将它传入，令其自动成为“未传信”状态。如果不想用h E v e n t O b j e c t参数来重设事件，那么可使用W S A R e s e t E v e n t 函数， 该函数早先已经讨论过了。最后一个参数是l p N e t w o r k E v e n t s，代表一个指针，指向W S A N E T W O R K E V E N T S结构，用于接收套接字上发<br />生的网络事件类型以及可能出现的任何错误代码。下面是W S A N E T W O R K E V E N T S结构的定义：</p>
		<p> typedef struct _WSANETWORKEVENTS<br /> {<br />  long lNetworkEvents;<br />  int iErrorCode[FD_MAX_EVENTS];<br /> }WSANETWORKEVENTS,FAR * LPWSANETWORKEVENTS;<br /> <br /> l N e t w o r k E v e n t s参数指定了一个值，对应于套接字上发生的所有网络事件类型（参见表8 - 3）。<br />注意一个事件进入传信状态时，可能会同时发生多个网络事件类型。例如，一个繁忙<br />的服务器应用可能同时收到FD_READ和FD_WRITE通知。<br />i E r r o r C o d e参数指定的是一个错误代码数组，同l N e t w o r k E v e n t s中的事件关联在一起。针对每个网络事件类型，都存在着一个特殊的事件索引，名字与事件类型的名字类似，只是要在事件名字后面添加一个“ _ B I T”后缀字串即可。例如，对F D _ R E A D事件类型来说，i E r r o r C o d e数组的索引标识符便是F D _ R E A D _ B I T。下述代码片断对此进行了阐释（针对F D _ R E A D事件）：</p>
		<p>  if(NetWorkEvents.lNetworkEvents &amp; FD_READ)<br />  {<br />   if(NetWorkEvents.iErrorCode[FD_READ_BIT] != 0)<br />   {<br />    printf("FD_READ FAILED with error %d\n",NetWorkEvents.iErrorCode[FD_READ_BIT]);<br />   }<br />  }</p>
		<p>完成了对W S A N E T W O R K E V E N T S结构中的事件的处理之后，我们的应用程序应在所有可用的套接字上，继续等待更多的网络事件。在程序清单8 - 6中，我们阐释了如何使用W S A E v e n t S e l e c t这种I / O模型，来开发一个服务器应用，同时对事件对象进行管理。这个程序主要着眼于开发一个基本的服务器应用要涉及到的步骤，令其同时负责一个或多个套接字的管理。</p>
		<p>完成了对W S A N E T W O R K E V E N T S结构中的事件的处理之后，我们的应用程序应在所有可用的套接字上，继续等待更多的网络事件。在程序清单8 - 6中，我们阐释了如何使用W S A E v e n t S e l e c t这种I / O模型，来开发一个服务器应用，同时对事件对象进行管理。这个程序主要着眼于开发一个基本的服务器应用要涉及到的步骤，令其同时负责一个或多个套接字的管理。<br />程序清单8-6 采用WSAEventSelect I/O模型的示范服务器源代码</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12512.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-15 15:43 <a href="http://www.cppblog.com/ivenher/articles/12512.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十八</title><link>http://www.cppblog.com/ivenher/articles/12419.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Wed, 13 Sep 2006 09:35:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12419.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12419.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12419.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12419.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12419.html</trackback:ping><description><![CDATA[1、WSAStartup函数<br />用于初始化Winsock<br />[声明]<br />int WSAStarup(WORD wVersionRequested,LPWSADATA lpWSAData);<br />[参数]<br />wVersionRequested - 要求使用Winsock的最低版本号<br />lpWSAData - Winsock的详细资料<br />[返回值]<br />当函数成功调用时返回0<br />失败时返回非0的值 
<p>2、socket函数<br />用于生成socket(soket Descriptor)<br />[声明]<br />SOCKET socket（int af,int type,int protocol）;<br />[参数]<br />af - 地址家族(通常使用:AF_INET)<br />type - socket的种类<br />SOCK_STREAM : 用于TCP协议<br />SOCK_DGRAM : 用于UDP协议<br />protocol - 所使用的协议<br />[返回值]<br />当函数成功调用时返回一个新的SOCKET(Socket Descriptor)<br />失败时返回INVALID_SOCKET. </p><p>3、inet_addr函数<br />把好象"xxx.xxx.xxx.xxx"的10进制的IP地址转换为32位整数表示方法<br />[声明]<br />unsigned long inet_addr ( const char FAR *cp );<br />[参数]<br />cp - 指向用"xxx.xxx.xxx.xxx"的10进制来表示的IP地址字符串的指针<br />[返回值]<br />当函数成功调用时返回用32位整数表示的IP地址(按网络字节排列顺序)<br />失败时返回INADDR_NONE. </p><p>4、gethostbyname函数<br />可以从主机名获取主机资料.<br />[声明]<br />struct hostent FAR * gethostbyname ( const char FAR *name );<br />[参数]<br />name - 指向主机名字符串的指针<br />[返回值]<br />当函数成功调用时返回主机信息<br />失败时返回NULL(空值) </p><p>5、Bind函数<br />指定本地IP地址所使用的端口号时候使用<br />[声明]<br />int bind ( SOCKET s , const struct sockaddr FAR *addr , int namelen );<br />[参数]<br />s - 指向用Socket函数生成的Socket Descriptor<br />addr - 指向Socket地址的指针<br />namelen - 该地址的长度.<br />[返回值]<br />当函数成功调用时返回0<br />调用失败时返回 SOCKET_ERROR </p><p>6、connect函数<br />用于与服务器建立连接,发出连接请求,必须在参数中指定服务器的IP地址和端口号<br />[声明]<br />int connect (SOCKET s , const struct sockaddr FAR *name , int namelen );<br />[参数]<br />s - 指向用Socket函数生成的Socket Descriptor<br />name - 指向服务器地址的指针<br />namelen - 该地址的长度.<br />[返回值]<br />当函数成功调用时返回0<br />调用失败时返回 SOCKET_ERROR </p><p>7、select函数<br />可以用于调查一个或多个SOCKET的状态.<br />[声明]<br />int select ( int nfds , fd_set FAR *readfds , fd_set FAR *writefds , fd_set FAR *exceptfds , const struct timeval FAR *timeout );<br />[参数]<br />nfds - 在WINDOWS SOCKET API 中该参数可以忽略,通常赋予NILL值<br />readfds - 由于接受的SOCKET设备的指针<br />writefds - 用于发送数据的SOCKET设备的指针<br />exceptfds - 检查错误的状态<br />timeout - 超时设定<br />[返回值]<br />返回大于0的值时,表示与条件相符的SOCKET数<br />返回0表示超时<br />失败时返回SOCKET_ERROR </p><p>8、recv函数<br />利用Socket进行接受数据.<br />[声明]<br />int recv ( SOCKET s , char FAR *buf , int len , int flags );<br />[参数]<br />s - 指向用Socket函数生成的Socket Descriptor<br />buf - 接受数据的缓冲区(数组)的指针<br />len - 缓冲区的大小<br />flag - 调用方式(MSG_PEEK 或 MSG_OOB) <br />[返回值]<br />成功时返回收到的字节数.<br />如果连接被中断则返回0<br />失败时返回 SOCKET_ERROR </p><p>9、sendto函数<br />利用Socket进行发送数据.<br />[声明]<br />int sendto ( SOCKET s , const char FAR *buf , int len , int flags , const struct sockaddr FAR *to , int token );<br />[参数]<br />s - 指向用Socket函数生成的Socket Descriptor<br />buf - 接受数据的缓冲区(数组)的指针<br />len - 缓冲区的大小<br />flag - 调用方式(MSG_DONTROUTE , MSG_OOB)<br />to - 指向发送方SOCKET地址的指针<br />token - 发送方SOCKET地址的大小 <br />[返回值]<br />成功时返回已经发送的字节数.<br />失败时返回SOCKET_ERROR </p><img src ="http://www.cppblog.com/ivenher/aggbug/12419.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-13 17:35 <a href="http://www.cppblog.com/ivenher/articles/12419.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十六</title><link>http://www.cppblog.com/ivenher/articles/12375.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Tue, 12 Sep 2006 08:52:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12375.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12375.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12375.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12375.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12375.html</trackback:ping><description><![CDATA[
		<p>8.2.2 WSAAsyncSelect<br />Wi n s o c k提供了一个有用的异步I / O模型。利用这个模型，应用程序可在一个套接字上，接收以Wi n d o w s消息为基础的网络事件通知。具体的做法是在建好一个套接字后，调用W S A A s y n c S e l e c t函数。该模型最早出现于Wi n s o c k的1 . 1版本中，用于帮助应用程序开发者面向一些早期的1 6位Wi n d o w s平台（如Windows for Wo r k g r o u p s），适应其“落后”的多任务消息环境。应用程序仍可从这种模型中得到好处，特别是它们用一个标准的Wi n d o w s例程（常<br />称为“ w i n p r o c”），对窗口消息进行管理的时候。该模型亦得到了Microsoft Foundation Class<br />（微软基本类，M F C）对象C S o c k e t的采纳。<br />消息通知<br />要想使用W S A A s y n c S e l e c t模型，在应用程序中，首先必须用C r e a t e Wi n d o w函数创建一个窗口，再为该窗口提供一个窗口例程支持函数（ Wi n p r o c）。亦可使用一个对话框，为其提供一个对话例程，而非窗口例程，因为对话框本质也是“窗口”。考虑到我们的目的，我们打算用一个简单的窗口来演示这种模型，采用的是一个支持窗口例程。设置好窗口的框架后，便可开始创建套接字，并调用W S A A s y n c S e l e c t函数，打开窗口消息通知。该函数的定义如下：</p>
		<p>int WSAAsyncSelect(<br />          SOCKET s,<br />          HWND hWnd,<br />          unsigned  int wMsg,<br />          long lEvent<br />         );</p>
		<p>其中， s参数指定的是我们感兴趣的那个套接字。h W n d参数指定的是一个窗口句柄，它对应于网络事件发生之后，想要收到通知消息的那个窗口或对话框。w M s g参数指定在发生网络事件时，打算接收的消息。该消息会投递到由h W n d窗口句柄指定的那个窗口。通常，应用程序需要将这个消息设为比Wi n d o w s的W M _ U S E R大的一个值，避免网络窗口消息与预定义的标准窗口消息发生混淆与冲突。最后一个参数是l E v e n t，它指定的是一个位掩码，对应于一<br />系列网络事件的组合（请参考表8 - 3），应用程序感兴趣的便是这一系列事件。大多数应用程序通常感兴趣的网络事件类型包括： F D _ R E A D、F D _ W R I T E、F D _ A C C E P T、F D _ C O N N E C T和<br />F D _ C L O S E。当然，到底使用F D _ A C C E P T，还是使用F D _ C O N N E C T类型，要取决于应用程序的身份到底是一个客户机呢，还是一个服务器。如应用程序同时对多个网络事件有兴趣，只需对各种类型执行一次简单的按位O R（或）运算，然后将它们分配给l E v e n t就可以了。举个例子来说：</p>
		<p>WSAAsyncSelect(s,hWnd,WM_SOCKET,FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE);</p>
		<p>这样一来，我们的应用程序以后便可在套接字s上，接收到有关连接、发送、接收以及套<br />接字关闭这一系列网络事件的通知。特别要注意的是，多个事件务必在套接字上一次注册！<br />另外还要注意的是，一旦在某个套接字上允许了事件通知，那么以后除非明确调用c l o s e s o c k e t命令，或者由应用程序针对那个套接字调用了W S A A s y n c S e l e c t，从而更改了注册的网络事件类型，否则的话，事件通知会永远有效！若将l E v e n t参数设为0，效果相当于停止在套接字上进行的所有网络事件通知。</p>
		<p>若应用程序针对一个套接字调用了W S A A s y n c S e l e c t，那么套接字的模式会从“锁定”自动变成“非锁定”，我们在前面已提到过这一点。这样一来，假如调用了像W S A R e c v这样的Wi n s o c k  I / O函数，但当时却并没有数据可用，那么必然会造成调用的失败，并返回W S A E W O U L D B L O C K<br />错误。为防止这一点，应用程序应依赖于由W S A A s y n c S e l e c t的u M s g参数指定的用户自定义窗口消息，来判断网络事件类型何时在套接字上发生；而不应盲目地进行调用。</p>
		<p>表8-3 用于W S A A s y n c S e l e c t函数的网络事件类型</p>
		<p>F D _ R E A D             应用程序想要接收有关是否可读的通知，以便读入数据<br />F D _ W R I T E          应用程序想要接收有关是否可写的通知，以便写入数据<br />F D _ O O B                应用程序想接收是否有带外（ O O B）数据抵达的通知<br />F D _ A C C E P T       应用程序想接收与进入连接有关的通知<br />F D _ C O N N E C T    应用程序想接收与一次连接或者多点j o i n操作完成的通知<br />F D _ C L O S E             应用程序想接收与套接字关闭有关的通知<br />F D _ Q O S            应用程序想接收套接字“服务质量”（Q o S）发生更改的通知<br />F D _ G R O U P _ Q O S             应用程序想接收套接字组“服务质量”发生更改的通知（现在没什么用处，为未来套接字组的使用保留）<br />F D _ R O U T I N G _ I N T E R FA C E _ C H A N G E       应用程序想接收在指定的方向上，与路由接口发生变化的通知<br />F D _ A D D R E S S _ L I S T _ C H A N G E       应用程序想接收针对套接字的协议家族，本地地址列表发生变化的通知</p>
		<p>应用程序在一个套接字上成功调用了W S A A s y n c S e l e c t之后，应用程序会在与h W n d窗口句柄参数对应的窗口例程中，以Windows消息的形式，接收网络事件通知。窗口例程通常定义如下：</p>
		<p>LRESULT CALLBACK WindProc(<br />             HWND hWnd,<br />             UINT uMsg,<br />             WPARAM wParam,<br />             LPARAM lParam<br />             );<br />其中，h W n d参数指定一个窗口的句柄，对窗口例程的调用正是由那个窗口发出的。u M s g参数指定需要对哪些消息进行处理。就我们的情况来说，感兴趣的是W S A A s y n c S e l e c t调用中定义的消息。w P a r a m参数指定在其上面发生了一个网络事件的套接字。假若同时为这个窗口例程分配了多个套接字，这个参数的重要性便显示出来了。在l P a r a m参数中，包含了两方面重要的信息。其中， l P a r a m的低字（低位字）指定了已经发生的网络事件，而l P a r a m的高字<br />（高位字）包含了可能出现的任何错误代码。<br />网络事件消息抵达一个窗口例程后，应用程序首先应检查l P a r a m的高字位，以判断是否在套接字上发生了一个网络错误。这里有一个特殊的宏： W S A G E T S E L E C T E R R O R，可用它返回高字位包含的错误信息。若应用程序发现套接字上没有产生任何错误，接着便应调查到底是哪个网络事件类型，造成了这条Wi n d o w s消息的触发—具体的做法便是读取l P a r a m之低字位的内容。此时可使用另一个特殊的宏：W S A G E T S E L E C T E V E N T，用它返回l P a r a m的低字部分。<br />在程序清单8 - 5中，我们向大家演示了如何使用W S A A s y n c S e l e c t这种I / O模型，来实现窗口消息的管理。在源程序中，我们着重强调的是开发一个基本服务器应用要涉及到的基本步骤，忽略了开发一个完整的Wi n d o w s应用需要涉及到的大量编程细节。</p>
		<p>程序清单8-5 WSAAsyncSelect服务器示范代码</p>
		<p>#define WM_SOCKET  WM_USER+1<br />#inlude &lt;windows.h&gt;</p>
		<p>int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR  lpCmdline,int nCmdShow)<br />{<br /> SOCKET listen;<br /> HWND window;<br /> //create a window  and assign the serverWinProc blew to it<br /> window = CreateWindow();<br /> //start winsock and create a socket <br /> <br /> WSAStarup(...);<br /> listen = Socket();<br /> //Bind the socket to port 5150<br /> // and begin listening for connection<br /> <br /> InternetAddr.sin_family = AF_INET;<br /> InternetAddr.sin_addr.s_addr=htonl(INADDR_ANY);<br /> InternetAddr.sin_port = htons(5150);<br /> bind(listen,(PSOCKETADDR)&amp;InternetAddr,sizeof(InternetAddr));<br /> <br /> //set up window message notification on the new socket using the WM_SOCKET define above<br /> WSAAsyncSelect(listen,window,WM_SOCKET,FD_ACCEPT|FD_CLOSE);<br /> <br /> listen(listen,5);<br /> <br /> //translate and dispatch window messages<br /> //until the appliation terminates<br />}             </p>
		<p>
				<br />BOOL CALLBACK ServerWinProc(HWND hDlg,WORD wMsg,WORD wParam,WORD lParam)<br />{<br /> SOCKET accept;<br /> switch(wMsg)<br /> {<br />  case WM_PAINT:<br />     break;<br />  case WM_SOCKET:<br />     //determine whether an error occured on the socket <br />     //by using the WSAGETSELECTERROR() macro<br />     <br />     if(WSAGETSELECTERROR(lWparam))<br />     {<br />      //display the error and close the socket <br />      closesocket(wParam);<br />      break;<br />     }<br />     //determine what event occured on the socket <br />     <br />     switch(WSAGETSELECTEVENT(lParam)<br />     {<br />      case FD_ACCEPT:<br />         //ACCEPT an incoming connection<br />         Accept = accept(wParam,NULL,NULL);<br />         <br />         //prepare accepted socket for read<br />         //write,and close notifation<br />         <br />         WSAAsyncSelect(Accept,hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);<br />      case FD_READ:<br />         //RECEIVE data from the socket in wParam<br />         break;<br />      case FD_WRITE:<br />         //THE socket in wParam is ready for sending data<br />         <br />         break;<br />      case FD_CLOSE:<br />         //THE connection is now closed<br />         closesocket(wParam);<br />         break;<br />         <br />     }<br />     break;<br /> }<br /> return TRUE;<br />}</p>
		<p>最后一个特别有价值的问题是应用程序如何对F D _ W R I T E事件通知进行处理。只有在三<br />种条件下，才会发出F D _ W R I T E通知：<br />■ 使用c o n n e c t或W S A C o n n e c t，一个套接字首次建立了连接。<br />■ 使用a c c e p t或W S A A c c e p t，套接字被接受以后。<br />■ 若s e n d、W S A S e n d、s e n d t o或W S A S e n d To操作失败，返回了W S A E W O U L D B L O C K错<br />误，而且缓冲区的空间变得可用因此，作为一个应用程序，自收到首条F D _ W R I T E消息开始，便应认为自己必然能在一个套接字上发出数据，直至一个s e n d、W S A S e n d、s e n d t o或W S A S e n d To返回套接字错误W S A E W O U L D B L O C K。经过了这样的失败以后，要再用另一条F D _ W R I T E通知应用程序再次发送数据。<br />            </p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12375.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-12 16:52 <a href="http://www.cppblog.com/ivenher/articles/12375.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十五</title><link>http://www.cppblog.com/ivenher/articles/12368.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Tue, 12 Sep 2006 06:41:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12368.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12368.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12368.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12368.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12368.html</trackback:ping><description><![CDATA[
		<p>8.2 套接字I/O模型<br />共有五种类型的套接字I / O模型，可让Wi n s o c k应用程序对I / O进行管理，它们包括： s e l e c t（选择）、W S A A s y n c S e l e c t（异步选择）、W S A E v e n t S e l e c t（事件选择）、o v e r l a p p e d（重叠）以及completion port（完成端口）<br />8.2.1 select模型<br />s e l e c t（选择）模型是Wi n s o c k中最常见的I / O模型。之所以称其为“ s e l e c t模型”，是由于它的“中心思想”便是利用s e l e c t函数，实现对I / O的管理！最初设计该模型时，主要面向的是某些使用U n i x操作系统的计算机，它们采用的是B e r k e l e y套接字方案。s e l e c t模型已集成到Winsock 1.1中，它使那些想避免在套接字调用过程中被无辜“锁定”的应用程序，采取一种有序的方式，同时进行对多个套接字的管理。由于Winsock 1.1向后兼容于B e r k e l e y套接字实<br />施方案，所以假如有一个B e r k e l e y套接字应用使用了s e l e c t函数，那么从理论角度讲，毋需对其进行任何修改，便可正常运行。<br />利用s e l e c t函数，我们判断套接字上是否存在数据，或者能否向一个套接字写入数据。之所以要设计这个函数，唯一的目的便是防止应用程序在套接字处于锁定模式中时，在一次I / O绑定调用（如s e n d或r e c v）过程中，被迫进入“锁定”状态；同时防止在套接字处于非锁定模式中时，产生W S A E W O U L D B L O C K错误。除非满足事先用参数规定的条件，否则s e l e c t函数会在进行I / O操作时锁定。s e l e c t的函数原型如下：</p>
		<p>int select(<br />      int nfds,<br />      fd_set FAR * readfds,<br />      fd_set FAR * writefds,<br />      fd_set FAR * exceptfds,<br />      const struct timeval FAR * timeout<br />     );<br />     <br />其中，第一个参数n f d s会被忽略。之所以仍然要提供这个参数，只是为了保持与早期的B e r k e l e y套接字应用程序的兼容。大家可注意到三个f d _ s e t参数：一个用于检查可读性（r e a d f d s），一个用于检查可写性（w r i t e f d s），另一个用于例外数据（e x c e p t f d s）。从根本上说,f d _ s e t数据类型代表着一系列特定套接字的集合。其中， r e a d f d s集合包括符合下述任何一个条件的套接字：<br />■ 有数据可以读入。<br />■ 连接已经关闭、重设或中止。<br />■ 假如已调用了l i s t e n，而且一个连接正在建立，那么a c c e p t函数调用会成功。<br />w r i t e f d s集合包括符合下述任何一个条件的套接字：<br />■ 有数据可以发出。<br />■ 如果已完成了对一个非锁定连接调用的处理，连接就会成功。<br />最后，e x c e p t f d s集合包括符合下述任何一个条件的套接字：<br />■ 假如已完成了对一个非锁定连接调用的处理，连接尝试就会失败。<br />■ 有带外（O u t - o f - b a n d，O O B）数据可供读取。<br />例如，假定我们想测试一个套接字是否“可读”，必须将自己的套接字增添到r e a d f d s集合，<br />再等待s e l e c t函数完成。s e l e c t完成之后，必须判断自己的套接字是否仍为r e a d f d s集合的一部分。若答案是肯定的，便表明该套接字“可读”，可立即着手从它上面读取数据。在三个参数中（r e a d f d s、w r i t e f d s和e x c e p t f d s），任何两个都可以是空值（ N U L L）；但是，至少有一个不能为空值！在任何不为空的集合中，必须包含至少一个套接字句柄；否则， s e l e c t函数便没有<br />任何东西可以等待。最后一个参数t i m e o u t对应的是一个指针，它指向一个t i m e v a l结构，用于决定s e l e c t最多等待I / O操作完成多久的时间。如t i m e o u t是一个空指针，那么s e l e c t调用会无限期地“锁定”或停顿下去，直到至少有一个描述符符合指定的条件后结束。对t i m e v a l结构的<br />定义如下：<br />struct timeval<br />{<br /> long tv_sec;<br /> long tv_usec;<br />};</p>
		<p>其中，t v _ s e c字段以秒为单位指定等待时间； t v _ u s e c字段则以毫秒为单位指定等待时间。<br />若将超时值设置为（ 0 , 0），表明s e l e c t会立即返回，允许应用程序对s e l e c t操作进行“轮询”。<br />出于对性能方面的考虑，应避免这样的设置。s e l e c t成功完成后，会在f d _ s e t结构中，返回刚好有未完成的I / O操作的所有套接字句柄的总量。若超过t i m e v a l设定的时间，便会返回0。不管由于什么原因，假如s e l e c t调用失败，都会返回S O C K E T _ E R R O R。<br />用s e l e c t对套接字进行监视之前，在自己的应用程序中，必须将套接字句柄分配给一个集合，设置好一个或全部读、写以及例外f d _ s e t结构。将一个套接字分配给任何一个集合后，再来调用s e l e c t，便可知道一个套接字上是否正在发生上述的I / O活动。Wi n s o c k提供了下列宏操作，可用来针对I / O活动，对f d _ s e t进行处理与检查：<br />■ FD_CLR(s, *set)：从s e t中删除套接字s。<br />■ FD_ISSET(s, *set)：检查s是否s e t集合的一名成员；如答案是肯定的是，则返回T R U E。<br />■ FD_SET(s, *set)：将套接字s加入集合s e t。<br />■ F D _ Z E R O ( * s e t )：将s e t初始化成空集合。</p>
		<p>例如，假定我们想知道是否可从一个套接字中安全地读取数据，同时不会陷于无休止的<br />“锁定”状态，便可使用F D _ S E T宏，将自己的套接字分配给f d _ r e a d集合，再来调用s e l e c t。要想检测自己的套接字是否仍属f d _ r e a d集合的一部分，可使用F D _ I S S E T宏。采用下述步骤，便可完成用s e l e c t操作一个或多个套接字句柄的全过程：<br />1) 使用F D _ Z E R O宏，初始化自己感兴趣的每一个f d _ s e t。<br />2) 使用F D _ S E T宏，将套接字句柄分配给自己感兴趣的每个f d _ s e t。<br />3) 调用s e l e c t函数，然后等待在指定的f d _ s e t集合中，I / O活动设置好一个或多个套接字句柄。<br />s e l e c t完成后，会返回在所有f d _ s e t集合中设置的套接字句柄总数，并对每个集合进行相应的更新。<br />4) 根据s e l e c t的返回值，我们的应用程序便可判断出哪些套接字存在着尚未完成（待决）的I / O操作—具体的方法是使用F D _ I S S E T宏，对每个f d _ s e t集合进行检查。<br />5) 知道了每个集合中“待决”的I / O操作之后，对I / O进行处理，然后返回步骤1 )，继续进行s e l e c t处理。<br />s e l e c t返回后，它会修改每个f d _ s e t结构，删除那些不存在待决I / O操作的套接字句柄。这正是我们在上述的步骤( 4 )中，为何要使用F D _ I S S E T宏来判断一个特定的套接字是否仍在集合中的原因。在程序清单8 - 4中，我们向大家阐述了为一个（只有一个）套接字设置s e l e c t模型所需的一系列基本步骤。若想在这个应用程序中添加更多的套接字，只需为额外的套接字维护它们的一个列表，或维护它们的一个数组即可。</p>
		<p>程序清单8-4 用s e l e c t管理一个套接字上的I / O操作</p>
		<p>SOCKET s;<br />fd_set fdread;<br />int ret;<br />//create a socket ,and accept a connection<br />...<br />//manage i/o on the socket</p>
		<p>while(TRUE)<br />{<br /> //aways clear the read set before calling <br /> //select()<br /> FD_ZERO(&amp;fdread);<br /> //add socket s to the read set<br /> FD_SET(s,&amp;fdread);<br /> if((ret = select(0,&amp;fdread,NULL,NULL,NULL))== SOCKET_ERROR)<br /> {<br />  //ERROR CONDITION<br /> }<br /> if(ret &gt; 0)<br /> <br /> {<br />  //for this simple case,select() should return the value 1.<br />  //an appliation dealing with more than one socket could get a value greater than 1;<br />  //at this point,your appliation should check to see whether the socket is part of a set.<br />  if(FD_ISSET(s.&amp;fdread))<br />  {<br />   //a read event has occurred on a socket s<br />  }<br /> }</p>
		<p>}<br />      </p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-12 14:41 <a href="http://www.cppblog.com/ivenher/articles/12368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十四</title><link>http://www.cppblog.com/ivenher/articles/12330.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Tue, 12 Sep 2006 03:10:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12330.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12330.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12330.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12330.html</trackback:ping><description><![CDATA[
		<p>8.1.2 非锁定模式<br />除了锁定模式，我们还可考虑采用非锁定模式的套接字。尽管这种套接字在使用上存在着些许难度，但只要排除了这项困难，它在功能上还是非常强大的。除具备锁定套接字已有的各项优点之外，还进行了少许扩充，功能更强。程序清单8 - 3向大家展示了如何创建一个套接字，并将其置为非锁定模式。<br />程序清单8-3 设置一个非锁定套接字</p>
		<p>SOCKET s;<br />unsigned long u1 = 1;<br />int nRet;<br />s = SOCKET(AF_INET,SOCK_STREA,0);<br />nRet = ioctlsocket(s,FIOBIO,(unsigned long *)&amp;ul);<br />if(nRet == SOCKET_ERROR)<br />{<br /> //Failed to put the socket into nonblocking mode<br />}</p>
		<p>将一个套接字置为非锁定模式之后， Winsock API调用会立即返回。大多数情况下，这些调用都会“失败”，并返回一个W S A E W O U L D B L O C K错误。什么意思呢？它意味着请求的操作在调用期间没有时间完成。举个例子来说，假如在系统的输入缓冲区中，尚不存在“待决”的数据，那么r e c v（接收数据）调用就会返回W S A E W O U L D B L O C K错误。通常，我们需要重复调用同一个函数，直至获得一个成功返回代码。在表8 - 2中，我们对常见Wi n s o c k调用返<br />回的W S A E W O U L D B L O C K错误的含义进行了总结。<br />由于非锁定调用会频繁返回W S A E W O U L D B L O C K错误，所以在任何时候，都应仔细检查所有返回代码，并作好“失败”的准备。许多程序员易犯的一个错误便是连续不停地调用一个函数，直到它返回成功的消息为止。例如，假定在一个紧凑的循环中不断地调用r e c v，以读入2 0 0个字节的数据，那么与使用前述的M S G _ P E E K标志来“轮询”一个锁定套接字相比，<br />前一种做法根本没有任何优势可言。为此， Wi n s o c k的套接字I / O模型可帮助应用程序判断一个套接字何时可供读写。<br />锁定和非锁定套接字模式都存在着优点和缺点。其中，从概念的角度说，锁定套接字更易使用。但在应付建立连接的多个套接字时，或在数据的收发量不均，时间不定时，却显得极难管理。而另一方面，假如需要编写更多的代码，以便在每个Wi n s o c k调用中，对收到一个W S A E W O U L D B L O C K错误的可能性加以应付，那么非锁定套接字便显得有些难于操作。在这些情况下，可考虑使用“套接字I / O模型”，它有助于应用程序通过一种异步方式，同时对一个或多个套接字上进行的通信加以管理。<br />表8-2 非锁定套接字上的W S A E W O U L D B L O C K错误函数名说明<br /><br />W S A A c c e p t和a c c e p t   应用程序没有收到连接请求。再次调用，便可检查连接情况<br />c l o s e s o c k e t          大多数情况下，这个错误意味着已随S O _ L I N G E R选项一道，调用了s e t s o c k o p t，而且已设定了一个非零的超时值<br />W S A C o n n e c t和c o n n e c t       应用程序已初始化。再次调用，便可检查是否完成<br />W S A R e c v、r e c v、W S A R e c v F r o m和r e c v f r o m 没有收到数据。稍后再次检查<br />W S A S e n d、s e n d、W S A S e n d To和s e n d t o 外出数据无缓冲区可用。稍后再试</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-12 11:10 <a href="http://www.cppblog.com/ivenher/articles/12330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十三</title><link>http://www.cppblog.com/ivenher/articles/12273.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Mon, 11 Sep 2006 09:48:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12273.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12273.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12273.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12273.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12273.html</trackback:ping><description><![CDATA[
		<p>第8章Winsock I/O方法<br />本章重点是如何在Wi n d o w s套接字应用程序中对I / O（输入／输出）操作进行管理。<br />Wi n s o c k分别提供了“套接字模式”和“套接字I / O模型”，可对一个套接字上的I / O行为加以控制。其中，套接字模式用于决定在随一个套接字调用时，那些Wi n s o c k函数的行为。而另一方面，套接字模型描述了一个应用程序如何对套接字上进行的I / O进行管理及处理。要注意的是，“套接字I / O模型”与“套接字模式”是无关的。套接字模型的出现，正是为了解决套接字模式存在的某些限制。</p>
		<p>Wi n s o c k提供了两种套接字模式：锁定和非锁定。本章第一部分将详细介绍这两种模式，并阐释一个应用程序如何通过它们管理I / O。如大家在本章的后面部分所见，Wi n s o c k提供了一些有趣的I / O模型，有助于应用程序通过一种“异步”方式，一次对一个或多个套接字上进行的通信加以管理。这些模型包括s e l e c t（选择）、W S A A s y n c S e l e c t（异步选择）、W S A E v e n t S e l e c t<br />（事件选择）、Overlapped I/O（重叠式I / O）以及Completion port（完成端口）等等。到本章结束时，我们打算对各种套接字模式以及I / O模型的优缺点进行总结。同时，帮助大家判断到底哪一种最适合自己应用程序的要求。<br />所有Wi n d o w s平台都支持套接字以锁定或非锁定方式工作。然而，并非每种平台都支持每一种I / O模型。如表8 - 1所示，在当前版本的Windows CE 中，仅提供了一个I / O模型。<br />Windows 98和Windows 95（取决于安装的是Winsock 1还是Winsock 2）则支持大多数I / O模型，唯一的例外便是I / O完成端口。而到了Windows NT和最新发布的Windows 2000中，每种I / O模型都是支持的。</p>
		<p>8.1 套接字模式<br />就像我们前面提到的那样， Wi n d o w s套接字在两种模式下执行I / O操作：锁定和非锁定。<br />在锁定模式下，在I / O操作完成前，执行操作的Wi n s o c k函数（比如s e n d和r e c v）会一直等候下去，不会立即返回程序（将控制权交还给程序）。而在非锁定模式下， Wi n s o c k函数无论如何都会立即返回。在Windows CE和Windows 95（安装Winsock 1）平台上运行的应用程序仅支持极少的I / O模型，所以我们必须采取一些适当的步骤，让锁定和非锁定套接字能够满足各种场合的要求</p>
		<p>8.1.1 锁定模式<br />对于处在锁定模式的套接字，我们必须多加留意，因为在一个锁定套接字上调用任何一个Winsock API函数，都会产生相同的后果—耗费或长或短的时间“等待”。大多数Wi n s o c k应用都是遵照一种“生产者－消费者”模型来编制的。在这种模型中，应用程序需要读取（或写入）指定数量的字节，然后以它为基础执行一些计算。程序清单8 - 1展示的代码片断便是一个典型的例子。<br /></p>
		<p>
		</p>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">程序清单8-1 简单的锁定模式示例</span>
				<span style="COLOR: #008000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />SOCKET s;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #0000ff">char</span>
				<span style="COLOR: #000000"> buff[</span>
				<span style="COLOR: #000000">256</span>
				<span style="COLOR: #000000">];<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> done </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><img src="http://www.cppblog.com/images/dot.gif" />.<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #0000ff">while</span>
				<span style="COLOR: #000000">(</span>
				<span style="COLOR: #000000">!</span>
				<span style="COLOR: #000000">done)<br /><img id="Codehighlighter1_81_220_Open_Image" onclick="this.style.display='none'; Codehighlighter1_81_220_Open_Text.style.display='none'; Codehighlighter1_81_220_Closed_Image.style.display='inline'; Codehighlighter1_81_220_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_81_220_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_81_220_Closed_Text.style.display='none'; Codehighlighter1_81_220_Open_Image.style.display='inline'; Codehighlighter1_81_220_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
				<span id="Codehighlighter1_81_220_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.cppblog.com/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_81_220_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    nBytes </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> recv(s,buff,</span>
						<span style="COLOR: #000000">65</span>
						<span style="COLOR: #000000">);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #0000ff">if</span>
						<span style="COLOR: #000000">(nBytes </span>
						<span style="COLOR: #000000">==</span>
						<span style="COLOR: #000000"> SOCKET_ERROR)<br /><img id="Codehighlighter1_139_211_Open_Image" onclick="this.style.display='none'; Codehighlighter1_139_211_Open_Text.style.display='none'; Codehighlighter1_139_211_Closed_Image.style.display='inline'; Codehighlighter1_139_211_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_139_211_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_139_211_Closed_Text.style.display='none'; Codehighlighter1_139_211_Open_Image.style.display='inline'; Codehighlighter1_139_211_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span>
						<span id="Codehighlighter1_139_211_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
								<img src="http://www.cppblog.com/images/dot.gif" />
						</span>
						<span id="Codehighlighter1_139_211_Open_Text">
								<span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />        printf(</span>
								<span style="COLOR: #000000">"</span>
								<span style="COLOR: #000000">recv failed with error %d</span>
								<span style="COLOR: #000000">"</span>
								<span style="COLOR: #000000">,WSAGetLastError());<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />        </span>
								<span style="COLOR: #0000ff">return</span>
								<span style="COLOR: #000000"> ;    <br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span>
						</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    <img src="http://www.cppblog.com/images/dot.gif" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
				</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
						<img src="http://www.cppblog.com/images/dot.gif" />.</span>
		</div>
		<p>
				<br />这段代码的问题在于，假如没有数据处于“待决”状态，那么r e c v函数可能永远都无法返回。这是由于从语句可以看出：只有从系统的输入缓冲区中读回点什么东西，才允许返回！有些程序员可能会在r e c v中使用M S G _ P E E K标志，或者调用i o c t l s o c k e（ t 设置F I O N R E A D选项），<br />在系统的缓冲区中，事先“偷看”是否存在足够的字节数量。然而，在不实际读入数据的前提下，仅仅“偷看”数据（如实际读入数据，便会将其从系统缓冲区中将其删除），可不是一件光彩的事情。我们认为，这是一种非常不好的编程习惯，应尽全力避免。在“偷看”的时候，对系统造成的开销是极大的，因为仅仅为了检查有多少个字节可用，便发出一个或者更多的系统调用。以后，理所当然地，还需要牵涉到进行实际r e c v调用，将数据从系统缓冲区内删除的开销。那么，如何避免这一情况呢？在此，我们的目标是防止由于数据的缺乏（这<br />可能是网络出了故障，也可能是客户机出了问题），造成应用程序完全陷于“凝固”状态，同时不必连续性地检视系统网络缓冲！为达此目的，一个办法是将应用程序划分为一个读线程，以及一个计算线程。两个线程都共享同一个数据缓冲区。对这个缓冲区的访问需要受到一定的限制，这是用一个同步对象来实现的，比如一个事件或者M u t e x（互斥体）。“读线程”的职责是从网络连续地读入数据，并将其置入共享缓冲区内。读线程将计算线程开始工作至少需<br />要的数据量拿到手后，便会触发一个事件，通知计算线程：你老兄可以开始干活了！随后，计算线程从缓冲区取走（删除）一个数据块，然后进行要求的计算。</p>
		<p>在程序清单8 - 2中，我们分别提供了两个函数，采取的便是上述办法。在两个函数中，一个负责读取网络数据（ R e a d T h r e a d），另一个则负责对数据执行计算（ P r o c e s s T h r e a d）。<br /><br /></p>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">程序清单8-2 多线程的锁定套接字示例</span>
				<span style="COLOR: #008000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />CRITICAL_SECTION    data;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />HANDLE    hEvent;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />TCHAR     buf[MAX_BUFFER_SIZE];<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000">         nBytes;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><img src="http://www.cppblog.com/images/dot.gif" /><img src="http://www.cppblog.com/images/dot.gif" />.<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">read thread</span>
				<span style="COLOR: #008000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> ReadThread(</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000">)<br /><img id="Codehighlighter1_151_650_Open_Image" onclick="this.style.display='none'; Codehighlighter1_151_650_Open_Text.style.display='none'; Codehighlighter1_151_650_Closed_Image.style.display='inline'; Codehighlighter1_151_650_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_151_650_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_151_650_Closed_Text.style.display='none'; Codehighlighter1_151_650_Open_Image.style.display='inline'; Codehighlighter1_151_650_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
				<span id="Codehighlighter1_151_650_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.cppblog.com/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_151_650_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000"> nTotal </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">0</span>
						<span style="COLOR: #000000">,<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />            nRead </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">0</span>
						<span style="COLOR: #000000">,<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />            nLeft </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">0</span>
						<span style="COLOR: #000000">,<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />            nBytes </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">0</span>
						<span style="COLOR: #000000">;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />            <br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />            </span>
						<span style="COLOR: #0000ff">while</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">!</span>
						<span style="COLOR: #000000">done)<br /><img id="Codehighlighter1_236_648_Open_Image" onclick="this.style.display='none'; Codehighlighter1_236_648_Open_Text.style.display='none'; Codehighlighter1_236_648_Closed_Image.style.display='inline'; Codehighlighter1_236_648_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_236_648_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_236_648_Closed_Text.style.display='none'; Codehighlighter1_236_648_Open_Image.style.display='inline'; Codehighlighter1_236_648_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />            </span>
						<span id="Codehighlighter1_236_648_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
								<img src="http://www.cppblog.com/images/dot.gif" />
						</span>
						<span id="Codehighlighter1_236_648_Open_Text">
								<span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                nTotal </span>
								<span style="COLOR: #000000">=</span>
								<span style="COLOR: #000000"> </span>
								<span style="COLOR: #000000">0</span>
								<span style="COLOR: #000000">;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                nLeft </span>
								<span style="COLOR: #000000">=</span>
								<span style="COLOR: #000000"> NUM_BYTES_REQUIRED;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                </span>
								<span style="COLOR: #0000ff">while</span>
								<span style="COLOR: #000000">(nTotal </span>
								<span style="COLOR: #000000">!=</span>
								<span style="COLOR: #000000"> NUM_BYTES_REQUIRED)<br /><img id="Codehighlighter1_330_621_Open_Image" onclick="this.style.display='none'; Codehighlighter1_330_621_Open_Text.style.display='none'; Codehighlighter1_330_621_Closed_Image.style.display='inline'; Codehighlighter1_330_621_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_330_621_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_330_621_Closed_Text.style.display='none'; Codehighlighter1_330_621_Open_Image.style.display='inline'; Codehighlighter1_330_621_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />                </span>
								<span id="Codehighlighter1_330_621_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
										<img src="http://www.cppblog.com/images/dot.gif" />
								</span>
								<span id="Codehighlighter1_330_621_Open_Text">
										<span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    EnterCriticalSection(</span>
										<span style="COLOR: #000000">&amp;</span>
										<span style="COLOR: #000000">data);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    nRead </span>
										<span style="COLOR: #000000">=</span>
										<span style="COLOR: #000000"> recv(sock,</span>
										<span style="COLOR: #000000">&amp;</span>
										<span style="COLOR: #000000">(buff[MAX_BUFFERS_SIZE</span>
										<span style="COLOR: #000000">-</span>
										<span style="COLOR: #000000">nBytes],nLeft);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    </span>
										<span style="COLOR: #0000ff">if</span>
										<span style="COLOR: #000000">(nRead </span>
										<span style="COLOR: #000000">==</span>
										<span style="COLOR: #000000"> </span>
										<span style="COLOR: #000000">-</span>
										<span style="COLOR: #000000">1</span>
										<span style="COLOR: #000000">)<br /><img id="Codehighlighter1_455_505_Open_Image" onclick="this.style.display='none'; Codehighlighter1_455_505_Open_Text.style.display='none'; Codehighlighter1_455_505_Closed_Image.style.display='inline'; Codehighlighter1_455_505_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_455_505_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_455_505_Closed_Text.style.display='none'; Codehighlighter1_455_505_Open_Image.style.display='inline'; Codehighlighter1_455_505_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />                    </span>
										<span id="Codehighlighter1_455_505_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
												<img src="http://www.cppblog.com/images/dot.gif" />
										</span>
										<span id="Codehighlighter1_455_505_Open_Text">
												<span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                        printf(</span>
												<span style="COLOR: #000000">"</span>
												<span style="COLOR: #000000">error</span>
												<span style="COLOR: #000000">"</span>
												<span style="COLOR: #000000">);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                        ExitThread();<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />                    }</span>
										</span>
										<span style="COLOR: #000000">
												<br />
												<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    nTotal </span>
										<span style="COLOR: #000000">+=</span>
										<span style="COLOR: #000000"> nRead;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    nLeft </span>
										<span style="COLOR: #000000">-=</span>
										<span style="COLOR: #000000">nRead;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    <br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    nBytes </span>
										<span style="COLOR: #000000">+=</span>
										<span style="COLOR: #000000"> nRead;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    LeaveCriticalSection(</span>
										<span style="COLOR: #000000">&amp;</span>
										<span style="COLOR: #000000">data);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                    <br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />                }</span>
								</span>
								<span style="COLOR: #000000">
										<br />
										<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />                SetEvent(hEvent);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />            }</span>
						</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
				</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
						<br />
						<img id="Codehighlighter1_653_675_Open_Image" onclick="this.style.display='none'; Codehighlighter1_653_675_Open_Text.style.display='none'; Codehighlighter1_653_675_Closed_Image.style.display='inline'; Codehighlighter1_653_675_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" />
						<img id="Codehighlighter1_653_675_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_653_675_Closed_Text.style.display='none'; Codehighlighter1_653_675_Open_Image.style.display='inline'; Codehighlighter1_653_675_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" />
				</span>
				<span id="Codehighlighter1_653_675_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span>
				<span id="Codehighlighter1_653_675_Open_Text">
						<span style="COLOR: #808080">//////</span>
						<span style="COLOR: #008000">compution thread</span>
						<span style="COLOR: #808080">
						</span>
				</span>
				<br />
				<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000">    ProcessThread(</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000">)<br /><img id="Codehighlighter1_702_836_Open_Image" onclick="this.style.display='none'; Codehighlighter1_702_836_Open_Text.style.display='none'; Codehighlighter1_702_836_Closed_Image.style.display='inline'; Codehighlighter1_702_836_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_702_836_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_702_836_Closed_Text.style.display='none'; Codehighlighter1_702_836_Open_Image.style.display='inline'; Codehighlighter1_702_836_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
				<span id="Codehighlighter1_702_836_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.cppblog.com/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_702_836_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    WatiForSingleObject(hEvent);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    EnterCriticalSection(</span>
						<span style="COLOR: #000000">&amp;</span>
						<span style="COLOR: #000000">data);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    <img src="http://www.cppblog.com/images/dot.gif" />..<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    nBytes </span>
						<span style="COLOR: #000000">-=</span>
						<span style="COLOR: #000000"> NUM_BYTES_REQUIRED;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    <br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    LeaveCriticalSection(</span>
						<span style="COLOR: #000000">&amp;</span>
						<span style="COLOR: #000000">data);<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
				</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				</span>
		</div>
		<br />
		<br />对锁定套接字来说，它的一个缺点在于：应用程序很难同时通过多个建好连接的套接字通信。使用前述的办法，我们可对应用程序进行修改，令其为连好的每个套接字都分配一个读线程，以及一个数据处理线程。尽管这仍然会增大一些开销，但的确是一种可行的方案。唯一的缺点便是扩展性极差，以后想同时处理大量套接字时，恐怕难以下手。<img src ="http://www.cppblog.com/ivenher/aggbug/12273.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-11 17:48 <a href="http://www.cppblog.com/ivenher/articles/12273.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十二</title><link>http://www.cppblog.com/ivenher/articles/12271.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Mon, 11 Sep 2006 09:20:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12271.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12271.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12271.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12271.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12271.html</trackback:ping><description><![CDATA[
		<p>7.4.3 基于消息的协议<br />正由于面向连接的协议同时也是流式协议，无连接协议几乎都是基于消息的。因此，在收发数据时，需要考虑这几点。首先，由于面向消息的协议对数据边界有保护，所以提交给发送函数的数据在被发送完之前累积成块。对异步或非块式I / O模式而言，如果数据未能完全发送，发送函数就会返回W S A E W O U L D B L O C K错误。这意味着基层的系统不能对不完整的那个数据进行处理，你应该稍后再次调用发送函数。下一章将对此进行详述。主要需要记住<br />的是，采用基于消息的协议时，对于写入数据来说，只能把它当作一个自治行为。<br />在连接另一端，对接收函数的调用必须提供一个足够大的缓冲空间。如果提供的缓冲不够，接收调用就会失败，出现W S A E M S G S I Z E。发生这种情况时，缓冲会尽力接收，但未收完的数据会被丢弃。被截断的数据无法恢复。唯一例外的是支持部分消息的协议却例外，比方说A p p l e Talk PA P协议。在W S A R e c v E x函数只收到部分消息时，它会在返回之前，便把自<br />己的出入标志参数设为M S G _ PA RT I A L。<br />对以支持部分消息的协议为基础的数据报来说，可考虑使用一个W S A R e c v函数。在调用r e c v时，不会有这一个通知“读取的数据只是消息的一部分”。至于接收端怎样判断是否已读取整条消息，具体方法则由程序员决定。后来的r e c v调用返回这个数据报的其他部分。由于有这个限制，所以利用W S A R e c v E x函数非常方便，它允许设置和读取M S G _ PA RT I A L标志</p>
		<p>M S G _ PA RT I A L标志指明整条消息是否已读取。Winsock 2函数W S A R e c v和W S A R e c v F r o m也支持这一标志。关于这个标志的更多知识，请参见对W S A R e c v、W S A R e c v E x和W S A R e c v F r o m<br />这三个函数的描述。<br />我们最后要谈的便是在有多个网络接口的机器上发送UDP /IP消息。这方面的问题颇多，<br />我们来看一个最常见的问题：在一个U D P套接字明显绑定到一个本地I P接口和发送数据报时，会发生什么情况？ U D P套接字并不会真正和网络接口绑定在一起。而是建立一种联系，即绑定的I P接口成为发出去的U D P数据报的源I P地址。路由表才真正决定数据报在哪个物理接口上传出去。如果不调用b i n d，而是先调用s e n d t o或W S A S e n d To执行连接，网络堆栈就会根据<br />路由表，自动选出最佳本地I P地址。这意味着；如果你先执行明显绑定，源I P地址就会有误。<br />也就是说，源I P可能不是真正在它上面发送数据报的那个接口的I P地址。<br />7.4.4 释放套接字资源<br />因为无连接协议没有连接，所以也不会有正式的关闭和从容关闭。在接收端或发送端结束收发数据时，它只是在套接字句柄上调用c l o s e s o c k e t函数。这样，便释放了为套接字分配的<br />所有相关资源。<br />7.4.5 综合分析<br />对于在无连接的套接字上收发数据的步骤，大家现在已经很清楚了。接下来，我们来看看执行这一进程的代码。程序清单7 - 3展示了一个无连接的接收端。这段代码说明了如何在默认接口或指定的本地接口上接收数据报。</p>
		<p>7.5 其他API函数<br />本小节介绍其他几个Winsock API函数，它们在实际网络应用中非常有用<br />1. getpeername<br />该函数用于获得通信方的套接字地址信息，该信息是关于已建立连接的那个套接字的。<br />它的定义如下：<br />int getpeername(<br />        SOCKET s,<br />        struct sockaddr FAR * name,<br />        int FAR *namelen<br />       );<br />第一个参数是准备连接的套接字，后两个参数则是指向基层协议类型及其长度的指针。<br />对数据报套接字来说，这个函数返回的是投向连接调用的那个地址；但不会返回投向s e n d t o或W S A S e n d To调用的那个地址。</p>
		<p>2. getsockname<br />该函数是g e t s o c k n a m e的对应函数。它返回的是指定套接字的本地接口的地址信息。它的定义如下：</p>
		<p>int getsockname(<br />        SOCKET s,<br />        struct sockaddr FAR * name,<br />        int FAR *namelen<br />       );<br /> 除了套接字s返回的地址信息本地地址信息外，它的参数和g e t p e e r n a m e的参数都是一样的。<br />T C P协议中，这个地址和监听指定端口和I P接口的那个服务器套接字是一样的。<br />3. WSADuplicateSocket<br />W S A D u p l i c a t e S o c k e t函数用来建立W S A P R O TO C O L _ I N F O结构，该结构可投入另一个进程，这样就可用另一个进程打开一个指向同一个基层套接字的句柄，如此一来，另一个进程也能对该资源进行操作。注意，这一点只适用于两个进程之间；同一个进程中的线程可自由投递套接字描述符。该函数的定义如下：</p>
		<p>int WSADuplicateSocket(<br />            SOCKET s,<br />            DWORD dwProcessId,<br />            LPWSAPROTOCOL_INFO lpProtocol<br />           );<br />第一个参数是准备复制的套接字句柄。第二个参数d w P r o c e s s I d，是打算使用复制套接字的进程之I D。第三个参数l p P r o t o c o l I n f o，是一个指向W S A P R O TO C O L _ I N F O结构的指针，将包含目标进程打开复制句柄时所需的信息。为了使目前的进程能够把W S A P R O TO C O L _ I N F O<br />结构投到目标进程，然后再利用该结构建立一个指向指定套接字的句柄（利用W S A S o c k e t函数），必须考虑进程间通信。<br />两个套接字的描述符都可独立使用I / O；但Wi n s o c k没有提供访问控制，因此这要由程序员决定是否执行同步。所有描述符中都可见到关联到一个套接字的所有状态信息，这是因为复制的是套接字描述符，而不是事实上的套接字。比方说，对于描述符上由s e t s o c k e t o p t函数设置的任何一个套接字选项，都可通过任何一个或所有描述符利用g e t s o c k o p t函数来看它们。<br />如果一个进程在一个复制套接字上调用c l o s e s o c k e t，就会导致该进程中的描述符变成解除定位；但在最后留下的那个描述符上调用c l o s e s o c k e t之前，基层套接字会保持打开状态。<br />另外，在使用W S A A s y n c S e l e c t和W S A E v e n t S e l e c t时，要了解与共享套接字的通知有关的几个问题。这两个函数用于异步I / O（我们将在第8章进行讨论）。利用任何一个共享描述符执行前两个函数的调用，都会删掉所有的套接字事件注册，不管注册所用的描述符究竟是哪一<br />个。例如，共享套接字不能把F D _ R E A D事件投递给进程A，不能把F D _ W R I T E投递给进程B。<br />如果需要这两个描述符的事件通知，就应该重新设计应用程序，用线程来代替进程。</p>
		<p>4. Tr a n s m i t F i l e<br />Tr a n s m i t F i l e是微软专有的Wi n s o c k扩展，它允许从一个文件中传输高性能数据。这是非常有效的，因为整个数据传输可在内核模式中进行。也就是说，如果你的应用从指定的文件中读取一堆数据，然后用s e n d或W S A S e n d时，涉及到“用户模式到内核模式传输”的发送调用就有若干个。有了Tr a n s m i t F i l e，整个读取和发送数据的进程就可在内核模式中进行。该函<br />数的定义如下：</p>
		<p>BOOL  TransmitFile(<br />          SOCKET hSocket,<br />          HANDLE hFile,<br />          DWORD  nNumberOfBytesToWrite,<br />          DWORD  nNumberOfBytesPerSend,<br />          LPOVERLAPPED lpOverlapped,<br />          LPTRANMIT_FILE_BUFFERS  lpTransmitBuffers,<br />          DWORD dwFlags<br />         );<br />h S o c k e t参数用于识别已连接上的套接字（文件的传输便在该套接字上进行）。n F i l e参数是一个句柄，该句柄指向一个已打开的套接字（即即将发送的文件）。n N u m b e r O f B y t e s To Wr i t e表<br />明写入多少指定文件中的字节。投递0表示将发送整个文件。n N u m b e r O f B y t e s P e r S e n d参数则表明写操作所用的发送长度。例如，指定2 0 4 8会引起Tr a n s m i t F i l e在套接字上以2 KB数据块的形<br />式发送指定文件。投递0表示采用默认的发送长度。l p O v e r l a p p e d参数指定一个O V E R L A P P E D<br />结构，该结构用于重叠I / O模式（关于重叠I / O，可参见第8章）。<br />另一个参数l p Tr a n s m i t B u ff e r s，是一个T R A N S M I T _ F I L E _ B U F F E R S结构，其中包含文件传输之前和之后准备发送的数据。该结构的格式如下：</p>
		<p>typedef struct _TANSMIT_FILE_BUFFERS{<br /> PVOID Head;<br /> DWORD HeadLenth;<br /> PVOID Tail;<br /> DWORD TailLength;<br /> }TAANSMIT_FILE_BUFFERS;     <br /> <br /> H e a d字段是一个指针，它指向文件传输之前准备发送的数据。H e a d L e n g t h表明预先准备发送的数据量。Ta i l字段则指向文件传输之后准备发送的数据。Ta i l L e n g t h是后来发送的数据量。          </p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12271.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-11 17:20 <a href="http://www.cppblog.com/ivenher/articles/12271.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十一</title><link>http://www.cppblog.com/ivenher/articles/12270.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Mon, 11 Sep 2006 09:04:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12270.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12270.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12270.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12270.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12270.html</trackback:ping><description><![CDATA[
		<p> 7.4 无连接协议<br />和面向连接的协议比较起来，无连接协议的行为极为不同，因此，收发数据的方式也会有所不同。由于在和面向会话的服务器比较时，无连接接收端改动不大，所以我们先谈谈接收端（如果你愿意，也可称之为服务器）。接下来再谈发送端。<br />7.4.1 接收端<br />对于在一个无连接套接字上接收数据的进程来说，步骤并不复杂。先用s o c k e t或W S A S o c k e t建立套接字。再把这个套接字和准备接收数据的接口绑定在一起。这是通过b i n d函数（和面向会话的示例一样）来完成的。和面向会话不同的是，我们不必调用l i s t e n和a c c e p t。相反，只需等待接收数据。由于它是无连接的，因此始发于网络上任何一台机器的数据报都可被接<br />收端的套接字接收。最简单的接收函数是r e c v f r o m。它的定义如下：</p>
		<p>int recvfrom(<br />       SOCKET s,<br />       char FAR * buf,<br />       int len,<br />       int flags,<br />       struct sockaddr FAR * from,<br />       int FAR * fromlen<br />      );<br />前面四个参数和r e c v是一样的，其中包括标志M S G _ O O B和M S G _ P E E K。在使用无连接套接字时，和前面一样，仍然提醒大家慎用M S G _ P E E K标志。对监听套接字的具体协议来说，f r o m参数是一个S O C K A D D R结构，带有指向地址结构的长度的f r o m l e n。这个A P I调用返回数<br />据时，S O C K A D D R结构内便填入发送数据的那个工作站的地址。</p>
		<p>r e c v f r o m函数的Winsock 2版本是W S A R e c v F r o m。后者的原型是：</p>
		<p>int WSARecvFrom(<br />         SOCKET s,<br />         LPWSABUF lpBuffers,<br />         DWORD dwBufferCount,<br />         LPDWORD lpNumberOfBytesRecvd,<br />         LPDWORD lpFlags,<br />         struct sockaddr FAR * lpFrom,<br />         LPINT lpFromlen,<br />         LPWSAOVERLAPPED lpOverlapped,<br />         LPWSAOVERLAPPED_COMPLETTION_ROUTINE lpComplettionROUTINE<br />        );<br />两者的差别在于接收数据的W S A B U F结构的用法上。你可以利用d w B u ff e r C o u n t为W S A R e c v F r o m提供一个或多个W S A B U F缓冲。提供多个缓冲，就可用发散集合了。读取的字节总数返回在l p N u m b e r O f B y t e s R e c v d中。在调用W S A R e c v F r o m时，l p F l a g s参数可以是代表<br />无选项的0、M S G _ O O B、M S G _ P E E K或M S G _ PA RT I A L。这些标志还可以累加起来。如果在调用这个函数时，指定M S G _ PA RT I A L，提供者就知道返回数据，即使只收到了部分消息。调用返回之后，如果只收到部分消息，就会设置M S G _ PA RT I A L标志。再次返回之后，<br />W S A R e c v F r o m就会把l p F r o m参数（它是一个指向S O C K A D D R结构的指针）设为发送端的地址。再次提醒大家注意， l p F r o m L e n指向S O C K A D D R结构的长度，另外，在这个函数中，它还是一个指针，指向D W O R D。最后两个参数， l p O v e r l a p p e d和l p C o m p l e t i o n R O U T I N E，用于<br />重叠I / O（我们将在下一章就此展开讨论）。<br />在无连接套接字上接收（发送）数据的另一种方法是建立连接。听起来有些奇怪吧，但事实的确如此。无连接的套接字一旦建立，便可利用S O C K A D D R参数（它被设为准备与之通信的远程接收端地址）调用c o n n e c t或W S A C o n n e c t。但事实上并没有建立连接。投入连接函数的套接字地址是与套接字关联在一起的，如此一来，才能够用R e c v和W S A R e c v来代替<br />r e c v f r o m和W S A R e c v F r o m。为什么呢？其原因是数据的始发处是已知的。如果在一次应用中，只和一个端点进行通信，便能很容易地与数据报套接字建立连接。</p>
		<p>7.4.2 发送端<br />要在一个无连接的套接字上发送数据，有两种选择。第一种，也是最简单的一种，便是建立一个套接字，然后调用s e n d t o或W S A S e n d To。我们先来讲解s e n d t o函数，它的定义是这样的：</p>
		<p> int sendto( <br />        SOCKET s,<br />        const char FAR * buf,<br />        int len,<br />        int flags,<br />        const struct sockaddr FAR * to,<br />        int tolen<br />       );<br /> 除了b u f是发送数据的缓冲， l e n指明发送多少字节外，其余参数和r e c v f r o m的参数一样。另外， t o参数是一个指向S O C K A D D R结构的指针，带有接收数据的那个工作站的目标地址。<br />另外，也可以用Winsock 2函数W S A S e n d To。它的定义如下：</p>
		<p>int WSASendTo(<br />        SOCKET s,<br />        LPWSABUF lpBuffers,<br />        DWORD dwBufferCount,<br />        LPWORD lpNumberOfBytesSent,<br />        DWORD dwFlags,<br />        const  struct sockaddr FAR * lpTo,<br />        int iToLen,<br />        LPWSAOVERLAPPED lpOverlapped,<br />        LPWSAOVERLAPPED_COMPLETTION_ROUTINE lpComplettionROUTINE<br />       );<br />       <br />再次提醒大家注意， W S A S e n d To和前一版本中的S e n d To函数类似。它把指向带有发给接<br />收端的数据的指针当作l p B u ff e r s参数， d w B u ff e r C o u n t参数指明现在的结构是多少。我们可发送多个W S A B U F结构启用发散集合I / O。在函数返回之前，W S A S e n d To把第四个参数l p N u m b e r<br />O f B y t e s S e n t设为真正发到接收端的字节数。l p To参数是针对具体协议的一个S O C K A D D R结构，并带有接收端的地址。i To L e n参数是S O C K A D D R结构的长度。最后两个参数， l p O v e r l a p p e d和l p C o m p l e t i o n R O U T I N E，用于重叠I / O（将在第8章讨论）。<br />通过接收数据的方式，就可把一个无连接的套接字连接到一个端点地址，并可以用s e n d和W S A S e n d发送数据了。这种关联一旦建立，就不能再用带有地址的s e n d t o和W S A S e n d To，<br />除非这个地址是投到其中一个连接函数的地址，否则调用就会失败，出现W S A E I S C O N N错误。<br />要取消套接字句柄与目标地址的关联，唯一的办法是在这个套接字句柄上调用c l o s e s o c k e t，<br />并建立一个新的套接字。<br />              </p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-11 17:04 <a href="http://www.cppblog.com/ivenher/articles/12270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程十</title><link>http://www.cppblog.com/ivenher/articles/12269.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Mon, 11 Sep 2006 09:02:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12269.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12269.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12269.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12269.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12269.html</trackback:ping><description><![CDATA[
		<p>7.3.4 流协议<br />由于大多面向连接的协议同时也是流式传输协议，所以，在此提一下流式协议。对于流套接字上收发数据所用的函数，需要明白的是：它们不能保证对请求的数据量进行读取或写入。比如说，一个2 0 4 8字节的字符缓冲，准备用s e n d函数来发送它。采用的代码是：</p>
		<p>char sendBuff[2048];<br />int  nBytes = 2048;</p>
		<p>int ret= 0;<br />ret = send(s,sendBuff,nBytes,0);</p>
		<p>对s e n d函数而言，可能会返回已发出的少于2 0 4 8的字节。r e t变量将设为发送的字节数，这是因为对每个收发数据的套接字来说，系统都为它们分配了相当充足的缓冲区空间。在发送数据时，内部缓冲区会将数据一直保留到应该将它发到线上为止。几种常见的情况都可导致这一情形的发生。比方说，大量数据的传输可以令缓冲区快速填满。同时，对T C P / I P来说，还有一个窗口大小的问题。接收端会对窗口大小进行调节，以指出它可以接收多少数据。如果有大量数据涌入接收端，接收端就会将窗口大小设为0，为待发数据做好准备。对发送端来<br />说，这样会强令它在收到一个新的大于0的窗口大小之前，不得再发数据。在使用s e n d调用时，缓冲区可能只能容纳1 0 2 4个字节，这时，便有必要再提取剩下的1 0 2 4个字节。要保证将所有的字节发出去，可采用下面的代码。</p>
		<p>char sendbuf[2048];<br />int nBytes= 2048,nLeft,idx;</p>
		<p>nLeft = nBytes;<br />idx = 0;</p>
		<p>while(nLeft&gt;0)<br />{<br /> ret = send(s,&amp;sendbuf[idx],nLeft,0);<br /> if(ret == SOCKET_ERROR)<br /> {<br />  //ERROR<br /> }<br /> nLeft -= ret;<br /> idx +=ret;<br /> }<br /> 对在流套接字上接收数据来说，前一段代码有用，但意义不大。因为流套接字是一个不间断的数据流，应用程序在读取它时，和它应该读多少数据之间通常没有关系。如果应用需要依赖于流协议的离散数据，你就有别的事要做。如果所有消息长度都一样，则比较简单，也就是说， 5 1 2个字节的消息看起来就像下面这样：</p>
		<p>char recvbuf[2048];<br />int ret,nLeft,idx;<br />nLeft = 512;<br />idx = 0;</p>
		<p>while(nLeft&gt;0)<br />{<br /> ret = recv(s,&amp;recvbuf[idx],nLeft,0);<br /> if(ret = SOCKET_ERROR)<br /> {<br />  //ERROR<br /> }<br /> idx += ret;<br /> nLeft -= ret;<br />}</p>
		<p>消息长度不同，处理也可能不同。因此，有必要利用你自己的协议来通知接收端，即将<br />到来的消息长度是多少。比方说，写入接收端的前4个字节一直是整数，表示即将到来的消息<br />有多少字节。然后，接收端先查看前4个字节的方式，把它们转换成一个整数，然后判断构成<br />消息的字节数是多少，通过这种方式，便开始逐次读取。</p>
		<p>分散集合I/O<br />分散集合支持是Berkeley Socket中首次随R e c v和Wr i t e v这两个函数一起出现的概念。<br />它随W S A R e c v、W S A R e c v F r o m、W S A S e n d和W S A S e n d To这几个Winsock 2函数一起使用。<br />对收发格式特别的数据这一类的应用来说，它是非常有用的。比方说，客户机发到服务器的消息可能一直都是这样构成的，一个指定某种操作的固定的3 2字节的头，一个6 4字节的数据块和一个1 6字节的尾。这时，就可用一个由三个W S A B U F结构组成的数组调用W S A S e n d，这三个结构分别对应的三种消息类型。在接收端，则用3个W S A B U F结构来调用W S A R e c v，各个结构包含的数据缓冲分别是3 2字节、6 4字节和1 6字节。<br />在使用基于流的套接字时，分散集合I / O模式只是把W S A B U F结构中提供的数据缓冲当作一个连续性的缓冲。另外，接收调用可能在所有缓冲填满之前就返回。在基于消息的套接字上，每次对接收操作的调用都会收到一条消息，其长度由所提供的缓冲决定。如果缓冲不够，调用就会失败，并出现W S A E M S G S I Z E错误，为了适应可用的缓冲数据就会被截断。当然，如果用支持部分消息的协议，就可用M S G _ PA RT I A L标志来避免数据的丢失。</p>
		<p>7.3.5 中断连接<br />一旦完成任务，就必须关掉连接，释放关联到那个套接字句柄的所有资源。要真正地释放与一个开着的套接字句柄关联的资源，执行c l o s e s o c k e t调用即可。但要明白这一点，<br />c l o s e s o c k e t可能会带来负面影响（和如何调用它有关），即可能会导致数据的丢失。鉴于此，应该在调用c l o s e s o c k e t函数之前，利用s h u t d o w n函数从容中断连接。接下来，我们来谈谈这两个A P I函数。<br />1. shutdown<br />为了保证通信方能够收到应用发出的所有数据，对一个编得好的应用来说，应该通知接收端“不再发送数据”。同样，通信方也应该如此。这就是所谓的“从容关闭”方法，并由s h u t d o w n函数来执行。s h u t d o w n的定义如下：</p>
		<p>int shutdown(<br />       SOCKET s,<br />       int how<br />      );<br />h o w参数可以是下面的任何一个值： S D _ R E C E I V E、S D _ S E N D或S D _ B O T H。如果是S D _ R E C E I V E，就表示不允许再调用接收函数。这对底部的协议层没有影响。另外，对T C P套接字来说，不管数据在等候接收，还是数据接连到达，都要重设连接。尽管如此， U D P套接字上，仍然接受并排列接入的数据。如果选择S E _ S E N D，表示不允许再调用发送函数。对T C P套接字来说，这样会在所有数据发出，并得到接收端确认之后，生成一个F I N包。最后，<br />如果指定S D _ B O T H，则表示取消连接两端的收发操作。</p>
		<p>2. closesocket<br />c l o s e s o c k e t函数用于关闭套接字，它的定义如下：</p>
		<p>int closesocket(SOCKET s);</p>
		<p>c l o s e s o c k e t的调用会释放套接字描述符，再利用套接字执行调用就会失败，并出现W S A E N O T S O C K错误。如果没有对该套接字的其他引用，所有与其描述符关联的资源都会被<br />释放。其中包括丢弃所有等侯处理的数据。<br />对这个进程中任何一个线程来说，它们执行的待决异步调用都在未投递任何通知消息的情况下被删除。待决的重叠操作也被删除。与该重叠操作关联的任何事件，完成例程或完成端口能执行，但最后会失败，出现W S A _ O P E R AT I O N _ A B O RT E D错误。异步和非封锁I / O模<br />式将在第8章深入讲解。另外，还有一点会对c l o s e s o c k e t的行为产生影响：套接字选项S O _ L I N G E R是否已经设置。要得知其中缘由，参考第9章中对S O _ L I N G E R选项的描述。</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-11 17:02 <a href="http://www.cppblog.com/ivenher/articles/12269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程九</title><link>http://www.cppblog.com/ivenher/articles/12130.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Thu, 07 Sep 2006 14:32:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12130.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12130.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12130.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12130.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12130.html</trackback:ping><description><![CDATA[
		<p> 3. recv和W S A R e c v<br />对在已连接套接字上接受接入数据来说， r e c v函数是最基本的方式。它的定义如下：</p>
		<p>int recv(<br />     SOCKET s,<br />     char FAR * buf,<br />     int len,<br />     int flags<br />    );<br />第一个参数s，是准备接收数据的那个套接字。第二个参数b u f，是即将收到数据的字符缓冲，而l e n则是准备接收的字节数或b u f缓冲的长度。最后， f l a g s参数可以是下面的值： 0、M S G _ P E E K或M S G _ O O B。另外，还可对这些标志中的每一个进行按位和运算。当然， 0表示无特殊行为。M S G _ P E E K会使有用的数据复制到所提供的接收端缓冲内，但是没有从系统缓<br />冲中将它删除。另外，还返回了待发字节数。<br />消息取数不太好。它不仅导致性能下降（因为需要进行两次系统调用，一次是取数，另一次是无M S G _ P E E K标志的真正删除数据的调用），在某些情况下还可能不可靠。返回的数据可能没有反射出真正有用的数量。与此同时，把数据留在系统缓冲，可容纳接入数据的系统空间就会越来越少。其结果便是，系统减少各发送端的T C P窗口容量。由此，你的应用就不能获得最大的流通。最好是把所有数据都复制到自己的缓冲中，并在那里计算数据。前面曾介绍过M S G _ O O B标志。有关详情，参见前面“带外数据”的内容。</p>
		<p>在面向消息或面向数据报的套接字上使用r e c v时，这几点应该注意。在待发数据大于所提供的缓冲这一事件中，缓冲内会尽量地填充数据。这时， r e c v调用就会产生W S A E M S G S I Z E错误。注意，消息长错误是在使用面向消息的协议时发生的。流协议把接入的数据缓存下来，<br />并尽量地返回应用所要求的数据，即使待发数据的数量比缓冲大。因此，对流式传输协议来说，就不会碰到W S A E M S G S I Z E这个错误。<br />W S A R e c v函数在r e c v的基础上增加了一些新特性。比如说重叠I / O和部分数据报通知。<br />W S A R e c v的定义如下：</p>
		<p>int WSARecv(<br />       SOCKET s,<br />       LPWSABUF lpBuffers,<br />       DWORD   dwBufferCount,<br />       LPWORD  lpNumberOfBytesRecved,<br />       LPWSAOVERLAPPED lpOverlapped,<br />       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE<br />      );<br />      <br />参数s，是已建立连接的套接字。第二和第三个参数是接收数据的缓冲。l p B u ff e r s参数是一个W S A B U F结构组成的数组，而d w B u ff e r C o u n t则表明前一个数组中W S A B U F结构的数目。<br />如果接收操作立即完成， l p N u m b e r O f B y t e s R e c e i v e d参数就会指向执行这个函数调用所收到的字节数。l p F l a g s参数可以是下面任何一个值： M S G _ P E E K、M S G _ O O B、M S G _ PA RT I A L或者对这些值进行按位和运算之后的结果。M S G _ PA RT I A L标志使用和出现的地方不同，其含<br />义也不同。对面向消息的协议来说，这个标志是W S A R e c v调用返回后设置的（如果因为缓冲空间不够导致整条消息未能在这次调用中返回的话）。这时，后面的W S A R e c v调用就会设置这个标志M A S G _ PA RT I A L，直到整条消息返回，才把这个标志清除。如果这个标志当作一个输入参数投递，接收操作应该在一收到数据就结束，即使它收到的只是整条消息中的一部分。<br />M S G _ PA RT I A L标志只随面向消息的协议一起使用。每个协议的协议条目都包含一个标志，表明是否支持这一特性。有关详情，参见第5章。l p O v e r l a p p e d和l p C o m p l e t i o n R O U T I N E参数用于重叠I / O操作</p>
		<p>4. WSARecvDisconnect<br />这函数与W S A S e n d D i s c o n n e c t函数对应，其定义如下：<br />int WSARecvDisconnect( <br />            SOCKET s,<br />            LPWSABUF lpOUTboundDisconnectData<br />           );<br />和W S A S e n d D i s c o n n e c t函数的参数一样，该函数的参数也是已建立连接的套接字句柄和<br />一个有效的W S A B U F结构（带有收到的数据）。收到的数据可以只是断开数据。这个断开数据是另一端执行W S A S e n d D i s c o n n e c t调用发出的，它不能用于接收普通数据。另外，一旦收到这个数据， W S A R e c v D i s c o n n e c t函数就会取消接收远程通信方的数据，其作用和调用带有S D _ R E C V的s h u t d o w n函数相同。<br />5. WSARecvEx<br />W S A R e c v E x函数是微软专有的Winsock 1扩展，除了f l a g s参数是按值引用外，其余和r e c v函数是一样的。它允许基层的提供者设置M S G _ PA RT I A L标志。该函数的原型如下：</p>
		<p>int PASCAL FAR WSARecvEx(<br />             SOCKET s,<br />             char FAR * buf,<br />             int len,<br />             int * flags<br />            );<br />            </p>
		<p>
				<br />如果收到的数据不是一条完整的消息， f l a g s参数中就会返回M S G _ PA RT I A L标志。对面向消息的协议（即非流协议）来说，这个标志比较有用（即非流协议）。在M S G _ PA RT I A L标志被当作f l a g s参数的一部分投递，而且收到的消息又不完整时，调用W S A R e c v E x，就会立即<br />返回收到的那个数据。如果提供的接收缓冲容纳不下整条消息， W S A R e c v E x就会失败，并出现W S A E M S G S I Z E 错误，剩下的数据也会被截掉。注意， M S G _ PA RT I A L 标志和W S A E M S G S I Z E错误之间的确区别是：有了这个错误，即使整条消息到达接收端，但由于提<br />供的数据缓冲太少，也不能对它进行接收。M S G _ P E E K 和M S G _ O O B标志还可以和W S A R e c v E x一起使用。</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-07 22:32 <a href="http://www.cppblog.com/ivenher/articles/12130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程七</title><link>http://www.cppblog.com/ivenher/articles/12128.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Thu, 07 Sep 2006 14:31:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12128.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12128.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12128.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12128.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12128.html</trackback:ping><description><![CDATA[
		<p> 7.3.2 客户机API函数<br /> <br /> 客户机要简单得多，建立成功连接所需的步骤也要少得多。客户机只需三步操作：<br />1) 用s o c k e t或W S A S o c k e t创建一个套接字。<br />2) 解析服务器名（以基层协议为准）。<br />3) 用c o n n e c t或W S A C o n n e c t初始化一个连接。</p>
		<p>TCP状态<br />作为一名Wi n s o c k程序员，通常没必要了解实际的T C P状态。但了解T C P状态，就能更好地理解Winsock API调用如何对基层协议中的改变产生影响。此外，许多程序员在关闭套接字时，会碰到一个常见问题；围绕套接字关闭的T C P状态是我们目前最感兴趣的问题。</p>
		<p>对每个套接字来说，它的初始状态都是C L O S E D。若客户机初始化了一个连接，就会向服务器发送一个S Y N包，同时将客户机套接字状态置为S Y N _ S E N T。服务器收到S Y N包后，会发出一个“ S Y N - A C K”包。作为客户机，需要用一个A C K包对它做出反应。此时，客户机的套接字会变成E S TA B L I S H E D状态。如果服务器一直不发送“ S Y N - A C K”包，客户机就会超时，并返回C L O S E D状态。</p>
		<p>若一个服务器的套接字同一个本地接口和端口绑定起来，并在它上面进行监听，那么套接字的状态便是L I S T E N。客户机试图与之连接时，服务器就会收到一个S Y N包，并用一个S Y N - A C K包做出响应。服务器套接字的状态就变成S Y N _ R C V D。最后，客户机发出一个A C K包，令服务器套接字的状态变成E S TA B L I S H E D。</p>
		<p>一旦应用处于E S TA B L I S H E D状态，可通过两种方法来关闭它。如果由应用程序来关闭，便叫作“主动套接字关闭”；否则，套接字的关闭便是被动的。图7 - 2对两种关闭方法进行了解释。如主动关闭，应用程序便会发出一个F I N包。应用程序调用c l o s e s o c k e t或s h u t d o w n时（把S D _ S E N D当作第二个参数），会向对方发出一个F I N包，而且套接字的状态则变成F I N _ WA I T _ 1。正常情况下，通信对方会回应一个A C K包，我们的套接字的状态<br />随之变成F I N _ WA I T _ 2。如对方也关闭了连接，便会发出一个F I N包，我们的机器则会响应一个A C K包，并将己方套接字的状态置为T I M E _ WA I T。</p>
		<p>T I M E _ WA I T状态也叫作2 M S L等待状态。其中， M S L代表“分段最长生存时间”（Maximum Segment Lifetime），表示一个数据包在丢弃之前，可在网络上存在多长时间。<br />每个I P包都含有一个“生存时间”（T T L）字段，若它递减为0，包便会被丢弃。一个包经过网络上的每个路由器时， T T L 值都会减1 ，然后继续传递。一旦应用程序进入T I M E _ WA I T状态，那么就会一直持续M S L时间的两倍之久。这样一来， T C P就可以在最后一个A C K丢失的前提下，重新发送它，也就是说， F I N会被重新传送出去。M S L时间两倍之久的等待状态结束之后，套接字便进入C L O S E D状态。<br />图7-2 TCP套接字的关闭状态<br /><br />套接字主动关闭<br />关闭套接字<br />发送: FIN-----&gt;FIN_WAIT_1--接收:ACK--&gt;FIN_WAIT_2----接收: FIN发送: ACK---&gt;TIME_WAIT(2MSL超时)-----&gt;CLOSED</p>
		<p>
				<br />套接字主动关闭<br />关闭套接字<br />发送: FIN-----&gt;接收: FIN发送: ACK----&gt;CLOSING--接收: ACK--&gt;TIME_WAIT(2MSL超时)-----&gt;CLOSED</p>
		<p>
				<br />套接字主动关闭<br />关闭套接字<br />发送: FIN-----&gt;接收: FIN_ACK发送: ACK-----&gt;TIME_WAIT(2MSL超时)-----&gt;CLOSED</p>
		<p>
				<br />套接字被动关闭<br />接收: FIN<br />发送: ACK------&gt;CLOSE_WAIT--关闭套接字发送: FIN--&gt;LAST_ACK-------&gt;CLOSED</p>
		<p>T I M E _ WA I T状态也叫作2 M S L等待状态。其中， M S L代表“分段最长生存时间”（Maximum Segment Lifetime），表示一个数据包在丢弃之前，可在网络上存在多长时间。<br />每个I P包都含有一个“生存时间”（T T L）字段，若它递减为0，包便会被丢弃。一个包经过网络上的每个路由器时， T T L 值都会减1 ，然后继续传递。一旦应用程序进入T I M E _ WA I T状态，那么就会一直持续M S L时间的两倍之久。这样一来， T C P就可以在最后一个A C K丢失的前提下，重新发送它，也就是说， F I N会被重新传送出去。M S L时间两倍之久的等待状态结束之后，套接字便进入C L O S E D状态 </p>
		<p>采取主动关闭措施时，有两个路径会进入T I M E _ WA I T状态。在我们以前的讨论中，<br />只有一方发出一个F I N，并接收一个A C K响应。然而，另一方仍然可以自由地发送数据，<br />直到它也被关闭为止。因此，需要两个路径发挥作用。在一个路径中(即同步关闭)，一台计算机和它的通信对方会同时要求关闭；计算机向对方送出一个F I N数据包，并从它那里接收一个F I N数据包。随后，计算机会发出一个A C K数据包，对对方的F I N包做出响应，并将自己的套接字置为C L O S I N G状态。计算机从对方那里接收到最后一个A C K包之后，它的套接字状态会变成T I M E _ WA I T。 </p>
		<p>主动关闭时，另一个路径其实就是同步关闭的变体：套接字从F I N _ WA I T _ 1状态直接变成T I M E _ WA I T。若应用程序发出一个F I N数据包，但几乎同时便从对方那里接收到一个F I N - A C K包，这种情况就会发生。在这种情况下，对方会确认收到应用程序的F I N包，并送出自己的F I N包。对于这个包，应用程序会用一个A C K包做出响应。<br />T I M E _ WA I T状态的主要作用是在T C P连接处于2 M S L等待状态的时候，规定用于建立那个连接的一对套接字不可被拒绝。这对套接字由本地I P端口以及远程I P端口组成。对某些T C P实施方案来说，它们不允许拒绝处于T I M E _ WA I T状态下的套接字对中的任何端口号。在微软的方案中，不会存在这个问题。然而，若试图通过一对已处于T I M E _ WA I T状态的套接字建立连接，就会失败，并返回W S A E A D D R I N U S E错误。要解决这一问题（除了等待使用那个本地端口来脱离T I M E _ WA I T状态的套接字对），一个办法是使用套接字选<br />项S O _ R E F U S E A D D R，我们将在第9章对这个选项进行详细讨论。<br />被动关闭情况下，应用程序会从对方那里接收一个F I N包，并用一个A C K包做出响应。此时，应用程序的套接字会变成C L O S E _ WA I T状态。由于对方已关闭自己的套接字，所以不能再发送数据了。但应用程序却不同，它能一直发送数据，直到对方的套接字已关闭为止。要想关闭对方的连接，应用程序需要发出自己的F I N，令应用程序的套接字状态变成L A S T _ A C K。应用程序从对方收到一个A C K包后，它的套接字就会逆转成C L O S E D状态。<br />要想了解T C P / I P协议的有关详情，请参阅RFC 793 文件。可在h t t p : / / w w w. r f c -e d i t o r. o rg那里找到这份文件。</p>
		<p>c o n n e c t函数和W S A C o n n e c t函数<br />最后一步就是连接。这是通过调用c o n n e c t函数或W S A C o n n e c t函数来完成的。我们先来看看该函数的Winsock 1版本，其定义如下：</p>
		<p>int connect(<br />       SOCKET s,<br />       const struct sockaddr FAR * addr,<br />       int namelen<br />      );<br /> <br /> 该函数的参数是相当清楚的： s是即将在其上面建立连接的那个有效T C P套接字； n a m e是针对T C P（说明连接的服务器）的套接字地址结构（ S O C K A D D R _ I N）；n a m e l e n则是名字参数的长度。Winsock 2版本中，它的定义是这样的：</p>
		<p>int WSAConnect(<br />        SOCKET s,<br />        const struct sockaddr FAR * addr,<br />        int namelen,<br />        LPWSABUF lpCallerData,<br />        LPWSABUF lpCalleeData,<br />        LPQOS   lpSQOS,<br />        LPQOS   lpGQOS<br />       );<br />       <br />前三个参数和connect API函数的参数是完全一样的。另外两个参数—l p C a l l e r D a t a和l p C a l l e e D a t a，是字串缓冲区，用于收发请求连接时的数据。l p C a l l e r D a t a参数是指向缓冲区的指针，缓冲区内包含客户机向服务器发出的请求连接的数据。l p C a l l e e D a t a参数则指向另一个缓冲区，区内包含服务器向客户机返回的建立连接时的数据。这两个参数都是W S A B U F结构，<br />因此，若是l p C a l l e r D a t a，l e n字段应该设为b u f字段中准备传输的数据长度。若是l p C a l l e e D a t a，l e n字段则代表b u f中的缓冲区长度，设为从服务器返回的数据长度。最后两个参数—l p S Q O S和l p G Q O S，表示Q O S结构，该结构对即将建立的连接上收发数据所需要的带宽进行了定义。<br />l p Q O S参数用于指定套接字s需要的服务质量，而l p G Q O S则用于指定套接字组所需要的服务质量。目前，尚未提供对套接字组的支持。若l p Q O S是空值，则表明没有某应用专用的Q O S。<br />如果你想连接的计算机没有监听指定端口这一进程， c o n n e c t调用就会失败，并发生错误W S A E C O N N R E F U S E D。另一个错误可能是W S A E T I M E D O U T，这种情况一般发生在试图连接的计算机不能用时（亦可能因为到主机之间的路由上出现硬件故障或主机目前不在网上）。</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12128.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-07 22:31 <a href="http://www.cppblog.com/ivenher/articles/12128.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程八</title><link>http://www.cppblog.com/ivenher/articles/12129.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Thu, 07 Sep 2006 14:31:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12129.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12129.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12129.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12129.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12129.html</trackback:ping><description><![CDATA[
		<p>.7.3.3 数据传输<br />收发数据是网络编程的主题。要在已建立连接的套接字上接收数据，可用这两个A P I函数：<br />s e n d和W S A S e n d。第二个函数是Winsock 2中专有的。同样地，在已建立了连接的套接字上接收数据也有两个函数： r e c v和W S A R e c v。后者也是Winsock 2函数。</p>
		<p>必须牢牢记住这一点：所有关系到收发数据的缓冲都属于简单的c h a r类型。也就是说，这些函数没有“U n i c o d e”版本。</p>
		<p>另外，所有收发函数返回的错误代码都是S O C K E T _ E R R O R。一旦返回错误，系统就会调用W S A G e t L a s t E r r o r获得详细的错误信息。最常见的错误是W S A E C O N N A B O RT E D和W S A E C O N N R E S E T。两者均涉及到即将关闭连接这一问题—要么通过超时，要么通过通信方关闭连接。另一个常见错误是W S A E W O U L D B L O C K，一般出现在套接字处于非暂停模<br />式或异步状态时。这个错误主要意味着指定函数暂不能完成。</p>
		<p>1. send和W S A S e n d<br />要在已建立连接的套接字上发送数据，第一个可用的A P I函数是s e n d，其原型为：</p>
		<p>int send(<br />     SOCKET s,<br />     const char FAR * buf,<br />     int len,<br />     int flags<br />    );<br />S O C K E T参数是已建立连接的套接字，将在这个套接字上发送数据。第二个参数b u f，则是字符缓冲区，区内包含即将发送的数据。第三个参数l e n，指定即将发送的缓冲区内的字符数。最后，f l a g s可为0、M S G _ D O N T R O U T E或M S G _ O O B。另外， f l a g s还可以是对那些标志进行按位“或运算”的一个结果。M S G _ D O N T R O U T E标志要求传送层不要将它发出的包路<br />由出去。由基层的传送决定是否实现这一请求（例如，若传送协议不支持该选项，这一请求就会被忽略）。M S G _ O O B标志预示数据应该被带外发送。<br />对返回数据而言，s e n d返回发送的字节数；若发生错误，就返回S O C K E T _ E R R O R。常见的错误是W S A E C O N N A B O RT E D，这一错误一般发生在虚拟回路由于超时或协议有错而中断的时候。发生这种情况时，应该关闭这个套接字，因为它不能再用了。远程主机上的应用通过执行强行关闭或意外中断操作重新设置虚拟虚路时，或远程主机重新启动时，发生的则是W S A E C O N N R E S E T错误。再次提醒大家注意，发生这一错误时，应该关闭这个套接字。最<br />后一个常见错误是W S A E T I M E O U T，它发生在连接由于网络故障或远程连接系统异常死机而引起的连接中断时。<br />send API函数的Winsock 2版本是W S A S e n d，它的定义如下：</p>
		<p>int WSASend(<br />       SOCKET s,<br />       LPWSABUF lpBuffers,<br />       DWORD   dwBufferCount,<br />       LPWORD  lpNumberOfBytesSent,<br />       DWORD   dwFlags,<br />       LPWSAOVERLAPPED lpOverlapped,<br />       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE<br />      );<br />这个套接字是一个连接会话的有效句柄。第二个参数是指向一个或多个W S A B U F结构的指针。它既可是一个独立的结构，又可以是一组结构。第三个参数指明准备投递的W S A B U F结构数。记住，每个W S A B U F结构本身就是一个字符缓冲和缓冲长度。为何打算同时发送多个缓冲呢？也许大家不太明白其中的原因。这就是我们稍后要讲的“分散集中I / O模式”；但是，在一个已建立连接的套接字上利用多缓冲来发送数据时，顺序是从第一个到最后一个W S A B U F结构。l p N u m b e r O f B y t e s S e n t是指向D W O R D（是W S A S e n d调用返回的）的指针，其中包含字节总发送数。d w F l a g s参数相当于它在s e n d中的等同物。最后两个参数—l p O v e r l a p p e d和l p C o m p l e t i o n R O U T I N E—用于重叠I / O。重叠I / O是Wi n s o c k支持的异步I / O模式之一，</p>
		<p>W S A S e n d函数把l p N u m b e r O f B y t e s S e n t设为写入的字节数。成功的话，该函数就返回0，<br />否则就返回S O C K _ E R R O R，常见错误和s e n d函数的情形一样。</p>
		<p>2. WSASendDisconnect<br />该函数非常特殊，一般不用。其原型是：</p>
		<p>int WSASendDisconnect( <br />            SOCKET s,<br />            LPWSABUF lpOUTboundDisconnectData<br />           );</p>
		<p>该函数起初将套接字置为关闭状态，发送无连接的数据。当然，它只能用于支持从容关机和无连接数据的传输协议。目前还没有传输提供者支持无连接的数据。W S A S e n d D i s c o n n e c t函数的行为和利用S D _ S E N D参数调用s h u t d o w n 函数差不多，但它另外还要发送包含在b o u n d D i s c o n n e c t D a t a参数中的数据。后来的数据禁止在这个套接字上发送。如果调用失败，<br />W S A S e n d D i s c o n n e c t i o n就会返回S O C K E T _ E R R O R。使用该函数可能会出现s e n d函数中出现的某些错误。</p>
		<p>带外数据<br />对已建立连接的流套接字上的应用来说，如果需要发送的数据比流上的普通数据重要得多，便可将这些重要数据标记成“带外数据”（Out-of-band, OOB）。位于连接另一端的应用可通过一个独立的逻辑信道（从概念上讲，该逻辑信道与数据流无关）来接收和处理O O B数据。<br />在T C P中，O O B数据由一个紧急1位标记（叫作U R G）和T C P分段头中的一个1 6位的指针组成。这里的标记和指针把指定的下行流字节当作紧急数据。实现紧急数据的两种特殊方法目前只能在T C P.RFC 793中见到，该索引对T C P进行了描述，并引入了“紧急数据”这一概念，表明T C P头中的紧急指针是紧急数据字节之后那个字节的绝对偏移。但是在RFC 11 2 2中，却将紧急偏移描述成指向紧急字节本身。<br />Wi n s o c k规格中，与协议无关的O O B数据和T C P的O O B数据实施（紧急数据）均采用了O O B这一术语。要查看待发数据中是否包含紧急数据，必须通过S I O C AT M A R K选项调用i o c t l s o c k e t函数。第9章将介绍S I O C AT M A R K的用法。<br />Wi n s o c k提供了获得紧急数据的几个方法。一是紧急数据一旦在线插入，它就会出现在普通数据流中；二是可以关闭在线插入，这样，不连续调用接收函数就会只返回紧急数据。至于控制O O B数据行为的套接字选项S O _ O O B I N L I N E，我们也将在第9章详细讨论。</p>
		<p>Te l n e t和R l o g i n使用紧急数据是有原因的。尽管如此，除非你计划编写自己的Te l n e t和R l o g i n，否则就应该远离紧急数据。因为它不容易定义，而且其他平台上的实施情况可能和Wi n 3 2有所不同。在迫不得已的情况下使用紧急数据，必须发信号通知通信方为紧急数据执行<br />一个独立的控制套接字，并为普通数据的传输保留主要的套接字连接。</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12129.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-07 22:31 <a href="http://www.cppblog.com/ivenher/articles/12129.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程六</title><link>http://www.cppblog.com/ivenher/articles/12142.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Thu, 07 Sep 2006 14:29:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12142.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12142.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12142.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12142.html</trackback:ping><description><![CDATA[
		<p> 第7章Winsock基础<br /> 本章专门讲解编写成功网络应用程序时所需的基本知识和A P I调用。<br /> <br /> 7.1 Winsock的初始化<br />每个Wi n s o c k应用都必须加载Winsock DLL的相应版本。如果调用Wi n s o c k之前，没有加载<br />Wi n s o c k库，这个函数就会返回一个S O C K E T _ E R R O R，错误信息是W S A N O T I N I T I A L I S E D。<br />加载Wi n s o c k库是通过调用W S A S t a r t u p函数实现的。这个函数的定义如下：<br />int  WSAStartup( WORD wVersionRequested,<br />         LPWSADATA lpWSAData<br />         );<br />w Ve r s i o n R e q u e s t e d参数用于指定准备加载的Wi n s o c k库的版本。高位字节指定所需要的<br />Wi n s o c k库的副版本，而低位字节则是主版本。然后，可用宏M A K E W O R D ( X , Y )（其中，x是<br />高位字节， y是低位字节）方便地获得w Ve r s i o n R e q u e s t e d的正确值。<br />l p W S A D a t a参数是指向L P W S A D ATA结构的指针， W S A S t a r t u p用其加载的库版本有关的<br />信息填在这个结构中：</p>
		<p> typedef struct WSAData<br /> {<br />   WORD wVersion;<br />   WORD wHighVersion;<br />   char szDescription[WSADESCRIPTION_LEN+1];<br />   char  saSystemStatus[WSASYS_STATUS_LEN+1];<br />   unsighd short iMaxSockets;<br />   unsighd short iMaxUdpDg;<br />   char FAR * lpVendorInfo;<br /> }WSADATA,FAR * LPWSADATA;<br /> <br /> W S A S t a r t u p把第一个字段w Ve r s i o n设成打算使用的Wi n s o c k版本。w H i g h Ve r s i o n参数容<br />纳的是现有的Wi n s o c k库的最高版本。记住，这两个字段中，高位字节代表的是Wi n s o c k副版<br />本，而低位字段代表的则是Wi n s o c k主版本。s z D e s c r i p t i o n和s z S y s t e m S t a t u s这两个字段由特定<br />的Wi n s o c k 实施方案设定，事实上没有用。不要使用下面这两个字段： i M a x S o c k e t s和<br />i M a x U d p D g，它们是假定的同时最多可打开多少套接字和数据报的最大长度。然而，要知道<br />数据报的最大长度应该通过W S A E n u m P r o t o c o l s来查询协议信息。同时最多打开套接字的数目<br />不是固定的，很大程度上和可用物理内存的多少有关。最后， l p Ve n d o r I n f o字段是为Wi n s o c k<br />实施方案有关的指定厂商信息预留的。任何一个Wi n 3 2平台上都没有使用这个字段。</p>
		<p>7.2 错误检查和控制<br />对编写成功的Wi n s o c k应用程序而言，错误检查和控制是至关重要的，因此，我们打算先<br />为大家介绍错误检查和控制。事实上，对Wi n s o c k函数来说，返回错误是非常常见的。。但是，<br />多数情况下，这些错误都是无关紧要的，通信仍可在套接字上进行。尽管其返回的值并非一<br />成不变，但不成功的Wi n s o c k调用返回的最常见的值是S O C K E T _ E R R O R。<br />实际上， S O C K E T _ E R R O R常量是- 1。<br />如果调用一个Wi n s o c k函数，错误情况发生了，就可用W S A G e t L a s t E r r o r函数来获得一段代码，<br />这段代码明确地表明发生的状况。该函数的定义如下：</p>
		<p>int WSAGetLastError(void);</p>
		<p>发生错误之后调用这个函数，就会返回所发生的特定错误的完整代码。W S A G e t L a s t E r r o r<br />函数返回的这些错误都已预定义常量值，为各种错误代码定义的常量<br />（带有#定义指令）一般都以W S A E开头。</p>
		<p>7.3 面向连接的协议<br />对服务器监听的连接来说，它必须在一个已知的名字上。在T C P / I P中，这个名字就是<br />本地接口的I P地址，加上一个端口编号。每种协议都有一套不同的定址方案，所以有一种不<br />同的命名方法。在Wi n s o c k中，第一步是将指定协议的套接字绑定到它已知的名字上。这个过<br />程是通过A P I调用b i n d来完成的。下一步是将套接字置为监听模式。这时，用A P I函数l i s t e n来<br />完成的。最后，若一个客户机试图建立连接，服务器必须通过a c c e p t或W S A A c c e p t调用来接<br />受连接。<br />1. bind<br />一旦为某种特定协议创建了套接字，就必须将套接字绑定到一个已知地址。b i n d函数可将<br />指定的套接字同一个已知地址绑定到一起。该函数声明如下；</p>
		<p>int bind(<br />     SOCKET s,<br />     const sturct sockaddr FAR * name,<br />     int  namelen<br />    );<br /> 其中，第一个参数s代表我们希望在上面等待客户机连接的那个套接字。第二个参数的类<br />型是struct sockaddr，它的作用很简单，就是一个普通的缓冲区。针对自己打算使用的那个协<br />议，必须把该参数实际地填充一个地址缓冲区，并在调用b i n d时将其造型为一个s t r u c t<br />s o c k a d d r。<br />举个例子来说，下列代码阐述了在一个T C P连接上，如何来做到这一点：</p>
		<p>SOCKET  s;<br />struct sockaddr_in tcpaddr;<br />int   port=5150;<br />s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);<br />tcpaddr.sin_family = AF_INET;<br />tcpaddr.sin_port = htons(port);<br />tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);</p>
		<p>bind(s,(SOCKADDR *)&amp;tcpaddr,sizeof(tcpaddr));</p>
		<p>一旦出错， b i n d 就会返回S O C K E T _ E R R O R 。对b i n d 来说，最常见的错误是<br />W S A E A D D R I N U S E。如使用的是T C P / I P，那么W S A E A D D R I N U S E就表示另一个进程已经同<br />本地I P接口和端口号绑定到了一起，或者那个I P接口和端口号处于T I M E _ WA I T状态。假如你<br />针对一个套接字调用b i n d，但那个套接字已经绑定，便会返回W S A E F FA U LT错误。</p>
		<p>2. listen<br />我们接下来要做的是将套接字置入监听模式。b i n d函数的作用只是将一个套接字和一个<br />指定的地址关联在一起。指示一个套接字等候进入连接的A P I函数则是l i s t e n，其定义如下：</p>
		<p>int listen(<br />      SOCKET s,<br />      int backlog<br />     );</p>
		<p>第一个参数同样是限定套接字。b a c k l o g参数指定了正在等待连接的最大队列长度。这个<br />参数非常重要，因为完全可能同时出现几个服务器连接请求。例如，假定b a c k l o g参数为2。如<br />果三个客户机同时发出请求，那么头两个会被放在一个“待决”（等待处理）队列中，以便应<br />用程序依次为它们提供服务。而第三个连接会造成一个W S A E C O N N R E F U S E D错误。注意，<br />一旦服务器接受了一个连接，那个连接请求就会从队列中删去，以便别人可继续发出请求。<br />b a c k l o g参数其实本身就存在着限制，这个限制是由基层的协议提供者决定的。如果出现非法<br />值，那么会用与之最接近的一个合法值来取代。除此以外，对于如何知道实际的b a c k l o g值，<br />其实并不存在一种标准手段。<br />与l i s t e n对应的错误是非常直观的。到目前为止，最常见的错误是W S A E I N VA L。该错误<br />通常意味着，你忘记在l i s t e n之前调用b i n d。否则，与b i n d调用相反，使用l i s t e n时可能收到<br />W S A E A D D R I N U S E。这个错误通常是在进行b i n d调用时发生的。</p>
		<p>3. accept和W S A A c c e p t<br />现在，我们已做好了接受客户连接的准备。这是通过a c c e p t或W S A A c c e p t函数来完成的。<br />a c c e p t格式如下：</p>
		<p>SOCKET accept(<br />        SOCKET s,<br />        struct sockaddr FAR * addr,<br />        int FAR * addrlen<br />       );<br />其中，参数s是一个限定套接字，它处在监听模式。第二个参数应该是一个有效的<br />S O C K A D D R _ I N结构的地址，而a d d r l e n应该是S O C K A D D R _ I N结构的长度。对于属于另一种<br />协议的套接字，应当用与那种协议对应的S O C K A D D R结构来替换S O C K A D D R _ I N。通过对<br />a c c p e t函数的调用，可为待决连接队列中的第一个连接请求提供服务。a c c e p t函数返回后，<br />a d d r结构中会包含发出连接请求的那个客户机的I P地址信息，而a d d r l e n参数则指出结构的长<br />度。此外，a c c e p t会返回一个新的套接字描述符，它对应于已经接受的那个客户机连接。对于<br />该客户机后续的所有操作，都应使用这个新套接字。至于原来那个监听套接字，它仍然用于<br />接受其他客户机连接，而且仍处于监听模式。</p>
		<p>Winsock 2引入了一个名为W S A A c c e p t的函数。它能根据一个条件函数的返回值，选择性<br />地接受一个连接。这个新函数的定义如下：</p>
		<p>SOCKET WSAAccept(<br />         SOCKET  s,<br />         struct sockaddr FAR * addr,<br />         LPINT addlen,<br />         LPCONDITIONPROC lpfnCondition,<br />         DWORD  dwCallbackData<br />        );<br /> <br />其中，头三个参数与a c c e p t的Winsock 1版本是相同的。l p f n C o n d i t i o n参数是指向一个函数<br />的指针，那个函数是根据客户请求来调用的。该函数决定是否接受客户的连接请求，定义如<br />下：</p>
		<p> int CALLBACK ConditionFunc(<br />                LPWSABUF lpCallerId,<br />                LPWSABUF  lpCallerData,<br />                LPQOS   lpSQOS,<br />                LPQOS   lpGQOS,<br />                LPWSABUF lpCalleeId,<br />                LPWSABUF  lpCalleeData,<br />                GROUP FAR * g,<br />                DWORD   dwCallbackData<br />               );<br /> <br /> l p C a l l e r I d是一个值参数，其中包含连接实体的地址。W S A B U F结构是许多Winsock 2函数<br />常用的。对它的声明如下：</p>
		<p>typedef struct __WSABUF{<br /> u_long len;<br /> char FAR * buf;<br /> } WSABUF,FAR * LPWSABUF;<br /> 对l p C a l l e r I d来说，b u f指针指向的是一个地址结构。该结构针对的是建立连接的那种特定<br />通信协议。为正确返回信息，只须将b u f指针建立为恰当的S O C K A D D R类型。在T C P / I P的情<br />况下，在S O C K A D D R _ I N结构中，当然应该包含建立连接的那个客户机的I P地址。在连接请<br />求期间，大多数网络协议都能提供对呼叫者I D信息的支持。<br />l p C a l l e r D a t a参数中包含了随连接请求一道，由客户机发出的任何连接数据。若其中未指<br />定呼叫者数据，那么该参数就默认为N U L L。要注意的是，对大多数网络协议（如T C P）来说，<br />它们并不提供对连接数据的支持。至于一种协议到底是支持连接数据，还是支持断开数据，<br />可用W S A E n u m P r o t o c o l s函数对Wi n s o c k目录中相应的条目进行查询，从而得出正确的结论。</p>
		<p>l p S Q O S和l p G Q O S参数对客户机请求的任何一个服务质量（ Q O S）参数进行指定，两个<br />参数都引用了一个Q O S结构，该结构中包含的信息是关于收发数据所需要的带宽。如果客户<br />机没有要求Q O S，这些参数都将是N U L L。这两个参数的不同之处在于l p S Q O S指定的是一个<br />独立的连接，而l p G Q O S则用于套接字组。在Winsock 1或2中没有实施或支持套接字组</p>
		<p>l p C l a l l e e I d属于另一种W S A B U F结构，这一结构中包含已与客户机需要与之连接的本地地<br />址。该结构的b u f字段同样指向其相应地址家族的一个S O C K A D D R对象。对正在一个多主机<br />的机器上运行的服务器来说，这种信息非常有用。记住，如果服务器和I N A D D R _ A N Y地址绑<br />定在一起，任何一个网络接口都可为连接请求提供服务。随后，该参数会返回实际建立连接<br />的那个接口。<br />l p C l a l l e e D a t a参数是l p C a l l e r D a t a的补充。l p C a l l e e D a t a参数指向一个W S A B U F结构，服务<br />器可利用这个结构把数据当作连接请求进程的一部分，返回客户机。如果服务提供者支持这<br />一选项， l e n字段就会指出作为这个连接请求一部分，服务器最多可向客户机返回多少字节。<br />这种情况下，服务器会根据这一数量，将尽可能多的字节复制到W S A B U F结构的b u f部分，同<br />时用l e n字段指出实际传输了多少个字节。如果服务器不想返回任何连接数据，那么，在返回<br />之前，条件接受函数应将l e n字段设为0。假如提供者不支持连接数据， l e n字段就会为0。大多<br />数协议同样都不支持在接受连接之前进行数据交换。事实上， Wi n 3 2平台当前支持的所有协议<br />都不支持这一特性。<br />服务器将传递给条件函数的参数处理完之后，必须指出到底是接受、拒绝还是延后客户机的连接请求。如果服务器打算接受连接，那么条件函数就应返回C F _ A C C E P T。如果拒绝，<br />函数就应返回C F _ R E J E C T。如果出于某种原因，现在还不能做出决定，就应返回C F _ D E F E R。<br />若服务器准备对这个连接请求进行处理，就应调用W S A A c c c e p t。要注意的是，条件函数在与<br />W S A A c c e p t函数相同的进程内运行，而且会立即返回。另外还要注意的是，对于当前的<br />Wi n 3 2平台支持的协议来说，条件接受函数并不意味着客户机的连接请求必须在从该函数返回<br />一个值之后才会得到满足。大多数情况下，最基层的网络堆栈在条件接受函数调用的那一刻，<br />就已经接受了连接。如果返回C F _ R E J E C T值，基层堆栈就会将连接简单地关闭了事。<br />如发生错误，就会返回I N VA L I D _ S O C K E T。最常见的错误是W S A E W O U L D B L O C K。如<br />果监听套接字处于异步状态或非暂停模式，同时没有要接受的连接时，就会产生此类的错误。<br />若条件函数返回C F _ D E F E R，W S A A c c e p t就会返回W S AT RY _ A G A I N错误。如果条件函数返<br />回C F _ R E J E C T，W S A A c c e p t错误就是W S A E C O N N R E F U S E D。</p>
		<p>
				<br />    </p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-07 22:29 <a href="http://www.cppblog.com/ivenher/articles/12142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程五</title><link>http://www.cppblog.com/ivenher/articles/12100.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Wed, 06 Sep 2006 12:04:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12100.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12100.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12100.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12100.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12100.html</trackback:ping><description><![CDATA[
		<p> 第6章地址家族和名字解析<br /> <br /> 要通过Wi n s o c k建立通信，必须了解如何利用指定的协议为工作站定址。本章将一一说明Wi n s o c k支持的协议以及各协议如何把一个指定家族的地址解析成网络上一台具体的机器。</p>
		<p>6.1 IP<br />网际协议（ Internet Protocol, IP）是一种用于互联网的网络协议.<br />从它的设计看来， I P是一个无连接的协议，不能保证数据投递万<br />无一失。两个比它高级的协议（ T C P和U D P）用于依赖I P协议的数据通信。</p>
		<p>6.1.1 TCP<br />面向连接的通信是通过“传输控制协议”（Transmission Control Protocol, TCP）来完成的。<br />T C P提供两台计算机之间的可靠无错的数据传输。应用程序利用T C P进行通信时，源和目标之间会建立一个虚拟连接。这个连接一旦建立，两台计算机之间就可以把数据当作一个双向字节流进行交换。</p>
		<p>6.1.2 UDP<br />无连接通信是通过“用户数据报协议”（User Datagram Protocol, UDP）来完成的。U D P不保障可靠数据的传输，但能够向若干个目标发送数据，接收发自若干个源的数据。<br />数据传输方法采用的是数据报。<br />T C P和U D P两者都利用I P来进行数据传输，一般称为T C P / I P和U D P / I P。Wi n s o c k通过A F _ I N E T地址家族为I P通信定址，</p>
		<p>6.1.3 定址</p>
		<p>Wi n s o c k中，应用通过S O C K A D D R _ I N结构来指定I P地址和服务端口信息，该结构的格式如下：</p>
		<p>struct sockaddr_in<br />{<br /> short  sin_family;<br /> u_short sin_port;<br /> struct in_addr sin_addr;<br /> char    sin_zero[8];<br />}</p>
		<p>。从本质上说，端口号分为下面这三类：“已知”端口、已注册端口、动态和（或）私用端口。<br />■ 0 ~ 1 0 2 3由I A N A控制，是为固定服务保留的。<br />■ 1 0 2 4 ~ 4 9 1 5 1是I A N A列出来的、已注册的端口，供普通用户的普通用户进程或程序使<br />用。<br />■ 4 9 1 5 2 ~ 6 5 5 3 5是动态和（或）私用端口。<br />普通用户应用应该选择1 0 2 4 ~ 4 9 1 5 1之间的已注册端口，<br />S O C K A D D R _ I N结构的s i n _ a d d r字段用于把一个I P地址保存为一个4字节的数，它是无符号长整数类型。根据这个字段的不同用法，还可表示一个本地或远程I P地址。I P地址一般是<br />用“互联网标准点分表示法”（像a . b . c . d一样）指定的，每个字母代表一个字节数，从左到右分配一个4字节的无符号长整数。最后一个字段sin_ zero ，只充当填充项的职责，以使S O C K A D D R _ I N结构和S O C K A D D R结构的长度一样。</p>
		<p>一个有用的、名为i n e t _ a d d r的支持函数，可把一个点式I P地址转换成一个3 2位的无符号长整数。它的定义如下：<br />unsigned long inet_addr(<br />const char FAR *cp<br />);<br />这个函数把I P地址当作一个按网络字节顺序排列的3 2位无符号长整数返回<br />1. 特殊地址<br />对于特定情况下的套接字行为，有两个特殊I P 地址可对它们产生影响。特殊地址I N A D D R _ A N Y允许服务器应用监听主机计算机上面每个网络接口上的客户机活动。一般情况下，在该地址绑定套接字和本地接口时，网络应用才利用这个地址来监听连接。如果你有一个多址系统，这个地址就允许一个独立应用接受发自多个接口的回应。特殊地址I N A D D R _ B R O A D C A S T用于在一个I P网络中发送广播U D P数据报。要使用这个特殊地址，需要应用设置套接字选项S O _ B R O A D C A S T。</p>
		<p>2. 字节排序<br />针对“大头”（b i g - e n d i a n）和“小头”（l i t t l e - e n d i a n）形式的编号，不同的计算机处理器的表示方法有所不同，这由各自的设计决定。比如， Intel 86处理器上，用“小头”形式来表示多字节编号：字节的排序是从最无意义的字节到最有意义的字节。在计算机中把I P地址和端口号指定成多字节数时，这个数就按“主机字节”（h o s t - b y t e）顺序来表示。但是，如果在网络上指定I P地址和端口号，“互联网联网标准”指定多字节值必须用“大头”形式来表示<br />（从最有意义的字节到最无意义的字节），一般称之为“网络字节”（n e t w o r k - b y t e）顺序。</p>
		<p>我们打算演示一下如何利用上面描述的i n e t _ a d d r 和h t o n s 函数来创建<br />S O C K A D D R _ I N结构。</p>
		<p>SOCKETADDR_IN InternetAddr;<br />int nPortId = 5150;<br />InternetAddr.sin_family = AF_INET;<br />InternetAddr.sin_addr.s_addr = inet_addr("198.198.10.216");<br />InternetAddr.sin_port = htonl(nPortId);</p>
		<p>6.1.4 创建套接字<br />创建一个I P套接字的好处是便于应用能够通过T C P、U D P和I P协议进行通信。如要用T C P协议打开一个I P套接字，需调用带有地址家族A F _ I N E T和套接字类型S O C K _ S T R E A M的s o c k e t函数或W S A S o c k e t函数，并把协议字段设成0，方式如下：<br />SOCKET s;<br />s = socket(AF_INET,SOCK_STREAM,0);</p>
		<p>s = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);<br />要利用U D P协议打开I P套接字，只须指定套接字类型，用这个指定的套接字类型代替s o c k e t函数中的S O C K _ S T R E A M和上面的W S A S o c k e t调用。还可以打开一个套接字通过I P直接通信。这是把套接字类型设成S O C K _ R AW来完成的。</p>
		<p>6.1.5 名字解析<br />Wi n s o c k应用打算通过I P和主机通信时，必须知道这个主机的I P地址。依用户看来， I P地址是不容易记的。在指定机器时，许多人更愿意利用一个易记的、友好的主机名而不是I P地址。Wi n s o c k提供了两个支持函数，它们有助于用户把一个主机名解析成I P地址。<br />Wi n d o w s套接字g e t h o s t b y n a m e和WSAAsynGetHostByName API函数从主机数据库中取回与指定的主机名对应的主机信息。两个函数均返回一个H O S T E N T结构.</p>
		<p>
				<br />6.3 IPX/SPX<br />“互联网包交换”（I P X）协议是一个常见协议，一般为承担Novell NetWa r e客户机／服务器联网服务的计算机所用。。I P X提供两个进程间的无连接通信；因此，如果一个工作站发出一个数据包，该协议无法保证这个数据包会准确无误地投递到目标地点。如果应用程序需要数据投递保证，但仍坚持使用I P X，它就会选用一个比I P X高级的协议，比如说“顺序分组交换”<br />（S P X）和SPX II协议，这两个协议中， S P X包通过I P X发送。Wi n s o c k为应用程序提供了在Wi n d o w s平台上通过I P X进行通信的能力（它们是Windows 95、Windows 98、Windows NT以及Windows 2000）</p>
		<p>6.6 ATM<br />异步传输模式（AT M）协议是目前已有的最新协议之一， Windows 98和Windows 2000平台上的Winsock 2均支持它。AT M通常用于L A N和WA N上的高速联网，也用于各种类型的通信，比如说要求高速通信的语音、视频和数据等。一般说来， AT M利用网络上的虚拟连接（V C）来提供服务质量（ Q O S）保证。正如大家即将看到的那样，Wi n s o c k能够通过AT M地址家族来使用AT M网络上的虚拟连接。AT M网络（如图6 - 1所示）一般由通过交换机（它们将<br />AT M网络桥接在一起）连接的端点（或计算机）构成。<br />针对AT M协议编程时，需要明白这几点。首先， AT M是一个媒体类型，而不是一个真正的协议。也就是说， AT M类似于直接在以太网上写入以太帧。和以太网一样， AT M协议没有提供流控制。它是一个面向连接的协议，要么提供消息模式，要么提供流模式。这还意味着如果数据不能快速发送出去，发送应用则可能溢出本地缓冲。同样地，接收应用必须频繁投递收到的数据：否则，接收缓冲填满之时，任何一个另外接入的数据都可能被丢弃。如果你的应用需要流控制，方法之一是在AT M上使用I P协议（它只是运行于AT M网络上的I P协议）。<br />这样一来，应用便紧跟在上面描述的I P地址家族之后。当然， AT M的确提供了比I P好的一些好处，比如说“根式多播方案”（第1 2章将对此进行说明）；然而，要根据自己的应用需要来决定最适合你的那种协议。</p>
		<p>6.8 小结<br />这一章论述了Wi n s o c k支持的协议地址家族，说明了各个家族特有的定址属性。针对每个地址家族，我们还讨论了如何创建套接字和如何设置套接字地址结构，以便开始通过协议进行通信。下一章，我们将描述适用于Wi n s o c k的基本通信技术，并把它们应用到本章讨论的所有地址家族上。</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12100.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-06 20:04 <a href="http://www.cppblog.com/ivenher/articles/12100.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程四</title><link>http://www.cppblog.com/ivenher/articles/12098.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Wed, 06 Sep 2006 11:00:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12098.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12098.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12098.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12098.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12098.html</trackback:ping><description><![CDATA[
		<p> 第二部分Winsock API<br /> <br /> Wi n s o c k是网络编程接口，而不是协议。它从U n i x平台的B e r k e l e y（B S D）套接字方案借鉴了许多东西，后者能访问多种网络协议。在Wi n 3 2环境中，Wi n s o c k接口最终成为一个真正的“与协议无关”接口，尤其是在Winsock 2发布之后。<br />本部分开始之前，我们假定大家已具备了Wi n s o c k（或B S D套接字）的基本知识，而且多少熟悉一些客户机／服务器的Wi n s o c k基本知识。</p>
		<p>第5章网络原理和协议</p>
		<p>建立Winsock 2规范的主要目的是提供一个与协议无关的传送接口。</p>
		<p>5.1 协议的特征<br />本章第一小节着重讲解目前常用网络传送协议的一些基本特征。通过这里的学习，大家可掌握与协议行为类型有关的一些背景知识。同时，作为程序员，可大致知道特定协议在程序中的行为方式。</p>
		<p>5.1.1 面向消息<br />对每个离散写命令来说，如果传送协议把它们（而且只有它们）当做一条独立的消息在网上传送，我们就说该协议是面向协议的。同时，还意味着接收端在接收数据时，返回的数据是发送端写入的一条离散消息。接收端不能得到更多的消息，仅此而已。比如，在图5 - 1中，<br />左边的工作站向右边的工作站提交了三条分别是1 2 8、6 4和3 2字节的消息。作为接收端的工作站发出三条读取命令，缓冲区是2 5 6个字节。后来的各次调用返回的分别是1 2 8、 6 4 和3 2个字节。第一次读取调用不会将这所有的三个数据包都返回，即使这些数据包已经收到也如此。这称为“保护消息边界”（preserving message boundaries），，一般出现在交换结构化数据时。<br />网络游戏是“保护消息边界”的较好范例。每个玩家均向别的玩家发出一个带有地图信息的数据包。这种通信后面的代码很简单：一个玩家请求一个数据包，另一个玩家又准确地从别的玩家处获得一个地图信息数据包。<br /><br />无保护消息边界的协议通常称作“基于流的协议”。大家要知道“基于流的协议”这一术语常用来指代附加特性。流服务的定义是连续的数据传输；不管消息边界是否存在，接收端都会尽量地读取有效数据。对发送端来说，意味着允许系统将原始消息分解成小消息或把几条消息积累在一起，形成一个较大的数据包。对接收端来说，则是数据一到达网络堆栈，<br />网络堆栈就开始读取它，并将它缓存下来等候进程处理。在进程请求处理大量数据时，系统会在不溢出为客户请求提供的缓冲区这一前提下，尽量返回更多的数据。在图5 - 2中，发送端提交了三个数据包：分别是1 2 8、6 4和3 2个字节；但是，本地系统堆栈自由地把这些数据聚合在一起，形成一个大的数据包。这种情况下，重组后的2个数据包就会一起传输。是否将各个独立的数据包累积在一起受许多因素的影响，比如最大传输单元或N a g l e算法。在T C P / I P中，在将积累起来的数据发到线上之前， N a g l e算法在等候数据积累的主机中进行。<br />在需要发送的数据积累到一定数量或预定时间已超过之前，这个主机会一直等下去。实施N a g l e算法时，主机的通信方在发送主机确认之前，会等一等外出数据，主机的通信方就不必发送一个只有确认的数据包。发送小数据包不仅没有多少意义，而且还会徒增错误检查和确认，相当烦人。<br />在接收端，网络堆栈把所有进来的数据包聚集在一起，归入既定进程。我们来看看图5 - 2。<br />如果接收端执行一次2 5 6字节缓冲区的读取，系统马上就会返回2 2 4个字节。如果接收端只要求读取2 0个字节，系统就会只返回2 0个字节。</p>
		<p>伪流<br />伪流（ p s e u d o - s t r e a m）这个术语常用于某种系统中，该系统使用的协议是基于消息的，<br />发送的数据分别在各自独立的数据包内，接收端读取并把消息缓存在一起，  这样，接收应用<br />程序便可读取任意大小的数据块。把图5 - 1中的发送端和图5 - 2中的接收端结合起来，便可说明<br />伪流的工作原理。发送端必须分别发送各自独立的数据包，但接收端可以对收到的数据包自<br />由组合。一般情况下，可把伪流视作一个普通的“面向流的协议”。</p>
		<p>
				<br />5.1.2 面向连接和无连接<br />通常情况下，一个协议提供面向连接的服务，或提供无连接的服务。面向连接的服务中，<br />进行数据交换之前，必须与通信方建立一条路径。这样既确定了通信方之间存在路由，又保证了通信双方都是活动的、都可彼此响应，但其特点是在通信双方之间建立一个通信信道需要很多开支。除此以外，大部分面向连接的协议为保证投递无误，可能会因为执行额外的计算来验证正确性，因此，进一步增加开支。而无连接协议却不保证接收端是否正在收听。无连接服务类似于邮政服务：发信人把信装入邮箱即可。至于收信人是否想收到这封信或邮局是否会因为暴风雨未能按时将信件投递到收信人处等等，发信人都不得而知。</p>
		<p>
				<br />5.1.3 可靠性和次序性<br />在设计用于特定协议的应用程序来说，可靠性和次序性是我们必须了解的最具决定性的特性。大多数情况下，可靠性和次序性与协议是无连接的，还是面向连接的密切相关。</p>
		<p>5.1.4 从容关闭<br />从容关闭只出现在面向连接的协议中。在这种关闭过程中，一方开始关闭通信会话，但另一方仍然可以读取线上或网络堆栈上已挂起的数据。如果面向连接的协议不支持从容关闭，<br />只要其中一方关闭了通信信道，都会导致连接立即中断，数据丢失，接收端不能读取数据这些情况出现。</p>
		<p>5.1.5 广播数据<br />广播数据即数据从一个工作站发出，局域网内的其他所有工作站都能收到它。这一特征<br />适用于无连接协议，因为L A N上的所有机器都可获得并处理广播消息。。一般情况下，路由器都不会传送广播包。  </p>
		<p>5.1.6 多播数据<br />多播是指一个进程发送数据的能力，这些数据即将由一个或多个接收端进行接收。进程加入一个多播会话的方法和采用的基层协议有关。视频会议应用常常使用多播。</p>
		<p>5.1.7 服务质量<br />服务质量（ Q o S）是应用的一种能力，用以请求针对专门用途分配特定的带宽。</p>
		<p>5.1.8 部分消息<br />部分消息只用于面向消息的协议。</p>
		<p>5.1.9 路由选择的考虑<br />一个重要考虑就是协议是否可路由。如果协议可路由，就可在两个工作站之间建立一条成功的通信路径（要么是面向连接的回路，要么是数据报的数据路径），不管这两个工作站之间存在的网络硬件是什么。路由器不对发自非路由协议的数据包进行转发，即便数据包的既定目的地在其连接的子网上。</p>
		<p>5.2 支持的协议<br />Wi n 3 2平台提供的最有用的特征之一是能够同步支持多种不同的网络协议。<br />利用Wi n s o c k编程接口的好<br />处之一是因为它是一个与协议无关的接口。不管使用的是哪一种协议，它们的操作大多数是<br />相通的。</p>
		<p>要想获得系统中安装的网络协议的相关信息，调用这个函数W S A E n u m P r o t o c o l s即可，<br />打开Winsock在可以调用一个Wi n s o c k函数之前，必须先加载一个版本正确的Wi n s o c k库。Wi n s o c k<br />启动例程是W S A S t a r t u p，它的定义是：<br />int WSAStartup(WORD wVe r s i o n R e q u e s t e d , L P W S A D ATA lpWSAData)<br />第一个参数是准备加载的Wi n s o c k库的版本号。就目前的Wi n 3 2平台而言，Winsock 2<br />库的最新版本是2 . 2。唯一的例外是Windows CE，它只支持Winsock 1.1版。如果需要<br />Winsock 2.2版，指定这个值（ 0 x 0 2 0 2）或使用宏M A K E W O R D ( 2 , 2 )即可。高位字节指定<br />副版本，而低位字节则指定主版本。<br />第二个参数是W S A D ATA结构，它是调用完成之后立即返回的。W S A D ATA包含了W S A S t a r t u p加载的关于Wi n s o c k版本的信息。<br />大致说来，在W S A D ATA结构中，返回的唯一有用的信息是w Ve r s i o n和w H i g h Ve r s i o n。<br />属于最大套接字和最大U D P长度的条目应该从自己正在使用的特定协议目录条目中获取。</p>
		<p>在结束Wi n s o c k库，而且不再需要调用任何Wi n s o c k函数时，会卸载这个库，并释放资源。这个函数的定义是：<br />int WSACleanup (void);<br />记住，每次调用W S A S t a r t u p，都需要调用相应的W S A C l e a n u p，因为每次启动调用都<br />会增加对加载Winsock DLL的引用次数，它要求调用同样多次的W S A C l e a n u p，以此抵消<br />引用次数。</p>
		<p>5.4 Windows套接字</p>
		<p>。所谓套接字，就是一个指向传输提<br />供者的句柄。Wi n 3 2中，套接字不同于文件描述符，所以它是一个独立的类型—S O C K E T。<br />套接字是由两个函数建立的：<br />SOCKET WSASocket(int af,<br />         int type,<br />         int protocol,<br />         LPWSAPROTOCOL_INOF lpProtocolInfo,<br />         GROUP g,<br />         DWORD dwFlag<br />         );<br />SOCKET socket(int af,<br />       int type,<br />       int protocol<br />       );<br />第一个参数a f，是协议的地址家族。比如，如果想建立一个U D P或T C P套接字，可用常量A F _ I N E T来指代互联网协议（ I P）。<br />第二个参数t y p e，是协议的套接字类型。套接字的类型可以是下面五个值：<br /> S O C K _ S T R E A M、S O C K _ D G R A M、S O C K _ S E Q PA C K E T、S O C K _ R AW和S O C K _ R D M。<br />第三个参数是p r o t o c o l。指定的地址家族和套接字类型有多个条目时，就可用<br />这个字段来限定使用特定传输。<br /></p>
		<p>最后两个W S A S o c k e t标志很简单。组参数始终为0，因为目前尚无可支持套接字组的<br />Wi n s o c k版本。要指定一个或多个下列标志，可用d w F l a g s参数：<br />■ W S A _ F L A G _ O V E R L A P P E D<br />■ W S A _ F L A G _ M U LT I P O I N T _ C _ R O O T<br />■ W S A _ F L A G _ M U LT I P O I N T _ C _ L E A F<br />■ W S A _ F L A G _ M U LT I P O I N T _ D _ R O O F<br />■ W S A _ F L A G _ M U LT I P O I N T _ D _ L E A F<br />第一个标志W S A _ F L A G _ O V E R L A P P E D，用于指定这个套接字具备重叠I / O（是适用于<br />Wi n s o c k的可能实现的通信模式之一）。这个主题将在第8章详细讨论。调用s o c k e t建立一个套<br />接字时， W S A _ F L A G _ O V E R L A P P E D便是默认设置。一般说来，在使用W S A S o c k e t时，最好<br />始终保持设定该标志。后面四个标志用于处理多播套接字。</p>
		<p>
				<br />原始套接字<br />利用W S A S o c k e t建立套接字时，可向函数调用传送一个W S A P R O TO CO L _ I N F O结构，以<br />定义准备建立的那个套接字的类型；尽管如此，还是可建立一些套接字类型（在传输提供者<br />目录中，它们没有相应的条目）。最佳示例是I P协议下的原始套接字。原始套接字一种通信，<br />允许你把其他协议封装在U D P数据包中，比如说“互联网控制消息协议”（I C M P）。I C M P的<br />目的是投递互联网主机间的控制、错误和信息型消息。由于I C M P不提供任何数据传输功能，<br />因此不能把它与U D P或T C P同等看待，但它和I P本身属于同一个级别。</p>
		<p>Winsock API安装在“会话层”和“传送层”之间。</p>
		<p>5.6 选择适当的协议<br />T C P / I P就是首选协议之一，至少从支持能力和微软的赞助这一角度来看，是<br />这样的</p>
		<p>5.7 小结<br />通过本章的学习，大家已了解为应用程序选择网络传输时应该知道的基本特性。在为指100计计第二部分附Winsock API<br />下载定的协议开发成功的网络应用程序时，了解这些特性是至关重要的。我们还有计划地深入探讨了如何获得安装在系统中的传输提供者列表和如何查询特定属性。最后，我们还学习了如何为指定的传输建立套接字，方法是为W S A S o c k e t或s o c k e t函数指定正确的参数，再利用<br />W S A E n u m P r o t o c o l s查询目录条目以及通过W S A P R O TO C O L _ I N F O结构，把函数投递到W S A S o c k e t。下一章，我们将进一步探讨各主要协议的定址方法。</p>
		<p> </p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12098.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-06 19:00 <a href="http://www.cppblog.com/ivenher/articles/12098.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程三</title><link>http://www.cppblog.com/ivenher/articles/12097.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Wed, 06 Sep 2006 09:24:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12097.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12097.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12097.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12097.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12097.html</trackback:ping><description><![CDATA[
		<p> 第4章命名管道<br /> <br /> “命名管道”或“命名管线”（Named Pipes ）是一种简单的进程间通信（ I P C）机制， 命名管道可在同一台计算机的不同进程之间，或在跨越一个网络的<br />不同计算机的不同进程之间，支持可靠的、单向或双向的数据通信。</p>
		<p>要记住的一个重点是，将命名管道作为一种网络编程方案使用时，它实际上建立一个简单的客户机／服务器数据通信体系，可在其中可靠地传输数据。<br />4.1.1 命名管道命名规范命名管道的标识是采用U N C格式进行的：<br />\ \ s e r v e r \ P i p e \ [ p a t h ] n a m e<br />上述字串可分为三部分来观看： \ \ s e r v e r、\ P i p e和\ [ p a t h ] n a m e。第一部分\ \ s e r v e r指定一个服务器的名字。命名管道便是在那个服务器上创建的，而且要由它对进入的连接请求进行“监听”。第二部分\ P i p e是一个不可变化的“硬编码”字串（必须原样照录，但不用区分大小写），用于指出该文件从属于N P F S。而第三部分\ [ p a t h ] n a m e则使应用程序可以“唯一”定义<br />及标定一个命名管道的名字，而且可在这里设置多级目录。</p>
		<p>4.1.2 字节模式及消息模式<br />命名管道提供了两种基本通信模式：字节模式和消息模式。</p>
		<p>命名管道服务器应用只能在Windows NT或Windows 2000上工作——Windows 95和Windows 98 不允许应用程序创建命名管道！</p>
		<p>4.2.2 高级服务器的细节</p>
		<p>在前面的程序清单4 - 1中，我们展示了如何设计一个命名管道服务器应用，令其只负责对一个管道实例的控制。所有A P I调用都采用同步模式工作。在这种模式下，每个调用都会一直等到I / O请求完成，才会返回。命名管道服务器也能拥有多个管道实例，所以客户机能够建立<br />同服务器的两个或更多的连接；管道实例的数量要受到C r e a t e N a m e d P i p e这个A P I调用之n M a x I n s t a n c e s参数指定的数字的限制。要想同时控制不止一个的管道实例，服务器必须考虑使用多个线程，或者使用异步Win32 I/O机制（比如重叠式I / O以及完成端口等），分别为每个管道实例提供服务。</p>
		<p>采用异步I / O机制，服务器可从单独一个应用程序线程中，同时为所有管<br />道实例提供服务。在此，我们将解释如何使用线程以及重叠式I / O，来开发更高级的服务器应用。 </p>
		<p>1 . 线程<br />要想开发一个高级服务器，令其使用线程，同时支持多个管道实例，整个过程是非常简单的。我们要做的唯一事情便是为每个管道实例都创建一个线程.</p>
		<p>2. 重叠式I / O<br />重叠式I / O是一种特殊的输入／输出机制，允许Win32 API函数（如R e a d F i l e和Wr i t e F i l e）在发出I / O请求之后，以异步方式工作。具体的工作原理是：向这些A P I函数传递一个O V E R L A P P E D<br />（重叠式）结构，然后使用A P I函数G e t O v e r l a p p e d R e s u l t，从原来那个O V E R L A P P E D结构中，取得一次I / O请求的结果。如果在使用重叠式结构的前提下，调用一个Win32 API函数，那么调用无论如何都会立即返回！</p>
		<p>3. 安全模拟<br />之所以会选择命名管道作为自己的网络编程方案，一个最好的理由便是它们依赖于Windows NT及Windows 2000的安全机制，Windows NT和Windows 2000安全机制具有“模拟”能力，允许一个命名管道服务器<br />应用在客户机的安全环境中执行。执行一个命名管道服务器应用时，它通常会在用于启动该应用的那个进程的安全环境许可级别上工作。例如，假如拥有管理员权限的某人启动了一个命名管道服务器，服务器便有权访问Windows NT或Windows 2000系统上的几乎任何资源。此时，假如在C r e a t e N a m e d P i p e中指定的S E C U R I T Y _ D E S C R I P TO R结构允许所有用户访问这个<br />命名管道，就会埋下极大的安全隐患。</p>
		<p>4.5 小结<br />本章向大家介绍了命名管道网络编程技术，它为我们建立了一个简单的客户机／服务器数据通信体系，可确保数据进行可靠传输。接口依赖于Wi n d o w s重定向器，以便通过一个网络来传送数据。对命令管道而言，它最大的一项好处便是直接利用了Windows NT及Wi n d o w s2 0 0 0的安全机制，该机制是本书讲到的其他网络技术均不具备的一项好处！下面第二部分将向大家深入讲解Wi n s o c k技术，以便应用程序利用一种网络传输协议，进行“直接”通信。</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12097.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-06 17:24 <a href="http://www.cppblog.com/ivenher/articles/12097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows网络编程二</title><link>http://www.cppblog.com/ivenher/articles/12055.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Tue, 05 Sep 2006 11:31:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12055.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12055.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12055.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12055.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12055.html</trackback:ping><description><![CDATA[
		<p> 第2章重定向器<br /> 首先，我们打算解释如<br />何通过网络，使用“多U N C提供者”（ Multiple UNC Provider, MUP）资源定位符，通过“通用命名规范”（Universal Naming Convention, UNC）来引用远程文件。</p>
		<p>随后，我们讲解了M U P如何调用一个网络提供者，从而揭示出怎样通过一个重定向器，在“服务器消息块”（Server Message Block, SMB）协议的帮助下，在不同的计算机之间建立数据通信。</p>
		<p>最后，我们探讨了网络安全方面的一些问题。使用基本的文件I / O操作，通过网络来访问文件时，这些安全问题是必须考虑到的。</p>
		<p>2.1 通用命名规范</p>
		<p>“U N C路径”  为网络文件及设备的访问建立了一套统一的规范。它最大的特点便是不必指定或引用一个已映射到远程文件系统的本地驱动器字母。</p>
		<p>U N C名字完全解决了这些问题，它的格式如下：<br />\ \ [服务器] \ [共享名] \ [路径]<br />第一部分是\ \ [服务器]，必须以两个反斜杠开头，紧跟着一个服务器名字。<br />第二部分是\ [共享名]，它对应着远程服务器上的一个“共享入口”或者“共享位置”。<br />。而第三部分\ [路径]  对应的是共享位置下的某个具体目录（或子目录）</p>
		<p>
				<br />第3章邮槽</p>
		<p>一种简单的单向“进程间通信”（interprocess communication,I P C）机制。这个机制的名字非常古怪，叫作“邮槽”（M a i l s l o t）。用最简单的话来说，通过<br />邮槽，客户机进程可将消息传送或广播给一个或多个服务器进程。在同一台计算机的不同进程之间，或在跨越整个网络的不同计算机的进程之间，协助进行消息的传输。用邮槽来开发应用程序是一件非常简单的事情，不要求对T C P / I P或I P X这样的基层网络传送协议有着非常深入的了解。由于邮槽是围绕一个广播通信体系设计出来的，所以当然不能指望能通过它实现数据的“可靠”传输。</p>
		<p>邮槽最大的一个缺点便是只允许从客户机到服务器，建立一种不可靠的单向数据通信。<br />而另一方面，邮槽最大的一个优点在于，它们使客户机应用能够非常容易地将广播消息发送给一个或多个服务器应用。</p>
		<p>3.1 邮槽实施细节<br />邮槽是围绕Wi n d o w s文件系统接口设计出来的。客户机和服务器应用需要使用标准的Wi n 3 2文件系统I / O（输入／输出）函数，比如R e a d F i l e和Wr i t e F i l e等等，以便在邮槽上收发数据，同时利用Wi n 3 2文件系统的命名规则。邮槽必须依赖Wi n d o w s重定向器，通过一个“邮槽文件系统”（Mailslot File System, MSFS），来创建及标识邮槽。</p>
		<p>3.1.1 邮槽的名字<br />对邮槽进行标识时，需遵守下述命名规则：<br />\ \ s e r v e r \ M a i l s l o t \ [ p a t h ] n a m e<br />请将上述字串分为三段来看： \ \ s e r v e r、\ M a i l s l o t和\ [ p a t h ] n a m e。第一部分\ \ s e r v e r对应于服务器的名字，我们要在上面创建邮槽，并在在上面运行服务器程序。第二部分\ M a i l s l o t是一个“硬编码”的固定字串，用于告诉系统这个文件名从属于M S F S。而第三部分\ [ p a t h ] n a m e则<br />允许应用程序独一无二地定义及标识一个邮槽名。其中，“p a t h”代表路径，可指定多级目录。<br />举个例子来说，对一个邮槽进行标识时，下面这些形式的名字都是合法的（注意M a i l s l o t不得变化，必须原文照输，亦即所谓的“硬编码”）：<br />由于邮槽要依赖Wi n d o w s文件系统服务在网上来创建和传输数据，所以接口是“与协议无关”的。<br />要想保证各种Wi n d o w s平台之间能够完全正常地通信，强烈建议将消息长度限制在4 2 4字节，或者更短。如果进行面向连接的传输，可考虑使用命名管道，而不是简单的邮槽。</p>
		<p>3.5 小结<br />本章讲解了邮槽（ M a i l s l o t）网络编程技术。利用这一技术，应用程序可以在Wi n d o w s重定向器的帮助下，实现简单的单向进程间数据通信。对邮槽来说，它最有价值的一项功能便是通过网络，将一条消息广播给一台或多台计算机。然而，邮槽并未提供对数据可靠传输的保障。假如希望用Wi n d o w s重定向器实现“可靠”的数据通信，请考虑使用命名管道，这是下一章的主题！</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12055.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-05 19:31 <a href="http://www.cppblog.com/ivenher/articles/12055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows 网络编程</title><link>http://www.cppblog.com/ivenher/articles/12040.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Tue, 05 Sep 2006 08:46:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/12040.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/12040.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/12040.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/12040.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/12040.html</trackback:ping><description><![CDATA[
		<p>1、netBIOS<br />非可路由协议<br />LAN适配器（LAN  adapter）编号很重要.<br />每个LANA编号对应于网卡和传输协议的唯一组合。</p>
		<p>netBIOS名字<br />在Wi n 3 2环境中，针对每个可用的L A N A编号，每<br />个进程都会为其维持一张N e t B I O S名字表。若为LANA 0增添一个名字，意味着你的应用程序<br />只能在LANA 0上同客户机建立连接。对每个L A N A来说，能够添加的名字的最大数量是2 5 4，<br />编号从1到2 5 4（0和2 5 5由系统保留）<br />，N e t B I O S名字共有两个性质：唯一名字和组名<br />微软网络中的机器命名就是NetBIOS命名。<br />“组名”的作用是将数据同时发给多个接收者；或者相反，接收发给多个<br />接收者的数据。组名并非一定要“独一无二”，它主要用于多播（多点发送）数据通信<br />若有“windows互联网命名服务器”即wins，则有它管理，若无则发广播探测是否重名。</p>
		<p>1.1.3 NetBIOS特性</p>
		<p>N e t B I O S同时提供了“面向连接”服务以及“无连接”服务。</p>
		<p>1.2 NetBIOS编程基础<br />NetBIOS API的设置，只有一个函数：<br />UCHAR Netbios(PNCB pNCB);</p>
		<p>用于N e t B I O S的所有函数声明、常数等等均是在头文件N b 3 0 . h内定义的。若想连接<br />N e t B I O S应用，唯一需要的库是N e t a p i 3 2 . l i b。<br />调用N e t b i o s函数时，可选择进行同步调用，还是进行异步调用。所有N e t B I O S命令本身均是同步的。要想异步调用一个命令，需要让N e t B I O S命令同A S Y N C H标志进行一次逻辑O R（或）运算。如指定了A S Y N C H标志，那么必须在n c b _ p o s t字段中指定一个后例程（Post Routine），或必须在n c b _e v e n t字段中指定一个事件句柄。执行一个异步命令时，从N e t b i o s返回的值是N R C _ G O O D R E T( 0 x 0 0 )，但n c b _ c m d _ c p l t字段会设为N R C _ P E N D I N G ( 0 x F F )。除此以外， N e t b i o s函数还会将N C B结构的n c b _ c m d _ c p l t字段设为N R C _ P E N D I N G（待决），直到命令完成为止。命令完成后，n c b _ c m d _ c p l t字段会设为该命令的返回值。N e t b i o s也会在完成后将n c b _ r e t c o d e字段设为命令的返回值。<br /><br /><br />1.4 数据报的工作原理</p>
		<p>“数据报”（D a t a g r a m）属于一种“无连接”的通信方法。作为发送方，只需用目标<br />N e t B I O S名字为发出的每个包定址，然后简单地送出了事。此时，不会执行任何检查，以确<br />保数据的完整性、抵达顺序或者传输的可靠性等等。</p>
		<p>发出一个数据报共有三种方式。<br />第一种是指挥数据报抵达一个特定的（或唯一的）组名。这意味着只能有一个进程负责数据报的接收—亦即注册了目标名字的那个进程。<br />第二种是将数据报发给一个组名。只有注册了指定组名的那些进程才有权接收消息。<br />最后，第三种方式是将数据报广播到整个网络。</p>
<img src ="http://www.cppblog.com/ivenher/aggbug/12040.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2006-09-05 16:46 <a href="http://www.cppblog.com/ivenher/articles/12040.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>