??xml version="1.0" encoding="utf-8" standalone="yes"?>国产精品视频久久久,国产精品欧美久久久久无广告,99久久这里只精品国产免费http://www.shnenglu.com/ivenher/category/1501.htmlzh-cnMon, 19 May 2008 21:52:22 GMTMon, 19 May 2008 21:52:22 GMT60CSocket 初始?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/19821.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/19821.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/19821.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/19821.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/19821.html</trackback:ping><description><![CDATA[如果在线E中使用socketQ也要用  AfxSocketInit(); q行初始化?img src ="http://www.shnenglu.com/ivenher/aggbug/19821.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2007-03-14 16:10 <a href="http://www.shnenglu.com/ivenher/articles/19821.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程十九(ji)http://www.shnenglu.com/ivenher/articles/12529.html爱饭?/dc:creator>爱饭?/author>Fri, 15 Sep 2006 14:05:00 GMThttp://www.shnenglu.com/ivenher/articles/12529.htmlhttp://www.shnenglu.com/ivenher/comments/12529.htmlhttp://www.shnenglu.com/ivenher/articles/12529.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/12529.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12529.html8.2.4 重叠模型
在Wi n s o c k中,相比我们q今为止解释q的其他所有I / O模型Q重叠I(yng) / OQOverlapped I/OQ?br />模型使应用程序能辑ֈ更佳的系l性能。重叠模型的基本设计原理便是让应用程序用一个重叠的数据l构Q一ơ投递一个或多个Winsock I/Oh。针寚w些提交的hQ在它们完成之后Q应用程序可为它们提供服务。该模型适用于除Windows CE之外的各UWi n d o w sq_?br />模型的M设计以Wi n 3 2重叠I(yng) / O机制为基。那个机制可通过R e a d F i l e和W(xu)r i t e F i l e两个函数Q针对设备执行I / O操作?br />最开始的时候,W(xu)i n s o c k重叠I(yng) / O模型只能应用于Windows NT操作pȝ上运行的Wi n s o c k1 . 1应用E序。作为应用程序,它可针对一个套接字句柄Q调用R e a d F i l e以及(qing)Wr i t e F i l eQ同时指定一个重叠式l构Q稍后详qͼ(j)Q从而利用这个模型。自Winsock 2发布开始,重叠I(yng) / O便已集成到新的Wi n s o c k函数中,比如W S A S e n d和W(xu) S A R e c v。这样一来,重叠I(yng) / O模型便能适用于安装了(jin)Winsock 2的所有Wi n d o w sq_?/p>

注意在Winsock 2发布之后Q重叠I(yng)/O仍可在Windows NT和W(xu)indows 2000q两个操作系l中Q随R e a d F i l e和W(xu)r i t e F i l e两个函数使用。但是,W(xu)indows 95和W(xu)indows 98均不具备q一功能。考虑到应用程序的跨^台兼定w题,同时考虑到性能斚w的因素,应尽量避免用Wi n 3 2的R e a d F i l e和W(xu)r i t e F i l e函数Q分别换以W S A R e c v和W(xu) S A S e n d函数。本节只打算讲述通过新的Winsock 2函数Q来使用重叠I(yng)/O模型?br />要想在一个套接字上用重叠I(yng) / O模型Q首先必M用W S A _ F L A G _ O V E R L A P P E Dq个标志Q创Z个套接字。如下所C:(x)

  s = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
  创徏套接字的时候,假如使用的是s o c k e t函数Q而非W S A S o c k e t函数Q那么会(x)默认讄W S A _ F L A G _ O V E R L A P P E D标志。成功徏好一个套接字Q同时将其与一个本地接口绑定到一起后Q便可开始进行重叠I(yng) / O 操作Q方法是调用下述的Wi n s o c k 函数Q同时指定一?br />W S A O V E R L A P P E Dl构Q可选)(j)Q?br />?W S A S e n d
?W S A S e n d To
?W S A R e c v
?W S A R e c v F r o m
?W S A I o c t l
?A c c e p t E x
?Tr n a s m i t F i l e
大家现在或许已经知道Q其中每个函数都与一个套接字上数据的发送、数据接收以?qing)连接的接受有关。因此,q些zd可能?x)花极少的时间才能完成。这正是每个函数都可接受一个W S A O V E R L A P P E Dl构作ؓ(f)参数的原因。若随一个W S A O V E R L A P P E Dl构一赯用这些函敎ͼ函数?x)立卛_成ƈq回Q无论套接字是否设ؓ(f)锁定模式Q本章开头已详细讲过?jin)?j)。它
们依赖于W S A O V E R L A P P E Dl构来返回一个I / Oh的返回?br />主要有两个方法可用来理一个重叠I(yng) / Oh的完成:(x)我们的应用程序可{待“事件对象通知”,亦可通过“完成例E”,对已l完成的h加以处理。上面列出的函数Q?A c c e p t E x除外Q还有另一个常用的参数Q?br />l p C o m p l e t i o n R O U T I N E。该参数指定的是一个可选的指针Q指向一个完成例E函敎ͼ在重叠请求完成后调用。接下去Q我们将探讨事g通知Ҏ(gu)。在本章E后Q还?x)介l如何用可选的完成例程Q代替事Ӟ对完成的重叠h加以处理?br />1. 事g通知
重叠I(yng) / O的事仉知Ҏ(gu)要求Wi n 3 2事g对象与W S A O V E R L A P P E Dl构兌在一赗若使用一个W S A O V E R L A P P E Dl构Q发出像W S A S e n d和W(xu) S A R e c vq样的I / O调用Q它们会(x)立即q回?br />通常Q大家会(x)发现q些I / O 调用?x)以p|告终Q返回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函数Q便可获得与错误状态有关的一个报告。这个错误状态意味着I / O操作正在q行。稍后的某个旉Q我们的应用E序需要等候与W S A O V E R L A P P E Dl构对应的事件对象,?jin)解一个重叠I(yng) / OL(fng)何时完成。W S A O V E R L A P P E Dl构在一个重叠I(yng) / Oh的初始化Q及(qing)其后l的完成之间Q提供了(jin)一U沟通或通信机制。下面是q个l构的定义:(x)

 typedef struct WSAOVERLAPPED
 {
  DWORD I(yng)nternal;
  DWORD InternalHigh;
  DWORD Offset;
  DWORD OffsetHigh;
  WSAEVENT hEvent;
 }WSAOVERLAPPED,FAR *LPWSAOVERLPPED;
 
其中QI 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字段均由pȝ在内部用,不应由应用程序直接进行处理或使用。而另一斚wQ?h E v e n t字段有点儿特D,它允许应用程序将一个事件对象句柄同一个套接字兌h。大家可能会(x)觉得奇怪,如何一个事件对象句柄分配给该字D呢Q正如我们早先在W S A E v e n t S e l e c t模型中讲q的那样Q可用W S A C r e a t e E v e n t函数来创Z个事件对象句柄。一旦创建好一个事件句柄,单地重叠结构的h E v e n t字段分配l事件句柄,再用重叠结构,调用一个Wi n s o c k函数卛_Q比如W S A S e n d或W S A R e c v?br />一个重叠I(yng) / Oh最l完成后Q我们的应用E序要负责取回重叠I(yng) / O操作的结果。一个重叠请求操作最l完成之后,在事仉知Ҏ(gu)中, Wi n s o c k?x)更改与一个W S A O V E R L A P P E Dl构对应的一个事件对象的事g传信状态,其从“未传信”变成“已传信”。由于一个事件对象已分配lW S A O V E R L A P P E Dl构Q所以只需单地调用W S AWa i t F o r M u l t i p l e E v e n t s函数Q从而判断出一个重叠I(yng) / O调用在什么时候完成。该函数已在我们前面讲述WSAEventSelect I/O模型时介l过?jin)。W S AWa i t F o r M u l t i p l e E v e n t s函数?x)等候一D规定的旉Q等待一个或多个事g对象q入“已传信”状态。在此再ơ提醒大家注意:(x) W S AWa i t F o r M u l t i p l e E v e n t s函数一?br />最多只能等? 4个事件对象。发Cơ重叠请求完成之后,接着需要调用W S A G e t O v e r l a p p e dR e s u l tQ取得重叠结构)(j)函数Q判断那个重叠调用到底是成功Q还是失败。该函数的定义如下:(x)
 BOOL WSAGetOverlappedResult(
                SOCKET s,
                LPWSAOVERLAPPED lpOverlapped,
                LPWORD lpcbTransfer,
                BOOL  fWait,
                LPWORD lpdwFlags
               );
其中Q?s参数用于指定在重叠操作开始的时候,与之对应的那个套接字。l p O v e r l a p p e d参数是一个指针,对应于在重叠操作开始时Q指定的那个W S A O V E R L A P P E D l构?br />l p c b Tr a n s f e r参数也是一个指针,对应一个D W O R DQ双字)(j)变量Q负责接收一ơ重叠发送或接收操作实际传输的字节数。f Wa i t参数用于军_函数是否应该{待一ơ待冻I未决Q的重叠操作完成。若f Wa i t设ؓ(f)T R U EQ那么除非操作完成,否则函数不会(x)q回Q若设ؓ(f)FA L S EQ而且操作仍然处于“待决”状态,那么W S A G e t O v e r l a p p e d R e s u l t函数?x)返回FA L S E|同时q回一个W S A _ I O _ I N C O M P L E T EQI / O操作未完成)(j)错误。但我们目前的情况来说Q由于需要等候重叠操作的一个已传信事g完成Q所以该参数无论采用什么设|,都没有Q何效果?br />最后一个参数是l p d w F l a g sQ它对应于一个指针,指向一个D W O R DQ双字)(j)Q负责接收结果标志(假如原先的重叠调用是用W S A R e c v或W S A R e c v F r o m函数发出的)(j)?br />如W S A G e t O v e r l a p p e d R e s u l t函数调用成功Q返回值就是T R U E。这意味着我们的重叠I(yng) / O操作已成功完成,而且由l p c b Tr a n s f e r参数指向的值已q行?jin)更新。若q回值是FA L S EQ那么可能是׃qCQ何一U原因造成的:(x)
?重叠I(yng) / O操作仍处在“待决”状态?br />?重叠操作已经完成Q但含有错误?br />?重叠操作的完成状态不可判冻I因ؓ(f)在提供给W S A G e t O v e r l a p p e d R e s u l t函数的一个或
多个参数中,存在着错误?/p>

p|后,由l p c b Tr a n s f e r参数指向的g?x)进行更斎ͼ而且我们的应用程序应调用W S A G e t L a s t E r r o r函数Q调查到底是何种原因造成?jin)调用失败?br />在程序清? - 7中,我们向大安qC(jin)如何~制一个简单的服务器应用,令其在一个套接字上对重叠I(yng) / O操作q行理Q程序完全利用了(jin)前述的事仉知机制。对该程序采用的~程步骤ȝ如下Q?br />1) 创徏一个套接字Q开始在指定的端口上监听q接h?br />2) 接受一个进入的q接h?br />3) 为接受的套接字新Z个W S A O V E R L A P P E Dl构Qƈl构分配一个事件对象句柄?br />也将事g对象句柄分配l一个事件数l,以便E后由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 vhQ指定参Cؓ(f)W S A O V E R L A P P E Dl构?br />注意函数通常?x)以p|告终Q返回S O C K E T _ E R R O R错误状态W S A _ I O _ P E N D I N GQI/O操作未完成Q?br />5) 使用步骤3 )的事件数l,调用W S AWa i t F o r M u l t i p l e E v e n t s函数Qƈ{待与重叠调用关联在一L(fng)事gq入“已传信”状态(换言之,{待那个事g的“触发”)(j)?br />6) WSAWa i t F o r M u l t i p l e E v e n t s函数完成后,针对事g数组Q调用W S A R e s e t E v e n tQ重设事Ӟ(j)函数Q从而重设事件对象,q对完成的重叠请求进行处理?br />7) 使用W S A G e t O v e r l a p p e d R e s u l t函数Q判断重叠调用的q回状态是什么?br />8) 在套接字上投递另一个重叠W S A R e c vh?br />9) 重复步骤5 ) ~ 8 )?br />q个例子极易扩展Q提供对多个套接字的支持。方法是代码的重叠I(yng) / O处理部分U至一个独立的U程内,让主应用E序U程为附加的q接h提供服务?br />E序清单8-7 采用事g机制的简单重叠I(yng) / O处理CZ



]]>
windows|络~程十七http://www.shnenglu.com/ivenher/articles/12512.html爱饭?/dc:creator>爱饭?/author>Fri, 15 Sep 2006 07:43:00 GMThttp://www.shnenglu.com/ivenher/articles/12512.htmlhttp://www.shnenglu.com/ivenher/comments/12512.htmlhttp://www.shnenglu.com/ivenher/articles/12512.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/12512.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12512.html8.2.3 WSAEventSelect
Wi n s o c k提供?jin)另一个有用的异步I / O模型。和W(xu) S A A s y n c S e l e c t模型cM的是Q它也允许应用程序在一个或多个套接字上Q接收以事g为基的网l事仉知。对于表8 - 3ȝ的、由W S A A s y n c S e l e c t模型采用的网l事件来_(d)它们均可原封不动地移植到新模型。在用新模型开发的应用E序中,也能接收和处理所有那些事件。该模型最主要的差别在于网l事件会(x)投递至一个事件对象句柄,而非投递至一个窗口例E?br />事g通知
事g通知模型要求我们的应用程序针Ҏ(gu)用的每一个套接字Q首先创Z个事件对象。创建方法是调用W S A C r e a t e E v e n t函数Q它的定义如下:(x)

WSAEVENT WSACreateEvent(void);

W S A C r e a t e E v e n t函数的返回值很单,是一个创建好的事件对象句柄。事件对象句柄到手后Q接下来必须其与某个套接字兌在一P同时注册自己感兴的|络事gcdQ如? - 3所C。要做到q一点,Ҏ(gu)是调用W S A E v e n t S e l e c t函数Q对它的定义如下Q?/p>

int WSAEventSelect(
          SOCKET s, 
          WSAEVENT hEventObject,
          long lNetwordEvents
         );
 其中Q?s参数代表自己感兴的套接字。h E v e n t O b j e c t参数指定要与套接字关联在一L(fng) 事g对象—用W S A C r e a t e E v e n t取得的那一个。而最后一个参数l(f) N e t w o r k E v e n t sQ则对应一个“位掩码”,用于指定应用E序感兴的各种|络事gcd的一个组合(如表8 - 3所C)(j)。要惌知对q些事gcd的详l说明,请参考早先讨的WSAAsyncSelect I/O模型?br />为W S A E v e n t S e l e c t创徏的事件拥有两U工作状态,以及(qing)两种工作模式。其中,两种工作状态分别是“已传信”(s i g n a l e dQ和“未传信”(n o n s i g n a l e dQ。工作模式则包括“h工重䏀(manual resetQ和“自动重䏀(auto resetQ。W S A C r e a t e E v e n t最开始在一U未传信的工作状态中Qƈ用一Uh工重设模式,来创Z件句柄。随着|络事g触发?jin)与一个套接字兌?br />一L(fng)事g对象Q工作状态便?x)从“未传信”{变成“已传信”。由于事件对象是在一Uh工重设模式中创徏的,所以在完成?jin)一个I / Oh的处理之后,我们的应用程序需要负责将工作状态从已传信更改ؓ(f)未传信。要做到q一点,可调用W S A R e s e t E v e n t函数Q对它的定义如下Q?/p>

 BOOL  WSAResetEvent(WSAEVENT hEvent);
 
 该函数唯一的参C是一个事件句柄;Z调用是成功还是失败,?x)分别返回T R U E或FA L S E。应用程序完成了(jin)对一个事件对象的处理后,便应调用W S A C l o s e E v e n t函数Q释攄事g句柄使用的系l资源。对W S A C l o s e E v e n t函数的定义如下:(x)

 BOOL WSACloseEvent(WSAEVENT hEvent);
 
 该函C要拿一个事件句柄作己唯一的参敎ͼq会(x)在成功后q回T R U EQ失败后q回FA L S E?br />一个套接字同一个事件对象句柄关联在一起后Q应用程序便可开始I / O处理Q方法是{待|络事g触发事g对象句柄的工作状态。W S AWa i t F o r M u l t i p l e E v e n t s函数的设计宗旨便是用来等待一个或多个事g对象句柄Qƈ在事先指定的一个或所有句柄进入“已传信”状态后Q或在超q了(jin)一个规定的旉周期后,立即q回。下面是W S AWa i t F o r M u l t i p l e E v e n t s函数的定义:(x)

DWORD WSAWaitForMultipleEvents(
                DWORD cEvent,
                const WSAEVENT FAR * lphEvents,
                BOOL  fWaitAll,
                DWORD dwTimeout,
                BOOL  fAlertable
               );
               
 其中Q?c E v e n t s和l p h E v e n t s参数定义?jin)由W S A E V E N T对象构成的一个数l。在q个数组中,c E v e n t s指定的是事g对象的数量,而l p h E v e n t s对应的是一个指针,用于直接引用该数l?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对象规定的一个最大|在此定义? 4个。因此,针对发出W S AWa i t F o r M u l t i p l e E v e n t s调用的每个线E,该I / O模型一ơ最多都只能支持6 4个套接字。假如想让这个模型同时管理不? 4个套
接字Q必d建额外的工作者线E,以便{待更多的事件对象。f Wa i t A l l 参数指定?jin)W S AWa i t F o r M u l t i p l e E v e n t s如何{待在事件数l中的对象。若设ؓ(f)T R U EQ那么只有等l p h E v e n t s数组内包含的所有事件对象都已进入“已传信”状态,函数才会(x)q回Q但若设为FA L S EQQ何一个事件对象进入“已传信”状态,函数׃(x)q回。就后一U情冉|_(d)q回值指Z(jin)到底是哪个事件对象造成?jin)函数的q回。通常Q应用程序应该参数设ؓ(f)FA L S EQ?br />一ơ只Z个套接字事g提供服务。d w Ti m e o u t参数规定?jin)W S AWa i t F o r M u l t i p l e E v e n t s最多可{待一个网l事件发生有多长旉Q以毫秒为单位,q是一“超时”设定。超q规定的旉Q函数就?x)立卌回,即由f Wa i t A l l参数规定的条件尚未满也如此。如时gؓ(f)0Q函C(x)(g)指定的事g对象的状态,q立卌回。这样一来,应用E序实际便可实现对事件对
象的“轮询”。但考虑到它Ҏ(gu)能造成的媄(jing)响,q是应尽量避免将时D?。假如没有等待处理的事gQ?W S AWa i t F o r M u l t i p l e E v e n t s便会(x)q回W S A _ WA I T _ T I M E O U T。如d w s Ti m e o u t设ؓ(f)W S A _ I N F I N I T EQ永q等待)(j)Q那么只有在一个网l事件传信了(jin)一个事件对象后Q函数才
?x)返回。最后一个参数是f A l e r t a b l eQ在我们使用W S A E v e n t S e l e c t模型的时候,它是可以忽略的,且应设ؓ(f)FA L S E。该参数主要用于在重叠式I / O模型中,在完成例E的处理q程中用?br />本章后面q会(x)Ҏ(gu)详述?/p>

