7.4.3 基于消息的协议
正由于面向连接的协议同时也是流式协议,无连接协议几乎都是基于消息的。因此,在收发数据时,需要考虑这几点。首先,由于面向消息的协议对数据边界有保护,所以提交给发送函数的数据在被发送完之前累积成块。对异步或非块式I / O模式而言,如果数据未能完全发送,发送函数就会返回W S A E W O U L D B L O C K错误。这意味着基层的系统不能对不完整的那个数据进行处理,你应该稍后再次调用发送函数。下一章将对此进行详述。主要需要记住
的是,采用基于消息的协议时,对于写入数据来说,只能把它当作一个自治行为。
在连接另一端,对接收函数的调用必须提供一个足够大的缓冲空间。如果提供的缓冲不够,接收调用就会失败,出现W S A E M S G S I Z E。发生这种情况时,缓冲会尽力接收,但未收完的数据会被丢弃。被截断的数据无法恢复。唯一例外的是支持部分消息的协议却例外,比方说A p p l e Talk PA P协议。在W S A R e c v E x函数只收到部分消息时,它会在返回之前,便把自
己的出入标志参数设为M S G _ PA RT I A L。
对以支持部分消息的协议为基础的数据报来说,可考虑使用一个W S A R e c v函数。在调用r e c v时,不会有这一个通知“读取的数据只是消息的一部分”。至于接收端怎样判断是否已读取整条消息,具体方法则由程序员决定。后来的r e c v调用返回这个数据报的其他部分。由于有这个限制,所以利用W S A R e c v E x函数非常方便,它允许设置和读取M S G _ PA RT I A L标志

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

7.5 其他API函数
本小节介绍其他几个Winsock API函数,它们在实际网络应用中非常有用
1. getpeername
该函数用于获得通信方的套接字地址信息,该信息是关于已建立连接的那个套接字的。
它的定义如下:
int getpeername(
        SOCKET s,
        struct sockaddr FAR * name,
        int FAR *namelen
       );
第一个参数是准备连接的套接字,后两个参数则是指向基层协议类型及其长度的指针。
对数据报套接字来说,这个函数返回的是投向连接调用的那个地址;但不会返回投向s e n d t o或W S A S e n d To调用的那个地址。

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

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

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

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扩展,它允许从一个文件中传输高性能数据。这是非常有效的,因为整个数据传输可在内核模式中进行。也就是说,如果你的应用从指定的文件中读取一堆数据,然后用s e n d或W S A S e n d时,涉及到“用户模式到内核模式传输”的发送调用就有若干个。有了Tr a n s m i t F i l e,整个读取和发送数据的进程就可在内核模式中进行。该函
数的定义如下:

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参数用于识别已连接上的套接字(文件的传输便在该套接字上进行)。n F i l e参数是一个句柄,该句柄指向一个已打开的套接字(即即将发送的文件)。n N u m b e r O f B y t e s To Wr i t e表
明写入多少指定文件中的字节。投递0表示将发送整个文件。n N u m b e r O f B y t e s P e r S e n d参数则表明写操作所用的发送长度。例如,指定2 0 4 8会引起Tr a n s m i t F i l e在套接字上以2 KB数据块的形
式发送指定文件。投递0表示采用默认的发送长度。l p O v e r l a p p e d参数指定一个O V E R L A P P E D
结构,该结构用于重叠I / O模式(关于重叠I / O,可参见第8章)。
另一个参数l p Tr a n s m i t B u ff e r s,是一个T R A N S M I T _ F I L E _ B U F F E R S结构,其中包含文件传输之前和之后准备发送的数据。该结构的格式如下:

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是后来发送的数据量。          

Posted on 2006-09-11 17:20 艾凡赫 阅读(801) 评论(1)  编辑 收藏 引用 所属分类: 网络编程

Feedback

# re: windows网络编程十二  回复  更多评论   

2008-01-26 20:11 by 舞天
晕晕的~~

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理