7.4 无连接协议
和面向连接的协议比较起来,无连接协议的行为极为不同,因此,收发数据的方式也会有所不同。由于在和面向会话的服务器比较时,无连接接收端改动不大,所以我们先谈谈接收端(如果你愿意,也可称之为服务器)。接下来再谈发送端。
7.4.1 接收端
对于在一个无连接套接字上接收数据的进程来说,步骤并不复杂。先用s o c k e t或W S A S o c k e t建立套接字。再把这个套接字和准备接收数据的接口绑定在一起。这是通过b i n d函数(和面向会话的示例一样)来完成的。和面向会话不同的是,我们不必调用l i s t e n和a c c e p t。相反,只需等待接收数据。由于它是无连接的,因此始发于网络上任何一台机器的数据报都可被接
收端的套接字接收。最简单的接收函数是r e c v f r o m。它的定义如下:
int recvfrom(
SOCKET s,
char FAR * buf,
int len,
int flags,
struct sockaddr FAR * from,
int FAR * fromlen
);
前面四个参数和r e c v是一样的,其中包括标志M S G _ O O B和M S G _ P E E K。在使用无连接套接字时,和前面一样,仍然提醒大家慎用M S G _ P E E K标志。对监听套接字的具体协议来说,f r o m参数是一个S O C K A D D R结构,带有指向地址结构的长度的f r o m l e n。这个A P I调用返回数
据时,S O C K A D D R结构内便填入发送数据的那个工作站的地址。
r e c v f r o m函数的Winsock 2版本是W S A R e c v F r o m。后者的原型是:
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 F结构的用法上。你可以利用d w B u ff e r C o u n t为W S A R e c v F r o m提供一个或多个W S A B U F缓冲。提供多个缓冲,就可用发散集合了。读取的字节总数返回在l p N u m b e r O f B y t e s R e c v d中。在调用W S A R e c v F r o m时,l p F l a g s参数可以是代表
无选项的0、M S G _ O O B、M S G _ P E E K或M S G _ PA RT I A L。这些标志还可以累加起来。如果在调用这个函数时,指定M S G _ PA RT I A L,提供者就知道返回数据,即使只收到了部分消息。调用返回之后,如果只收到部分消息,就会设置M S G _ PA RT I A L标志。再次返回之后,
W S A R e c v F r o m就会把l p F r o m参数(它是一个指向S O C K A D D R结构的指针)设为发送端的地址。再次提醒大家注意, l p F r o m L e n指向S O C K A D D R结构的长度,另外,在这个函数中,它还是一个指针,指向D W O R D。最后两个参数, l p O v e r l a p p e d和l p C o m p l e t i o n R O U T I N E,用于
重叠I / O(我们将在下一章就此展开讨论)。
在无连接套接字上接收(发送)数据的另一种方法是建立连接。听起来有些奇怪吧,但事实的确如此。无连接的套接字一旦建立,便可利用S O C K A D D R参数(它被设为准备与之通信的远程接收端地址)调用c o n n e c t或W S A C o n n e c t。但事实上并没有建立连接。投入连接函数的套接字地址是与套接字关联在一起的,如此一来,才能够用R e c v和W S A R e c v来代替
r e c v f r o m和W S A R e c v F r o m。为什么呢?其原因是数据的始发处是已知的。如果在一次应用中,只和一个端点进行通信,便能很容易地与数据报套接字建立连接。
7.4.2 发送端
要在一个无连接的套接字上发送数据,有两种选择。第一种,也是最简单的一种,便是建立一个套接字,然后调用s e n d t o或W S A S e n d To。我们先来讲解s e n d t o函数,它的定义是这样的:
int sendto(
SOCKET s,
const char FAR * buf,
int len,
int flags,
const struct sockaddr FAR * to,
int tolen
);
除了b u f是发送数据的缓冲, l e n指明发送多少字节外,其余参数和r e c v f r o m的参数一样。另外, t o参数是一个指向S O C K A D D R结构的指针,带有接收数据的那个工作站的目标地址。
另外,也可以用Winsock 2函数W S A S e n d To。它的定义如下:
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
);
再次提醒大家注意, W S A S e n d To和前一版本中的S e n d To函数类似。它把指向带有发给接
收端的数据的指针当作l p B u ff e r s参数, d w B u ff e r C o u n t参数指明现在的结构是多少。我们可发送多个W S A B U F结构启用发散集合I / O。在函数返回之前,W S A S e n d To把第四个参数l p N u m b e r
O f B y t e s S e n t设为真正发到接收端的字节数。l p To参数是针对具体协议的一个S O C K A D D R结构,并带有接收端的地址。i To L e n参数是S O C K A D D R结构的长度。最后两个参数, l p O v e r l a p p e d和l p C o m p l e t i o n R O U T I N E,用于重叠I / O(将在第8章讨论)。
通过接收数据的方式,就可把一个无连接的套接字连接到一个端点地址,并可以用s e n d和W S A S e n d发送数据了。这种关联一旦建立,就不能再用带有地址的s e n d t o和W S A S e n d To,
除非这个地址是投到其中一个连接函数的地址,否则调用就会失败,出现W S A E I S C O N N错误。
要取消套接字句柄与目标地址的关联,唯一的办法是在这个套接字句柄上调用c l o s e s o c k e t,
并建立一个新的套接字。