若W S AWa i t F o r M u l t i p l e E v e n t s收到一个事件对象的|络事g通知Q便?x)返回一个|指出造成函数q回的事件对象。这样一来,我们的应用程序便可引用事件数l中已传信的事gQƈ(g)索与那个事g对应的套接字Q判断到底是在哪个套接字上,发生?jin)什么网l事件类型?br />对事件数l中的事件进行引用时Q应该用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 _ 0Q得到具体的引用|即烦(ch)引位|)(j)。如下例所C:(x)

Index = WSAWaitForMultipleEvents(...);
MyEvent = EventArray[Index - WSA_WAIT_EVENT_0];
知道?jin)造成|络事g的套接字后,接下来可调用W S A E n u m N e t w o r k E v e n t s函数Q调查发生了(jin)什么类型的|络事g。该函数定义如下Q?/p>

 int WSAEnumNetworkEvents(
              SOCKET s,
              WSAEVENT hEventObject,
              LPWSANETWORKEVENTS lpNetworkEvents
             );
 s参数对应于造成?jin)网l事件的套接字。h E v e n t O b j e c t参数则是可选的Q它指定?jin)一个事件句柄,对应于打重讄那个事g对象。由于我们的事g对象处在一个“已传信”状态,所以可它传入Qo(h)其自动成为“未传信”状态。如果不想用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 函数Q?该函数早先已l讨?jin)。最后一个参数是l p N e t w o r k E v e n t sQ代表一个指针,指向W S A N E T W O R K E V E N T Sl构Q用于接收套接字上发
生的|络事gcd以及(qing)可能出现的Q何错误代码。下面是W S A N E T W O R K E V E N T Sl构的定义:(x)

 typedef struct _WSANETWORKEVENTS
 {
  long lNetworkEvents;
  int iErrorCode[FD_MAX_EVENTS];
 }WSANETWORKEVENTS,FAR * LPWSANETWORKEVENTS;
 
 l N e t w o r k E v e n t s参数指定?jin)一个|对应于套接字上发生的所有网l事件类型(参见? - 3Q?br />注意一个事件进入传信状态时Q可能会(x)同时发生多个|络事gcd。例如,一个繁?br />的服务器应用可能同时收到FD_READ和FD_WRITE通知?br />i E r r o r C o d e参数指定的是一个错误代码数l,同l N e t w o r k E v e n t s中的事g兌在一赗针Ҏ(gu)个网l事件类型,都存在着一个特D的事g索引Q名字与事gcd的名字类|只是要在事g名字后面d一个?_ B I T”后~字串卛_。例如,对F D _ R E A D事gcd来说Qi E r r o r C o d e数组的烦(ch)引标识符便是F D _ R E A D _ B I T。下qC码片断对此进行了(jin)阐释Q针对F D _ R E A D事gQ:(x)

  if(NetWorkEvents.lNetworkEvents & FD_READ)
  {
   if(NetWorkEvents.iErrorCode[FD_READ_BIT] != 0)
   {
    printf("FD_READ FAILED with error %d\n",NetWorkEvents.iErrorCode[FD_READ_BIT]);
   }
  }

完成?jin)对W S A N E T W O R K E V E N T Sl构中的事g的处理之后,我们的应用程序应在所有可用的套接字上Ql等待更多的|络事g。在E序清单8 - 6中,我们阐释?jin)如何用W S A E v e n t S e l e c tq种I / O模型Q来开发一个服务器应用Q同时对事g对象q行理。这个程序主要着g开发一个基本的服务器应用要涉及(qing)到的步骤Qo(h)其同时负责一个或多个套接字的理?/p>

完成?jin)对W S A N E T W O R K E V E N T Sl构中的事g的处理之后,我们的应用程序应在所有可用的套接字上Ql等待更多的|络事g。在E序清单8 - 6中,我们阐释?jin)如何用W S A E v e n t S e l e c tq种I / O模型Q来开发一个服务器应用Q同时对事g对象q行理。这个程序主要着g开发一个基本的服务器应用要涉及(qing)到的步骤Qo(h)其同时负责一个或多个套接字的理?br />E序清单8-6 采用WSAEventSelect I/O模型的示范服务器源代?/p>

]]>
windows|络~程十八http://www.shnenglu.com/ivenher/articles/12419.html爱饭?/dc:creator>爱饭?/author>Wed, 13 Sep 2006 09:35:00 GMThttp://www.shnenglu.com/ivenher/articles/12419.htmlhttp://www.shnenglu.com/ivenher/comments/12419.htmlhttp://www.shnenglu.com/ivenher/articles/12419.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/12419.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12419.html用于初始化Winsock
[声明]
int WSAStarup(WORD wVersionRequested,LPWSADATA lpWSAData);
[参数]
wVersionRequested - 要求使用Winsock的最低版本号
lpWSAData - Winsock的详l资?br />[q回值]
当函数成功调用时q回0
p|时返回非0的?

2、socket函数
用于生成socket(soket Descriptor)
[声明]
SOCKET socketQint af,int type,int protocolQ?
[参数]
af - 地址家族(通常使用:AF_INET)
type - socket的种c?br />SOCK_STREAM : 用于TCP协议
SOCK_DGRAM : 用于UDP协议
protocol - 所使用的协?br />[q回值]
当函数成功调用时q回一个新的SOCKET(Socket Descriptor)
p|时返回INVALID_SOCKET.

3、inet_addr函数
把好?xxx.xxx.xxx.xxx"?0q制的IP地址转换?2位整数表C方?br />[声明]
unsigned long inet_addr ( const char FAR *cp );
[参数]
cp - 指向?xxx.xxx.xxx.xxx"?0q制来表C的IP地址字符串的指针
[q回值]
当函数成功调用时q回?2位整数表C的IP地址(按网l字节排列顺?
p|时返回INADDR_NONE.

4、gethostbyname函数
可以从主机名获取L资料.
[声明]
struct hostent FAR * gethostbyname ( const char FAR *name );
[参数]
name - 指向L名字W串的指?br />[q回值]
当函数成功调用时q回L信息
p|时返回NULL(I?

5、Bind函数
指定本地IP地址所使用的端口号时候?br />[声明]
int bind ( SOCKET s , const struct sockaddr FAR *addr , int namelen );
[参数]
s - 指向用Socket函数生成的Socket Descriptor
addr - 指向Socket地址的指?br />namelen - 该地址的长?
[q回值]
当函数成功调用时q回0
调用p|时返?SOCKET_ERROR

6、connect函数
用于与服务器建立q接,发出q接h,必须在参C指定服务器的IP地址和端口号
[声明]
int connect (SOCKET s , const struct sockaddr FAR *name , int namelen );
[参数]
s - 指向用Socket函数生成的Socket Descriptor
name - 指向服务器地址的指?br />namelen - 该地址的长?
[q回值]
当函数成功调用时q回0
调用p|时返?SOCKET_ERROR

7、select函数
可以用于调查一个或多个SOCKET的状?
[声明]
int select ( int nfds , fd_set FAR *readfds , fd_set FAR *writefds , fd_set FAR *exceptfds , const struct timeval FAR *timeout );
[参数]
nfds - 在WINDOWS SOCKET API 中该参数可以忽略,通常赋予NILL?br />readfds - ׃接受的SOCKET讑֤的指?br />writefds - 用于发送数据的SOCKET讑֤的指?br />exceptfds - (g)查错误的状?br />timeout - 时讑֮
[q回值]
q回大于0的值时,表示与条件相W的SOCKET?br />q回0表示时
p|时返回SOCKET_ERROR

8、recv函数
利用Socketq行接受数据.
[声明]
int recv ( SOCKET s , char FAR *buf , int len , int flags );
[参数]
s - 指向用Socket函数生成的Socket Descriptor
buf - 接受数据的缓冲区(数组)的指?br />len - ~冲区的大小
flag - 调用方式(MSG_PEEK ?MSG_OOB)
[q回值]
成功时返回收到的字节?
如果q接被中断则q回0
p|时返?SOCKET_ERROR

9、sendto函数
利用Socketq行发送数?
[声明]
int sendto ( SOCKET s , const char FAR *buf , int len , int flags , const struct sockaddr FAR *to , int token );
[参数]
s - 指向用Socket函数生成的Socket Descriptor
buf - 接受数据的缓冲区(数组)的指?br />len - ~冲区的大小
flag - 调用方式(MSG_DONTROUTE , MSG_OOB)
to - 指向发送方SOCKET地址的指?br />token - 发送方SOCKET地址的大?
[q回值]
成功时返回已l发送的字节?
p|时返回SOCKET_ERROR



]]>
windows|络~程十六http://www.shnenglu.com/ivenher/articles/12375.html爱饭?/dc:creator>爱饭?/author>Tue, 12 Sep 2006 08:52:00 GMThttp://www.shnenglu.com/ivenher/articles/12375.htmlhttp://www.shnenglu.com/ivenher/comments/12375.htmlhttp://www.shnenglu.com/ivenher/articles/12375.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/12375.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12375.html8.2.2 WSAAsyncSelect
Wi n s o c k提供?jin)一个有用的异步I / O模型。利用这个模型,应用E序可在一个套接字上,接收以Wi n d o w s消息为基的网l事仉知。具体的做法是在建好一个套接字后,调用W S A A s y n c S e l e c t函数。该模型最早出CWi n s o c k? . 1版本中,用于帮助应用E序开发者面向一些早期的1 6位Wi n d o w sq_Q如Windows for Wo r k g r o u p sQ,适应其“落后”的多Q务消息环境。应用程序仍可从q种模型中得到好处,特别是它们用一个标准的Wi n d o w s例程Q常
UCؓ(f)?w i n p r o c”)(j)Q对H口消息q行理的时候。该模型亦得C(jin)Microsoft Foundation Class
Q微软基本类QM F CQ对象C S o c k e t的采U?br />消息通知
要想使用W S A A s y n c S e l e c t模型Q在应用E序中,首先必须用C r e a t e Wi n d o w函数创徏一个窗口,再ؓ(f)该窗口提供一个窗口例E支持函敎ͼ Wi n p r o cQ。亦可用一个对话框Qؓ(f)其提供一个对话例E,而非H口例程Q因为对话框本质也是“窗口”。考虑到我们的目的Q我们打用一个简单的H口来演CU模型,采用的是一个支持窗口例E。设|好H口的框架后Q便可开始创建套接字Qƈ调用W S A A s y n c S e l e c t函数Q打开H口消息通知。该函数的定义如下:(x)

int WSAAsyncSelect(
          SOCKET s,
          HWND hWnd,
          unsigned  int wMsg,
          long lEvent
         );

其中Q?s参数指定的是我们感兴的那个套接字。h W n d参数指定的是一个窗口句柄,它对应于|络事g发生之后Q想要收到通知消息的那个窗口或对话框。w M s g参数指定在发生网l事件时Q打接收的消息。该消息?x)投递到由h W n dH口句柄指定的那个窗口。通常Q应用程序需要将q个消息设ؓ(f)比Wi n d o w s的W M _ U S E R大的一个|避免|络H口消息与预定义的标准窗口消息发生؜淆与冲突。最后一个参数是l E v e n tQ它指定的是一个位掩码Q对应于一
pd|络事g的组合(请参考表8 - 3Q,应用E序感兴的便是q一pd事g。大多数应用E序通常感兴的|络事gcd包括Q?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。当?dng)到底使用F D _ A C C E P TQ还是用F D _ C O N N E C TcdQ要取决于应用程序的w䆾到底是一个客h呢,q是一个服务器。如应用E序同时对多个网l事件有兴趣Q只需对各U类型执行一ơ简单的按位O RQ或Q运,然后它们分配给l E v e n t可以了(jin)。D个例子来_(d)(x)

WSAAsyncSelect(s,hWnd,WM_SOCKET,FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE);

q样一来,我们的应用程序以后便可在套接字s上,接收到有兌接、发送、接收以?qing)?br />接字关闭q一pd|络事g的通知。特别要注意的是Q多个事件务必在套接字上一ơ注册!
另外q要注意的是Q一旦在某个套接字上允许?jin)事仉知Q那么以后除非明调用c l o s e s o c k e t命o(h)Q或者由应用E序针对那个套接字调用了(jin)W S A A s y n c S e l e c tQ从而更改了(jin)注册的网l事件类型,否则的话Q事仉知?x)永q有效!若将l E v e n t参数设ؓ(f)0Q效果相当于停止在套接字上进行的所有网l事仉知?/p>

若应用程序针对一个套接字调用?jin)W S A A s y n c S e l e c tQ那么套接字的模式会(x)从“锁定”自动变成“非锁定”,我们在前面已提到q这一炏V这样一来,假如调用?jin)像W S A R e c vq样的Wi n s o c k  I / O函数Q但当时却ƈ没有数据可用Q那么必然会(x)造成调用的失败,q返回W S A E W O U L D B L O C K
错误。ؓ(f)防止q一点,应用E序应依赖于由W S A A s y n c S e l e c t的u M s g参数指定的用戯定义H口消息Q来判断|络事gcd何时在套接字上发生;而不应盲目地q行调用?/p>

?-3 用于W S A A s y n c S e l e c t函数的网l事件类?/p>

F D _ R E A D             应用E序惌接收有关是否可读的通知Q以便读入数?br />F D _ W R I T E          应用E序惌接收有关是否可写的通知Q以便写入数?br />F D _ O O B                应用E序x收是否有带外Q?O O BQ数据抵辄通知
F D _ A C C E P T       应用E序x收与q入q接有关的通知
F D _ C O N N E C T    应用E序x收与一ơ连接或者多点j o i n操作完成的通知
F D _ C L O S E             应用E序x收与套接字关闭有关的通知
F D _ Q O S            应用E序x收套接字“服务质量”(Q o SQ发生更改的通知
F D _ G R O U P _ Q O S             应用E序x收套接字l“服务质量”发生更改的通知Q现在没什么用处,为未来套接字l的使用保留Q?br />F D _ R O U T I N G _ I N T E R FA C E _ C H A N G E       应用E序x收在指定的方向上Q与路由接口发生变化的通知
F D _ A D D R E S S _ L I S T _ C H A N G E       应用E序x攉对套接字的协议家族,本地地址列表发生变化的通知

应用E序在一个套接字上成功调用了(jin)W S A A s y n c S e l e c t之后Q应用程序会(x)在与h W n dH口句柄参数对应的窗口例E中Q以Windows消息的Ş式,接收|络事g通知。窗口例E通常定义如下Q?/p>

LRESULT CALLBACK WindProc(
             HWND hWnd,
             UINT uMsg,
             WPARAM wParam,
             LPARAM lParam
             );
其中Qh W n d参数指定一个窗口的句柄Q对H口例程的调用正是由那个H口发出的。u M s g参数指定需要对哪些消息q行处理。就我们的情冉|_(d)感兴的是W S A A s y n c S e l e c t调用中定义的消息。w P a r a m参数指定在其上面发生?jin)一个网l事件的套接字。假若同时ؓ(f)q个H口例程分配?jin)多个套接字Q这个参数的重要性便昄出来?jin)。在l P a r a m参数中,包含?jin)两斚w重要的信息。其中, l P a r a m的低字(低位字)(j)指定?jin)已l发生的|络事gQ而l P a r a m的高?br />Q高位字Q包含了(jin)可能出现的Q何错误代码?br />|络事g消息抵达一个窗口例E后Q应用程序首先应(g)查l P a r a m的高字位Q以判断是否在套接字上发生了(jin)一个网l错误。这里有一个特D的宏:(x) W S A G E T S E L E C T E R R O RQ可用它q回高字位包含的错误信息。若应用E序发现套接字上没有产生M错误Q接着便应调查到底是哪个网l事件类型,造成?jin)这条Wi n d o w s消息的触发—具体的做法便是dl P a r a m之低字位的内宏V此时可使用另一个特D的宏:(x)W S A G E T S E L E C T E V E N TQ用它返回l P a r a m的低字部分?br />在程序清? - 5中,我们向大家演CZ(jin)如何使用W S A A s y n c S e l e c tq种I / O模型Q来实现H口消息的管理。在源程序中Q我们着重强调的是开发一个基本服务器应用要涉?qing)到的基本步骤,忽略了(jin)开发一个完整的Wi n d o w s应用需要涉?qing)到的大量编E细节?/p>

E序清单8-5 WSAAsyncSelect服务器示范代?/p>

#define WM_SOCKET  WM_USER+1
#inlude <windows.h>

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR  lpCmdline,int nCmdShow)
{
 SOCKET listen;
 HWND window;
 //create a window  and assign the serverWinProc blew to it
 window = CreateWindow();
 //start winsock and create a socket
 
 WSAStarup(...);
 listen = Socket();
 //Bind the socket to port 5150
 // and begin listening for connection
 
 I(yng)nternetAddr.sin_family = AF_INET;
 I(yng)nternetAddr.sin_addr.s_addr=htonl(INADDR_ANY);
 I(yng)nternetAddr.sin_port = htons(5150);
 bind(listen,(PSOCKETADDR)&InternetAddr,sizeof(InternetAddr));
 
 //set up window message notification on the new socket using the WM_SOCKET define above
 WSAAsyncSelect(listen,window,WM_SOCKET,FD_ACCEPT|FD_CLOSE);
 
 listen(listen,5);
 
 //translate and dispatch window messages
 //until the appliation terminates
}             


