WinsockAPI基本函数.docx
《WinsockAPI基本函数.docx》由会员分享,可在线阅读,更多相关《WinsockAPI基本函数.docx(39页珍藏版)》请在冰豆网上搜索。
WinsockAPI基本函数
(VClink->ProjectOptions加入ws2_32.lib)
4.4WinsockAPI基本函数——套接口与连接的建立
4.4.1打开Winsock——WSAStartup(?
)
应用程序或DLL只能在一次成功的WSAStartup(?
)调用之后才能进一步调用其他的WindowsSocketsAPI函数。
1.函数格式
WSAStartup(?
)函数的格式如下:
intWSAStartup(
WORDwVersionRequested,
LPWSADATAlpWSAData
);
2.函数参数说明
wVersionRequested:
此参数是一个WORD型(双字节型)数值,它指定准备在应用程序中要使用的Winsock库的版本号。
其中,用高位字节指定副版本,用低位字节指定主版本。
就目前的Win32平台而言,Winsock2库的最新版本是2.2(Win95为Winsock1.1)。
如果需要加载Winsock2.2版,指定此参数的值为0x0202;也可使用宏MAKEWORD(X,Y),其中X为高位字节,Y为低位字节,如MAKEWORD(2,2)。
lpWSAData:
此参数是一个指向WSADATA结构的指针。
当该函数被调用时,它返回关于WindowsSockets实现的详细信息,该结构的定义如下:
typedefstructWSAData{
WORDwVersion;
WORDwHighVersion;
CharszDescription[WSADESCRIPTION_LEN+1];
CharszSystemStatus[WSASYS_STATUS_LEN+1];
unsignedshortiMaxSockets;
unsignedshortiMaxUdpDg;
charFAR*lpVendorInfo;
}WSADATA,FAR*LPWSADATA;
各字段的含义说明如下:
●wVersion:
调用者希望使用的Winsock版本号;
●wHighVersion:
加载的Winsock库所支持的最高Winsock版本,?
?
通常和wVersion的值相同;
●szDescription:
系统加载的Winsock库的说明字符串,如“Winsock2.0”;
●szSystemStatus:
系统状态或配置信息的说明字符串;
●iMaxSockets:
套接口的最大编号(该字段被Winsock2或其后的版本忽略);
●iMaxUdpDg:
UDP数据报的最大容量(该字段被Winsock2或其后的版本忽略);
●lpVendorInfo:
厂商专有信息(该字段被Winsock2或其后的版本忽略)。
3.函数返回信息
WSAStartup(?
)函数的返回值是一个整数,如果调用成功则返回0。
WSAStartup(?
)函数调用不成功时返回如下的错误信息:
●WSASYSNOTREADY:
在Winsock的头文件Winsock2.h中,该错误代码定义的数值为10091,它表明加载的WinsockDLL不存在或底层的网络子系统无法使用。
●WSAVERNOTSUPPORTED:
该代码的数值为10092,所需的WindowsSocketsAPI的版本未由特定的WindowsSockets实现提供。
?
如果由wVersion返回的版本用户不能接受,则要调用WSACleanup(?
)函数清除对Winsock的加载。
●WSAEINVAL:
该代码的数值为10022,说明应用程序指出的WindowsSockets版本不能被该WinsockDLL的实现所支持。
●WSAEINPROGRESS:
该代码的数值为10034,说明一个阻塞的Winsock调用正在进行中。
●WSAEPROCLIM:
该代码的数值为10047,说明已经达到了WindowsSockets实现所支持的任务数量的极限。
●WSAEFAULT:
该代码数值为10014,说明lpWSAData参数是一个无效的指针。
注意:
在这里为了便于大家理解错误代码,列出了给错误代码定义的数值。
以后为了节省篇幅,不再列出错误代码的数值。
如果要查询对应错误代码对应的数值,可以在Winsock的头文件Winsock.h或Winsock2.h中去查找。
4.函数使用说明
如果用户在没有正确加载WinsockDLL的情况下使用了其他的WinsockAPI函数,则被调用的函数返回WSANOTINITIALISED错误信息,代码为10093。
该函数在程序中的基本使用方法如下:
#include
…//其他代码
WORDwVersionRequested;
WSADATAwsaData;
wVersionRequested=MAKEWORD(2,2);
if(WSAStartup(wVersionRequested,&wasData)!
=0){
//Winsock初始化错误
//输出Winsock初始化错误提示信息,如“WSAStartupfailed”
return;
}
//下面可以用两种方法中的任一种进行版本号匹配的检查
//if(LOBYTE(wsaData.wVersion)!
=2||HIBYTE(wsaData.wVersion)!
=0)
if(wsaData.wVersion!
=wVersionRequested){
//Winsock版本号不匹配
//输出Winsock版本号不匹配的错误提示信息;
WSACleanup(?
);
return;
}
//说明WinsockDLL的加载正确,可以执行以下的其他代码
…//其他程序代码
结束对WinsockDLL库的使用时,一定要调用WSACleanup(?
)函数卸载所加载的库。
4.4.2创建套接口——socket(?
)或WSASocket(?
)
应用程序在使用套接口通信前,必须要拥有一个套接口。
使用socket(?
)或WSASocket(?
)函数来给应用程序创建一个套接口。
1.函数格式
在Winsock1中提供的创建套接口函数的格式如下:
SOCKETsocket(
intaf,
inttype,
intprotocol
);
在Winsock2中提供的该函数的扩展格式如下:
SOCKETWSASocket(
intaf,
inttype,
intprotocol,
LPWSAPROTOCOL_INFOlpProtocolInfo,
Groupg,
intiFlags
);
2.函数参数说明
以上两种格式中,前面三个参数的含义是一样的,说明如下:
●af:
该参数说明套接口要使用的协议地址族,地址族与协议族的含义相同。
如果想建立一个UDP或TCP套接口,只能用常量AF_INET表示使用互联网协议(IP)地址。
当然,Winsock2还支持其他的协议,但一般在程序中很少使用。
●type:
该参数描述套接口的协议类型。
当第一个参数af是AF_INET时,它只能使用SOCK_STREAM、SOCK_DGRAM或SOCK_RAW三个协议类型中的任一个,分别表示要创建的是流式套接口、数据报套接口或原始套接口。
●protocol:
该参数说明该套接口使用的特定协议。
当协议地址族af和协议类型type已经确定后,协议字段可以使用的值是限定的,如表4-1所示。
如果调用者不希望特别指定所使用的协议,可以将此参数设置为0,系统就根据前两个参数的值自动确定一个协议字段的取值。
以上三个参数就可以确定一个套接口,它们之间的对应关系可以用表4-1表示。
表4-1套接口参数
系统可以根据这三个参数建立一个套接口,并给它分配相应的资源,同时返回一个整型套接口号。
因此,socket(?
)函数调用实际上指定了相关五元组中的“协议”这一元。
使用Winsock2时,一般可以先用WSAEnumProtocols(?
)函数(该函数在第8章介绍),以获得系统所安装协议的相关信息。
当然对现在的绝大部分用户来说,直接使用AF_INET(IP协议族)就可以了,因为几乎所有的协议实现系统都支持IP协议族。
但要编写通用性好的应用程序时,最好还是先使用WSAEnumProtocols(?
)函数查询一下系统安装的协议。
在Winsock2提供的扩展格式中,增加了三个参数,其含义说明如下(因为常用的是格式1,所以这三个参数只需了解其大概的含义):
●lpProtocolInfo:
一个指向WSAPROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。
如果本参数不指向空(NULL),则前三个参数(af,type,protocol)被忽略,系统就根据该结构中三个字段的值确定套接口类型。
●g:
套接口组的描述字。
组参数始终为0,因为目前尚无可支持套接口组的Winsock版本。
●iFlags:
套接口属性描述。
iFlags可用参数如下:
WSA_FLAG_OVERLAPPED
WSA_FLAG_MULTIPOINT_C_ROOT
WSA_FLAG_MULTIPOINT_C_LEAF
WSA_FLAG_MULTIPOINT_C_ROOF
WSA_FLAG_MULTIPOINT_D_LEAF
第一个标志WSA_FLAG_OVERLAPPED用于指定这个套接口具备重叠I/O(是适用于Winsock的通信模式之一)的特性。
调用socket(?
)建立一个套接字时,WSA_FLAG_OVERLAPPED便是默认设置。
一般说来,在使用WSASocket时,最好始终保持设定该标志。
后面4个标志用于处理多播套接口。
3.函数返回信息
该函数调用成功后,返回新创建的套接口号,它被定义成是一个无符号的整型数据。
函数调用错误时返回INVALID_SOCKET,应用程序可进一步调用WSAGetLastError(?
)函数来获取相应的错误代码。
可能获得的错误代码说明如下:
●WSANOTINITIALISED:
在调用本API之前应成功调用WSAStartup(?
);
●WSAENETDOWN:
网络子系统失效;
●WSAEAFNOSUPPORT:
不支持指定的地址族;
●WSAEINPROGRESS:
一个阻塞的Winsock调用正在进行中,或者服务提供者仍在处理一个回调函数;
●WSAEMFILE:
无可用的套接口描述字;
●WSAENOBUFS:
无可用的缓冲区空间,套接口无法创建;
●?
WSAEPROTONOSUPPORT:
不支持指定的协议;
●?
WSAEPROTOTYPE:
指定的协议对于本套接口类型错误;
●?
WSAESOCKTNOSUPPORT:
本地址族不支持指定的套接口类型;
●?
WSAEINVAL:
g参数非法。
4.函数使用说明
要创建一个流套接口时,可以使用下列三种格式之一:
SOCKETsockid=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
SOCKETsockid=WSASocket(AF_INET,SOCK_STREAM,0);
SOCKETsockid=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
要创建一个数据报套接口时,其格式如下(为节省篇幅,两种套接口的创建只给出了一种格式):
SOCKETsockid=socket(AF_INET,SOCK_GDRAM,IPPROTO_UDP);
要创建一个原始套接口时,其格式如下:
SOCKETsockid=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
4.4.3指定本地地址——bind()
当用socket()创建了一个套接口后,该套接口还是不能直接使用,因为它只存在于一个名字空间(地址族)中,也就是说它只确定了通信所希望使用的服务类型,并没有与该主机上提供服务的某端口联系在一起,这样的套接口可以叫未命名的套接口。
bind()函数通过给一个未命名的套接口分配一个本地名字来为套接口建立本地捆绑,即把一个套接口与一个主机地址和端口号联系起来。
本函数适用于数据报或流类套接口。
1.函数格式
bind()函数的格式如下:
intbind(
SOCKETs,
conststructsockaddrFAR*name,
intnamelen
);
2.函数参数说明
bind()函数中各参数的说明如下:
●s:
标识一未绑定套接口的描述字,它是socket()函数调用成功时返回的值。
●name:
是一个与指定协议有关的地址结构指针,它指向的值是赋予套接口的地址信息。
在Winsock中使用sockaddr_in结构指定IP地址和端口信息,它的定义如下:
structsockaddr_in{
shortsin_family;
u_shortsin_port;
structin_addrsin_addr;
charsin_zero[8];
}
其中,sin_family字段必须为AF_INET,以告诉Winsock所使用的是IP地址族;sin_port是以网络字节顺序表示的14位端口号;sin_addr是以网络字节顺序表示的32位IP地址;sin_zero字段不用,一般用0填充,在程序中通常是使用sockaddr_in之前将整个结构置0。
●namelen:
bind()函数的这个参数指地址参数(name)的长度。
3.函数返回信息
如果调用没有错误发生,则bind()返回0,否则将返回SOCKET_ERROR,应用程序可进一步通过WSAGetLastError(?
)函数来获取相应的错误代码。
下面是可能获取的错误代码:
●?
WSANOTINITIALISED:
在使用此API之前应首先成功调用WSAStartup(?
);
●?
WSAENETDOWN:
Windows套接口实现检测到网络子系统失效;
●?
WSAEADDRINUSE:
所指定的端口已在使用中;
●?
WSAEFAULT:
namelen参数太小(小于sockaddr结构的大小);
●?
WSAEINPROGRESS:
一个阻塞的Windows套接口调用正在运行中;
●WSAEAFNOSUPPORT:
本协议不支持所指定的地址族;
●WSAEINVAL:
该套接口已与一个地址捆绑;
●WSAENOBUFS:
无足够可用缓冲区,连接过多;
●WSAENOTSOCK:
指定的描述字不是一个套接口。
4.函数使用说明
各种IP地址和端口的设置可用表4-2表示。
在程序中使用bind( )调用的典型方式如下所示:
#include
SOCKETs;
sockaddr_intcpaddr;
intiSockErr;
intport=5000;//端口号
//先创建一个使用IP地址族的流式套接口
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
//以下给结构类型的地址提供数值
tcpaddr.sin_family=AF_INET;
//htons( )函数把一个14位主机字节顺序的端口号转化为网络字节顺序
tcpaddr.sin_port=htons(port);
//htons( )函数把一个32位主机字节顺序的IP地址转化为网络字节顺序
tcpaddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(s,(LPSOCKADDR)&tcpaddr,sizeof(tcpaddr))==SOCKET_ERROR){
//该函数的调用失败,进行错误处理
iSockErr=WSAGetLastError( );
//根据不同的错误类型进行输出提示信息
……
return;
}
//函数调用成功,进行其他处理
……
该程序段是使用bind( )调用常用的形式,由内核指定IP地址,应用程序指定端口号。
至此,通信过程中一端的协议、地址和所使用的端口号已经确定,一个套接口就可以正常使用了,即可以进行数据的接收或发送操作了。
4.4.4监听连接——listen( )
在一个服务器端用socket( )调用成功创建了一个套接口,并用bind( )函数和一个指定的地址关联(即绑定)在一起后,要指示该套接口进入监听连接请求的状态以及接收由客户发出的连接请求,就要用到WinsockAPI函数listen( )。
1.函数格式
listen( )函数格式如下:
intlisten(
SOCKETs;
intbacklog;
);
2.函数参数说明
listen( )函数中各参数说明如下:
●s:
用于标识一个已绑定了地址,但还未建立连接的套接口描述字。
●backlog:
该参数指定了正在等待连接的最大队列长度。
这个参数非常重要, 因为完全可能同时出现几个对服务器的连接请求。
例如,假定backlog参数为2时有三个客户机同时发出连接请求,那么前两个会被放在一个“等待处理”队列中,以便应用程序依次为它们提供服务。
而第三个连接的请求会造成一个WSAECONNREFUSED错误。
一旦服务器接受了一个连接请求,那个连接请求就会从队列中删去,以便可以继续接收其他客户发出的连接请求。
要注意的是,backlog参数本身的大小就存在着限制,这个限制是由协议提供者决定的。
3.函数返回信息
如果listen( )函数的调用无错误发生,则返回值为0,否则返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError( )获取相应的错误代码。
下面是可能获得的错误代码的说明:
●WSANOTINITIALISED:
在使用此API之前应成功调用WSAStartup( );
●WSAENETDOWN:
Windows套接口实现检测到网络子系统失效;
●WSAEADDRINUSE:
试图用listen( )去监听一个正在使用中的地址;
●WSAEINPROGRESS:
一个阻塞的Windows套接口调用正在运行中;
●WSAEINVAL:
该套接口未用bind( )进行捆绑,或已被连接;
●WSAEISCONN:
套接口已被连接;
●WSAEMFILE:
无可用文件描述字;
●WSAENOBUFS:
无可用缓冲区空间;
●WSAENOTSOCK:
描述字不是一个套接口
●WSAEOPNOTSUPP:
该套接口不能正常支持对listen( )的调用。
4.函数使用说明
listen( )仅适用于支持连接的套接口,如SOCK_STREAM类型的套接口。
也就是说,在IP地址族中,它只适用于同时有多个连接请求的TCP服务器。
如果当一个连接请求到来时队列已满,那么客户将收到一个WSAECONNREFUSED错误。
4.4.5请求连接——connect( )或WSAConnect( )
当服务器端建立好套接口并与一个本地地址绑定后,就进入监听状态,等待客户发出连接请求,从而为客户提供服务。
在客户端当套接口建立好之后,就要调用connect( )函数,提出与一个服务器建立连接的请求,如果服务器接受请求,就可以在服务器的远程套接口与客户端的本地套接口之间建立一条连接。
1.函数格式
在Winsock1中提供的connect( )函数格式是:
intconnect(
SOCKETs,
conststructsockaddrFAR*name,
intnamelen
);
在Winsock2中提供的扩展格式是:
intWSAConnect(
SOCKETs,
conststructsockaddrFAR*name,
intnamelen,
LPWSABUFlpCallerData,
LPWSABUFlpCalleeData,
LPQOSlpSQOS,
LPQOSlpGQOS
);
它是Winsock2对connect( )函数的扩展版本,支持连接数据交换和确定服务质量(QOS)。
2.函数参数说明
connect( )函数中各参数说明如下:
●s:
将要建立连接的套接口描述字。
●name:
是一个指向远端套接口地址结构(sockaddr_in)的指针,表示s套接口欲与其建立一条连接。
●namelen:
name名字的长度。
Winsock1中提供的请求建立连接函数的格式和Winsock2提供的扩展格式前三个参数是完全一样的。
Winsock2中后四个参数的含义是:
●lpCallerData:
指向用户数据缓冲区的一个指针,该缓冲区中包含有在建立连接时由本机传输到服务器端(远端)的数据。
● lpCalleeData:
指向另一个用户数据缓冲区的指针,该缓冲区中包含在建立连接时从服务器端(远端)传送到本机的数据。
以上两个参数都是WSABUF结构型的指针,WSABUF是一种在Winsock2中很常用的结构,该结构的定义如下:
typedefstruct_WSABUF{
inlen;//thelengthofthebuffer
charFAR*buf;//thepointertothebuffer
}WSABUF,FAR*LPWSABUF;
根据该结构具体应用的不同,len字段要么指定由buf字段指向的那个缓冲区的长度,要么指定包含在数据缓冲区buf中的数据长度。
在该函数中len字段显然表示缓冲区的长度。
●lpSQOS:
是一个指向服务质量(QOS)结构的指针,它用于指定流式套接口s需要的服务质量,在数据的发送与接收方各有一个;若为空值,则表明没有对该应用指定专用的QOS。
●lpGQOS:
也是一个指向服务质量(QOS)结构的指针,它指出套接口组所需要的服务质量;目前,尚未提供对套接字组的支持,因此该指针可设置为NULL。
这两个参数都是QOS型的指针,QOS结构的定义如下:
typedefstruct_QualityOfService{
WSABUFSendingFlowspec;//theflowspecfordatasending
WSABUFReceivingFlow