1、网络编程技术,Windows 环境的Socket网络编程,思考题:Unix与Windows的区别,界面友好? 单线程/多线程? 操作习惯?,第3章 Windows环境的网络编程,Windows Sockets 规范 Winsock 规范与Berkeley套接字的区别 Winsock 1.1 的库函数 Winsock 2 介绍 Winsock 1.1 的基础编程,3.1 Windows Sockets规范,3.1.1 概述 Microsoft公司以Berkeley Sockets规范为范例,定义了Windows Socktes规范,简称Winsock规范。 这是Windows操作系统环境下的套接
2、字网络应用程序编程接口(API)。 包含: Berkeley Socket 风格的库函数; 针对Windows操作系统的扩展库函数。 可以充分利用Windows的消息驱动机制编程。 Winsock规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数和相关语义,让各个软件供应商共同遵守,做到Winsock兼容。,图3.1 网络应用进程利用Windock进行通信,3.1.2 Windows Sockets规范 Windows Sockets 规范是一套开放的、支持多种协议的Windows下的网络编程接口。 从1991年到1995年,从1.0版发展到2.0.8版,已成为Windo
3、ws网络编程的事实上的标准。 1Windows Sockets 1.1版本 在Winsock.h包含文件中,定义了WinSock 1.1版本库函数的语法、相关的符号常量和数据结构。 库函数的实现在Winsock.dll动态链接库文件中。,(1) WinSock 1.1 全面继承了Berkeley Sockets规范。 Winsock1.1继承了Berkeley Sockets规范的主要特征,一部分库函数与之在形式上保持一致,包括库函数的名称、参数格式、结构定义。 见表 3.1。 其中,带*号的表明该例程在某些情况下可能会发生阻塞。,表3.1 WinSock 1.1继承Berkeley Sock
4、ets的函数,表3.1 WinSock 1.1继承Berkeley Sockets的函数(cont.),(2) 数据库函数 表3.2列出了Winsock规范定义的数据库查询函数。 其中6个采用getXbyY()的形式,大多要借助网络上的数据库来获得信息,而不采用本地数据库来实现。,getXbyY( )形式的数据库例程都返回一个指针,指向某种类型的结构区域,用来存放函数返回的数据信息。 这些结构区域是由winsock实现(即Winsock.dll)分配的,由系统管理,所以指针指向的结构数据是易失的,只在该线程的下一个Winsock API调用前有效。 一个线程中只有一个该结构的副本,因此应用程序
5、在发出下一个Winsock API调用前,应把所需的信息复制下来。 应用程序不应试图修改或释放这个结构。,(3) WinSock 1.1 扩充了Berkeley Sockets规范 针对微软 Windows的特点,WinSock 1.1定义了一批新的库函数,提供了对消息驱动机制的支持,有效地利用Windows多任务多线程的机制。 扩充主要是提供了一些异步函数,增加了符合Windows消息驱动特性的网络事件异步选择机制,有利于开发符合Windows编程模式的软件,使得开发高性能网络通信程序成为可能。 这些扩充函数的名字都以WSA开头,后面跟async表示是专为实现异步机制而设置的。 编程时必须使
6、用WSAStartup()和WSACleanup(),其它函数随意使用。,13,表3.3 Winsock 1.1的常用扩展函数,14,表3.3 Winsock 1.1的常用扩展函数(续),(4) WinSock 1.1只支持TCP/IP协议栈 Winsock 1.1的实现,即Winsock.dll和底层协议栈的接口是唯一的,且是独占的,只能访问TCP/IP协议栈。 因此, Winsock 1.1套接字仅支持单一的通信域,即Internet域。,2WinSock 2.0规范 WinSock 2.0在源码和二进制代码方面与WinSock 1.1兼容,此外还增强了许多功能。 (1)支持多种协议 (2
7、)引入了重叠I/O的概念 (3)使用事件对象异步通知 (4)服务的质量(QOS) (5)套接字组 (6)扩展的字节顺序转换例程 (7)分散/聚集方式I/O (8)新增了许多函数。,3. WinSock 1.1中的阻塞问题 阻塞是在把应用程序从Berkeley套接字环境中移植到Windows环境中的一个主要焦点,虽然Windows Sockets支持关于套接字的阻塞操作,但是这种应用是被强烈反对的。 阻塞,是指唤起一个函数,该函数直到相关操作完成时才返回。 在Berkeley套接字模型中,一个套接字的操作的缺省行为是阻塞方式的,除非程序员显式地请求该操作为非阻塞方式。 在Windows环境下,强
8、烈推荐程序员尽可能使用非阻塞方式(异步方式)的操作,因为非阻塞方式的操作能够更好地在非占先的Windows环境下工作。,有些Sockets操作在阻塞和非阻塞方式下没什么区别;而有些Sockets操作取决于传输情况,会立即完成或阻塞一段时间。 当操作用于阻塞套接字(打*号标记的)时,这些操作被认为是工作于阻塞方式的。 在Windows Sockets实现中,一个无法立刻完成的阻塞操作是按如下方式处理的: DLL先初始化操作,然后进入一个循环,在循环中发送收到的任何信息,以便必要时将处理器交给其它线程; 然后检查Windows Sockets功能是否完成,如果完成了,WSACancleBlocki
9、ngCall()被唤起,阻塞操作以一个适当的返回值结束。,如果一个正在运行某一阻塞操作的进程收到了一个Windows消息,那么应用程序有可能试图发出另一个Windows Sockets调用。 由于难以安全的处理这种情况,Windows Sockets规范不支持这种应用程序的工作方式,此时可以借助两个函数来解决: WSAIsBlocking () 可以用来确定在该进程上是否有阻塞的Windows Sockets调用; WSACancleBlockingCall()可以用来取消在线的阻塞调用,如果有的话。 其它任何Windows Sockets函数此时被调用,都会失败并返回错误代码WSAEINPR
10、OGRESS。 这一限制适用于所有阻塞和非阻塞操作。,3.1.3 WinSock规范与Berkeley套接字的区别,1套接字数据类型和该类型的错误返回值 在UNIX中,包括套接字句柄在内的所有句柄,都是非负的短整数; 在WinSock规范中定义了一个新的数据类型,称作SOCKET,用来代表套接字描述符。 typedef u_int SOCKET; socket( )和accept( )函数返回时,返回的就是SOCKET类型。 SOCKET可以取从0到INVALID_SOCKET-1之间的任意值。,要判断socket()和accept()是否正确执行,可以将返回值和INVALID_SOCKET来
11、比较,该常量已在Winsock.h中定义。 例如: 在UNIX套接字规范中 s=socket(); if(s=-1)执行错误处理代码 在Winsock套接字规范中: s=socket(); if(s=INVALID_SOCKET)执行错误处理代码,2select()函数和FD_*宏 在Winsock中,可以使用select()函数来选择对某些事件的处理,比如连接请求到来、有数据待接收等等。应用程序采用FD_XXX宏来设置、初始化、清除和检查fd_set结构(用来代表一组套接字)。 3错误代码的获得 UNIX套接字规范中,如果函数执行时发生了错误,会把错误代码放到errno或h_errno变量中
12、。 在Winsock中错误代码可以使用WSAGetLastError()调用得到。 4指针 所有应用程序与Windows Sockets使用的指针都必须是FAR指针,访问距离超过了64K,适用于大程序 。,5. 重命名的函数 有两个伯克利套接字函数改了名字,避免与其它API冲突。 close()改变为closesocket() ioctl()改变为ioctlsocket() 6. Winsock支持的最大套接字数目 一个特定的Windows Sockets提供者所支持的套接字的最大数目是由实现确定的;任何一个应用程序都不应假设某个待定数目的套接字可用。 一个Windows Sockets应用程
13、序可以使用的套接字的最大数目在Winsock.h中缺省值是64,在编译时由常量FD_SETSIZE决定。,7. 头文件 Berkeley头文件被包含在Winsock.h中。 一个Windows Sockets应用程序只需简单地包含Winsock.h就足够了。 8原始套接字 Windows Sockets规范并没有规定Windows Sockets DLL必须支持原始套接字(用SOCK_RAW打开的套接字),但是鼓励提供原始套接字支持。 9、Winsock规范对于消息驱动机制的支持 体现在异步选择机制、异步请求函数、阻塞处理方法、错误处理、启动和终止等方面。,3.2 Winsock 1.1的库函
14、数,3.2.1 Winsock的注册与注销 1初始化函数WSAStartup() Winsock应用程序要做的第一件事,就是必须首先调用WSAStartup()函数对Winsock进行初始化。 初始化也称为注册,注册成功后,才能调用其他的Winsock API函数。 (1) WSAStartup()函数的调用格式 int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData ); wVersionRequested:应用程序要使用的winsock最高版本号; lpWSAData:指向WSADATA结构,返回Winsock API实现细节
15、。,图3.2 在一台计算机中,使用同一Winsock实现的多个网络应用程序,(2) WSAStartup()函数的初始化过程,27,(3) WSADATA结构的定义 #define WSADESCRIPTION_LEN 256 #define WSASYS_STATUS_LEN 128 typedef struct WSAData WORD wVersion;WORD wHighVersion;char szDescriptionWSADESCRIPTION_LEN+1;char szSystemStatusWSASYS_STATUS_LEN+1;unsigned short iMaxSock
16、ets;unsigned short iMaxUdpDg;char * lpVendorInfo; WSADATA;,28,(4) 初始化函数可能返回的错误代码 WSASYSNOTREADY: 网络通信依赖的网络子系统没有准备好。 WSAVERNOTSUPPORTED:找不到所需的Winsock API相应的动态连接库。 WSAEINVAL: DLL不支持应用程序所需的Winsock版本。 WSAEINPROGRESS: 正在执行一个阻塞的Winsock 1.1操作。 WSAEPROCLIM: 已经达到Winsock支持的任务数上限。WSAEFAULT: 参数lpWSAData不是合法指针。,
17、29,(5)初始化Winsock的示例 #include / 对于Winsock 2.2,应包括 Winsock2.h文件 main() WORD wVersionRequested; / 应用程序所需的Winsock版本号 WSADATA wsaData; / 用来返回Winsock 实现的细节信息 int err; / 出错代码。 wVersionRequested =MAKEWORD(1,1); / 生成版本号1.1 err = WSAStartup(wVersionRequested, /* 至此,可以确认初始化成功,Winsock.DLL可用。 ,2注销函数WSACleanup()
18、程序使用完Winsock.DLL提供的服务后,应用程序必须调用WSACleanup()函数,来解除与Winsock.DLL库的绑定,释放Winsock实现分配给应用程序的系统资源,中止对Windows Sockets DLL的使用。(1) WSACleanup()函数的调用格式 int WSACleanup ( void ); 返回值:如果操作成功返回0,否则返回SOCKET_ERROR.,30,(2) WSACleanup()函数的功能 对应于一个任务进行的每一次WSAStartup()调用,必须有一个WSACleanup()调用。 只有最后的WSACleanup()做实际的清除工作;前面的
19、调用仅仅将Windows Sockets DLL中的内置引用计数递减。 一个简单的应用程序为确保WSACleanup()调用了足够的次数,可以在一个循环中不断调用WSACleanup()直至返WSANOTINITIALISED。(3) WSACleanup()函数可能返回的错误代码 WSANOTINITIALISED:在调用本API之前应成功调用WSAStartup()。 WSAENETDOWN:检测到网络子系统失效。 WSAEINPROGRESS:一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。,31,3.2.2 Winsock的错误处理函数 Winsock函数执
20、行时都有一个返回值,只能说明函数的执行成功与否,不能从返回值了解出错原因。 1WSAGetLastError()函数 int WSAGetLastError ( void ); 返回本线程进行的上一次Winsock函数调用时,产生的错误代码。 在Winsock.h中定义了所有的错误代码,基数是10000 2WSASetLastError()函数 void WSASetLastError ( int iError ); 本函数允许应用程序为当前线程设置错误代码,并可由后来的WSAGetLastError()调用返回。,3.2.3 主要的Winsock函数 1创建套接字socket() SOCKE
21、T socket (int af, int type, int protocol); 返回值:创建成功,返回套接字描述符,否则返回SOCK_ERROR。 举例: SOCKET sockfd=socket( AF_INET, SOCK_STREAM, 0); /* 创建一个流式套接字。 SOCKET sockfd=socket( AF_INET, SOCK_DGRAM, 0); /* 创建一个数据报套接字。,2将套接字绑定到指定的网络地址BIND() int bind( SOCKET s, const struct sockaddr * name, int namelen); 有许多函数都需要套
22、接字的地址信息,像UNIX 套接字一样,Winsock也定义了三种关于地址的结构,经常使用。 通用的Winsock地址结构,针对各种通信域的套接字,存储它们的地址信息。 struct sockaddr u_short sa_family; /* 地址家族char sa_data14; /* 协议地址 ,34,专门针对Internet 通信域的Winsock地址结构 struct sockaddr_in short sin_family; /*指定地址家族,一定是AF_INET.u_short sin_port; /*指定将要分配给套接字的传输层端口号,struct in_addr sin_ad
23、dr; /*指定套接字的主机的IP 地址char sin_zero8; /* 全置为0,是一个填充数。 专用于存储IP地址的结构 struct in_addr union struct u_char s_b1,s_b2,s_b3,s_b4; s_un_b;struct u_short s_w1,s_w2; s_un_w;u_long s_addr; ,35,在使用Internet域的套接字时,这三个数据结构的一般用法是: 首先,定义一个sockaddr_in的结构实例变量,并将它清零; 然后,为这个结构的各成员变量赋值; 第三步,在调用bind()绑定函数时,将指向这个结构的指针强制转换为 s
24、ockaddr*类型。,36,举例: SOCKET serSock; / 定义了一个SOCKET 类型的变量。 sockaddr_in my_addr; / 定义一个Sockaddr_in型的结构实例变量。 int err; / 出错码。 int slen=sizeof( sockaddr); / sockaddr 结构的长度。 serSock = socket(AF_INET, SOCK_DGRAM, 0 ); / 创建数据报套接字。 memset(my_addr,0,slen); / 将sockaddr_in的结构实例变量清零。 my_addr.sin_family = AF_INET;
25、/ 指定通信域是Internet。 my_addr.sin_port = htons(21); / 指定端口,将端口号转换为网络字节顺序。,37,/* 指定IP地址,将IP地址转换为网络字节顺序。 my_addr.sin_addr.s_addr = htonl( INADDR-ANY); /* 将套接字绑定到指定的网络地址,对 /* 以下可以报错,进行错误处理。 ,38,3启动服务器监听客户端的连接请求listen() int listen( SOCKET s, int backlog);仅适用于支持连接的SOCK_STREAM类型的套接字。套接字s处于一种“变动”模式,申请进入的连接请求被确
26、认,并排队等待被接受。 这个函数特别适用于同时有多个连接请求的服务器;如果当一个连接请求到来时,队列已满,那么客户将收到一个WSAECONNREFUSED错误。,39,4接收连接请求 accept() SOCKET accept( SOCKET s, struct sockaddr* client_addr, int* addrlen);s 是服务器正在侦听的套接字,accept()之后变回侦听状态。 如果返回值是INVALID_SOCKET表示失败,否则就是返回成功建立的新套接字,用来与这个客户通信 成功时client_addr 中包含客户IP和端口,40,5请求连接connect() in
27、t connect( SOCKET s, struct sockaddr * addr, int addrlen); 举例(先创建ClientSocket) struct sockaddr_in saddr; memset(void *),41,42,6向一个已连接的套接字发送数据send() int send( SOCKET s, char * buf, int len, int flags);,图3.3 同步套接字的send()函数的执行流程,43,7从一个已连接套接字接收数据RECV() int recv( SOCKET s, char * buf, int len, int flags
28、); 图3-4说明了send和recv的作用,套接字缓冲区与应用进程缓冲区的关系,以及协议栈所作的传送。,图3.4 send()和recv()都是对本地套接字的操作,8按照指定目的地向数据报套接字发送数据sendto() int sendto( SOCKET s, char * buf, int len, int flags, struct sockaddr * to, int tolen); 9接收一个数据报并保存源地址,从数据报套接字接收数据recvform() int recvfrom( SOCKET s, char * buf, int len, int flags, struct s
29、ockaddr* from, int* fromlen);,44,10关闭套接字closesocket() int closesocket( SOCKET s); 关闭一个套接字,释放套接字描述字s,以后对s的访问均以WSAENOTSOCK错误返回。 若本次为对套接字的最后一次访问,则相应的名字信息及数据队列都将被释放。 closesocket()的语义受SO_LINGER与SO_DONTLINGER选项影响(setsockopt 设置的套接字行为),对比如下: 选项 间隔 关闭方式 等待关闭与否 SO_DONTLINGER 不关心 优雅 否 SO_LINGER 零 强制 否 SO_LINGE
30、R 非零 优雅 是,45,11有选择的在套接字上进行数据关闭 shutdown()int shutdown( SOCKET s, int how); 用于任何类型的套接字禁止接收、禁止发送或禁止收发 how参数为0,则该套接字上的后续接收操作将被禁止。 若how为1,则禁止后续发送操作,对于TCP,将发送FIN包,表示请求结束连接。 若how为2,则同时禁止收和发。 请注意shutdown()函数并不关闭套接字,且套接字所占有的资源将被一直保持到closesocket()调用。,46,3.2.4 Winsock的辅助函数 1Winsock中的字节顺序转换函数,图3-5 两种本机字节顺序,低地址
31、,高地址,Winsock API特为此设置了四个函数, (1)htonl() 将主机的无符号长整型数本机顺序转换为网络字节顺序 (Host to Network Long),用于IP地址。 u_long PASCAL FAR htonl( u_long hostlong); hostlong是主机字节顺序表达的32位数。htonl()返回一个网络字节顺序的值。 (2)htons() 将主机的无符号短整型数转换成网络字节顺序(Host to Network Short),用于端口号。 u_short PASCAL FAR htons( u_short hostshort); hostshort:
32、主机字节顺序表达的16位数。htons()返回一个网络字节顺序的值。,48,(3)ntohl() 将一个无符号长整型数从网络字节顺序转换为主机字节顺序。(Network to Host Long),用于IP地址。 u_long PASCAL FAR ntohl( u_long netlong); netlong是一个以网络字节顺序表达的32位数,ntohl()返回一个以主机字节顺序表达的数。 (4)ntohs() 将一个无符号短整型数从网络字节顺序转换为主机字节顺序。(Network to Host Sort),用于端口号 u_short PASCAL FAR ntohs( u_short n
33、etshort); netshort是一个以网络字节顺序表达的16位数。ntohs()返回一个以主机字节顺序表达的数。,49,2获取与套接字相连的端地址getpeername() int getpeername( SOCKET s, struct sockaddr * name, int * namelen); 成功的话,name就存储了对方的地址和端口3获取一个套接字的本地名字getsockname() int getsockname( SOCKET s, struct sockaddr * name, int * namelen); 成功的话,name就存储了套接字绑定的地址和端口,50,
34、4将一个点分十进制形式的IP地址转换成一个长整型数inet_addr() unsigned long inet_addr (const char * cp); 5将网络地址转换成点分十进制的字符串格式inet_ntoa() char * inet_ntoa( struct in_addr in);,51,3.2.5 Winsock的信息查询函数 Winsock API提供了一组信息查询函数,让我们能方便地获取套接字所需要的网络地址信息以及其它信息, (1)gethostname() 用来返回本地计算机的标准主机名。 int gethostname(char* name, int namelen
35、); (2)gethostbyname() 返回对应于给定主机名的主机信息(含地址) struct hostent* gethostbyname(char* name);,(3)gethostbyaddr() 根据一个IP地址取回相应的主机信息。 struct hostent* gethostbyaddr(const char* addr, int len, int type); (4)getservbyname() 返回对应于给定服务名和协议名的相关服务信息。 struct servent* getservbyname(const char* name, const char* proto)
36、; (5)getservbyport() 返回对应于给定端口号和协议名的相关服务信息。 struct servent * getservbyport(int port, const char *proto);,53,(6)getprotobyname() 返回对应于给定协议名的相关协议信息。 struct protoent * getprotobyname(const char * name); (7)getprotobynumber () 返回对应于给定协议号的相关协议信息。 struct protoent * getprotobynumber(int number);,54,getserv
37、byport示例,#include main() /*getserv.c*/ int port;struct servent* service;for(port =0; port s_port, service-s_name, service -s_aliases0); ,getservbyport示例,$ ./getserv2: biff : comsat 258: who : whod 264: nfsd : nfs 514: syslog : (null),getprotobynumber示例,#include main() /*getproto.c*/ int number;struc
38、t protoent *protocol;for(number=0; numberp_proto, protocol-p_name, protocol-p_aliases0); ,getprotobynumber示例,$ ./getproto.exe0: ip : IP1: icmp : ICMP3: ggp : GGP6: tcp : TCP8: egp : EGP 12: pup : PUP 17: udp : UDP,除gethostname()外,其它六个函数有以下共同特点: 函数名都采用getXbyY的形式。 如果函数成功地执行,就返回一个指向某种结构的指针,该结构包含所需要的信息。
39、 如果函数执行发生错误,就返回一个空指针。应用程序可以立即调用WSAGetLastError()来得到一个特定的错误代码。 函数执行时,可能在本地计算机上查询,也可能通过网络向域名服务器发送请求,来获得所需要的信息,这取决于用户网络的配置方式。 为了能让程序在等待响应时能作其他的事情,Winsock API扩充了一组作用相同的异步查询函数,不会引起进程的阻塞。并且可以使用Windows的消息驱动机制。也是六个函数,与getXbyY各函数对应,在每个函数名前面加上了WSAAsync前缀,名字采用WSAAsyncGetXByY()的形式。它们的工作机制在后面详述。,59,3.2.6 WSAAsyn
40、cGetXByY类型的扩展函数 WSAAsyncGetXByY类型的扩展函数是GetXByY函数的异步版本,这些函数可以很好地利用Windows的消息驱动机制。 Winsock的实现启动WSAAsyncGetXByY()操作后立即返回调用方,并传回一个异步任务句柄,应用程序可以用该句柄标识操作; 当操作完成时,如果有结果将会把结果复制到调用方提供的缓冲区buf中,同时向应用程序的窗口发一条消息; 应用程序窗口hWnd接收到消息wMsg,该消息结构的wParam参数包含了初次函数调用时返回的异步任务句柄;Iprarm参数的高16位包含错误代码。,60,1WSAAsyncGetHostByName
41、()函数 gethostbyname()的异步版本,用于获取对应一个主机名的主机名称和地址信息。 HANDLE WSAAsyncGetHostByName ( HWND hWnd, unsigned int wMsg, const char * name, char * buf, int buflen ); hWnd:异步请求完成时,接收消息的窗口句柄; wMsg:异步请求完成时,将要接收的消息;即定义一个消息,当函数调用返回时,将消息传递给窗口 name:指向主机名的指针; buf:接收hostent数据的数据区指针,需大于hostent结构大小,因为hostent结构引用的数据也都在该区域
42、内。 buflen:数据区的大小。 若操作成功地初启,返回一个HANDLE类型的非0值,作为请求需要的异步任务句柄。 该HANDLE类型的值可通过WSACancelAsyncRequest()用来取消操作,也可通过检查wParam消息参数,以匹配异步操作和完成消息.,61,2WSAAsyncGetHostByAddr()函数 本函数是gethostbyaddr()的异步版本,用来获取对应于一个网络地址的主机名和地址信息. HANDLE WSAAsyncGetHostByAddr ( HWND hWnd, unsigned int wMsg, const char * addr, int len
43、, int type, char * buf, int buflen ); hWnd 异步请求完成时,应该接收消息的窗口句柄. wMsg 异步请求完成时,将要接收的消息. addr 主机网络地址的指针,以网络字节次序存储. len 地址长度.对于PF_INET来说必须为4. type 地址类型,必须是PF_INET. buf 接收hostent数据的数据区指针. buflen 上述数据区的大小. 返回值指出异步操作是否成功地初启. 若操作成功地初启,WSAAsyncGetHostByAddr()返回一个HANDLE类型的非0值,作为请求需要的异步任务句柄. 如果异步操作不能初启,WSAAsyn
44、cGetHostByAddr()返回一个0值,并且可使用WSAGetLastError()来获取错误号.,62,3WSAAsyncGetServByName()函数 本函数是getservbyname()的异步版本,用来获取对应于一个服务名的服务信息. HANDLE WSAAsyncGetServByName ( HWND hWnd, unsigned int wMsg, const char * name, const char * proto, char * buf, int buflen ); hWnd 异步请求完成时,应该接收消息的窗口句柄. wMsg 异步请求完成时,将要接收的消息.
45、 name 指向服务名的指针. proto 指向协议名称的指针.它可能是NULL,在这种情况下,WSAAsyncGetServByName()将搜索第一个服务入口(满足s_name或s_aliases之一和所给的名字匹配.)否则, WSAAsyncGetServByName()将和名和协议同时匹配. buf 接收hostent数据的数据区指针. buflen 上述数据区的大小. 返回值指出异步操作是否成功地初启.,63,4WSAAsyncGetServByPort() 本函数是getservbyport()的异步版本,用来获取对应于一个端口号的服务信息. HANDLE WSAAsyncGetS
46、ervByPort ( HWND hWnd, unsigned int wMsg, int port, const char * proto, char * buf, int buflen ); hWnd 异步请求完成时,应该接收消息的窗口句柄. wMsg 异步请求完成时,将要接收的消息. port 服务的接口.以网络字节序. proto 指向协议名称的指针.它可能是NULL,在这种情况下,WSAAsyncGetServByName()将搜索第一个服务入口(满足s_name或s_aliases之一和所给的名字匹配.)否则, WSAAsyncGetServByName()将和名和协议同时匹配.
47、buf 接收hostent数据的数据区指针. buflen 上述数据区的大小. 返回值指出异步操作是否成功地初启.,64,5WSAAsyncGetProtoByName()函数 本函数是getprotobyname()的异步版本,用来获取对应于一个协议名的协议名称和代号. HANDLE WSAAsyncGetProtoByName ( HWND hWnd, unsigned int wMsg, const char * name, char * buf, int buflen ); hWnd 异步请求完成时,应该接收消息的窗口句柄. wMsg 异步请求完成时,将要接收的消息. name 指向要
48、获得的协议名的指针. buf 接收hostent数据的数据区指针. buflen 上述数据区的大小. 返回值指出异步操作是否成功地初启. 若操作成功地初启,WSAAsyncGetProtoByName()返回一个HANDLE类型的非0值,作为请求需要的异步任务句柄. 如果异步操作不能初启, WSAAsyncGetProtoByName()返回一个0值,并且可使用WSAGetLastError()来获取错误号.,65,6WSAAsyncGetProtoByNumber()函数 本函数是getprotobynumber()的异步版本,用来获取对应于一个协议号的协议名称和代号. HANDLE WSA
49、AsyncGetProtoByNumber ( HWND hWnd, unsigned int wMsg, int number, char * buf, int buflen); hWnd 异步请求完成时,应该接收消息的窗口句柄. wMsg 异步请求完成时,将要接收的消息. number 要获得的协议号,以主机字节序. buf 接收hostent数据的数据区指针. buflen 上述数据区的大小. 返回值指出异步操作是否成功地初启. 若操作成功地初启,WSAAsyncGetProtoByNumber()返回一个HANDLE类型的非0值,作为请求需要的异步任务句柄. 如果异步操作不能初启, WSAAsyncGetProtoByNumber)返回一个0值,并且可使用WSAGetLastError()来获取错误号.,