BOOL CALLBACK ServerWinProc(HWND hDlg,WORD wMsg,WORD wParam,WORD lParam)
{
 SOCKET accept;
 switch(wMsg)
 {
  case WM_PAINT:
     break;
  case WM_SOCKET:
     //determine whether an error occured on the socket
     //by using the WSAGETSELECTERROR() macro
     
     if(WSAGETSELECTERROR(lWparam))
     {
      //display the error and close the socket
      closesocket(wParam);
      break;
     }
     //determine what event occured on the socket
     
     switch(WSAGETSELECTEVENT(lParam)
     {
      case FD_ACCEPT:
         //ACCEPT an incoming connection
         Accept = accept(wParam,NULL,NULL);
         
         //prepare accepted socket for read
         //write,and close notifation
         
         WSAAsyncSelect(Accept,hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);
      case FD_READ:
         //RECEIVE data from the socket in wParam
         break;
      case FD_WRITE:
         //THE socket in wParam is ready for sending data
         
         break;
      case FD_CLOSE:
         //THE connection is now closed
         closesocket(wParam);
         break;
         
     }
     break;
 }
 return TRUE;
}

最后一个特别有价值的问题是应用程序如何对F D _ W R I T E事g通知q行处理。只有在?br />U条件下Q才?x)发出F D _ W R I T E通知Q?br />?使用c o n n e c t或W S A C o n n e c tQ一个套接字首次建立?jin)连接?br />?使用a c c e p t或W S A A c c e p tQ套接字被接受以后?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操作p|Q返回了(jin)W S A E W O U L D B L O C K?br />误,而且~冲区的I间变得可用因此Q作Z个应用程序,自收到首条F D _ W R I T E消息开始,便应认ؓ(f)自己必然能在一个套接字上发出数据,直至一个s e n d、W S A S e n d、s e n d t o或W S A S e n d Toq回套接字错误W S A E W O U L D B L O C K。经q了(jin)q样的失败以后,要再用另一条F D _ W R I T E通知应用E序再次发送数据?br />            



]]>
windows|络~程十五http://www.shnenglu.com/ivenher/articles/12368.html爱饭?/dc:creator>爱饭?/author>Tue, 12 Sep 2006 06:41:00 GMThttp://www.shnenglu.com/ivenher/articles/12368.htmlhttp://www.shnenglu.com/ivenher/comments/12368.htmlhttp://www.shnenglu.com/ivenher/articles/12368.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/12368.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12368.html8.2 套接字I/O模型
共有五种cd的套接字I / O模型Q可让Wi n s o c k应用E序对I / Oq行理Q它们包括:(x) s e l e c tQ选择Q、W S A A s y n c S e l e c tQ异步选择Q、W S A E v e n t S e l e c tQ事仉择Q、o v e r l a p p e dQ重叠)(j)以及(qing)completion portQ完成端口)(j)
8.2.1 select模型
s e l e c tQ选择Q模型是Wi n s o c k中最常见的I / O模型。之所以称其ؓ(f)?s e l e c t模型”,是由于它的“中?j)思想”便是利用s e l e c t函数Q实现对I / O的管理!最初设计该模型Ӟ主要面向的是某些使用U n i x操作pȝ的计机Q它们采用的是B e r k e l e y套接字方案。s e l e c t模型已集成到Winsock 1.1中,它那些想避免在套接字调用过E中被无辜“锁定”的应用E序Q采取一U有序的方式Q同时进行对多个套接字的理。由于Winsock 1.1向后兼容于B e r k e l e y套接字实
施方案,所以假如有一个B e r k e l e y套接字应用用了(jin)s e l e c t函数Q那么从理论角度Ԍ毋需对其q行M修改Q便可正常运行?br />利用s e l e c t函数Q我们判断套接字上是否存在数据,或者能否向一个套接字写入数据。之所以要设计q个函数Q唯一的目的便是防止应用程序在套接字处于锁定模式中Ӟ在一ơI / Ol定调用Q如s e n d或r e c vQ过E中Q被q进入“锁定”状态;同时防止在套接字处于非锁定模式中Ӟ产生W S A E W O U L D B L O C K错误。除非满事先用参数规定的条Ӟ否则s e l e c t函数?x)在q行I / O操作旉定。s e l e c t的函数原型如下:(x)

int select(
      int nfds,
      fd_set FAR * readfds,
      fd_set FAR * writefds,
      fd_set FAR * exceptfds,
      const struct timeval FAR * timeout
     );
     
其中Q第一个参数n f d s?x)被忽略。之所以仍然要提供q个参数Q只是ؓ(f)?jin)保持与早期的B e r k e l e y套接字应用程序的兼容。大家可注意C个f d _ s e t参数Q一个用于检查可L(r e a d f d sQ,一个用于检查可写性(w r i t e f d sQ,另一个用于例外数据(e x c e p t f d sQ。从Ҏ(gu)上说,f d _ s e t数据cd代表着一pd特定套接字的集合。其中, r e a d f d s集合包括W合下述M一个条件的套接字:(x)
?有数据可以读入?br />?q接已经关闭、重设或中止?br />?假如已调用了(jin)l i s t e nQ而且一个连接正在徏立,那么a c c e p t函数调用?x)成功?br />w r i t e f d s集合包括W合下述M一个条件的套接字:(x)
?有数据可以发出?br />?如果已完成了(jin)对一个非锁定q接调用的处理,q接׃(x)成功?br />最后,e x c e p t f d s集合包括W合下述M一个条件的套接字:(x)
?假如已完成了(jin)对一个非锁定q接调用的处理,q接试׃(x)p|?br />?有带外(O u t - o f - b a n dQO O BQ数据可供读取?br />例如Q假定我们想试一个套接字是否“可诠Z,必须自q套接字增dr e a d f d s集合Q?br />再等待s e l e c t函数完成。s e l e c t完成之后Q必d断自q套接字是否仍为r e a d f d s集合的一部分。若{案是肯定的Q便表明该套接字“可诠Z,可立即着手从它上面读取数据。在三个参数中(r e a d f d s、w r i t e f d s和e x c e p t f d sQ,M两个都可以是I| N U L LQ;但是Q至有一个不能ؓ(f)I|在Q何不为空的集合中Q必d含至一个套接字句柄Q否则, s e l e c t函数便没?br />M东西可以{待。最后一个参数t i m e o u t对应的是一个指针,它指向一个t i m e v a ll构Q用于决定s e l e c t最多等待I / O操作完成多久的时间。如t i m e o u t是一个空指针Q那么s e l e c t调用?x)无限期地“锁定”或停顿下去Q直到至有一个描q符W合指定的条件后l束。对t i m e v a ll构?br />定义如下Q?br />struct timeval
{
 long tv_sec;
 long tv_usec;
};

其中Qt v _ s e c字段以秒为单位指定等待时_(d) t v _ u s e c字段则以毫秒为单位指定等待时间?br />若将时D|ؓ(f)Q?0 , 0Q,表明s e l e c t?x)立卌回,允许应用E序对s e l e c t操作q行“轮询”?br />ZҎ(gu)能斚w的考虑Q应避免q样的设|。s e l e c t成功完成后,?x)在f d _ s e tl构中,q回刚好有未完成的I / O操作的所有套接字句柄的总量。若过t i m e v a l讑֮的时_(d)便会(x)q回0。不由于什么原因,假如s e l e c t调用p|Q都?x)返回S O C K E T _ E R R O R?br />用s e l e c t对套接字q行监视之前Q在自己的应用程序中Q必d套接字句柄分配给一个集合,讄好一个或全部诅R写以及(qing)例外f d _ s e tl构。将一个套接字分配lQ何一个集合后Q再来调用s e l e c tQ便可知道一个套接字上是否正在发生上q的I / Ozd。Wi n s o c k提供?jin)下列宏操作Q可用来针对I / OzdQ对f d _ s e tq行处理与检查:(x)
?FD_CLR(s, *set)Q从s e t中删除套接字s?br />?FD_ISSET(s, *set)Q检查s是否s e t集合的一名成员;如答案是肯定的是Q则q回T R U E?br />?FD_SET(s, *set)Q将套接字s加入集合s e t?br />?F D _ Z E R O ( * s e t )Q将s e t初始化成I集合?/p>

例如Q假定我们想知道是否可从一个套接字中安全地d数据Q同时不?x)陷于无休止?br />“锁定”状态,便可使用F D _ S E T宏,自q套接字分配给f d _ r e a d集合Q再来调用s e l e c t。要x自q套接字是否仍属f d _ r e a d集合的一部分Q可使用F D _ I S S E T宏。采用下q步骤,便可完成用s e l e c t操作一个或多个套接字句柄的全过E:(x)
1) 使用F D _ Z E R O宏,初始化自己感兴趣的每一个f d _ s e t?br />2) 使用F D _ S E T宏,套接字句柄分配l自己感兴趣的每个f d _ s e t?br />3) 调用s e l e c t函数Q然后等待在指定的f d _ s e t集合中,I / Ozd讄好一个或多个套接字句柄?br />s e l e c t完成后,?x)返回在所有f d _ s e t集合中设|的套接字句柄LQƈҎ(gu)个集合进行相应的更新?br />4) Ҏ(gu)s e l e c t的返回|我们的应用程序便可判断出哪些套接字存在着未完成Q待冻I(j)的I / O操作—具体的Ҏ(gu)是用F D _ I S S E T宏,Ҏ(gu)个f d _ s e t集合q行(g)查?br />5) 知道?jin)每个集合中“待决”的I / O操作之后Q对I / Oq行处理Q然后返回步? )Ql进行s e l e c t处理?br />s e l e c tq回后,它会(x)修改每个f d _ s e tl构Q删除那些不存在待决I / O操作的套接字句柄。这正是我们在上q的步骤( 4 )中,Z要用F D _ I S S E T宏来判断一个特定的套接字是否仍在集合中的原因。在E序清单8 - 4中,我们向大安qC(jin)Z个(只有一个)(j)套接字设|s e l e c t模型所需的一pd基本步骤。若惛_q个应用E序中添加更多的套接字,只需为额外的套接字维护它们的一个列表,或维护它们的一个数l即可?/p>

E序清单8-4 用s e l e c t理一个套接字上的I / O操作

SOCKET s;
fd_set fdread;
int ret;
//create a socket ,and accept a connection
...
//manage i/o on the socket

while(TRUE)
{
 //aways clear the read set before calling
 //select()
 FD_ZERO(&fdread);
 //add socket s to the read set
 FD_SET(s,&fdread);
 if((ret = select(0,&fdread,NULL,NULL,NULL))== SOCKET_ERROR)
 {
  //ERROR CONDITION
 }
 if(ret > 0)
 
 {
  //for this simple case,select() should return the value 1.
  //an appliation dealing with more than one socket could get a value greater than 1;
  //at this point,your appliation should check to see whether the socket is part of a set.
  if(FD_ISSET(s.&fdread))
  {
   //a read event has occurred on a socket s
  }
 }

}
      



]]>
windows|络~程十四http://www.shnenglu.com/ivenher/articles/12330.html爱饭?/dc:creator>爱饭?/author>Tue, 12 Sep 2006 03:10:00 GMThttp://www.shnenglu.com/ivenher/articles/12330.htmlhttp://www.shnenglu.com/ivenher/comments/12330.htmlhttp://www.shnenglu.com/ivenher/articles/12330.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/12330.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12330.html8.1.2 非锁定模?br />除了(jin)锁定模式Q我们还可考虑采用非锁定模式的套接字。尽这U套接字在用上存在着些许隑ֺQ但只要排除?jin)这困难,它在功能上还是非常强大的。除具备锁定套接字已有的各项优点之外Q还q行?jin)少许扩充,功能更强。程序清? - 3向大家展CZ(jin)如何创徏一个套接字Qƈ其|ؓ(f)非锁定模式?br />E序清单8-3 讄一个非锁定套接?/p>

SOCKET s;
unsigned long u1 = 1;
int nRet;
s = SOCKET(AF_INET,SOCK_STREA,0);
nRet = ioctlsocket(s,FIOBIO,(unsigned long *)&ul);
if(nRet == SOCKET_ERROR)
{
 //Failed to put the socket into nonblocking mode
}

一个套接字|ؓ(f)非锁定模式之后, Winsock API调用?x)立卌回。大多数情况下,q些调用都会(x)“失败”,q返回一个W S A E W O U L D B L O C K错误。什么意思呢Q它意味着h的操作在调用期间没有旉完成。D个例子来_(d)假如在系l的输入~冲ZQ尚不存在“待决”的数据Q那么r e c vQ接收数据)(j)调用׃(x)q回W S A E W O U L D B L O C K错误。通常Q我们需要重复调用同一个函敎ͼ直至获得一个成功返回代码。在? - 2中,我们对常见Wi n s o c k调用q?br />回的W S A E W O U L D B L O C K错误的含义进行了(jin)ȝ?br />׃非锁定调用会(x)频繁q回W S A E W O U L D B L O C K错误Q所以在M时候,都应仔细(g)查所有返回代码,q作好“失败”的准备。许多程序员易犯的一个错误便是连l不停地调用一个函敎ͼ直到它返回成功的消息为止。例如,假定在一个紧凑的循环中不断地调用r e c vQ以d2 0 0个字节的数据Q那么与使用前述的M S G _ P E E K标志来“轮询”一个锁定套接字相比Q?br />前一U做法根本没有Q何优势可a。ؓ(f)此, Wi n s o c k的套接字I / O模型可帮助应用程序判断一个套接字何时可供d?br />锁定和非锁定套接字模式都存在着优点和缺炏V其中,从概늚角度_(d)锁定套接字更易用。但在应付徏立连接的多个套接字时Q或在数据的收发量不均,旉不定Ӟ却显得极隄理。而另一斚wQ假如需要编写更多的代码Q以便在每个Wi n s o c k调用中,Ҏ(gu)C个W S A E W O U L D B L O C K错误的可能性加以应付,那么非锁定套接字便显得有些难于操作。在q些情况下,可考虑使用“套接字I / O模型”,它有助于应用E序通过一U异步方式,同时对一个或多个套接字上q行的通信加以理?br />?-2 非锁定套接字上的W S A E W O U L D B L O C K错误函数名说?br />
W S A A c c e p t和a c c e p t   应用E序没有收到q接h。再ơ调用,便可(g)查连接情?br />c l o s e s o c k e t          大多数情况下Q这个错误意味着已随S O _ L I N G E R选项一道,调用?jin)s e t s o c k o p tQ而且已设定了(jin)一个非零的时?br />W S A C o n n e c t和c o n n e c t       应用E序已初始化。再ơ调用,便可(g)查是否完?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>

]]>
windows|络~程十三http://www.shnenglu.com/ivenher/articles/12273.html爱饭?/dc:creator>爱饭?/author>Mon, 11 Sep 2006 09:48:00 GMThttp://www.shnenglu.com/ivenher/articles/12273.htmlhttp://www.shnenglu.com/ivenher/comments/12273.htmlhttp://www.shnenglu.com/ivenher/articles/12273.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/12273.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12273.htmlW?章Winsock I/OҎ(gu)
本章重点是如何在Wi n d o w s套接字应用程序中对I / OQ输入/输出Q操作进行管理?br />Wi n s o c k分别提供?jin)“套接字模式”和“套接字I / O模型”,可对一个套接字上的I / O行ؓ(f)加以控制。其中,套接字模式用于决定在随一个套接字调用Ӟ那些Wi n s o c k函数的行为。而另一斚wQ套接字模型描述?jin)一个应用程序如何对套接字上q行的I / Oq行理?qing)处理。要注意的是Q“套接字I / O模型”与“套接字模式”是无关的。套接字模型的出玎ͼ正是Z(jin)解决套接字模式存在的某些限制?/p>

Wi n s o c k提供?jin)两U套接字模式Q锁定和非锁定。本章第一部分详l介l这两种模式Qƈ阐释一个应用程序如何通过它们理I / O。如大家在本章的后面部分所见,W(xu)i n s o c k提供?jin)一些有的I / O模型Q有助于应用E序通过一U“异步”方式,一ơ对一个或多个套接字上q行的通信加以理。这些模型包括s e l e c tQ选择Q、W S A A s y n c S e l e c tQ异步选择Q、W S A E v e n t S e l e c t
Q事仉择Q、Overlapped I/OQ重叠式I / OQ以?qing)Completion portQ完成端口)(j){等。到本章l束Ӟ我们打算对各U套接字模式以及(qing)I / O模型的优~点q行ȝ。同Ӟ帮助大家判断到底哪一U最适合自己应用E序的要求?br />所有Wi n d o w sq_都支持套接字以锁定或非锁定方式工作。然而,q每种q_都支持每一UI / O模型。如? - 1所C,在当前版本的Windows CE 中,仅提供了(jin)一个I / O模型?br />Windows 98和W(xu)indows 95Q取决于安装的是Winsock 1q是Winsock 2Q则支持大多数I / O模型Q唯一的例外便是I / O完成端口。而到?jin)Windows NT和最新发布的Windows 2000中,每种I / O模型都是支持的?/p>

8.1 套接字模?br />像我们前面提到的那P Wi n d o w s套接字在两种模式下执行I / O操作Q锁定和非锁定?br />在锁定模式下Q在I / O操作完成前,执行操作的Wi n s o c k函数Q比如s e n d和r e c vQ会(x)一直等候下去,不会(x)立即q回E序Q将控制权交q给E序Q。而在非锁定模式下Q?Wi n s o c k函数无论如何都会(x)立即q回。在Windows CE和W(xu)indows 95Q安装Winsock 1Q^Cq行的应用程序仅支持极少的I / O模型Q所以我们必采取一些适当的步骤,让锁定和非锁定套接字能够满各种场合的要?/p>

8.1.1 锁定模式
对于处在锁定模式的套接字Q我们必d加留意,因ؓ(f)在一个锁定套接字上调用Q何一个Winsock API函数Q都?x)生相同的后果—耗费或长或短的时间“等待”。大多数Wi n s o c k应用都是늅一U“生产者-消费者”模型来~制的。在q种模型中,应用E序需要读取(或写入)(j)指定数量的字节,然后以它为基执行一些计。程序清? - 1展示的代码片断便是一个典型的例子?br />

// E序清单8-1 单的锁定模式CZ

SOCKET s;
char  buff[ 256 ];
int  done  =   0 ;

.

while ( ! done)
{
    nBytes 
=  recv(s,buff, 65 );
    
if (nBytes  ==  SOCKET_ERROR)
    
{
        printf(
" recv failed with error %d " ,WSAGetLastError());
        
return  ;    
    }

    
    
}

.


q段代码的问题在于,假如没有数据处于“待决”状态,那么r e c v函数可能永远都无法返回。这是由于从语句可以看出Q只有从pȝ的输入缓冲区中读回点什么东西,才允许返回!有些E序员可能会(x)在r e c v中用M S G _ P E E K标志Q或者调用i o c t l s o c k eQ?t 讄F I O N R E A D选项Q,
在系l的~冲ZQ事先“偷看”是否存在够的字节数量。然而,在不实际d数据的前提下Q仅仅“偷看”数据(如实际读入数据,便会(x)其从系l缓冲区中将其删除)(j)Q可不是一件光彩的事情。我们认为,q是一U非怸好的~程?fn)惯Q应全力避免。在“偷看”的时候,对系l造成的开销是极大的Q因Z仅ؓ(f)?jin)检查有多少个字节可用,便发Z个或者更多的pȝ调用。以后,理所当然圎ͼq需要牵涉到q行实际r e c v调用Q将数据从系l缓冲区内删除的开销。那么,如何避免q一情况呢?在此Q我们的目标是防止由于数据的~ZQ这
可能是网l出?jin)故障,也可能是客户机出了(jin)问题?j)Q造成应用E序完全陷于“凝固”状态,同时不必q箋性地(g)视系l网l缓Ԍ此目的,一个办法是应用程序划分ؓ(f)一个读U程Q以?qing)一个计线E。两个线E都׃n同一个数据缓冲区。对q个~冲区的讉K需要受C定的限制Q这是用一个同步对象来实现的,比如一个事件或者M u t e xQ互斥体Q。“读U程”的职责是从|络q箋地读入数据,q将其置入共享缓冲区内。读U程计线E开始工作至需
要的数据量拿到手后,便会(x)触发一个事Ӟ通知计算U程Q你老兄可以开始干zM(jin)Q随后,计算U程从缓冲区取走Q删除)(j)一个数据块Q然后进行要求的计算?/p>

