[疑难] 请教iocp AcceptEx问题

hurd 2009-03-21
源文件: http://dlang.group.iteye.com/topics/download/d3707525-c01d-37ad-9adf-62ba64fbe719


iocp线程GetQueuedCompletionStatus 后, 在accept时setsockopt出现WSAENOTSOCK   = 10038 错误。
检查了acceptSocket和ListSocket值和之前的都一样, 不知道什么原因?
有空的朋友给看下。。
DavidL 2009-03-22
貌似是网上找来的代码, 我把它们拼凑了一下,c++的版本和你有点出入,不懂你为什么要那样写:

// geez.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>    //微软扩展的类库
#include <windows.h>
using namespace std;
#define SEND 0
#define RECV 1
#define ACCEPT 2

#define DATA_LENGTH 1000

//单句柄数据定义
typedef struct _PER_HANDLE_DATA
{
    SOCKET socket;    //相关的套接字
    SOCKADDR_STORAGE clientAddr;    //客户端的地址
}PER_HANDLE_DATA,*LPPER_HANDLE_DATA;
#define OP_READ   1 
#define OP_WRITE  2 
#define OP_ACCEPT 3
//但IO操作数据
typedef struct{
    OVERLAPPED overlapped;
    WSABUF buffer;    //一个数据缓冲区,用于WSASend/WSARecv中的第二个参数
    char dataBuffer[DATA_LENGTH];    //实际的数据缓冲区
    int dataLength;                    //实际的数据缓冲区长度
    int operatorType;                //操作类型,可以为SEND/RECV两种
    SOCKET client;                    //分别表示发送的字节数和接收的字节数
}PER_IO_DATA,*LPPER_IO_DATA;

DWORD WINAPI ServerThread( LPVOID lpParam );
int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE CompletionPort;
    WSADATA data;
    SYSTEM_INFO info;
    SOCKADDR_IN addr;
    SOCKET Listen;

    unsigned int i;
    WSAStartup(MAKEWORD(2,2),&data);

    //创建一个IO完成端口
    CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
    //确定处理器的数量
    GetSystemInfo(&info);    //创建线城
    for(i=0;i<info.dwNumberOfProcessors * 2;i++)
    {
        //根据处理器的数量创建相应多的处理线程
        HANDLE thread = CreateThread(NULL,0,ServerThread,CompletionPort,0,NULL);
        CloseHandle(thread);
    }
    //创建一个监听套接字(进行重叠操作)
    Listen = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
    //将监听套接字与完成端口绑定
    LPPER_HANDLE_DATA perDandleData;
    perDandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA));
    perDandleData->socket = Listen;
    CreateIoCompletionPort((HANDLE)Listen,CompletionPort,(ULONG_PTR)perDandleData,0);

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(5500);

    bind(Listen,(PSOCKADDR)&addr,sizeof(addr));
    listen(Listen,5);

    LPFN_ACCEPTEX lpfnAcceptEx = NULL;    //AcceptEx函数指针
    //Accept function GUID
    GUID guidAcceptEx = WSAID_ACCEPTEX;
    //get acceptex function pointer
    DWORD dwBytes = 0;
    if(WSAIoctl(Listen,SIO_GET_EXTENSION_FUNCTION_POINTER,
        &guidAcceptEx,sizeof(guidAcceptEx),&lpfnAcceptEx,sizeof(lpfnAcceptEx),
        &dwBytes,NULL,NULL)==0)
        cout<<"WSAIoctl success..."<<endl;
    else{
        cout<<"WSAIoctl failed..."<<endl;
        switch(WSAGetLastError())
        {
        case WSAENETDOWN:
            cout<<""<<endl;
            break;
        case WSAEFAULT:
            cout<<"WSAEFAULT"<<endl;
            break;
        case WSAEINVAL:
            cout<<"WSAEINVAL"<<endl;
            break;
        case WSAEINPROGRESS:
            cout<<"WSAEINPROGRESS"<<endl;
            break;
        case WSAENOTSOCK:
            cout<<"WSAENOTSOCK"<<endl;
            break;
        case WSAEOPNOTSUPP:
            cout<<"WSAEOPNOTSUPP"<<endl;
            break;
        case WSA_IO_PENDING:
            cout<<"WSA_IO_PENDING"<<endl;
            break;
        case WSAEWOULDBLOCK:
            cout<<"WSAEWOULDBLOCK"<<endl;
            break;
        case WSAENOPROTOOPT:
            cout<<"WSAENOPROTOOPT"<<endl;
            break;
        }
        return 1;
    }

    //while(true)
    //{
        //准备调用 AcceptEx 函数,该函数使用重叠结构并于完成端口连接
        LPPER_IO_DATA perIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_DATA));
        memset(&(perIoData->overlapped),0,sizeof(OVERLAPPED));    
        perIoData->operatorType = ACCEPT;
        //在使用AcceptEx前需要事先重建一个套接字用于其第二个参数。这样目的是节省时间
        //通常可以创建一个套接字库
        perIoData->client = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,0,0,WSA_FLAG_OVERLAPPED);

        perIoData->dataLength = DATA_LENGTH;
        DWORD flags = 0;
        
        //调用AcceptEx函数,地址长度需要在原有的上面加上16个字节
        //注意这里使用了重叠模型,该函数的完成将在与完成端口关联的工作线程中处理
        cout<<"Process AcceptEx function wait for client connect..."<<endl;
        int rc = lpfnAcceptEx(Listen,perIoData->client,perIoData->dataBuffer,
            perIoData->dataLength-((sizeof(SOCKADDR_IN)+16)*2),
            sizeof(SOCKADDR_IN)+16,sizeof(SOCKADDR_IN)+16,&dwBytes,
            &(perIoData->overlapped));
        if(rc == FALSE)
        {
            if(WSAGetLastError()!=ERROR_IO_PENDING)
                cout<<"lpfnAcceptEx failed.."<<endl;
        }

        cin>>i;
    closesocket(Listen);
    WSACleanup();
}


