《ASCE1885的网络编程》---Windows API基本函数の套接口与连接的建立

1)打开Winsock---WSAStartup()

使用Winsock API编制的网络应用程序中,在调用任何一个Winsock函数之前,都必须检查协议栈的安装情况,也就是检查系统中是否有Windows Sockets的实现库。调用的是WSAStartup函数,详见:《Windows API巡礼》---WSAStartupWSACleanuphttp://blog.csdn.net/ACE1985/archive/2010/07/03/5710430.aspx )。

2)创建套接口---socket()WSASocket()

Winsock1中提供的创建套接口的函数是socket,详见:《Windows API巡礼》---socketbindhttp://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_STREAMSOCK_DGRAMSOCK_RAW之一

__in int protocol, //取值由type决定,详见下表…

__in LPWSAPROTOCOL_INFO lpProtocolInfo, //一个指向WSAPROTOCOL_INFO结构的指针,

//该结构定义所创建套接口的特性。如果本参数不指向NULL,则前三个参数被忽略

//系统将根据该结构中三个字段的值确定套接口类型

__in GROUP g, //套接口组的描述字,该参数始终为0,因为目前尚无可支持套接口组的Winsock版本

__in DWORD dwFlags //套接口属性描述,可取值如下:

//WSA_FLAG_OVERLAPPEDWSA_FLAG_MULTIPOINT_C_ROOT

);

其中WSA_FLAG_OVERLAPPED用于指定这个套接口具备重叠I/O的特性,调用socket()建立一个套接字时,WSA_FLAG_OVERLAPPED便是默认设置。

函数调用成功,返回新创建的套接口号,它被定义成一个无符号的整型数据;调用失败,返回INVALID_SOCKET,调用WSAGetLastError()可获取相应错误代码。

附表:套接口参数

《ASCE1885的网络编程》---Windows API基本函数の套接口与连接的建立

以下实例演示了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巡礼》---socketbindhttp://blog.csdn.net/ACE1985/archive/2010/07/03/5710521.aspx )。

4)监听连接---listen()

详见:《Windows API巡礼》---listenacceptconnecthttp://blog.csdn.net/ACE1985/archive/2010/07/03/5710696.aspx )。

5)请求连接---connect()WSAConnect()

Winsock1中提供的connect()函数详见:《Windows API巡礼》---listenacceptconnecthttp://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错误返回;如果返回值指出连接试图失败(例如WSAECONNREFUSEDWSAENETUNREACHWSAETIMEOUT),则应用程序可对该套接口再次调用WSAConnect()函数。

6)接受连接---accept()WSAAccept()

Winsock1中提供的accept()函数详见:《Windows API巡礼》---listenacceptconnecthttp://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;

}

你可能感兴趣的:(windows)