在程序清? - 2中,我们分别提供?jin)两个函敎ͼ采取的便是上q办法。在两个函数中,一个负责读取网l数据( R e a d T h r e a dQ,另一个则负责Ҏ(gu)据执行计( P r o c e s s T h r e a dQ?br />

// E序清单8-2 多线E的锁定套接字示?/span>

CRITICAL_SECTION    data;
HANDLE    hEvent;
TCHAR     buf[MAX_BUFFER_SIZE];
int          nBytes;

.

// read thread

void  ReadThread( void )
{
    
int  nTotal  =   0 ,
            nRead 
=   0 ,
            nLeft 
=   0 ,
            nBytes 
=   0 ;
            
            
while ( ! done)
            
{
                nTotal 
=   0 ;
                nLeft 
=  NUM_BYTES_REQUIRED;
                
while (nTotal  !=  NUM_BYTES_REQUIRED)
                
{
                    EnterCriticalSection(
& data);
                    nRead 
=  recv(sock, & (buff[MAX_BUFFERS_SIZE - nBytes],nLeft);
                    
if (nRead  ==   - 1 )
                    
{
                        printf(
" error " );
                        ExitThread();
                    }

                    nTotal 
+=  nRead;
                    nLeft 
-= nRead;
                    
                    nBytes 
+=  nRead;
                    LeaveCriticalSection(
& data);
                    
                }

                SetEvent(hEvent);
            }

}


////// compution thread

void     ProcessThread( void )
{
    WatiForSingleObject(hEvent);
    
    EnterCriticalSection(
& data);
    ..
    nBytes 
-=  NUM_BYTES_REQUIRED;
    
    LeaveCriticalSection(
& data);
}



寚w定套接字来说Q它的一个缺点在于:(x)应用E序很难同时通过多个建好q接的套接字通信。用前q的办法Q我们可对应用程序进行修改,令其好的每个套接字都分配一个读U程Q以?qing)一个数据处理线E。尽这仍然?x)增大一些开销Q但的确是一U可行的Ҏ(gu)。唯一的缺点便是扩展性极差,以后惛_时处理大量套接字Ӟ恐怕难以下手?img src ="http://www.shnenglu.com/ivenher/aggbug/12273.html" width = "1" height = "1" />

]]>
windows|络~程十二http://www.shnenglu.com/ivenher/articles/12271.html爱饭?/dc:creator>爱饭?/author>Mon, 11 Sep 2006 09:20:00 GMThttp://www.shnenglu.com/ivenher/articles/12271.htmlhttp://www.shnenglu.com/ivenher/comments/12271.htmlhttp://www.shnenglu.com/ivenher/articles/12271.html#Feedback1http://www.shnenglu.com/ivenher/comments/commentRss/12271.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12271.html7.4.3 Z消息的协?br />正由于面向连接的协议同时也是式协议Q无q接协议几乎都是Z消息的。因此,在收发数据时Q需要考虑q几炏V首先,׃面向消息的协议对数据边界有保护,所以提交给发送函数的数据在被发送完之前累积成块。对异步或非块式I / O模式而言Q如果数据未能完全发送,发送函数就?x)返回W S A E W O U L D B L O C K错误。这意味着基层的系l不能对不完整的那个数据q行处理Q你应该E后再次调用发送函数。下一章将Ҏ(gu)q行详述。主要需要记?br />的是Q采用基于消息的协议Ӟ对于写入数据来说Q只能把它当作一个自治行为?br />在连接另一端,Ҏ(gu)收函数的调用必须提供一个够大的缓冲空间。如果提供的~冲不够Q接收调用就?x)失败,出现W S A E M S G S I Z E。发生这U情冉|Q缓冲会(x)力接收Q但未收完的数据?x)被丢弃。被截断的数据无法恢复。唯一例外的是支持部分消息的协议却例外Q比方说A p p l e Talk PA P协议。在W S A R e c v E x函数只收到部分消息时Q它?x)在q回之前Q便把自
q出入标志参数设ؓ(f)M S G _ PA RT I A L?br />对以支持部分消息的协议ؓ(f)基础的数据报来说Q可考虑使用一个W S A R e c v函数。在调用r e c vӞ不会(x)有这一个通知“读取的数据只是消息的一部分”。至于接收端怎样判断是否已读取整条消息,具体Ҏ(gu)则由E序员决定。后来的r e c v调用q回q个数据报的其他部分。由于有q个限制Q所以利用W S A R e c v E x函数非常方便Q它允许讄和读取M S G _ PA RT I A L标志

M S G _ PA RT I A L标志指明整条消息是否已读取。Winsock 2函数W S A R e c v和W(xu) S A R e c v F r o m也支持这一标志。关于这个标志的更多知识Q请参见对W S A R e c v、W S A R e c v E x和W(xu) S A R e c v F r o m
q三个函数的描述?br />我们最后要谈的便是在有多个|络接口的机器上发送UDP /IP消息。这斚w的问题颇多,
我们来看一个最常见的问题:(x)在一个U D P套接字明昄定到一个本地I P接口和发送数据报Ӟ?x)发生什么情况? U D P套接字ƈ不会(x)真正和网l接口绑定在一赗而是建立一U联p,即绑定的I P接口成ؓ(f)发出ȝU D P数据报的源I P地址。\p才真正决定数据报在哪个物理接口上传出厅R如果不调用b i n dQ而是先调用s e n d t o或W S A S e n d To执行q接Q网l堆栈就?x)根?br />路由表,自动选出最x地I P地址。这意味着Q如果你先执行明昄定,源I P地址׃(x)有误?br />也就是说Q源I P可能不是真正在它上面发送数据报的那个接口的I P地址?br />7.4.4 释放套接字资?br />因ؓ(f)无连接协议没有连接,所以也不会(x)有正式的关闭和从容关闭。在接收端或发送端l束收发数据Ӟ它只是在套接字句柄上调用c l o s e s o c k e t函数。这P侉K放了(jin)为套接字分配?br />所有相兌源?br />7.4.5 l合分析
对于在无q接的套接字上收发数据的步骤Q大家现在已l很清楚?jin)。接下来Q我们来看看执行q一q程的代码。程序清? - 3展示?jin)一个无q接的接收端。这D代码说明了(jin)如何在默认接口或指定的本地接口上接收数据报?/p>

7.5 其他API函数
本小节介l其他几个Winsock API函数Q它们在实际|络应用中非常有?br />1. getpeername
该函数用于获得通信方的套接字地址信息Q该信息是关于已建立q接的那个套接字的?br />它的定义如下Q?br />int getpeername(
        SOCKET s,
        struct sockaddr FAR * name,
        int FAR *namelen
       );
W一个参数是准备q接的套接字Q后两个参数则是指向基层协议cd?qing)其长度的指针?br />Ҏ(gu)据报套接字来_(d)q个函数q回的是投向q接调用的那个地址Q但不会(x)q回投向s e n d t o或W S A S e n d To调用的那个地址?/p>

2. getsockname
该函数是g e t s o c k n a m e的对应函数。它q回的是指定套接字的本地接口的地址信息。它的定义如下:(x)

int getsockname(
        SOCKET s,
        struct sockaddr FAR * name,
        int FAR *namelen
       );
 除了(jin)套接字sq回的地址信息本地地址信息外,它的参数和g e t p e e r n a m e的参数都是一L(fng)?br />T C P协议中,q个地址和监听指定端口和I P接口的那个服务器套接字是一L(fng)?br />3. WSADuplicateSocket
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 Ol构Q该l构可投入另一个进E,q样可用另一个进E打开一个指向同一个基层套接字的句柄,如此一来,另一个进E也能对该资源进行操作。注意,q一点只适用于两个进E之_(d)同一个进E中的线E可自由投递套接字描述W。该函数的定义如下:(x)

int WSADuplicateSocket(
            SOCKET s,
            DWORD dwProcessId,
            LPWSAPROTOCOL_INFO lpProtocol
           );
W一个参数是准备复制的套接字句柄。第二个参数d w P r o c e s s I dQ是打算使用复制套接字的q程之I D。第三个参数l(f) p P r o t o c o l I n f oQ是一个指向W S A P R O TO C O L _ I N F Ol构的指针,包含目标进E打开复制句柄时所需的信息。ؓ(f)?jin)目前的进E能够把W S A P R O TO C O L _ I N F O
l构投到目标q程Q然后再利用该结构徏立一个指向指定套接字的句柄(利用W S A S o c k e t函数Q,必须考虑q程间通信?br />两个套接字的描述W都可独立用I / OQ但Wi n s o c k没有提供讉K控制Q因此这要由E序员决定是否执行同步。所有描q符中都可见到关联到一个套接字的所有状态信息,q是因ؓ(f)复制的是套接字描q符Q而不是事实上的套接字。比方说Q对于描q符上由s e t s o c k e t o p t函数讄的Q何一个套接字选项Q都可通过M一个或所有描q符利用g e t s o c k o p t函数来看它们?br />如果一个进E在一个复制套接字上调用c l o s e s o c k e tQ就?x)导致该q程中的描述W变成解除定位;但在最后留下的那个描述W上调用c l o s e s o c k e t之前Q基层套接字?x)保持打开状态?br />另外Q在使用W S A A s y n c S e l e c t和W(xu) S A E v e n t S e l e c tӞ要了(jin)解与׃n套接字的通知有关的几个问题。这两个函数用于异步I / OQ我们将在第8章进行讨论)(j)。利用Q何一个共享描q符执行前两个函数的调用Q都?x)删掉所有的套接字事件注册,不管注册所用的描述W究竟是哪一
个。例如,׃n套接字不能把F D _ R E A D事g投递给q程AQ不能把F D _ W R I T E投递给q程B?br />如果需要这两个描述W的事g通知Q就应该重新设计应用E序Q用U程来代替进E?/p>

4. Tr a n s m i t F i l e
Tr a n s m i t F i l e是微软专有的Wi n s o c k扩展Q它允许从一个文件中传输高性能数据。这是非常有效的Q因为整个数据传输可在内核模式中q行。也是_(d)如果你的应用从指定的文g中读取一堆数据,然后用s e n d或W S A S e n dӞ涉及(qing)到“用h式到内核模式传输”的发送调用就有若q个。有?jin)Tr a n s m i t F i l eQ整个读取和发送数据的q程可在内核模式中q行。该?br />数的定义如下Q?/p>

BOOL  TransmitFile(
          SOCKET hSocket,
          HANDLE hFile,
          DWORD  nNumberOfBytesToWrite,
          DWORD  nNumberOfBytesPerSend,
          LPOVERLAPPED lpOverlapped,
          LPTRANMIT_FILE_BUFFERS  lpTransmitBuffers,
          DWORD dwFlags
         );
h S o c k e t参数用于识别已连接上的套接字Q文件的传输便在该套接字上进行)(j)。n F i l e参数是一个句柄,该句柄指向一个已打开的套接字Q即卛_发送的文gQ。n N u m b e r O f B y t e s To Wr i t e?br />明写入多指定文件中的字节。投?表示发送整个文件。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?x)引起Tr a n s m i t F i l e在套接字上以2 KB数据块的?br />式发送指定文件。投?表示采用默认的发送长度。l p O v e r l a p p e d参数指定一个O V E R L A P P E D
l构Q该l构用于重叠I(yng) / O模式Q关于重叠I(yng) / OQ可参见W?章)(j)?br />另一个参数l(f) p Tr a n s m i t B u ff e r sQ是一个T R A N S M I T _ F I L E _ B U F F E R Sl构Q其中包含文件传输之前和之后准备发送的数据。该l构的格式如下:(x)

typedef struct _TANSMIT_FILE_BUFFERS{
 PVOID Head;
 DWORD HeadLenth;
 PVOID Tail;
 DWORD TailLength;
 }TAANSMIT_FILE_BUFFERS;     
 
 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>

]]>
windows|络~程十一http://www.shnenglu.com/ivenher/articles/12270.html爱饭?/dc:creator>爱饭?/author>Mon, 11 Sep 2006 09:04:00 GMThttp://www.shnenglu.com/ivenher/articles/12270.htmlhttp://www.shnenglu.com/ivenher/comments/12270.htmlhttp://www.shnenglu.com/ivenher/articles/12270.html#Feedback1http://www.shnenglu.com/ivenher/comments/commentRss/12270.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12270.html 7.4 无连接协?br />和面向连接的协议比较hQ无q接协议的行为极Z同,因此Q收发数据的方式也会(x)有所不同。由于在和面向会(x)话的服务器比较时Q无q接接收端改动不大,所以我们先谈谈接收端(如果你愿意,也可UC为服务器Q。接下来再谈发送端?br />7.4.1 接收?br />对于在一个无q接套接字上接收数据的进E来_(d)步骤q不复杂。先用s o c k e t或W S A S o c k e t建立套接字。再把这个套接字和准备接收数据的接口l定在一赗这是通过b i n d函数Q和面向?x)话的示例一P(j)来完成的。和面向?x)话不同的是Q我们不必调用l i s t e n和a c c e p t。相反,只需{待接收数据。由于它是无q接的,因此始发于网l上M一台机器的数据报都可被?br />收端的套接字接收。最单的接收函数是r e c v f r o m。它的定义如下:(x)

int recvfrom(
       SOCKET s,
       char FAR * buf,
       int len,
       int flags,
       struct sockaddr FAR * from,
       int FAR * fromlen
      );
前面四个参数和r e c v是一L(fng)Q其中包括标志M S G _ O O B和M S G _ P E E K。在使用无连接套接字Ӟ和前面一P仍然提醒大家慎用M S G _ P E E K标志。对监听套接字的具体协议来说Qf r o m参数是一个S O C K A D D Rl构Q带有指向地址l构的长度的f r o m l e n。这个A P I调用q回?br />据时QS O C K A D D Rl构内便填入发送数据的那个工作站的地址?/p>

r e c v f r o m函数的Winsock 2版本是W S A R e c v F r o m。后者的原型是:(x)

int WSARecvFrom(
         SOCKET s,
         LPWSABUF lpBuffers,
         DWORD dwBufferCount,
         LPDWORD lpNumberOfBytesRecvd,
         LPDWORD lpFlags,
         struct sockaddr FAR * lpFrom,
         LPINT lpFromlen,
         LPWSAOVERLAPPED lpOverlapped,
         LPWSAOVERLAPPED_COMPLETTION_ROUTINE lpComplettionROUTINE
        );
两者的差别在于接收数据的W S A B U Fl构的用法上。你可以利用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~冲。提供多个缓Ԍ可用发散集合了(jin)。读取的字节Lq回在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 />无选项?、M S G _ O O B、M S G _ P E E K或M S G _ PA RT I A L。这些标志还可以累加h。如果在调用q个函数Ӟ指定M S G _ PA RT I A LQ提供者就知道q回数据Q即使只收到?jin)部分消息。调用返回之后,如果只收到部分消息,׃(x)讄M S G _ PA RT I A L标志。再ơ返回之后,
W S A R e c v F r o m׃(x)把l p F r o m参数Q它是一个指向S O C K A D D Rl构的指针)(j)设ؓ(f)发送端的地址。再ơ提醒大家注意, l p F r o m L e n指向S O C K A D D Rl构的长度,另外Q在q个函数中,它还是一个指针,指向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 EQ用?br />重叠I(yng) / OQ我们将在下一章就此展开讨论Q?br />在无q接套接字上接收Q发送)(j)数据的另一U方法是建立q接。听h有些奇怪吧Q但事实的确如此。无q接的套接字一旦徏立,便可利用S O C K A D D R参数Q它被设为准备与之通信的远E接收端地址Q调用c o n n e c t或W S A C o n n e c t。但事实上ƈ没有建立q接。投入连接函数的套接字地址是与套接字关联在一L(fng)Q如此一来,才能够用R e c v和W(xu) S A R e c v来代?br />r e c v f r o m和W(xu) S A R e c v F r o m。ؓ(f)什么呢Q其原因是数据的始发处是已知的。如果在一ơ应用中Q只和一个端点进行通信Q便能很Ҏ(gu)C数据报套接字建立q接?/p>

7.4.2 发送端
要在一个无q接的套接字上发送数据,有两U选择。第一U,也是最单的一U,便是建立一个套接字Q然后调用s e n d t o或W S A S e n d To。我们先来讲解s e n d t o函数Q它的定义是q样的:(x)

 int sendto( 
        SOCKET s,
        const char FAR * buf,
        int len,
        int flags,
        const struct sockaddr FAR * to,
        int tolen
       );
 除了(jin)b u f是发送数据的~冲Q?l e n指明发送多字节外Q其余参数和r e c v f r o m的参C栗另外, t o参数是一个指向S O C K A D D Rl构的指针,带有接收数据的那个工作站的目标地址?br />另外Q也可以用Winsock 2函数W S A S e n d To。它的定义如下:(x)

int WSASendTo(
        SOCKET s,
        LPWSABUF lpBuffers,
        DWORD dwBufferCount,
        LPWORD lpNumberOfBytesSent,
        DWORD dwFlags,
        const  struct sockaddr FAR * lpTo,
        int iToLen,
        LPWSAOVERLAPPED lpOverlapped,
        LPWSAOVERLAPPED_COMPLETTION_ROUTINE lpComplettionROUTINE
       );
       
