1)打开Winsock---WSAStartup()
使用Winsock API编制的网络应用程序中,在调用任何一个Winsock函数之前,都必须检查协议栈的安装情况,也就是检查系统中是否有Windows Sockets的实现库。调用的是WSAStartup函数,详见:《Windows API巡礼》---WSAStartup和WSACleanup(http://blog.csdn.net/ACE1985/archive/2010/07/03/5710430.aspx )。
2)创建套接口---socket()或WSASocket()
在Winsock1中提供的创建套接口的函数是socket,详见:《Windows API巡礼》---socket和bind(http://blog.csdn.net/ACE1985/archive/2010/07/03/5710521.aspx )。
在Winsock2中提供的该函数的扩展格式是WSASocket:
SOCKET WSASocket(
__in int af, //使用的协议地址族,一般是AF_INET
__in int type, //协议类型,当af=AF_INET时,
//取值为:SOCK_STREAM、SOCK_DGRAM或SOCK_RAW之一
__in int protocol, //取值由type决定,详见下表…
__in LPWSAPROTOCOL_INFO lpProtocolInfo, //一个指向WSAPROTOCOL_INFO结构的指针,
//该结构定义所创建套接口的特性。如果本参数不指向NULL,则前三个参数被忽略
//系统将根据该结构中三个字段的值确定套接口类型
__in GROUP g, //套接口组的描述字,该参数始终为0,因为目前尚无可支持套接口组的Winsock版本
__in DWORD dwFlags //套接口属性描述,可取值如下:
//WSA_FLAG_OVERLAPPED、WSA_FLAG_MULTIPOINT_C_ROOT…
);
其中WSA_FLAG_OVERLAPPED用于指定这个套接口具备重叠I/O的特性,调用socket()建立一个套接字时,WSA_FLAG_OVERLAPPED便是默认设置。
函数调用成功,返回新创建的套接口号,它被定义成一个无符号的整型数据;调用失败,返回INVALID_SOCKET,调用WSAGetLastError()可获取相应错误代码。
附表:套接口参数
以下实例演示了WSASocket函数的使用:
#include <windows.h>
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA wsaData;
SOCKET recvSocket;
int iResult;
//-----------------------------------------------
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(iResult != 0)
{
printf("WSAStartup failed: %d/n", iResult);
return 1;
}
//-----------------------------------------------
// Initialize Winsock
recvSocket = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
NULL, 0, WSA_FLAG_OVERLAPPED);
if(recvSocket == INVALID_SOCKET)
{
printf("WSASocket call failed with error : %d/n", WSAGetLastError());
WSACleanup();
return 1;
}
printf("WSASocket call success!/n");
system("pause");
return 0;
}
3)指定本地地址---bind()
详见:《Windows API巡礼》---socket和bind(http://blog.csdn.net/ACE1985/archive/2010/07/03/5710521.aspx )。
4)监听连接---listen()
详见:《Windows API巡礼》---listen、accept和connect(http://blog.csdn.net/ACE1985/archive/2010/07/03/5710696.aspx )。
5)请求连接---connect()或WSAConnect()
在Winsock1中提供的connect()函数详见:《Windows API巡礼》---listen、accept和connect(http://blog.csdn.net/ACE1985/archive/2010/07/03/5710696.aspx )。
在Winsock2中提供的扩展格式是WSAConnect:
int WSAConnect(
__in SOCKET s, //将要建立连接的套接口描述字
__in const struct sockaddr *name, //指向远端套接口地址结构的指针
__in int namelen, //name名字的长度
__in LPWSABUF lpCallerData, //指向用户数据缓冲区的一个指针,该缓冲区中包含有在建立连接时
//由本机传输到服务器端的数据
__out LPWSABUF lpCalleeData, //指向另一个用户数据缓冲区的指针,该缓冲区中包含在建立连接时
//从服务器端传送到本机的数据
__in LPQOS lpSQOS, //指向服务质量(QOS)结构的指针,它用于指定流式套接口s需要的服务质量
__in LPQOS lpGQOS //指向服务质量(QOS)结构的指针,
//它指出套接口组所需要的服务质量;目前总为NULL
);
WSABUF是一种在Winsock2中很常用的结构,定义如下:
typedef struct __WSABUF {
u_long len; //
char FAR *buf; //
} WSABUF, *LPWSABUF;
QOS结构定义如下:
typedef struct _QualityOfService {
FLOWSPEC SendingFlowspec;
FLOWSPEC ReceivingFlowspec;
WSABUF ProviderSpecific;
} QOS, *LPQOS;
函数调用成功返回0,否则返回INVALID_SOCKET错误;
对于阻塞套接口来说,返回值表示连接试图是否成功;
对于非阻塞套接口来说,连接试图不一定马上成功。在这种情况下,WSAConnect()返回SOCKET_ERROR,且WSAGetLastError()返回WSAEWOULDBLOCK,此时应用程序可以采用下面方法进一步做出判断:
1) 利用select()函数,通过检查套接口是否可写来判断连接请求是否完成;
2) 如果应用程序已使用WSAAsyncSelect()函数来确定对连接事件的兴趣,则当连接操作完成时应用程序将收到FD_CONNECT通知;
3) 如果应用程序已使用WSAEventSelect()函数来确定对连接事件的兴趣,则当连接操作完成时相应的事件对象将设置信号。
对于一个非阻塞套接口来说,在连接试图完成之前,任何对套接口的WSAConnect()调用都将以WSAEALREADY错误返回;如果返回值指出连接试图失败(例如WSAECONNREFUSED、WSAENETUNREACH、WSAETIMEOUT),则应用程序可对该套接口再次调用WSAConnect()函数。
6)接受连接---accept()和WSAAccept()
在Winsock1中提供的accept()函数详见:《Windows API巡礼》---listen、accept和connect(http://blog.csdn.net/ACE1985/archive/2010/07/03/5710696.aspx )。
在Winsock2中提供的accept()函数的扩展格式是WSAAccept():
SOCKET WSAAccept(
__in SOCKET s, //一个处于监听连接状态的套接口描述字
__out struct sockaddr *addr, //用于存放发出连接请求的客户端IP地址信息
__inout LPINT addrlen, //addr结构长度
__in LPCONDITIONPROC lpfnCondition, //指向条件函数进程的指针
__in DWORD dwCallbackData //作为条件函数参数返回给应用程序的回调数据,
//Winsock不分析该参数
);
其中lpfnCondition指向的函数是根据客户的请求来调用的,该函数决定是否接受客户的连接请求,定义如下:
int CALLBACK ConditionFunc(
IN LPWSABUF lpCallerId, //指出客户连接实体的地址
IN LPWSABUF lpCallerData, //由客户在连接请求中发来的数据
IN OUT LPQOS lpSQOS, //指定一个客户连接请求的服务质量
IN OUT LPQOS lpGQOS, //指定套接口组的服务质量
IN LPWSABUF lpCalleeId, //指出本地连接实体的地址
OUT LPWSABUF lpCalleeData, //建立连接时发给客户的数据
OUT GROUP FAR *g, //确定一个组
IN DWORD_PTR dwCallbackData //返回的数据
);
函数调用成功,返回一个新的套接口描述符,它对应于已经接受的那个客户机的连接。对该客户机后续的所有操作,都使用这个新的套接口描述符,一般称为已连接套接口描述符;而原来的那个监听套接口,仍然用于接收其他客户机发送的连接请求,一般称为监听套接口描述符。
函数调用失败,返回INVALID_SOCKET错误,调用WSAGetLastError()获取相应错误代码。
注意:该函数用于面向连接的服务器端,在IP协议族中,只用于TCP服务器端。
以下代码演示了WSAAccept函数的使用:
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
/* Define an example conditional function that depends on the pQos field */
int CALLBACK ConditionAcceptFunc(
LPWSABUF lpCallerId,
LPWSABUF lpCallerData,
LPQOS pQos,
LPQOS lpGQOS,
LPWSABUF lpCalleeId,
LPWSABUF lpCalleeData,
GROUP FAR * g,
DWORD_PTR dwCallbackData
)
{
if (pQos != NULL) {
RtlZeroMemory(pQos, sizeof(QOS));
return CF_ACCEPT;
} else
return CF_REJECT;
}
int main() {
/* Declare and initialize variables */
WSADATA wsaData;
SOCKET ListenSocket, AcceptSocket;
struct sockaddr_in saClient;
int iClientSize = sizeof(saClient);
u_short port = 27015;
char* ip;
sockaddr_in service;
int error;
/* Initialize Winsock */
error = WSAStartup(MAKEWORD(2,2), &wsaData);
if (error) {
printf("WSAStartup() failed with error: %d/n", error);
return 1;
}
/* Create a TCP listening socket */
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
printf("socket() failed with error: %d/n", WSAGetLastError() );
WSACleanup();
return 1;
}
/*-------------------------------------------------------------
* Set up the sock addr structure that the listening socket
* will be bound to. In this case, the structure holds the
* local IP address and the port specified. */
service.sin_family = AF_INET;
service.sin_port = htons(port);
hostent* thisHost;
thisHost = gethostbyname("");
ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);
service.sin_addr.s_addr = inet_addr(ip);
/*-------------------------------------------------------------
* Bind the listening socket to the IP address.
* and port number specified by the sockaddr structure. */
error = bind(ListenSocket, (SOCKADDR *) &service, sizeof(SOCKADDR));
if (error == SOCKET_ERROR) {
printf("bind() failed with error: %d/n", WSAGetLastError() );
closesocket(ListenSocket);
WSACleanup();
return 1;
}
/* Make the socket listen for incoming connection requests */
error = listen(ListenSocket, 1);
if (error == SOCKET_ERROR) {
printf("listen() failed with error: %d/n", WSAGetLastError() );
closesocket(ListenSocket);
WSACleanup();
return 1;
}
printf("Listening.../n");
/*-------------------------------------------------------
* Accept an incoming connnection request on the
* listening socket and transfer control to the
* accepting socket. */
AcceptSocket = WSAAccept(ListenSocket, (SOCKADDR*) &saClient, &iClientSize,
&ConditionAcceptFunc, NULL);
/* Now do some work with the AcceptSocket
* At this point, the application could
* handle data transfer on the socket, or other socket
* functionality. */
/* Then clean up and quit */
closesocket(AcceptSocket);
closesocket(ListenSocket);
WSACleanup();
return 0;
}