• 【网络编程】之十一、重叠IO Overlapped IO 完成例程


    完成例程是Win Sockets提供的另一种管理完成的重叠I/O方法,完成例程是一个函数,当发起重叠操作时,将该函数传递给发起操作的函数,当重叠IO操作完成时由系统调用。

    下面来看一下例程必须拥有下面的函数原型:

    void CALLBACK CompletionROUTINE{
            IN DWORD dwError,  //重叠操作的完成状态。
            IN DWORD cbTransferred,  //实际传输的字节数。
            IN LPWSAOVERLAPPED lpOVerlapped,   //初始化重叠操作的那个重叠结构
            IN DWORD dwFlags  //一个标志参数
    };


      OK ,在应用程序中使用完成例程管理重叠IO操作时,你必须给调用函数制定一个完成例程,一个WSAOVERLAPPED结构,在这里WSAOVERLAPPED的hEvent字段不会被使用,因为你使用完成例程来管理重叠操作的最终结果。

       当我们的调用写成在重叠操作完成后必须为完成例程提供服务,将调用线程置为“可警告的县城等待状态”。在这种状态下,重叠IO操作完成时,完成例程也被调用。

    下面来看两个函数,他们可以设置线程为一种可警告的等待状态:

    WSAWatiForMultipleEvents

     DWORD WSAAPI WSAWaitForMultipleEvents( 
      DWORD cEvents,//The number of event object handles in the array pointed to by lphEvents. WSA_MAXIMUM_WAIT_EVENTS是句柄最大值
      const WSAEVENT FAR * lphEvents, //A pointer to an array of event object handles.
        BOOL fWaitAll,// the wait type!TURE:当lphEvents的所有对象有信号的时候函数返回。FALSE:当任意一个事件对象有信号时函数返回
      DWORD dwTimeout,//The time-out interval,秒为单位
      BOOL fAlertable );//指定当系统将一个输入/输出完成例程放入队列以供执行时,函数是否返回。TRUE:函数返回并执行完成例程FALSE:函数不返回也不执行完成例程。

    如果函数成功,返回值指出造成函数返回的事件对象。  如果函数失败,返回值为WSA_WAIT_FAILED。

    来说一下最后一个参数,当为TRUE时,说明该函数返回时完成例程已经被执行。  如果该参数为FALSE,那么说明该函数返回时完成例程还没有执行。

                                                       通过设置TRUE,将调用线程置于可警告的等待状态,当该函数返回的时候,返回值为:WAIT_IO_COMPLETION。

    在这里,我们为了使用WSAWaitForMultipleEvents,在应用程序中可以定义一个不和任何对相关联的事件对象。  然后我们用这个事件对象为参数来调用该函数。

    函数返回的时候,我们要判断一下返回值是否是:WAIT_IO_COMPLETION,如果返回这个值,那么就说明这时候完成例程已经被调用了。如果不是,那么就说明发生了错误。

    SleepEx

    DWORD SleepEx{
          DWORD dwMilliseconds,//等待时间,ms为单位,   INFINITE说明函数将无限等待
          BOOL bAlertable//函数的返回方式1、FALSE,在不调用超时的情况下是不返回的。2、TRUE 调用超时或者                     //发生IO回调,那么就返回。
    };

    这里存在一个调用超时的问题,如果超时了,那么返回值就是0,  如果发生了IO完成回调,那么函数就会返回,返回值是:WAIT_IO_COMPLETION。


    重叠io完成例程要求我们讲一个瓦按成例程指定给发起IO操作的函数,  当io操作完成后,完成例程就被调用。    下面来看一下开发win  socket的步骤:

    1、创建具有WSAOVERLAPPED标志的套接字。

    2、定义完成例程。

    3、调用输入或者输出函数,初始化重叠IO操作,调用函数的最后一个参数为应用从程序定义的完成例程。

    4、调用WSAWaitForMultipleEvents()函数或者SleepEx()函数,使线程处于可警告的等待状态。如果调用WSAWaitForMultipleEvents()函数  我们需要一个应用程序定义的事件对象,以便该函数等待事件的发生。这两个函数的最后一个参数要设置为TRUE。

    5、当发生重叠IO回调时,完成例程被调用。



    下面来看一下WSAOVERLAPPED结构:

    typedef struct _WSAOVERLAPPED {
      ULONG_PTR Internal;//os保留,指出一个和系统相关的状态
      ULONG_PTR InternalHigh;//发送,接收数据的长度
      union {
        struct {
          DWORD Offset;//文件的初始位置
          DWORD OffsetHigh;//文件的偏移量
        };
        PVOID  Pointer;//指向文件传送位置,do not use after initialization to zero.
      };
      HANDLE    hEvent;//指定一个IO操作完成后触发的事件
    } WSAOVERLAPPED, *LPWSAOVERLAPPED;

    下面来看一下WSAOVERLAPPED结构的实例:

    #include <winsock2.h>
    #include <ws2tcpip.h>
    
    #include <stdio.h>
    #include <stdlib.h>
    
    // Need to link with Ws2_32.lib
    #pragma comment(lib, "ws2_32.lib")
    
    int __cdecl main()
    {
    
        //---------------------------------------------
        // Declare and initialize variables
        WSADATA wsaData;
        WSABUF DataBuf;
    
        WSAOVERLAPPED Overlapped;
        SOCKET SendToSocket = INVALID_SOCKET;
    
        struct sockaddr_in RecvAddr;
        struct sockaddr_in LocalAddr;
        int RecvAddrSize = sizeof (RecvAddr);
        int LocalAddrSize = sizeof (LocalAddr);
    
        u_short Port = 27015;
        struct hostent *localHost;
        char *ip;
    
        char SendBuf[1024] = "Data buffer to send";
        int BufLen = 1024;
        DWORD BytesSent = 0;
        DWORD Flags = 0;
    
        int rc, err;
        int retval = 0;
    
        //---------------------------------------------
        // Initialize Winsock
        // Load Winsock
        rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (rc != 0) {
            printf("Unable to load Winsock: %d\n", rc);
            return 1;
        }
    
        // Make sure the SendOverlapped struct is zeroed out
        SecureZeroMemory((PVOID)&Overlapped, sizeof(WSAOVERLAPPED));
    
        // Create an event handle and setup the overlapped structure.
        Overlapped.hEvent = WSACreateEvent();
        if (Overlapped.hEvent == NULL) {
            printf("WSACreateEvent failed with error: %d\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }
        //---------------------------------------------
        // Create a socket for sending data
        SendToSocket =
            WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
                      WSA_FLAG_OVERLAPPED);
        if (SendToSocket == INVALID_SOCKET) {
            printf("socket failed with error: %d\n", WSAGetLastError());
            WSACloseEvent(Overlapped.hEvent);
            WSACleanup();
            return 1;
        }
        //---------------------------------------------
        // Set up the RecvAddr structure with the IP address of
        // the receiver (in this example case "127.0.0.1")
        // and the specified port number.
        RecvAddr.sin_family = AF_INET;
        RecvAddr.sin_port = htons(Port);
        RecvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        //---------------------------------------------
        // Set up the LocalAddr structure with the local IP address
        // and the specified port number.
        localHost = gethostbyname("");//返回本地主机的标准主机名
        ip = inet_ntoa(*(struct in_addr *) *localHost->h_addr_list);
    
        LocalAddr.sin_family = AF_INET;
        LocalAddr.sin_addr.s_addr = inet_addr(ip);
        LocalAddr.sin_port = htons(Port);
    
        //---------------------------------------------
        // Bind the sending socket to the LocalAddr structure
        // that has the internet address family, local IP address
        // and specified port number.  
        rc = bind(SendToSocket, (struct sockaddr *) &LocalAddr, LocalAddrSize);
        if (rc == SOCKET_ERROR) {
            printf("bind failed with error: %d\n", WSAGetLastError());
            WSACloseEvent(Overlapped.hEvent);
            closesocket(SendToSocket);
            WSACleanup();
            return 1;
        }
        //---------------------------------------------
        // Send a datagram to the receiver
        printf("Sending a datagram...\n");
        DataBuf.len = BufLen;
        DataBuf.buf = SendBuf;
        rc = WSASendTo(SendToSocket, &DataBuf, 1,
                       &BytesSent, Flags, (SOCKADDR *) & RecvAddr,
                       RecvAddrSize, &Overlapped, NULL);
    
        if ((rc == SOCKET_ERROR) && (WSA_IO_PENDING != (err = WSAGetLastError()))) {
            printf("WSASendTo failed with error: %d\n", err);
            WSACloseEvent(Overlapped.hEvent);
            closesocket(SendToSocket);
            WSACleanup();
            return 1;
        }
    
        rc = WSAWaitForMultipleEvents(1, &Overlapped.hEvent, TRUE, INFINITE, TRUE);
        if (rc == WSA_WAIT_FAILED) {
            printf("WSAWaitForMultipleEvents failed with error: %d\n",
                    WSAGetLastError());
            retval = 1;
        }
    
        rc = WSAGetOverlappedResult(SendToSocket, &Overlapped, &BytesSent,
                                    FALSE, &Flags);//返回指定套接字口上一个重叠操作结果。
        if (rc == FALSE) {
            printf("WSASendTo failed with error: %d\n", WSAGetLastError());
            retval = 1;
        }
    
        //---------------------------------------------
        // When the application is finished sending, close the socket.
        printf("Finished sending. Closing socket.\n");
        WSACloseEvent(Overlapped.hEvent);
        closesocket(SendToSocket);
        printf("Exiting.\n");
    
        //---------------------------------------------
        // Clean up and quit.
        WSACleanup();
        return (retval);
    }

    WSAOVERLAPPED   和  WSAGetOverlappedResult在上一节中都给出过:http://blog.csdn.net/jofranks/article/details/7895316


     ----2012/9/20

    ----jofranks 于南昌


  • 相关阅读:
    ES6+ 现在就用系列(二):let 命令
    ES6+ 现在就用系列(一):为什么使用ES6+
    程序员之网络安全系列(六):动态密码
    程序员之网络安全系列(五):数字证书以及12306的证书问题
    程序员之网络安全系列(四):数据加密之非对称秘钥
    程序员之网络安全系列(三):数据加密之对称加密算法
    ddd
    vue加载时文件的执行顺序
    Kafka安装教程(详细过程)
    Kafka的安装与使用(转)
  • 原文地址:https://www.cnblogs.com/java20130723/p/3211402.html
Copyright © 2020-2023  润新知