再次提醒大家注意Q?W S A S e n d To和前一版本中的S e n d To函数cM。它把指向带有发l接
收端的数据的指针当作l p B u ff e r s参数Q?d w B u ff e r C o u n t参数指明现在的结构是多少。我们可发送多个W S A B U Fl构启用发散集合I / O。在函数q回之前QW(xu) S A S e n d To把第四个参数l(f) p N u m b e r
O f B y t e s S e n t设ؓ(f)真正发到接收端的字节数。l p To参数是针对具体协议的一个S O C K A D D Rl构Qƈ带有接收端的地址。i To L e n参数是S O C K A D D Rl构的长度。最后两个参敎ͼ 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 EQ用于重叠I(yng) / OQ将在第8章讨论)(j)?br />通过接收数据的方式,可把一个无q接的套接字q接C个端点地址Qƈ可以用s e n d和W(xu) S A S e n d发送数据了(jin)。这U关联一旦徏立,׃能再用带有地址的s e n d t o和W(xu) S A S e n d ToQ?br />除非q个地址是投到其中一个连接函数的地址Q否则调用就?x)失败,出现W S A E I S C O N N错误?br />要取消套接字句柄与目标地址的关联,唯一的办法是在这个套接字句柄上调用c l o s e s o c k e tQ?br />q徏立一个新的套接字?br />              



]]>
windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12269.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12269.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12269.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12269.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12269.html</trackback:ping><description><![CDATA[ <p>7.3.4 协?br />׃大多面向q接的协议同时也是流式传输协议,所以,在此提一下流式协议。对于流套接字上收发数据所用的函数Q需要明白的是:(x)它们不能保证对请求的数据量进行读取或写入。比如说Q一? 0 4 8字节的字W缓Ԍ准备用s e n d函数来发送它。采用的代码是:(x)</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函数而言Q可能会(x)q回已发出的于2 0 4 8的字节。r e t变量设为发送的字节敎ͼq是因ؓ(f)Ҏ(gu)个收发数据的套接字来_(d)pȝ都ؓ(f)它们分配?jin)相当充的~冲区空间。在发送数据时Q内部缓冲区?x)将数据一直保留到应该它发到U上为止。几U常见的情况都可Dq一情Ş的发生。比方说Q大量数据的传输可以令缓冲区快速填满。同Ӟ对T C P / I P来说Q还有一个窗口大的问题。接收端?x)对H口大小q行调节Q以指出它可以接收多数据。如果有大量数据涌入接收端,接收端就?x)将H口大小设ؓ(f)0Qؓ(f)待发数据做好准备。对发送端?br />_(d)q样?x)强令它在收C个新的大?的窗口大之前,不得再发数据。在使用s e n d调用Ӟ~冲区可能只能容U? 0 2 4个字节,q时Q便有必要再提取剩下? 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>0)<br />{<br /> ret = send(s,&sendbuf[idx],nLeft,0);<br /> if(ret == SOCKET_ERROR)<br /> {<br />  //ERROR<br /> }<br /> nLeft -= ret;<br /> idx +=ret;<br /> }<br /> 对在套接字上接收数据来_(d)前一D代码有用,但意义不大。因为流套接字是一个不间断的数据流Q应用程序在d它时Q和它应该读多少数据之间通常没有关系。如果应用需要依赖于协议的L数据Q你有别的事要做。如果所有消息长度都一P则比较简单,也就是说Q?5 1 2个字节的消息看v来就像下面这P(x)</p> <p>char recvbuf[2048];<br />int ret,nLeft,idx;<br />nLeft = 512;<br />idx = 0;</p> <p>while(nLeft>0)<br />{<br /> ret = recv(s,&recvbuf[idx],nLeft,0);<br /> if(ret = SOCKET_ERROR)<br /> {<br />  //ERROR<br /> }<br /> idx += ret;<br /> nLeft -= ret;<br />}</p> <p>消息长度不同Q处理也可能不同。因此,有必要利用你自己的协议来通知接收端,卛_<br />到来的消息长度是多少。比方说Q写入接收端的前4个字节一直是整数Q表C即到来的消息<br />有多字节。然后,接收端先查看?个字节的方式Q把它们转换成一个整敎ͼ然后判断构成<br />消息的字节数是多,通过q种方式Q便开始逐次d?/p> <p>分散集合I/O<br />分散集合支持是Berkeley Socket中首ơ随R e c v和W(xu)r i t e vq两个函C起出现的概念?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(xu) S A S e n d Toq几个Winsock 2函数一起用?br />Ҏ(gu)发格式特别的数据q一cȝ应用来说Q它是非常有用的。比方说Q客h发到服务器的消息可能一直都是这h成的Q一个指定某U操作的固定? 2字节的头Q一? 4字节的数据块和一? 6字节的尾。这Ӟ可用一个由三个W S A B U Fl构l成的数l调用W S A S e n dQ这三个l构分别对应的三U消息类型。在接收端,则用3个W S A B U Fl构来调用W S A R e c vQ各个结构包含的数据~冲分别? 2字节? 4字节? 6字节?br />在用基于流的套接字Ӟ分散集合I / O模式只是把W S A B U Fl构中提供的数据~冲当作一个连l性的~冲。另外,接收调用可能在所有缓冲填满之前就q回。在Z消息的套接字上,每次Ҏ(gu)收操作的调用都会(x)收到一条消息,光度由所提供的缓冲决定。如果缓冲不够,调用׃(x)p|Qƈ出现W S A E M S G S I Z E错误Qؓ(f)?jin)适应可用的缓冲数据就?x)被截断。当?dng)如果用支持部分消息的协议Q就可用M S G _ PA RT I A L标志来避免数据的丢失?/p> <p>7.3.5 中断q接<br />一旦完成Q务,必d掉连接,释放兌到那个套接字句柄的所有资源。要真正地释放与一个开着的套接字句柄兌的资源,执行c l o s e s o c k e t调用卛_。但要明白这一点,<br />c l o s e s o c k e t可能?x)带来负面?jing)响(和如何调用它有关Q,卛_能会(x)D数据的丢失。鉴于此Q应该在调用c l o s e s o c k e t函数之前Q利用s h u t d o w n函数从容中断q接。接下来Q我们来谈谈q两个A P I函数?br />1. shutdown<br />Z(jin)保证通信方能够收到应用发出的所有数据,对一个编得好的应用来_(d)应该通知接收端“不再发送数据”。同P通信方也应该如此。这是所谓的“从容关闭”方法,q由s h u t d o w n函数来执行。s h u t d o w n的定义如下:(x)</p> <p>int shutdown(<br />       SOCKET s,<br />       int how<br />      );<br />h o w参数可以是下面的M一个|(x) 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 EQ就表示不允许再调用接收函数。这对底部的协议层没有媄(jing)响。另外,对T C P套接字来_(d)不管数据在等候接Ӟq是数据接连到达Q都要重设连接。尽如此, U D P套接字上Q仍然接受ƈ排列接入的数据。如果选择S E _ S E N DQ表CZ允许再调用发送函数。对T C P套接字来_(d)q样?x)在所有数据发出,q得到接收端认之后Q生成一个F I N包。最后,<br />如果指定S D _ B O T HQ则表示取消q接两端的收发操作?/p> <p>2. closesocket<br />c l o s e s o c k e t函数用于关闭套接字,它的定义如下Q?/p> <p>int closesocket(SOCKET s);</p> <p>c l o s e s o c k e t的调用会(x)释放套接字描q符Q再利用套接字执行调用就?x)失败,q出现W S A E N O T S O C K错误。如果没有对该套接字的其他引用,所有与其描q符兌的资源都?x)?br />释放。其中包括丢弃所有等侯处理的数据?br />对这个进E中M一个线E来_(d)它们执行的待军_步调用都在未投递Q何通知消息的情况下被删除。待决的重叠操作也被删除。与该重叠操作关联的M事gQ完成例E或完成端口能执行,但最后会(x)p|Q出现W S A _ O P E R AT I O N _ A B O RT E D错误。异步和非封锁I / O?br />式将在第8章深入讲解。另外,q有一点会(x)对c l o s e s o c k e t的行Z生媄(jing)响:(x)套接字选项S O _ L I N G E R是否已经讄。要得知其中~由Q参考第9章中对S O _ L I N G E R选项的描q?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-11 17:02 <a href="http://www.shnenglu.com/ivenher/articles/12269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12130.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12130.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12130.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12130.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12130.html</trackback:ping><description><![CDATA[ <p> 3. recv和W(xu) S A R e c v<br />对在已连接套接字上接受接入数据来_(d) r e c v函数是最基本的方式。它的定义如下:(x)</p> <p>int recv(<br />     SOCKET s,<br />     char FAR * buf,<br />     int len,<br />     int flags<br />    );<br />W一个参数sQ是准备接收数据的那个套接字。第二个参数b u fQ是卛_收到数据的字W缓Ԍ而l e n则是准备接收的字节数或b u f~冲的长度。最后, f l a g s参数可以是下面的|(x) 0、M S G _ P E E K或M S G _ O O B。另外,q可对这些标志中的每一个进行按位和q算。当?dng)?0表示无特D行为。M S G _ P E E K?x)有用的数据复制到所提供的接收端~冲内,但是没有从系l缓<br />冲中它删除。另外,q返回了(jin)待发字节数?br />消息取数不太好。它不仅D性能下降Q因为需要进行两ơ系l调用,一ơ是取数Q另一ơ是无M S G _ P E E K标志的真正删除数据的调用Q,在某些情况下q可能不可靠。返回的数据可能没有反射出真正有用的数量。与此同Ӟ把数据留在系l缓Ԍ可容Ux入数据的pȝI间׃(x)来少。其l果便是Q系l减各发送端的T C PH口定w。由此,你的应用׃能获得最大的通。最好是把所有数据都复制到自q~冲中,q在那里计算数据。前面曾介绍qM S G _ O O B标志。有兌情,参见前面“带外数据”的内容?/p> <p>在面向消息或面向数据报的套接字上使用r e c vӞq几点应该注意。在待发数据大于所提供的缓冲这一事g中,~冲内会(x)量地填充数据。这Ӟ r e c v调用׃(x)产生W S A E M S G S I Z E错误。注意,消息镉K误是在用面向消息的协议时发生的。流协议把接入的数据~存下来Q?br />q尽量地q回应用所要求的数据,即待发数据的数量比~冲大。因此,Ҏ(gu)式传输协议来_(d)׃?x)碰到W S A E M S G S I Z Eq个错误?br />W S A R e c v函数在r e c v的基上增加了(jin)一些新Ҏ(gu)。比如说重叠I(yng) / O和部分数据报通知?br />W S A R e c v的定义如下:(x)</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 />参数sQ是已徏立连接的套接字。第二和W三个参数是接收数据的缓册Ӏl p B u ff e r s参数是一个W S A B U Fl构l成的数l,而d w B u ff e r C o u n t则表明前一个数l中W S A B U Fl构的数目?br />如果接收操作立即完成Q?l p N u m b e r O f B y t e s R e c e i v e d参数׃(x)指向执行q个函数调用所收到的字节数。l p F l a g s参数可以是下面Q何一个|(x) M S G _ P E E K、M S G _ O O B、M S G _ PA RT I A L或者对q些D行按位和q算之后的结果。M S G _ PA RT I A L标志使用和出现的地方不同Q其?br />义也不同。对面向消息的协议来_(d)q个标志是W S A R e c v调用q回后设|的Q如果因为缓冲空间不够导致整条消息未能在q次调用中返回的话)(j)。这Ӟ后面的W S A R e c v调用׃(x)讄q个标志M A S G _ PA RT I A LQ直到整条消息返回,才把q个标志清除。如果这个标志当作一个输入参数投递,接收操作应该在一收到数据q束,即它收到的只是整条消息中的一部分?br />M S G _ PA RT I A L标志只随面向消息的协议一起用。每个协议的协议条目都包含一个标志,表明是否支持q一Ҏ(gu)。有兌情,参见W?章。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(yng) / O操作</p> <p>4. WSARecvDisconnect<br />q函CW S A S e n d D i s c o n n e c t函数对应Q其定义如下Q?br />int WSARecvDisconnect( <br />            SOCKET s,<br />            LPWSABUF lpOUTboundDisconnectData<br />           );<br />和W(xu) S A S e n d D i s c o n n e c t函数的参CP该函数的参数也是已徏立连接的套接字句柄和<br />一个有效的W S A B U Fl构Q带有收到的数据Q。收到的数据可以只是断开数据。这个断开数据是另一端执行W(xu) 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函数׃(x)取消接收q程通信方的数据Q其作用和调用带有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扩展Q除?jin)f l a g s参数是按值引用外Q其余和r e c v函数是一L(fng)。它允许基层的提供者设|M S G _ PA RT I A L标志。该函数的原型如下:(x)</p> <p>int PASCAL FAR WSARecvEx(<br />             SOCKET s,<br />             char FAR * buf,<br />             int len,<br />             int * flags<br />            );<br />            </p> <p> <br />如果收到的数据不是一条完整的消息Q?f l a g s参数中就?x)返回M S G _ PA RT I A L标志。对面向消息的协议(即非协议)(j)来说Q这个标志比较有用(即非协议)(j)。在M S G _ PA RT I A L标志被当作f l a g s参数的一部分投递,而且收到的消息又不完整时Q调用W S A R e c v E xQ就?x)立?br />q回收到的那个数据。如果提供的接收~冲容纳不下整条消息Q?W S A R e c v E x׃(x)p|Qƈ出现W S A E M S G S I Z E 错误Q剩下的数据也会(x)被截掉。注意, M S G _ PA RT I A L 标志和W(xu) S A E M S G S I Z E错误之间的确区别是:(x)有了(jin)q个错误Q即使整条消息到达接收端Q但׃?br />供的数据~冲太少Q也不能对它q行接收。M S G _ P E E K 和M S G _ O O B标志q可以和W(xu) S A R e c v E x一起用?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-07 22:32 <a href="http://www.shnenglu.com/ivenher/articles/12130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12128.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12128.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12128.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12128.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12128.html</trackback:ping><description><![CDATA[ <p> 7.3.2 客户机API函数<br /> <br /> 客户单得多,建立成功q接所需的步骤也要少得多。客h只需三步操作Q?br />1) 用s o c k e t或W S A S o c k e t创徏一个套接字?br />2) 解析服务器名Q以基层协议为准Q?br />3) 用c o n n e c t或W S A C o n n e c t初始化一个连接?/p> <p>TCP状?br />作ؓ(f)一名Wi n s o c kE序员,通常没必要了(jin)解实际的T C P状态。但?jin)解T C P状态,p更好地理解Winsock API调用如何对基层协议中的改变生媄(jing)响。此外,许多E序员在关闭套接字时Q会(x)到一个常见问题;围绕套接字关闭的T C P状态是我们目前最感兴的问题?/p> <p>Ҏ(gu)个套接字来说Q它的初始状态都是C L O S E D。若客户机初始化?jin)一个连接,׃(x)向服务器发送一个S Y N包,同时客h套接字状态置为S Y N _ S E N T。服务器收到S Y N包后Q会(x)发出一个?S Y N - A C K”包。作为客hQ需要用一个A C K包对它做出反应。此Ӟ客户机的套接字会(x)变成E S TA B L I S H E D状态。如果服务器一直不发送?S Y N - A C K”包Q客h׃(x)时Qƈq回C L O S E D状态?/p> <p>若一个服务器的套接字同一个本地接口和端口l定hQƈ在它上面q行监听Q那么套接字的状态便是L I S T E N。客h试图与之q接Ӟ服务器就?x)收C个S Y N包,q用一个S Y N - A C K包做出响应。服务器套接字的状态就变成S Y N _ R C V D。最后,客户机发Z个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状态,可通过两种Ҏ(gu)来关闭它。如果由应用E序来关闭,便叫作“主动套接字关闭”;否则Q套接字的关闭便是被动的。图7 - 2对两U关闭方法进行了(jin)解释。如d关闭Q应用程序便?x)发Z个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当作W二个参敎ͼ(j)Q会(x)向对方发Z个F I N包,而且套接字的状态则变成F I N _ WA I T _ 1。正常情况下Q通信Ҏ(gu)?x)回应一个A C K包,我们的套接字的状?br />随之变成F I N _ WA I T _ 2。如Ҏ(gu)也关闭了(jin)q接Q便?x)发Z个F I N包,我们的机器则?x)响应一个A C K包,q将己方套接字的状态置为T I M E _ WA I T?/p> <p>T I M E _ WA I T状态也叫作2 M S L{待状态。其中, M S L代表“分D|长生存时间”(Maximum Segment LifetimeQ,表示一个数据包在丢弃之前,可在|络上存在多长时间?br />每个I P包都含有一个“生存时间”(T T LQ字D,若它递减?Q包便会(x)被丢弃。一个包l过|络上的每个路由器时Q?T T L 值都?x)? Q然后l传递。一旦应用程序进入T I M E _ WA I T状态,那么׃(x)一直持lM S L旉的两倍之久。这样一来, T C P可以在最后一个A C K丢失的前提下Q重新发送它Q也是_(d) F I N?x)被重新传送出厅RM S L旉两倍之久的{待状态结束之后,套接字便q入C L O S E D状态?br />?-2 TCP套接字的关闭状?br /><br />套接字主动关?br />关闭套接?br />发? FIN----->FIN_WAIT_1--接收:ACK-->FIN_WAIT_2----接收: FIN发? ACK--->TIME_WAIT(2MSL时)----->CLOSED</p> <p> <br />套接字主动关?br />关闭套接?br />发? FIN----->接收: FIN发? ACK---->CLOSING--接收: ACK-->TIME_WAIT(2MSL时)----->CLOSED</p> <p> <br />套接字主动关?br />关闭套接?br />发? FIN----->接收: FIN_ACK发? ACK----->TIME_WAIT(2MSL时)----->CLOSED</p> <p> <br />套接字被动关?br />接收: FIN<br />发? ACK------>CLOSE_WAIT--关闭套接字发? FIN-->LAST_ACK------->CLOSED</p> <p>T I M E _ WA I T状态也叫作2 M S L{待状态。其中, M S L代表“分D|长生存时间”(Maximum Segment LifetimeQ,表示一个数据包在丢弃之前,可在|络上存在多长时间?br />每个I P包都含有一个“生存时间”(T T LQ字D,若它递减?Q包便会(x)被丢弃。一个包l过|络上的每个路由器时Q?T T L 值都?x)? Q然后l传递。一旦应用程序进入T I M E _ WA I T状态,那么׃(x)一直持lM S L旉的两倍之久。这样一来, T C P可以在最后一个A C K丢失的前提下Q重新发送它Q也是_(d) F I N?x)被重新传送出厅RM S L旉两倍之久的{待状态结束之后,套接字便q入C L O S E D状态?/p> <p>采取d关闭措施Ӟ有两个\径会(x)q入T I M E _ WA I T状态。在我们以前的讨ZQ?br />只有一方发Z个F I NQƈ接收一个A C K响应。然而,另一方仍然可以自由地发送数据,<br />直到它也被关闭ؓ(f)止。因此,需要两个\径发挥作用。在一个\径中(卛_步关?Q一台计机和它的通信Ҏ(gu)?x)同时要求关闭;计算机向?gu)送出一个F I N数据包,q从它那里接收一个F I N数据包。随后,计算Z(x)发出一个A C K数据包,对对方的F I N包做出响应,q将自己的套接字|ؓ(f)C L O S I N G状态。计机从对斚w里接收到最后一个A C K包之后,它的套接字状态会(x)变成T I M E _ WA I T。?/p> <p>d关闭Ӟ另一个\径其实就是同步关闭的变体Q套接字从F I N _ WA I T _ 1状态直接变成T I M E _ WA I T。若应用E序发出一个F I N数据包,但几乎同时便从对斚w里接收到一个F I N - A C K包,q种情况׃(x)发生。在q种情况下,Ҏ(gu)?x)确认收到应用程序的F I N包,q出自己的F I N包。对于这个包Q应用程序会(x)用一个A C K包做出响应?br />T I M E _ WA I T状态的主要作用是在T C Pq接处于2 M S L{待状态的时候,规定用于建立那个q接的一对套接字不可被拒l。这对套接字由本地I P端口以及(qing)q程I P端口l成。对某些T C P实施Ҏ(gu)来说Q它们不允许拒绝处于T I M E _ WA I T状态下的套接字对中的Q何端口号。在微Y的方案中Q不?x)存在这个问题。然而,若试N过一对已处于T I M E _ WA I T状态的套接字徏立连接,׃(x)p|Qƈq回W S A E A D D R I N U S E错误。要解决q一问题Q除?jin)等待用那个本地端口来qT I M E _ WA I T状态的套接字对Q,一个办法是使用套接字?br />S O _ R E F U S E A D D RQ我们将在第9章对q个选项q行详细讨论?br />被动关闭情况下,应用E序?x)从?gu)那里接收一个F I N包,q用一个A C K包做出响应。此Ӟ应用E序的套接字?x)变成C L O S E _ WA I T状态。由于对方已关闭自己的套接字Q所以不能再发送数据了(jin)。但应用E序却不同,它能一直发送数据,直到Ҏ(gu)的套接字已关闭ؓ(f)止。要惛_闭对方的q接Q应用程序需要发?gu)qF I NQo(h)应用E序的套接字状态变成L A S T _ A C K。应用程序从Ҏ(gu)收到一个A C K包后Q它的套接字׃(x)逆{成C L O S E D状态?br />要想?jin)解T C P / I P协议的有兌情,请参阅RFC 793 文g。可在h t t p : / / w w w. r f c -e d i t o r. o rg那里扑ֈq䆾文g?/p> <p>c o n n e c t函数和W(xu) 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版本Q其定义如下Q?/p> <p>int connect(<br />       SOCKET s,<br />       const struct sockaddr FAR * addr,<br />       int namelen<br />      );<br /> <br /> 该函数的参数是相当清楚的Q?s是即在其上面徏立连接的那个有效T C P套接字; n a m e是针对T C PQ说明连接的服务器)(j)的套接字地址l构Q?S O C K A D D R _ I NQ;n a m e l e n则是名字参数的长度。Winsock 2版本中,它的定义是这L(fng)Q?/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(fng)。另外两个参数—l p C a l l e r D a t a和l p C a l l e e D a t aQ是字串~冲区,用于收发hq接时的数据。l p C a l l e r D a t a参数是指向缓冲区的指针,~冲区内包含客户机向服务器发出的hq接的数据。l p C a l l e e D a t a参数则指向另一个缓冲区Q区内包含服务器向客hq回的徏立连接时的数据。这两个参数都是W S A B U Fl构Q?br />因此Q若是l p C a l l e r D a t aQl e n字段应该设ؓ(f)b u f字段中准备传输的数据长度。若是l p C a l l e e D a t aQl e n字段则代表b u f中的~冲区长度,设ؓ(f)从服务器q回的数据长度。最后两个参数—l p S Q O S和l p G Q O SQ表CQ O Sl构Q该l构对即徏立的q接上收发数据所需要的带宽q行?jin)定义?br />l p Q O S参数用于指定套接字s需要的服务质量Q而l p G Q O S则用于指定套接字l所需要的服务质量。目前,未提供对套接字l的支持。若l p Q O S是空|则表明没有某应用专用的Q O S?br />如果你想q接的计机没有监听指定端口q一q程Q?c o n n e c t调用׃(x)p|Qƈ发生错误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 TQ这U情况一般发生在试图q接的计机不能用时Q亦可能因ؓ(f)CZ间的路由上出现硬件故障或L目前不在|上Q?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12128.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-07 22:31 <a href="http://www.shnenglu.com/ivenher/articles/12128.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12129.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12129.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12129.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12129.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12129.html</trackback:ping><description><![CDATA[ <p>.7.3.3 数据传输<br />收发数据是网l编E的主题。要在已建立q接的套接字上接收数据,可用q两个A P I函数Q?br />s e n d和W(xu) S A S e n d。第二个函数是Winsock 2中专有的。同样地Q在已徏立了(jin)q接的套接字上接收数据也有两个函敎ͼ(x) r e c v和W(xu) S A R e c v。后者也是Winsock 2函数?/p> <p>必须牢牢Cq一点:(x)所有关pd收发数据的缓冲都属于单的c h a rcd。也是_(d)q些函数没有“U n i c o d e”版本?/p> <p>另外Q所有收发函数返回的错误代码都是S O C K E T _ E R R O R。一旦返回错误,pȝ׃(x)调用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(xu) S A E C O N N R E S E T。两者均涉及(qing)到即关闭连接这一问题—要么通过时Q要么通过通信方关闭连接。另一个常见错误是W S A E W O U L D B L O C KQ一般出现在套接字处于非暂停?br />式或异步状态时。这个错误主要意味着指定函数暂不能完成?/p> <p>1. send和W(xu) S A S e n d<br />要在已徏立连接的套接字上发送数据,W一个可用的A P I函数是s e n dQ其原型为:(x)</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参数是已建立q接的套接字Q将在这个套接字上发送数据。第二个参数b u fQ则是字W缓冲区Q区内包含即发送的数据。第三个参数l(f) e nQ指定即发送的~冲区内的字W数。最后,f l a g s可ؓ(f)0、M S G _ D O N T R O U T E或M S G _ O O B。另外, f l a g sq可以是寚w些标志进行按位“或q算”的一个结果。M S G _ D O N T R O U T E标志要求传送层不要它发出的包?br />由出厅R由基层的传送决定是否实现这一hQ例如,若传送协议不支持该选项Q这一h׃(x)被忽略)(j)。M S G _ O O B标志预示数据应该被带外发送?br />对返回数据而言Qs e n dq回发送的字节敎ͼ若发生错误,p回S O C K E T _ E R R O R。常见的错误是W S A E C O N N A B O RT E DQ这一错误一般发生在虚拟回\׃时或协议有错而中断的时候。发生这U情冉|Q应该关闭这个套接字Q因为它不能再用?jin)。远E主Z的应用通过执行关闭或意外中断操作重新设|虚拟虚路时Q或q程L重新启动Ӟ发生的则是W S A E C O N N R E S E T错误。再ơ提醒大家注意,发生q一错误Ӟ应该关闭q个套接字。最<br />后一个常见错误是W S A E T I M E O U TQ它发生在连接由于网l故障或q程q接pȝ异常L而引L(fng)q接中断时?br />send API函数的Winsock 2版本是W S A S e n dQ它的定义如下:(x)</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 />q个套接字是一个连接会(x)话的有效句柄。第二个参数是指向一个或多个W S A B U Fl构的指针。它既可是一个独立的l构Q又可以是一l结构。第三个参数指明准备投递的W S A B U Fl构数。记住,每个W S A B U Fl构本n是一个字W缓冲和~冲长度。ؓ(f)何打同时发送多个缓冲呢Q也许大家不太明白其中的原因。这是我们E后要讲的“分散集中I / O模式”;但是Q在一个已建立q接的套接字上利用多~冲来发送数据时Q顺序是从第一个到最后一个W S A B U Fl构。l p N u m b e r O f B y t e s S e n t是指向D W O R DQ是W S A S e n d调用q回的)(j)的指针,其中包含字节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(yng) / O。重叠I(yng) / O是Wi n s o c k支持的异步I / O模式之一Q?/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设ؓ(f)写入的字节数。成功的话,该函数就q回0Q?br />否则p回S O C K _ E R R O RQ常见错误和s e n d函数的情形一栗?/p> <p>2. WSASendDisconnect<br />该函数非常特D,一般不用。其原型是:(x)</p> <p>int WSASendDisconnect( <br />            SOCKET s,<br />            LPWSABUF lpOUTboundDisconnectData<br />           );</p> <p>该函数v初将套接字置为关闭状态,发送无q接的数据。当?dng)它只能用于支持从容关机和无连接数据的传输协议。目前还没有传输提供者支持无q接的数据。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 函数差不多,但它另外q要发送包含在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׃(x)q回S O C K E T _ E R R O R。用该函数可能?x)出现s e n d函数中出现的某些错误?/p> <p>带外数据<br />对已建立q接的流套接字上的应用来_(d)如果需要发送的数据比流上的普通数据重要得多,便可这些重要数据标记成“带外数据”(Out-of-band, OOBQ。位于连接另一端的应用可通过一个独立的逻辑信道Q从概念上讲Q该逻辑信道与数据流无关Q来接收和处理O O B数据?br />在T C P中,O O B数据׃个紧?位标讎ͼ叫作U R GQ和T C P分段头中的一? 6位的指针l成。这里的标记和指针把指定的下行流字节当作紧急数据。实现紧急数据的两种Ҏ(gu)Ҏ(gu)目前只能在T C P.RFC 793中见刎ͼ该烦(ch)引对T C Pq行?jin)描qͼq引入了(jin)“紧急数据”这一概念Q表明T C P头中的紧急指针是紧急数据字节之后那个字节的l对偏移。但是在RFC 11 2 2中,却将紧急偏ULq成指向紧急字节本w?br />Wi n s o c k规格中,与协议无关的O O B数据和T C P的O O B数据实施Q紧急数据)(j)均采用了(jin)O O Bq一术语。要查看待发数据中是否包含紧急数据,必须通过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提供?jin)获得紧急数据的几个Ҏ(gu)。一是紧急数据一旦在U插入,它就?x)出现在普通数据流中;二是可以关闭在线插入Q这P不连l调用接收函数就?x)只q回紧急数据。至于控制O O B数据行ؓ(f)的套接字选项S O _ O O B I N L I N EQ我们也在W?章详l讨论?/p> <p>Te l n e t和R l o g i n使用紧急数据是有原因的。尽如此,除非你计划编写自qTe l n e t和R l o g i nQ否则就应该q离紧急数据。因为它不容易定义,而且其他q_上的实施情况可能和W(xu)i n 3 2有所不同。在q不得已的情况下使用紧急数据,必须发信号通知通信方ؓ(f)紧急数据执?br />一个独立的控制套接字,qؓ(f)普通数据的传输保留主要的套接字q接?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12129.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-07 22:31 <a href="http://www.shnenglu.com/ivenher/articles/12129.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12142.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12142.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12142.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12142.html</trackback:ping><description><![CDATA[ <p> W?章Winsock基础<br /> 本章专门讲解~写成功|络应用E序时所需的基本知识和A P I调用?br /> <br /> 7.1 Winsock的初始化<br />每个Wi n s o c k应用都必d载Winsock DLL的相应版本。如果调用Wi n s o c k之前Q没有加?br />Wi n s o c k库,q个函数׃(x)q回一个S O C K E T _ E R R O RQ错误信息是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函数实现的。这个函数的定义如下Q?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 )Q其中,x?br />高位字节Q?y是低位字节)(j)方便地获得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 ATAl构的指针, W S A S t a r t u p用其加蝲的库版本有关?br />信息填在q个l构中:(x)</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把第一个字Dw Ve r s i o n设成打算使用的Wi n s o c k版本。w H i g h Ve r s i o n参数?br />U的是现有的Wi n s o c k库的最高版本。记住,q两个字D中Q高位字节代表的是Wi n s o c k副版<br />本,而低位字D代表的则是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 sq两个字D는特定<br />的Wi n s o c k 实施Ҏ(gu)讑֮Q事实上没有用。不要用下面这两个字段Q?i M a x S o c k e t s?br />i M a x U d p D gQ它们是假定的同时最多可打开多少套接字和数据报的最大长度。然而,要知?br />数据报的最大长度应该通过W S A E n u m P r o t o c o l s来查询协议信息。同时最多打开套接字的数目<br />不是固定的,很大E度上和可用物理内存的多有兟뀂最后, l p Ve n d o r I n f o字段是ؓ(f)Wi n s o c k<br />实施Ҏ(gu)有关的指定厂商信息预留的。Q何一个Wi n 3 2q_上都没有使用q个字段?/p> <p>7.2 错误(g)查和控制<br />对编写成功的Wi n s o c k应用E序而言Q错误检查和控制是至关重要的Q因此,我们打算?br />为大家介l错误检查和控制。事实上Q对Wi n s o c k函数来说Q返回错误是非常常见的。。但是,<br />多数情况下,q些错误都是无关紧要的,通信仍可在套接字上进行。尽其q回的值ƈ非一<br />成不变,但不成功的Wi n s o c k调用q回的最常见的值是S O C K E T _ E R R O R?br />实际上, S O C K E T _ E R R O R帔R? 1?br />如果调用一个Wi n s o c k函数Q错误情况发生了(jin)Q就可用W S A G e t L a s t E r r o r函数来获得一D代码,<br />q段代码明确地表明发生的状况。该函数的定义如下:(x)</p> <p>int WSAGetLastError(void);</p> <p>发生错误之后调用q个函数Q就?x)返回所发生的特定错误的完整代码。W S A G e t L a s t E r r o r<br />函数q回的这些错误都已预定义帔R|为各U错误代码定义的帔R<br />Q带?定义指o(h)Q一般都以W S A E开头?/p> <p>7.3 面向q接的协?br />Ҏ(gu)务器监听的连接来_(d)它必d一个已知的名字上。在T C P / I P中,q个名字是<br />本地接口的I P地址Q加上一个端口编受每U协议都有一套不同的定址Ҏ(gu)Q所以有一U不<br />同的命名Ҏ(gu)。在Wi n s o c k中,W一步是指定协议的套接字绑定到它已知的名字上。这个过<br />E是通过A P I调用b i n d来完成的。下一步是套接字|ؓ(f)监听模式。这Ӟ用A P I函数l(f) i s t e n?br />完成的。最后,若一个客h试图建立q接Q服务器必须通过a c c e p t或W S A A c c e p t调用来接<br />受连接?br />1. bind<br />一旦ؓ(f)某种特定协议创徏?jin)套接字Q就必须套接字l定C个已知地址。b i n d函数可将<br />指定的套接字同一个已知地址l定C赗该函数声明如下Q?/p> <p>int bind(<br />     SOCKET s,<br />     const sturct sockaddr FAR * name,<br />     int  namelen<br />    );<br /> 其中Q第一个参数s代表我们希望在上面等待客hq接的那个套接字。第二个参数的类<br />型是struct sockaddrQ它的作用很单,是一个普通的~冲区。针对自己打用的那个?br />议,必须把该参数实际地填充一个地址~冲区,q在调用b i n d时将光型Z个s t r u c t<br />s o c k a d d r?br />举个例子来说Q下列代码阐qC(jin)在一个T C Pq接上,如何来做到这一点:(x)</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 *)&tcpaddr,sizeof(tcpaddr));</p> <p>一旦出错, b i n d ׃(x)q回S O C K E T _ E R R O R 。对b i n d 来说Q最常见的错误是<br />W S A E A D D R I N U S E。如使用的是T C P / I PQ那么W S A E A D D R I N U S EpC另一个进E已l同<br />本地I P接口和端口号l定C(jin)一P或者那个I P接口和端口号处于T I M E _ WA I T状态。假如你<br />针对一个套接字调用b i n dQ但那个套接字已l绑定,便会(x)q回W S A E F FA U LT错误?/p> <p>2. listen<br />我们接下来要做的是将套接字置入监听模式。b i n d函数的作用只是将一个套接字和一?br />指定的地址兌在一赗指CZ个套接字{候进入连接的A P I函数则是l i s t e nQ其定义如下Q?/p> <p>int listen(<br />      SOCKET s,<br />      int backlog<br />     );</p> <p>W一个参数同h限定套接字。b a c k l o g参数指定?jin)正在等待连接的最大队列长度。这?br />参数非常重要Q因为完全可能同时出现几个服务器q接h。例如,假定b a c k l o g参数?。如<br />果三个客h同时发出hQ那么头两个?x)被攑֜一个“待决”({待处理Q队列中Q以便应<br />用程序依ơؓ(f)它们提供服务。而第三个q接?x)造成一个W S A E C O N N R E F U S E D错误。注意,<br />一旦服务器接受?jin)一个连接,那个q接h׃(x)从队列中删去Q以便别人可l箋发出h?br />b a c k l o g参数其实本n存在着限制Q这个限制是由基层的协议提供者决定的。如果出现非?br />|那么?x)用与之最接近的一个合法值来取代。除此以外,对于如何知道实际的b a c k l o g|<br />其实q不存在一U标准手Dc(din)?br />与l i s t e n对应的错误是非常直观的。到目前为止Q最常见的错误是W S A E I N VA L。该错误<br />通常意味着Q你忘记在l i s t e n之前调用b i n d。否则,与b i n d调用相反Q用l i s t e n时可能收?br />W S A E A D D R I N U S E。这个错误通常是在q行b i n d调用时发生的?/p> <p>3. accept和W(xu) S A A c c e p t<br />现在Q我们已做好?jin)接受客戯接的准备。这是通过a c c e p t或W S A A c c e p t函数来完成的?br />a c c e p t格式如下Q?/p> <p>SOCKET accept(<br />        SOCKET s,<br />        struct sockaddr FAR * addr,<br />        int FAR * addrlen<br />       );<br />其中Q参数s是一个限定套接字Q它处在监听模式。第二个参数应该是一个有效的<br />S O C K A D D R _ I Nl构的地址Q而a d d r l e n应该是S O C K A D D R _ I Nl构的长度。对于属于另一U?br />协议的套接字Q应当用与那U协议对应的S O C K A D D Rl构来替换S O C K A D D R _ I N。通过?br />a c c p e t函数的调用,可ؓ(f)待决q接队列中的W一个连接请求提供服务。a c c e p t函数q回后,<br />a d d rl构中会(x)包含发出q接h的那个客h的I P地址信息Q而a d d r l e n参数则指出结构的?br />度。此外,a c c e p t?x)返回一个新的套接字描述W,它对应于已经接受的那个客hq接。对?br />该客h后箋的所有操作,都应使用q个新套接字。至于原来那个监听套接字Q它仍然用于<br />接受其他客户接,而且仍处于监听模式?/p> <p>Winsock 2引入?jin)一个名为W S A A c c e p t的函数。它能根据一个条件函数的q回|选择?br />地接受一个连接。这个新函数的定义如下:(x)</p> <p>SOCKET WSAAccept(<br />         SOCKET  s,<br />         struct sockaddr FAR * addr,<br />         LPINT addlen,<br />         LPCONDITIONPROC lpfnCondition,<br />         DWORD  dwCallbackData<br />        );<br /> <br />其中Q头三个参数与a c c e p t的Winsock 1版本是相同的。l p f n C o n d i t i o n参数是指向一个函?br />的指针,那个函数是根据客戯求来调用的。该函数军_是否接受客户的连接请求,定义?br />下:(x)</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是一个值参敎ͼ其中包含q接实体的地址。W S A B U Fl构是许多Winsock 2函数<br />常用的。对它的声明如下Q?/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来说Qb u f指针指向的是一个地址l构。该l构针对的是建立q接的那U特?br />通信协议。ؓ(f)正确q回信息Q只db u f指针建立为恰当的S O C K A D D Rcd。在T C P / I P的情<br />况下Q在S O C K A D D R _ I Nl构中,当然应该包含建立q接的那个客h的I P地址。在q接?br />求期_(d)大多数网l协议都能提供对呼叫者I D信息的支持?br />l p C a l l e r D a t a参数中包含了(jin)随连接请求一道,由客h发出的Q何连接数据。若其中未指<br />定呼叫者数据,那么该参数就默认为N U L L。要注意的是Q对大多数网l协议(如T C PQ来_(d)<br />它们q不提供对连接数据的支持。至于一U协议到底是支持q接数据Q还是支持断开数据Q?br />可用W S A E n u m P r o t o c o l s函数对Wi n s o c k目录中相应的条目q行查询Q从而得出正的l论?/p> <p>l p S Q O S和l p G Q O S参数对客hh的Q何一个服务质量( Q O SQ参数进行指定,两个<br />参数都引用了(jin)一个Q O Sl构Q该l构中包含的信息是关于收发数据所需要的带宽。如果客?br />机没有要求Q O SQ这些参数都是N U L L。这两个参数的不同之处在于l p S Q O S指定的是一?br />独立的连接,而l p G Q O S则用于套接字l。在Winsock 1?中没有实施或支持套接字组</p> <p>l p C l a l l e e I d属于另一UW S A B U Fl构Q这一l构中包含已与客h需要与之连接的本地?br />址。该l构的b u f字段同样指向其相应地址家族的一个S O C K A D D R对象。对正在一个多L<br />的机器上q行的服务器来说Q这U信息非常有用。记住,如果服务器和I N A D D R _ A N Y地址l?br />定在一PM一个网l接口都可ؓ(f)q接h提供服务。随后,该参C(x)q回实际建立q接<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 Fl构Q服?br />器可利用q个l构把数据当作连接请求进E的一部分Q返回客h。如果服务提供者支持这<br />一选项Q?l e n字段׃(x)指出作ؓ(f)q个q接h一部分Q服务器最多可向客hq回多少字节?br />q种情况下,服务器会(x)Ҏ(gu)q一数量Q将可能多的字节复制到W S A B U Fl构的b u f部分Q同<br />时用l e n字段指出实际传输?jin)多个字节。如果服务器不想q回Mq接数据Q那么,在返?br />之前Q条件接受函数应l e n字段设ؓ(f)0。假如提供者不支持q接数据Q?l e n字段׃(x)?。大?br />数协议同样都不支持在接受q接之前q行数据交换。事实上Q?Wi n 3 2q_当前支持的所有协?br />都不支持q一Ҏ(gu)?br />服务器将传递给条g函数的参数处理完之后Q必L出到底是接受、拒l还是g后客h的连接请求。如果服务器打算接受q接Q那么条件函数就应返回C F _ A C C E P T。如果拒l,<br />函数应q回C F _ R E J E C T。如果出于某U原因,现在q不能做出决定,应q回C F _ D E F E R?br />若服务器准备对这个连接请求进行处理,应调用W S A A c c c e p t。要注意的是Q条件函数在?br />W S A A c c e p t函数相同的进E内q行Q而且?x)立卌回。另外还要注意的是,对于当前?br />Wi n 3 2q_支持的协议来_(d)条g接受函数q不意味着客户机的q接h必须在从该函数返?br />一个g后才?x)得到满뀂大多数情况下,最基层的网l堆栈在条g接受函数调用的那一刻,<br />已l接受了(jin)q接。如果返回C F _ R E J E C T|基层堆栈׃(x)连接简单地关闭?jin)事?br />如发生错误,׃(x)q回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 />果监听套接字处于异步状态或非暂停模式,同时没有要接受的q接Ӟ׃(x)产生此类的错误?br />若条件函数返回C F _ D E F E RQW(xu) S A A c c e p t׃(x)q回W S AT RY _ A G A I N错误。如果条件函数返<br />回C F _ R E J E C TQW(xu) 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.shnenglu.com/ivenher/aggbug/12142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-07 22:29 <a href="http://www.shnenglu.com/ivenher/articles/12142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12100.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12100.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12100.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12100.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12100.html</trackback:ping><description><![CDATA[ <p> W?章地址家族和名字解?br /> <br /> 要通过Wi n s o c k建立通信Q必M(jin)解如何利用指定的协议为工作站定址。本章将一一说明Wi n s o c k支持的协议以?qing)各协议如何把一个指定家族的地址解析成网l上一台具体的机器?/p> <p>6.1 IP<br />|际协议Q?Internet Protocol, IPQ是一U用于互联网的网l协?<br />从它的设计看来, I P是一个无q接的协议,不能保证数据投递万<br />无一失。两个比它高U的协议Q?T C P和U D PQ用于依赖I P协议的数据通信?/p> <p>6.1.1 TCP<br />面向q接的通信是通过“传输控制协议”(Transmission Control Protocol, TCPQ来完成的?br />T C P提供两台计算Z间的可靠无错的数据传输。应用程序利用T C Pq行通信Ӟ源和目标之间?x)徏立一个虚拟连接。这个连接一旦徏立,两台计算Z间就可以把数据当作一个双向字节流q行交换?/p> <p>6.1.2 UDP<br />无连接通信是通过“用h据报协议”(User Datagram Protocol, UDPQ来完成的。U D P不保障可靠数据的传输Q但能够向若q个目标发送数据,接收发自若干个源的数据?br />数据传输Ҏ(gu)采用的是数据报?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通信定址Q?/p> <p>6.1.3 定址</p> <p>Wi n s o c k中,应用通过S O C K A D D R _ I Nl构来指定I P地址和服务端口信息,该结构的格式如下Q?/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>。从本质上说Q端口号分ؓ(f)下面q三c:(x)“已知”端口、已注册端口、动态和Q或Q私用端口?br />?0 ~ 1 0 2 3由I A N A控制Q是为固定服务保留的?br />?1 0 2 4 ~ 4 9 1 5 1是I A N A列出来的、已注册的端口,供普通用L(fng)普通用戯E或E序?br />用?br />?4 9 1 5 2 ~ 6 5 5 3 5是动态和Q或Q私用端口?br />普通用户应用应该选择1 0 2 4 ~ 4 9 1 5 1之间的已注册端口Q?br />S O C K A D D R _ I Nl构的s i n _ a d d r字段用于把一个I P地址保存Z?字节的数Q它是无W号长整数类型。根据这个字D늚不同用法Q还可表CZ个本地或q程I P地址。I P地址一般是<br />用“互联网标准点分表示法”(像a . b . c . d一P(j)指定的,每个字母代表一个字节数Q从左到叛_配一?字节的无W号长整数。最后一个字Dsin_ zero Q只充当填充的职责Q以使S O C K A D D R _ I Nl构和S O C K A D D Rl构的长度一栗?/p> <p>一个有用的、名为i n e t _ a d d r的支持函敎ͼ可把一个点式I P地址转换成一? 2位的无符号长整数。它的定义如下:(x)<br />unsigned long inet_addr(<br />const char FAR *cp<br />);<br />q个函数把I P地址当作一个按|络字节序排列? 2位无W号长整数返?br />1. Ҏ(gu)地址<br />对于特定情况下的套接字行为,有两个特DI P 地址可对它们产生影响。特D地址I N A D D R _ A N Y允许服务器应用监听主机上面每个|络接口上的客户机活动。一般情况下Q在该地址l定套接字和本地接口Ӟ|络应用才利用这个地址来监听连接。如果你有一个多址pȝQ这个地址允怸个独立应用接受发自多个接口的回应。特D地址I N A D D R _ B R O A D C A S T用于在一个I P|络中发送广播U D P数据报。要使用q个Ҏ(gu)地址Q需要应用设|套接字选项S O _ B R O A D C A S T?/p> <p>2. 字节排序<br />针对“大头”(b i g - e n d i a nQ和“小头”(l i t t l e - e n d i a nQŞ式的~号Q不同的计算机处理器的表C方法有所不同Q这由各自的设计军_。比如, Intel 86处理器上Q用“小头”Ş式来表示多字节编P(x)字节的排序是从最无意义的字节到最有意义的字节。在计算Z把I P地址和端口号指定成多字节数时Q这个数按“主机字节”(h o s t - b y t eQ顺序来表示。但是,如果在网l上指定I P地址和端口号Q“互联网联网标准”指定多字节值必ȝ“大头”Ş式来表示<br />Q从最有意义的字节到最无意义的字节Q,一般称之ؓ(f)“网l字节”(n e t w o r k - b y t eQ顺序?/p> <p>我们打算演示一下如何利用上面描q的i n e t _ a d d r 和h t o n s 函数来创?br />S O C K A D D R _ I Nl构?/p> <p>SOCKETADDR_IN I(yng)nternetAddr;<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协议q行通信。如要用T C P协议打开一个I P套接字,需调用带有地址家族A F _ I N E T和套接字cdS O C K _ S T R E A M的s o c k e t函数或W S A S o c k e t函数Qƈ把协议字D设?Q方式如下:(x)<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和主机通信Ӟ必须知道q个L的I P地址。依用户看来Q?I P地址是不Ҏ(gu)记的。在指定机器Ӟ许多人更愿意利用一个易记的、友好的L名而不是I P地址。Wi n s o c k提供?jin)两个支持函敎ͼ它们有助于用h一个主机名解析成I P地址?br />Wi n d o w s套接字g e t h o s t b y n a m e和W(xu)SAAsynGetHostByName API函数从主机数据库中取回与指定的主机名对应的主Z息。两个函数均q回一个H O S T E N Tl构.</p> <p> <br />6.3 IPX/SPX<br />“互联网包交换”(I P XQ协议是一个常见协议,一般ؓ(f)承担Novell NetWa r e客户机/服务器联|服务的计算机所用。。I P X提供两个q程间的无连接通信Q因此,如果一个工作站发出一个数据包Q该协议无法保证q个数据包会(x)准确无误地投递到目标地点。如果应用程序需要数据投递保证,但仍坚持使用I P XQ它?yu)׃?x)选用一个比I P X高的协议,比如说“顺序分l交换?br />QS P XQ和SPX II协议Q这两个协议中, S P X包通过I P X发送。Wi n s o c k为应用程序提供了(jin)在Wi n d o w sq_上通过I P Xq行通信的能力(它们是Windows 95、Windows 98、Windows NT以及(qing)Windows 2000Q?/p> <p>6.6 ATM<br />异步传输模式QAT MQ协议是目前已有的最新协议之一Q?Windows 98和W(xu)indows 2000q_上的Winsock 2均支持它。AT M通常用于L A N和W(xu)A N上的高速联|,也用于各U类型的通信Q比如说要求高速通信的语韟뀁视频和数据{。一般说来, AT M利用|络上的虚拟q接QV CQ来提供服务质量Q?Q O SQ保证。正如大家即看到的那样QW(xu)i n s o c k能够通过AT M地址家族来用AT M|络上的虚拟q接。AT M|络Q如? - 1所C)(j)一般由通过交换机(它们?br />AT M|络桥接在一P(j)q接的端点(或计机Q构成?br />针对AT M协议~程Ӟ需要明白这几点。首先, AT M是一个媒体类型,而不是一个真正的协议。也是_(d) AT McM于直接在以太|上写入以太帧。和以太|一P AT M协议没有提供控制。它是一个面向连接的协议Q要么提供消息模式,要么提供模式。这q意味着如果数据不能快速发送出去,发送应用则可能溢出本地~冲。同样地Q接收应用必频J投递收到的数据Q否则,接收~冲填满之时QQ何一个另外接入的数据都可能被丢弃。如果你的应用需要流控制Q方法之一是在AT M上用I P协议Q它只是q行于AT M|络上的I P协议Q?br />q样一来,应用便紧跟在上面描述的I P地址家族之后。当?dng)?AT M的确提供?jin)比I P好的一些好处,比如说“根式多播方案”(W? 2章将Ҏ(gu)q行说明Q;然而,要根据自q应用需要来军_最适合你的那种协议?/p> <p>6.8 结<br />q一章论qC(jin)Wi n s o c k支持的协议地址家族Q说明了(jin)各个家族Ҏ(gu)的定址属性。针Ҏ(gu)个地址家族Q我们还讨论?jin)如何创建套接字和如何设|套接字地址l构Q以便开始通过协议q行通信。下一章,我们描q适用于Wi n s o c k的基本通信技术,q把它们应用到本章讨论的所有地址家族上?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12100.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-06 20:04 <a href="http://www.shnenglu.com/ivenher/articles/12100.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12098.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12098.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12098.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12098.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12098.html</trackback:ping><description><![CDATA[ <p> W二部分Winsock API<br /> <br /> Wi n s o c k是网l编E接口,而不是协议。它从U n i xq_的B e r k e l e yQB S DQ套接字Ҏ(gu)借鉴?jin)许多东西,后者能讉K多种|络协议。在Wi n 3 2环境中,W(xu)i n s o c k接口最l成Z个真正的“与协议无关”接口,其是在Winsock 2发布之后?br />本部分开始之前,我们假定大家已具备了(jin)Wi n s o c kQ或B S D套接字)(j)的基本知识,而且多少熟?zhn)一些客hQ服务器的Wi n s o c k基本知识?/p> <p>W?章网l原理和协议</p> <p>建立Winsock 2规范的主要目的是提供一个与协议无关的传送接口?/p> <p>5.1 协议的特?br />本章W一节着重讲解目前常用网l传送协议的一些基本特征。通过q里的学?fn),大家可掌握与协议行?f)cd有关的一些背景知识。同Ӟ作ؓ(f)E序员,可大致知道特定协议在E序中的行ؓ(f)方式?/p> <p>5.1.1 面向消息<br />Ҏ(gu)个离散写命o(h)来说Q如果传送协议把它们Q而且只有它们Q当做一条独立的消息在网上传送,我们p该协议是面向协议的。同Ӟq意味着接收端在接收数据Ӟq回的数据是发送端写入的一条离散消息。接收端不能得到更多的消息,仅此而已。比如,在图5 - 1中,<br />左边的工作站向右边的工作站提交了(jin)三条分别? 2 8? 4? 2字节的消息。作为接收端的工作站发出三条d命o(h)Q缓冲区? 5 6个字节。后来的各次调用q回的分别是1 2 8? 6 4 ? 2个字节。第一ơ读取调用不?x)将q所有的三个数据包都q回Q即使这些数据包已经收到也如此。这UCؓ(f)“保护消息边界”(preserving message boundariesQ,Q一般出现在交换l构化数据时?br />|络游戏是“保护消息边界”的较好范例。每个玩家均向别的玩家发Z个带有地图信息的数据包。这U通信后面的代码很单:(x)一个玩家请求一个数据包Q另一个玩家又准确C别的玩家处获得一个地图信息数据包?br /><br />无保护消息边界的协议通常UC“基于流的协议”。大家要知道“基于流的协议”这一术语常用来指代附加特性。流服务的定义是q箋的数据传输;不管消息边界是否存在Q接收端都会(x)量地读取有效数据。对发送端来说Q意味着允许pȝ原始消息分解成消息或把几条消息积累在一P形成一个较大的数据包。对接收端来_(d)则是数据一到达|络堆栈Q?br />|络堆栈开始读取它Qƈ它~存下来{候进E处理。在q程h处理大量数据Ӟpȝ?x)在不溢Zؓ(f)客户h提供的缓冲区q一前提下,量q回更多的数据。在? - 2中,发送端提交?jin)三个数据包Q分别是1 2 8? 4? 2个字节;但是Q本地系l堆栈自由地把这些数据聚合在一P形成一个大的数据包。这U情况下Q重l后?个数据包׃(x)一起传输。是否将各个独立的数据包累积在一起受许多因素的媄(jing)响,比如最大传输单元或N a g l e法。在T C P / I P中,在将U篏h的数据发到线上之前, N a g l e法在等候数据积累的L中进行?br />在需要发送的数据U篏C定数量或预定旉已超q之前,q个L?x)一直等下去。实施N a g l e法ӞL的通信方在发送主机确认之前,?x)等一{外出数据,L的通信方就不必发送一个只有确认的数据包。发送小数据包不仅没有多意义,而且q会(x)徒增错误(g)查和认Q相当烦(ch)人?br />在接收端Q网l堆栈把所有进来的数据包聚集在一P归入既定q程。我们来看看? - 2?br />如果接收端执行一? 5 6字节~冲区的dQ系l马上就?x)返? 2 4个字节。如果接收端只要求读? 0个字节,pȝ׃(x)只返? 0个字节?/p> <p>伪流<br />伪流Q?p s e u d o - s t r e a mQ这个术语常用于某种pȝ中,该系l用的协议是基于消息的Q?br />发送的数据分别在各自独立的数据包内Q接收端dq把消息~存在一P  q样Q接收应?br />E序便可dL大小的数据块。把? - 1中的发送端和图5 - 2中的接收端结合v来,便可说明<br />伪流的工作原理。发送端必须分别发送各自独立的数据包,但接收端可以Ҏ(gu)到的数据包自<br />q合。一般情况下Q可把伪视作一个普通的“面向流的协议”?/p> <p> <br />5.1.2 面向q接和无q接<br />通常情况下,一个协议提供面向连接的服务Q或提供无连接的服务。面向连接的服务中,<br />q行数据交换之前Q必M通信方徏立一条\径。这h定?jin)通信方之间存在\由,又保证了(jin)通信双方都是zd的、都可彼此响应,但其特点是在通信双方之间建立一个通信信道需要很多开支。除此以外,大部分面向连接的协议Z证投递无误,可能?x)因为执行额外的计算来验证正性,因此Q进一步增加开支。而无q接协议却不保证接收端是否正在收听。无q接服务cM于邮政服务:(x)发信人把信装入邮即可。至于收信h是否x到这信或邮局是否?x)因为暴风雨未能按时信件投递到收信人处{等Q发信h都不得而知?/p> <p> <br />5.1.3 可靠性和ơ序?br />在设计用于特定协议的应用E序来说Q可靠性和ơ序性是我们必须?jin)解的最具决定性的Ҏ(gu)。大多数情况下,可靠性和ơ序性与协议是无q接的,q是面向q接的密切相兟?/p> <p>5.1.4 从容关闭<br />从容关闭只出现在面向q接的协议中。在q种关闭q程中,一方开始关闭通信?x)话Q但另一方仍然可以读取线上或|络堆栈上已挂v的数据。如果面向连接的协议不支持从容关闭,<br />只要其中一方关闭了(jin)通信信道Q都?x)导致连接立即中断,数据丢失Q接收端不能d数据q些情况出现?/p> <p>5.1.5 q播数据<br />q播数据x据从一个工作站发出Q局域网内的其他所有工作站都能收到它。这一特征<br />适用于无q接协议Q因为L A N上的所有机器都可获得ƈ处理q播消息。。一般情况下Q\由器都不?x)传送广播包? </p> <p>5.1.6 多播数据<br />多播是指一个进E发送数据的能力Q这些数据即由一个或多个接收端进行接收。进E加入一个多播会(x)话的Ҏ(gu)和采用的基层协议有关。视频会(x)议应用常怋用多播?/p> <p>5.1.7 服务质量<br />服务质量Q?Q o SQ是应用的一U能力,用以h针对专门用途分配特定的带宽?/p> <p>5.1.8 部分消息<br />部分消息只用于面向消息的协议?/p> <p>5.1.9 路由选择的考虑<br />一个重要考虑是协议是否可\由。如果协议可路由Q就可在两个工作站之间徏立一条成功的通信路径Q要么是面向q接的回路,要么是数据报的数据\径)(j)Q不这两个工作站之间存在的|络g是什么。\由器不对发自非\由协议的数据包进行{发,即便数据包的既定目的地在其连接的子网上?/p> <p>5.2 支持的协?br />Wi n 3 2q_提供的最有用的特征之一是能够同步支持多U不同的|络协议?br />利用Wi n s o c k~程接口的好<br />处之一是因为它是一个与协议无关的接口。不用的是哪一U协议,它们的操作大多数?br />盔R的?/p> <p>要想获得pȝ中安装的|络协议的相关信息,调用q个函数W S A E n u m P r o t o c o l s卛_Q?br />打开Winsock在可以调用一个Wi n s o c k函数之前Q必d加蝲一个版本正的Wi n s o c k库。Wi n s o c k<br />启动例程是W S A S t a r t u pQ它的定义是Q?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 />W一个参数是准备加蝲的Wi n s o c k库的版本受就目前的Wi n 3 2q_而言QW(xu)insock 2<br />库的最新版本是2 . 2。唯一的例外是Windows CEQ它只支持Winsock 1.1版。如果需?br />Winsock 2.2版,指定q个| 0 x 0 2 0 2Q或使用宏M A K E W O R D ( 2 , 2 )卛_。高位字节指?br />副版本,而低位字节则指定ȝ本?br />W二个参数是W S A D ATAl构Q它是调用完成之后立卌回的。W S A D ATA包含?jin)W S A S t a r t u p加蝲的关于Wi n s o c k版本的信息?br />大致说来Q在W S A D ATAl构中,q回的唯一有用的信息是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库,而且不再需要调用Q何Wi n s o c k函数Ӟ?x)卸载这个库Qƈ释放资源。这个函数的定义是:(x)<br />int WSACleanup (void);<br />CQ每ơ调用W S A S t a r t u pQ都需要调用相应的W S A C l e a n u pQ因为每ơ启动调用都<br />?x)增加对加蝲Winsock DLL的引用次敎ͼ它要求调用同样多ơ的W S A C l e a n u pQ以此抵?br />引用ơ数?/p> <p>5.4 Windows套接?/p> <p>。所谓套接字Q就是一个指向传输提<br />供者的句柄。Wi n 3 2中,套接字不同于文g描述W,所以它是一个独立的cd—S O C K E T?br />套接字是׃个函数徏立的Q?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 />W一个参数a fQ是协议的地址家族。比如,如果惛_立一个U D P或T C P套接字,可用帔RA F _ I N E T来指代互联网协议Q?I PQ?br />W二个参数t y p eQ是协议的套接字cd。套接字的类型可以是下面五个|(x)<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 />W三个参数是p r o t o c o l。指定的地址家族和套接字cd有多个条目时Q就可用<br />q个字段来限定用特定传输?br /></p> <p>最后两个W S A S o c k e t标志很简单。组参数始终?Q因为目前尚无可支持套接字组?br />Wi n s o c k版本。要指定一个或多个下列标志Q可用d w F l a g s参数Q?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一个标志W S A _ F L A G _ O V E R L A P P E DQ用于指定这个套接字具备重叠I(yng) / OQ是适用?br />Wi n s o c k的可能实现的通信模式之一Q。这个主题将在第8章详l讨论。调用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建立套接字时Q可向函数调用传送一个W S A P R O TO CO L _ I N F Ol构Q以<br />定义准备建立的那个套接字的类型;管如此Q还是可建立一些套接字cdQ在传输提供?br />目录中,它们没有相应的条目)(j)。最佳示例是I P协议下的原始套接字。原始套接字一U通信Q?br />允许你把其他协议装在U D P数据包中Q比如说“互联网控制消息协议”(I C M PQ。I C M P?br />目的是投递互联网L间的控制、错误和信息型消息。由于I C M P不提供Q何数据传输功能,<br />因此不能把它与U D P或T C P同等看待Q但它和I P本n属于同一个别?/p> <p>Winsock API安装在“会(x)话层”和“传送层”之间?/p> <p>5.6 选择适当的协?br />T C P / I P是首选协议之一Q至从支持能力和微软的赞助q一角度来看Q是<br />q样?/p> <p>5.7 结<br />通过本章的学?fn),大家已?jin)解ؓ(f)应用E序选择|络传输时应该知道的基本Ҏ(gu)。在为指100计计W二部分附Winsock API<br />下蝲定的协议开发成功的|络应用E序Ӟ?jin)解q些Ҏ(gu)是臛_重要的。我们还有计划地深入探讨?jin)如何获得安装在pȝ中的传输提供者列表和如何查询特定属性。最后,我们q学?fn)?jin)如何为指定的传输建立套接字,Ҏ(gu)是ؓ(f)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查询目录条目以及(qing)通过W S A P R O TO C O L _ I N F Ol构Q把函数投递到W S A S o c k e t。下一章,我们进一步探讨各主要协议的定址Ҏ(gu)?/p> <p> </p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12098.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-06 19:00 <a href="http://www.shnenglu.com/ivenher/articles/12098.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12097.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12097.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12097.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12097.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12097.html</trackback:ping><description><![CDATA[ <p> W?章命名管?br /> <br /> “命名管道”或“命名管U(k)(Named Pipes Q是一U简单的q程间通信Q?I P CQ机Ӟ 命名道可在同一台计机的不同进E之_(d)或在跨越一个网l的<br />不同计算机的不同q程之间Q支持可靠的、单向或双向的数据通信?/p> <p>要记住的一个重Ҏ(gu)Q将命名道作ؓ(f)一U网l编E方案用时Q它实际上徏立一个简单的客户机/服务器数据通信体系Q可在其中可靠地传输数据?br />4.1.1 命名道命名规范命名道的标识是采用U N C格式q行的:(x)<br />\ \ s e r v e r \ P i p e \ [ p a t h ] n a m e<br />上述字串可分Z部分来观看:(x) \ \ s e r v e r、\ P i p e和\ [ p a t h ] n a m e。第一部分\ \ s e r v e r指定一个服务器的名字。命名管道便是在那个服务器上创徏的,而且要由它对q入的连接请求进行“监听”。第二部分\ P i p e是一个不可变化的“硬~码”字Ԍ必须原样照录Q但不用区分大小写)(j)Q用于指?gu)文g从属于N P F S。而第三部分\ [ p a t h ] n a m e则应用E序可以“唯一”定?br />?qing)标定一个命名管道的名字Q而且可在q里讄多目录?/p> <p>4.1.2 字节模式?qing)消息模?br />命名道提供?jin)两U基本通信模式Q字节模式和消息模式?/p> <p>命名道服务器应用只能在Windows NT或Windows 2000上工作——Windows 95和W(xu)indows 98 不允许应用程序创建命名管道!</p> <p>4.2.2 高服务器的l节</p> <p>在前面的E序清单4 - 1中,我们展示?jin)如何设计一个命名管道服务器应用Qo(h)其只负责对一个管道实例的控制。所有A P I调用都采用同步模式工作。在q种模式下,每个调用都会(x)一直等到I / Oh完成Q才?x)返回。命名管道服务器也能拥有多个道实例Q所以客h能够建立<br />同服务器的两个或更多的连接;道实例的数量要受到C r e a t e N a m e d P i p eq个A P I调用之n M a x I n s t a n c e s参数指定的数字的限制。要惛_时控制不止一个的道实例Q服务器必须考虑使用多个U程Q或者用异步Win32 I/O机制Q比如重叠式I / O以及(qing)完成端口{)(j)Q分别ؓ(f)每个道实例提供服务?/p> <p>采用异步I / O机制Q服务器可从单独一个应用程序线E中Q同时ؓ(f)所有管<br />道实例提供服务。在此,我们解释如何用线E以?qing)重叠式I / OQ来开发更高的服务器应用?</p> <p>1 . U程<br />要想开发一个高U服务器Qo(h)其用线E,同时支持多个道实例Q整个过E是非常单的。我们要做的唯一事情便是为每个管道实例都创徏一个线E?</p> <p>2. 重叠式I / O<br />重叠式I / O是一U特D的输入Q输出机Ӟ允许Win32 API函数Q如R e a d F i l e和W(xu)r i t e F i l eQ在发出I / Oh之后Q以异步方式工作。具体的工作原理是:(x)向这些A P I函数传递一个O V E R L A P P E D<br />Q重叠式Q结构,然后使用A P I函数G e t O v e r l a p p e d R e s u l tQ从原来那个O V E R L A P P E Dl构中,取得一ơI / Oh的结果。如果在使用重叠式结构的前提下,调用一个Win32 API函数Q那么调用无论如何都?x)立卌回?/p> <p>3. 安全模拟<br />之所以会(x)选择命名道作ؓ(f)自己的网l编E方案,一个最好的理由便是它们依赖于Windows NT?qing)Windows 2000的安全机ӞW(xu)indows NT和W(xu)indows 2000安全机制h“模拟”能力,允许一个命名管道服务器<br />应用在客h的安全环境中执行。执行一个命名管道服务器应用Ӟ它通常?x)在用于启动该应用的那个q程的安全环境许可别上工作。例如,假如拥有理员权限的某h启动?jin)一个命名管道服务器Q服务器便有权访问Windows NT或Windows 2000pȝ上的几乎M资源。此Ӟ假如在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 Rl构允许所有用戯问这?br />命名道Q就?x)埋下极大的安全隐(zhn)?/p> <p>4.5 结<br />本章向大家介l了(jin)命名道|络~程技术,它ؓ(f)我们建立?jin)一个简单的客户机/服务器数据通信体系Q可保数据q行可靠传输。接口依赖于Wi n d o w s重定向器Q以侉K过一个网l来传送数据。对命o(h)道而言Q它最大的一好处便是直接利用了(jin)Windows NT?qing)Wi n d o w s2 0 0 0的安全机Ӟ该机制是本书讲到的其他网l技术均不具备的一好处!下面W二部分向大家深入讲解Wi n s o c k技术,以便应用E序利用一U网l传输协议,q行“直接”通信?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12097.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-06 17:24 <a href="http://www.shnenglu.com/ivenher/articles/12097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.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.shnenglu.com/ivenher/articles/12055.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12055.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12055.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12055.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12055.html</trackback:ping><description><![CDATA[ <p> W?章重定向?br /> 首先Q我们打解释如<br />何通过|络Q用“多U N C提供者”( Multiple UNC Provider, MUPQ资源定位符Q通过“通用命名规范”(Universal Naming Convention, UNCQ来引用q程文g?/p> <p>随后Q我们讲解了(jin)M U P如何调用一个网l提供者,从而揭C出怎样通过一个重定向器,在“服务器消息块”(Server Message Block, SMBQ协议的帮助下,在不同的计算Z间徏立数据通信?/p> <p>最后,我们探讨?jin)网l安全方面的一些问题。用基本的文gI / O操作Q通过|络来访问文件时Q这些安全问题是必须考虑到的?/p> <p>2.1 通用命名规范</p> <p>“U N C路径? 为网l文件及(qing)讑֤的访问徏立了(jin)一套统一的规范。它最大的特点便是不必指定或引用一个已映射到远E文件系l的本地驱动器字母?/p> <p>U N C名字完全解决?jin)这些问题,它的格式如下Q?br />\ \ [服务器] \ [׃n名] \ [路径]<br />W一部分是\ \ [服务器]Q必M两个反斜杠开_(d)紧跟着一个服务器名字?br />W二部分是\ [׃n名]Q它对应着q程服务器上的一个“共享入口”或者“共享位|”?br />。而第三部分\ [路径]  对应的是׃n位置下的某个具体目录Q或子目录)(j)</p> <p> <br />W?章邮?/p> <p>一U简单的单向“进E间通信”(interprocess communication,I P CQ机制。这个机制的名字非常古怪,叫作“邮槽”(M a i l s l o tQ。用最单的话来_(d)通过<br />邮槽Q客hq程可将消息传送或q播l一个或多个服务器进E。在同一台计机的不同进E之_(d)或在跨越整个|络的不同计机的进E之_(d)协助q行消息的传输。用邮槽来开发应用程序是一仉常简单的事情Q不要求对T C P / I P或I P Xq样的基层网l传送协议有着非常深入的了(jin)解。由于邮槽是围绕一个广播通信体系设计出来的,所以当然不能指望能通过它实现数据的“可靠”传输?/p> <p>邮槽最大的一个缺点便是只允许从客h到服务器Q徏立一U不可靠的单向数据通信?br />而另一斚wQ邮槽最大的一个优点在于,它们使客h应用能够非常Ҏ(gu)地将q播消息发送给一个或多个服务器应用?/p> <p>3.1 邮槽实施l节<br />邮槽是围lWi n d o w s文gpȝ接口设计出来的。客h和服务器应用需要用标准的Wi n 3 2文gpȝI / OQ输入/输出Q函敎ͼ比如R e a d F i l e和W(xu)r i t e F i l e{等Q以便在邮槽上收发数据,同时利用Wi n 3 2文gpȝ的命名规则。邮槽必M赖Wi n d o w s重定向器Q通过一个“邮槽文件系l”(Mailslot File System, MSFSQ,来创建及(qing)标识邮槽?/p> <p>3.1.1 邮槽的名?br />寚w槽进行标识时Q需遵守下述命名规则Q?br />\ \ s e r v e r \ M a i l s l o t \ [ p a t h ] n a m e<br />请将上述字串分ؓ(f)三段来看Q?\ \ 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对应于服务器的名字,我们要在上面创徏邮槽Qƈ在在上面q行服务器程序。第二部分\ M a i l s l o t是一个“硬~码”的固定字串Q用于告诉系l这个文件名从属于M S F S。而第三部分\ [ p a t h ] n a m e?br />允许应用E序独一无二地定义及(qing)标识一个邮槽名。其中,“p a t h”代表\径,可指定多U目录?br />举个例子来说Q对一个邮槽进行标识时Q下面这些Ş式的名字都是合法的(注意M a i l s l o t不得变化Q必d文照输,亦即所谓的“硬~码”)(j)Q?br />׃邮槽要依赖Wi n d o w s文gpȝ服务在网上来创徏和传输数据,所以接口是“与协议无关”的?br />要想保证各种Wi n d o w sq_之间能够完全正常地通信Q强烈徏议将消息长度限制? 2 4字节Q或者更短。如果进行面向连接的传输Q可考虑使用命名道Q而不是简单的邮槽?/p> <p>3.5 结<br />本章讲解?jin)邮槽?M a i l s l o tQ网l编E技术。利用这一技术,应用E序可以在Wi n d o w s重定向器的帮助下Q实现简单的单向q程间数据通信。对邮槽来说Q它最有h(hun)值的一功能便是通过|络Q将一条消息广播给一台或多台计算机。然而,邮槽q未提供Ҏ(gu)据可靠传输的保障。假如希望用Wi n d o w s重定向器实现“可靠”的数据通信Q请考虑使用命名道Q这是下一章的主题Q?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12055.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-05 19:31 <a href="http://www.shnenglu.com/ivenher/articles/12055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows |络~程http://www.shnenglu.com/ivenher/articles/12040.html爱饭?/dc:creator>爱饭?/author>Tue, 05 Sep 2006 08:46:00 GMThttp://www.shnenglu.com/ivenher/articles/12040.htmlhttp://www.shnenglu.com/ivenher/comments/12040.htmlhttp://www.shnenglu.com/ivenher/articles/12040.html#Feedback1http://www.shnenglu.com/ivenher/comments/commentRss/12040.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12040.html1、netBIOS
非可路由协议
LAN适配器(LAN  adapterQ编号很重要.
每个LANA~号对应于网卡和传输协议的唯一l合?/p>

netBIOS名字
在Wi n 3 2环境中,针对每个可用的L A N A~号Q每
个进E都?x)?f)其维持一张N e t B I O S名字表。若为LANA 0增添一个名字,意味着你的应用E序
只能在LANA 0上同客户机徏立连接。对每个L A N A来说Q能够添加的名字的最大数量是2 5 4Q?br />~号?? 5 4Q?? 5 5ql保留)(j)
QN e t B I O S名字共有两个性质Q唯一名字和组?br />微Y|络中的机器命名是NetBIOS命名?br />“组名”的作用是将数据同时发给多个接收者;或者相反,接收发给多个
接收者的数据。组名ƈ非一定要“独一无二”,它主要用于多播(多点发送)(j)数据通信
若有“windows互联|命名服务器”即winsQ则有它理Q若无则发广播探是否重名?/p>