DWORD WINAPI ServerThread( LPVOID lpParam )
{
    HANDLE hIocp = ( HANDLE )lpParam;
    if( hIocp == NULL )
    {
        return -1;
    }
    DWORD dwTrans = 0;
    LPPER_HANDLE_DATA pPerHandle;
    LPPER_IO_DATA     pPerIo;
    
    while( TRUE )
    {
        // 在关联到此完成端口的所有套接字上等待I/O完成 
        BOOL bRet = ::GetQueuedCompletionStatus( hIocp, &dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIo, WSA_INFINITE );
        if( !bRet )     // 发生错误 
        {
            ::closesocket( pPerHandle->socket );
            ::GlobalFree( pPerHandle );
            ::GlobalFree( pPerIo );
            cout << "error" << endl;
            continue;
        }
        // 套接字被对方关闭 
        if( dwTrans == 0 && ( pPerIo->operatorType == OP_READ || pPerIo->operatorType == OP_WRITE ) )
        {
            ::closesocket( pPerHandle->socket );
            ::GlobalFree( pPerHandle );
            ::GlobalFree( pPerIo );
            cout << "client closed" << endl;
            continue;
        }
        switch ( pPerIo->operatorType )
        {
        case OP_READ:       // 完成一个接收请求 
            {
                pPerIo->dataBuffer[dwTrans] = '\0';
                printf( "%.*s\n", pPerIo->dataLength, pPerIo->dataBuffer );
                // 继续投递接受操作 
                WSABUF buf;
                buf.buf = pPerIo->dataBuffer;
                buf.len = pPerIo->dataLength;
                pPerIo->operatorType = OP_READ;
                
                DWORD dwRecv = 0;
                DWORD dwFlags = 0;
                
                ::WSARecv( pPerHandle->socket, &buf, 1, &dwRecv, &dwFlags, &pPerIo->overlapped, NULL );
            }
            break;
        case OP_WRITE:
        case OP_ACCEPT:
            break;
        }
    }
    return 0;
}


另外,把你的代码改成下面的样子的时候:
scope(exit){ Stdout("closesocket\n").flush;closesocket(Listen);}

0Xc8000006
Line:43 CompletionPort = 7a8
Line:48 Listen = 1924
Line:53 CompletionPort = 7a8
Line:67 lpfnAcceptEx = 719c753b
line:68  AcceptEx 41c636
line:72 sizeof PER_IO_DATA:1040 OVERLAPPED:20 WSABUF :8
line:77  perIoData.client  1888
d
closesocket
line:123 GetQueuedCompletionStatus return 0, 0
line:137 ACCEPT new socket  WSAGetLastError 995
line:140 setsockopt AcceptSocket:1888, ListenSocket:1924
line:143  WSAGetLastError 10038
Line:153 re = 1960
line:159  wait for data arrive(Accept)..
line:173 WSAGetLastError: 10057

你的Listen已经关闭了. 如果去掉scope(exit) closesocket(Listen);你的程序就没报错,停在那儿了
貌似这个东西和D关联不大,更接近windows编程比较吧
为什么要自己写个这个玩意儿呢,mango不是提供了http服务的实现了吗?
hurd 2009-03-22
楼上的大哥, 我写的哪个w32api里的alias ProtocolType.IP IPPROTO_TCP;是错的。
应该 alias ProtocolType.TCP IPPROTO_TCP;
改了后对了。。。
DavidL 2009-03-22
我之前拿到你的代码已经是alias ProtocolType.TCP IPPROTO_TCP;
编译运行后和你帖子里描述的错误一样.
hurd 2009-03-22
http://dlang.group.iteye.com/topics/download/d3707525-c01d-37ad-9adf-62ba64fbe719

现在可以了,运行example.exe后, 在浏览器输入http://127.0.0.1:5500/就可以在控制台看到接收的数据。

现在还不会发送数据。。。
hurd 2009-03-22
mango的哪个也不错,不过我想写个fcgi的服务端,计划用这个。
mango哪个程序运行时间长了可能出现空指针错误。找不到原因,可能我应用部分写的有问题。
ww21xx 2009-03-24
   强烈希望 完善 IOCP EOPLL 这样写服务器 就莫得问题了!急等!
llemmx 2009-03-28
用D写IOCP有一个隐藏的问题我使用的D版本是1.28,其它的版本不知道,一开始也是反复遇到该问题,但后来调试后发现一个恶心的问题,就是结构体字节对齐问题,所以我这么修改了结构体就成功了。
struct IOData{
align(4):
WSAOVERLAPPED Overlapped;// 完成结构
ubyte   op;              // 操作类型
ubyte   buf[MAX_BUFSIZE];// 连接缓冲
ubyte   len;             // 缓冲区实际长度
}
解释,由于windows与D的子对齐是不一样的,结果导致在发生事件后地址偏移错误从而引发异常
ideage 2009-03-29
Tango曾经计划IOCP.可惜还没有进展.

试验了下Tango的SSL,还是不能用.
Global site tag (gtag.js) - Google Analytics