WIN32汇编语言教程:第16章 TCP/IP和网络通信 · 16.2 WinSock接口(3)

WSAAsyncSelect函数的用法是:

   invoke WSAAsyncSelect,s,hWnd,wMsg,lEvent

s参数指定需要设置的套接字句柄,hWnd指定一个窗口句柄,套接字的通知消息将被发送到与其对应的窗口过程中。

通知消息的ID可以由程序自己定义,当不同的动作完成以后,不同的通知码将被包含在消息的参数中传递给指定的窗口过程,wMsg参数用来定义通知消息使用的ID,可以在WM_USER以上数值中任意选择一个用做ID,最后的参数lEvent指定哪些通知码需要发送,它可以被指定为几个通知码的组合,一般常用的通知码有下面这些:

● FD_READ——套接字收到对端发送过来的数据包,表明这时可以去读套接字。

● FD_WRITE——当短时间内向一个套接字发送太多数据造成缓冲区满以后,发送函数会返回出错信息,当缓冲区再次变空的时候,WinSock接口通过这个通知码通知应用程序,表示可以继续发送数据了。但是缓冲区未溢出的情况下数据被发送完毕的时候并不会发送这个通知码。

● FD_ACCEPT——监听中的套接字检测到有连接进入(适用于流套接字)。

● FD_CONNECT——如果用一个套接字去连接对方主机,当连接动作完成以后将收到这个通知码(适用于流套接字)。

● FD_CLOSE——检测到套接字对应的连接被关闭(适用于流套接字)。

在使用中并不需要指定全部这些通知码。例如,对于数据报套接字来说,它并不需要一个连接的过程,所以FD_CONNECT,FD_CLOSE和FD_ACCEPT等通知码是没有意义的,而在流套接字中,如果一个套接字不是用于监听,那么FD_ACCEPT也是没有意义的,所以要根据套接字的类型选用合适的通知码。

4. 将套接字绑定到IP地址和端口

对于一个流套接字和数据报套接字来说,它使用的IP地址和端口的组合在系统中必须是惟一的,这样WinSock内核才能将它收发的数据和其他套接字分辨开来。当创建一个流套接字和数据报套接字用于通信的时候,如果不去人工指定它使用哪个IP地址和哪个端口,那么系统自动将套接字的IP地址指定为本机的IP地址,端口指定为1 024 ~ 65 536之间的任意一个未使用的端口号(编号在1 024以下的端口被视为保留端口而不予自动分配)。

但是在某些情况下,需要人工指定IP地址或端口号,比如,当创建的套接字用于提供特定服务的时候,必须使用一个固定的端口号,如提供Web服务的套接字一般工作在80号端口,而提供FTP服务的套接字一般工作在21号端口,不然每次由系统随机指定端口号的话,其他计算机就不知道该与哪个端口连接。另外,当一台计算机上指定有多个IP地址,而我们希望套接字使用其中的一个IP地址而不是全部IP地址时(比如,用做虚拟主机服务的计算机往往指定多个IP地址,然后指派IP地址1的端口80提供网站A服务,IP地址2的端口80提供网站B服务等),这时就需要指定套接字只使用其中的一个IP地址。

使用bind函数可以为套接字指定IP地址和端口,这个过程称为绑定。函数用法如下:

   invoke bind,s,lpsockaddr,len

参数s指定套接字句柄,lpsockaddr参数是一个指向sockaddr_in结构的指针,len参数指定sockaddr_in结构的长度。sockaddr_in的定义是:

sockaddr_in STRUCT

 sin_family   WORD     ?      ;地址格式

 sin_port     WORD     ?      ;端口号(使用网络字节顺序)

 sin_addr     in_addr <>      ;IP地址(使用网络字节顺序)

 sin_zero     BYTE 8 dup (?)  ;空字节

sockaddr_in ENDS

结构中的sin_family 字段用来指定地址格式,这个字段和socket函数中af参数的含义是相同的,所以惟一可以使用的值就是AF_INET。sin_port字段和sin_addr字段分别指定套接字需要绑定的端口号和IP地址,放入这两个字段的数据的字节顺序必须是Internet顺序,由于字节顺序和Intel CPU的字节顺序刚好相反,所以必须首先经过转换,比如当端口号为9 999时,转换成16进制是270Fh,那么放入sin_port字段的数值就应该是转换以后的0F27h。

 sockaddr_in 结构是WinSock编程中最常用的结构,凡是涉及通信地址的时候都会用到这个结构,比如使用connect发起连接和使用sendto发送数据时的目标地址都是使用这个结构指定的。

读者可能会问一个问题,结构中存在一个定义IP地址的sin_addr字段,这样的话岂不是必须先获取本机的IP地址后才能绑定,否则如何填写这个字段呢?实际上仅在当前计算机配置有多个IP地址,而程序又只想绑定到其中一个IP地址的时候才需要具体指定使用哪一个地址,否则可以使用预定义值ADDR_ANY,这样系统会自动使用当前主机配置的所有IP地址。

使用bind函数绑定IP地址和端口的时候,端口号可以指定为任意一个16位值,也可以使用小于1 024的值,惟一的限制就是不能去绑定已经在使用中的端口号。

如果绑定成功,函数返回0,否则函数返回SOCKET_ERROR,这时程序可以继续调用WSAGetLastError函数获取进一步的出错代码。一般来说,绑定错误是由下面几个原因引起的,对应的出错代码如下:

● WSAEADDRINUSE——指定的IP地址或端口已经在使用中。

● WSAEFAULT——指定的结构、地址和端口等数据无效。

● WSAEINVAL——套接字已经被绑定到某个地址,绑定只能进行一次,对一个已经绑定过的套接字再次调用bind函数就会出现这个错误。

5. 用于连接和收发数据的函数

当完成了前面的库初始化,创建套接字,设置套接字模式以及绑定工作后,就可以使用套接字收发数据了。流套接字和数据报套接字数据收发时适用的函数有所不同,一般对流套接字使用send和recv函数来收发数据,对数据报套接字使用sendto和recvfrom函数。对于流套接字来说,开始收发数据之前还需要有一个监听或连接的过程,这时还要用到listen,accept和connect函数。另外,WinSock接口还提供一些与主机名解析有关的函数,如gethostbyname和gethostname等。

所有这些函数将在下面的章节中逐一介绍。

6. 常用的转换函数

WinSock接口中也提供了一些常用的转换函数,比如将32位的IP地址和 “aa.bb.cc.dd”类型的10进制IP地址字符串互相转换,或者将IP地址和端口号等数据在不同的字节顺序之间进行转换,这些函数可以为我们带来很多方便。

inet_addr函数和inet_ntoa可以在IP地址和字符串之间进行转换。

inet_addr函数将一个由小数点分隔的10进制IP地址字符串转换成由32位二进制数表示的IP地址(网络字节顺序):

上页:第16章 TCP/IP和网络通信 · 16.2 WinSock接口(2) 下页:第16章 TCP/IP和网络通信 · 16.2 WinSock接口(4)

第16章 TCP/IP和网络通信

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号