1.1.3 NetBIOSҎ(gu)?/p>

N e t B I O S同时提供?jin)“面向连接”服务以?qing)“无q接”服务?/p>

1.2 NetBIOS~程基础
NetBIOS API的设|,只有一个函敎ͼ(x)
UCHAR Netbios(PNCB pNCB);

用于N e t B I O S的所有函数声明、常数等{均是在头文件N b 3 0 . h内定义的。若惌?br />N e t B I O S应用Q唯一需要的库是N e t a p i 3 2 . l i b?br />调用N e t b i o s函数Ӟ可选择q行同步调用Q还是进行异步调用。所有N e t B I O S命o(h)本n均是同步的。要惛_步调用一个命令,需要让N e t B I O S命o(h)同A S Y N C H标志q行一ơ逻辑O RQ或Q运。如指定?jin)A S Y N C H标志Q那么必dn c b _ p o s t字段中指定一个后例程QPost RoutineQ,或必dn c b _e v e n t字段中指定一个事件句柄。执行一个异步命令时Q从N e t b i o sq回的值是N R C _ G O O D R E T( 0 x 0 0 )Q但n c b _ c m d _ c p l t字段?x)设为N R C _ P E N D I N G ( 0 x F F )。除此以外, N e t b i o s函数q会(x)N C Bl构的n c b _ c m d _ c p l t字段设ؓ(f)N R C _ P E N D I N GQ待冻I(j)Q直到命令完成ؓ(f)止。命令完成后Qn c b _ c m d _ c p l t字段?x)设命o(h)的返回倹{N e t b i o s也会(x)在完成后n c b _ r e t c o d e字段设ؓ(f)命o(h)的返回倹{?br />

1.4 数据报的工作原理

“数据报”(D a t a g r a mQ属于一U“无q接”的通信Ҏ(gu)。作为发送方Q只需用目?br />N e t B I O S名字为发出的每个包定址Q然后简单地送出?jin)事。此Ӟ不会(x)执行M(g)查,以确
保数据的完整性、抵N序或者传输的可靠性等{?/p>

发出一个数据报共有三种方式?br />W一U是指挥数据报抵达一个特定的Q或唯一的)(j)l名。这意味着只能有一个进E负责数据报的接收—亦x册了(jin)目标名字的那个进E?br />W二U是数据报发给一个组名。只有注册了(jin)指定l名的那些进E才有权接收消息?br />最后,W三U方式是数据报q播到整个网l?/p>

]]>
ɫۺϾþ| þ99Ʒþþþþò| 99þþƷѿһ | þþƷ| þþƷƷʢۿ| ޾ƷŮþþþ99| ƷӰӾþۺ| þ99Ʒ| ëƬŷëƬþþ| ƷһþþƷ| ŷ龫Ʒþþþþþ| ƷþþþþóAV| ˳վ999þþۺ| þþƷĻ̾| 91Ʒþþþþù۲| ŷۺϾþͼƬ| ƷþĻ| þþþùƷ۲ӰԺ| Ʒþþþþù˽| һɫþۺϺݺƪ| 99þþƷѿ| þþһ| ݹ97þ÷ѹۿ| 99þùۺϾƷӰԺ | þþƷAV뽿ɫ| Ʒ˾þþ| ƷþþӰ㽶| þþþƷһ| avþþƷ| ˾þ뾫ƷĻ| ŷþþþþҹƷ| ɫۺϾþ| þۺۺϾþ97ɫ| þþþ޾Ʒһ | 77777ҹþö| ٸƷþþһ| ɫþþþþþС˵ | 97ȾþƵƷ99| ŷպþþƷһ| ŷղþ99| 99ξþþŷƷվ|