《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基本函数の套接口与连接的建立_第1张图片 

以下实例演示了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,api,网络,socket,struct)