1

私のプログラムでは、重複したバージョンを使用してAcceptEx()新しい接続を受け入れています。新しい接続が受け入れられた後、プログラムは、AcceptEx()さらに接続を受け入れるために、への別の重複呼び出しを開始します。これは正常に機能し、複数のクライアントをサーバーに正常に接続できます。

しかし、1つのクライアントを接続し、サーバーアプリケーションにこのソケットでWSARecv(オーバーラップ)を呼び出させると、 AcceptEx()魔法のように新しい「ゴースト」接続を受け入れます(最初のクライアントは何も実行していません)。もちろん、私がそれを呼び出すとWSARecv、エラーが発生します。

プログラムには、重複するすべての呼び出し用のI/O完了ポートが組み込まれています。

偽の接続がどこから来ているのかわかりません。しかし、それは私が見つけることができない私のコードのバグのようです。

エラーの理由から明確に除外できるもの:1。使用する重複構造とキャストのパラメーターは正しく機能します。2.IOCPラッパークラス

以下は関連するコードです(私の意見では)-もっと必要な場合は教えてください:)

//schematic
main()
{
    Server.Init(...);
    Server.Start();         //Run-loop
}

CServer::Init(/*...*/)
{
    [...]


    //Create the listen socket...
    Ret = InitAcceptorSocket(strLocalAddress, strListenPort, nBacklog);
    if(Ret != Inc::INC_OK)
        return Ret;

    //...Associate it with the IOCP
    if(!m_pIOCP->AssociateHandle((HANDLE) m_pListenSocket->operator size_t(), 2))
        return Inc::INC_FATAL;

    [...]
}

CServer::InitAcceptorSocket(const std::wstring& strLocalAddress, const std::wstring& strListenPort, int nBacklog)
{
    //Create the socket
    m_pListenSocket.reset(new Inc::CSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP));

    //Bind to specific port
    if(!m_pListenSocket->Bind(Inc::WStringToString(strLocalAddress), Inc::WStringToString(strListenPort)))      //Works as bind just calls getadrrinfo within itself

    //Put the socket into listen mode
    if(!m_pListenSocket->Listen(nBacklog))      //simple listen-wrapper: just calls the function and returns status indication
}

//Starts the server's work-cycle
CServer::Start(/**/)
{
    //Call accept
    DoCallAccept(m_pListenSocket.get());

    //Resume the threads
    //std::for_each(m_vecThreadHandles.begin(), m_vecThreadHandles.end(), [] (HANDLE hThread) {::ResumeThread(hThread);} );

    //TEST: Enter the Loop, too
    ServerMainWorkerThreadProc(this);

    return Inc::INC_OK;
}


//Worker thread proc
uintptr_t WINAPI ServerMainWorkerThreadProc(void* pvArgs)
{
    CServer* pServer = (CServer*)pvArgs;
    bool bLooping = true;

    try
    {
        while(bLooping)
        {
            bLooping = pServer->DoWork();
        };
    }
    catch(Inc::CException& e)
    {
        DebugBreak();
    }

    return 0;
}


bool CServer::DoWork()
{

    DWORD dwBytes = 0;
    ULONG_PTR ulKey = 0;
    OVERLAPPED* pOverlapped = nullptr;

    //Dequeue a completion packet
    if(!m_pIOCP->GetCompletionStatus(&dwBytes, &ulKey, &pOverlapped, INFINITE))
    {
        //error stuff
    }

    //Check for termination request:
    if(!dwBytes && !ulKey && !pOverlapped)
        return false;

    //Convert the Overlapped and check which work has to be done
    switch(((MYOVERLAPPED*)pOverlapped)->WorkType)
    {
    case WT_ACCEPT:                 //A new connection has been accepted
        HandleAcceptedConnection((WORK_ACCEPT*)pOverlapped);
        break;
    case WT_SEND:                   //Send data
        //HandleSendRequest((WORK_SEND*)pOverlapped);
        break;
    case WT_RECV:                   //Data has been received
        //HandleReceivedData((WORK_RECV*)pOverlapped);
        break;
    [...]

    return true;
}

    //New connection has been accepted
bool CServer::HandleAcceptedConnection(WORK_ACCEPT* pWork)
{
    //Create a new client socket object
    std::unique_ptr<Inc::CSocket> pSocket(new Inc::CSocket(pWork->SocketNewConnection));        //obtains the nescessary information (like AF_INET , etc by calls to getsockopt - works fine)

    //Associate with the IOCP
    if(!m_pIOCP->AssociateHandle((HANDLE)((SOCKET)(*(pSocket.get()))), 2))
    {
        //Report the error
    }

    //Queue a recv-packet
    if(!DoCallRecv(pSocket.get()))
    {
        //Report the error
    }

    //Release the client-socket-object
    pSocket.release();

    //Call accept another time
    DoCallAccept(pWork->pListenSocket);

    //Cleanuo
    delete pWork;

    return true;
}


//Call Recv on the socket
bool CServer::DoCallRecv(Inc::CSocket* pSocket)
{
    //Create the work object for receiving data
    std::unique_ptr<WORK_RECV> pWorkRecv(new WORK_RECV);
    memset((OVERLAPPED*)pWorkRecv.get(), 0, sizeof(OVERLAPPED));
    pWorkRecv->pSocket = pSocket;


    //Call Recv
    std::string strRecvBuffer;      //temporary receive buffer for immediate completion
    short sRet = pSocket->Recv(strRecvBuffer, pWorkRecv->pTestWSABuf, 2048, (OVERLAPPED*)pWorkRecv.get());
    [...]
    if(sRet == Inc::REMOTETRANSACTION_PENDING)
    {
        //release the work item so it is still on the heap when the overlapped operation completes
        pWorkRecv.release();
    }

    return true;
}

//Queue a call to accept
bool CServer::DoCallAccept(Inc::CSocket* pListenSocket)
{
    //Create the overlapped-structure
    std::unique_ptr<WORK_ACCEPT> pWork(new WORK_ACCEPT);
    memset((OVERLAPPED*)pWork.get(), 0, sizeof(OVERLAPPED));
    pWork->pListenSocket = pListenSocket;
    pWork->pSocket = m_pListenSocket.get();

    //Call accept
    pWork->SocketNewConnection = m_pListenSocket->Accept(nullptr, nullptr, (OVERLAPPED*)pWork.get());

    //Release the work object
    pWork.release();

    return true;
}


//The accept function for My custom socket-wrapper-class
SOCKET Inc::CSocket::Accept(sockaddr_storage* pAddr, int* pAddrLen, OVERLAPPED* pOverlapped)
{
    [...]
    else        //Overlapped
    {
        //create the client socket
        SOCKET ClientSock = socket(m_SocketAF, SOCK_STREAM, 0);
        if(ClientSock == INVALID_SOCKET)
            throw(Inc::CException(WSAGetLastError(), "Socket creation failed."));
        //address structure & size
        sockaddr_storage *ClientAddress = {0}; DWORD dwClientAddressSize = sizeof(sockaddr_storage);
        //output buffer
        //char acOutputBuffer[(2 * sizeof(sockaddr_storage)) + 32] = "";
        //received bytes
        DWORD dwBytes = 0;

        if(m_lpfnAcceptEx(m_Socket, ClientSock, (PVOID)m_acOutputBuffer, 0, (dwClientAddressSize + 16), (dwClientAddressSize + 16), &dwBytes, pOverlapped) == FALSE)
        {
            int nError = WSAGetLastError();
            if(nError != WSA_IO_PENDING)
                throw(Inc::CException(nError, "AcceptEx failed."));

            return ClientSock;
        }

        //if immidiately & successfully connected, get the client address
        [...]

        return ClientSock;
    }
}


//The receive function
short Inc::CSocket::RecvHelper(std::string& strIncomingDataBuffer, WSABUF*& pWSABuf, unsigned int nBytesToRecv, OVERLAPPED* pOverlapped)
{
    int iRet = 0;                   //ret code
    DWORD dwReceived = 0, dwFlags = 0;

    //Clear the Buffer
    strIncomingDataBuffer.clear();

    //create the receiving buffer
    std::unique_ptr<char[]> pcBuf(new char[nBytesToRecv]);
    //create the WSABUF
    std::unique_ptr<WSABUF> pWSABufBuf (new WSABUF);
    pWSABufBuf->len = nBytesToRecv;
    pWSABufBuf->buf = pcBuf.get();


    iRet = WSARecv(m_Socket, pWSABufBuf.get(), 1, pOverlapped ? NULL : (&dwReceived), &dwFlags, pOverlapped, NULL);
    if(iRet == 0)
    {
        //closed (gracefully) by the client (indicated by zero bytes returned)
        if(dwReceived == 0 && (!pOverlapped))
            return REMOTECONNECTION_CLOSED;     //return

        //successfull received
        strIncomingDataBuffer.assign(pWSABufBuf->buf, dwReceived);

        return SUCCESS;
    }
    if(iRet == SOCKET_ERROR)
    {
        int nError = WSAGetLastError();

        //Overlapped transaction initiated successfully
        //waiting for completion
        if(nError == WSA_IO_PENDING)
        {
            //release the buffers
            pcBuf.release();
            pWSABuf = pWSABufBuf.release();     //hand it over to the user

            return REMOTETRANSACTION_PENDING;   //return "transaction pending"-status
        }

        //forced closure(program forced to exit)
        if(nError == WSAECONNRESET)
        {
        [...]
}

編集:問題なく動作するテストサーバーを作成しました

//Accept a new connection
        ACCEPTLAPPED* pOverAccept = new ACCEPTLAPPED;
        pOverAccept->pSockListen = &SockListen;
        pOverAccept->pSockClient = new Inc::CSocket(SockListen.Accept(nullptr, nullptr, pOverAccept));

        //Main loop
        DWORD dwBytes = 0, dwFlags = 0;
        ULONG_PTR ulKey = 0;
        OVERLAPPED* pOverlapped = nullptr;
        while(true)
        {
            dwBytes = 0; dwFlags = 0; ulKey = 0; pOverlapped = nullptr;

            //Dequeue a packet
            pIOCP->GetCompletionStatus(&dwBytes, &ulKey, &pOverlapped, INFINITE);

            switch(((BASELAPPED*)pOverlapped)->Type)
            {
            case 1:     //Accept
                {
                    //ASsociate handle
                    ACCEPTLAPPED* pOld = (ACCEPTLAPPED*)pOverlapped;
                    pIOCP->AssociateHandle((HANDLE)(pOld->pSockClient)->operator SOCKET(),2);
                    //call recv
                    RECVLAPPED* pRecvLapped = new RECVLAPPED;
                    pRecvLapped->pSockClient = pOld->pSockClient;
                    short sRet = (pRecvLapped->pSockClient)->Recv(pRecvLapped->strBuf, pRecvLapped->pBuf, 10, pRecvLapped);

                    //Call accept again
                    ACCEPTLAPPED* pNewAccLapp = new ACCEPTLAPPED;
                    pNewAccLapp->pSockListen = ((ACCEPTLAPPED*)pOverlapped)->pSockListen;
                    pNewAccLapp->pSockClient = new Inc::CSocket((pNewAccLapp->pSockListen)->Accept(nullptr, nullptr, pNewAccLapp));

                    delete pOverlapped;
                };
                break;
            case 2:     //Recv
                {
                    RECVLAPPED* pOld = (RECVLAPPED*)pOverlapped;
                    if(!pOverlapped->InternalHigh)
                    {
                        delete pOld->pSockClient;
                        Inc::CSocket::freewsabufpointer(&(pOld->pBuf));
                        delete pOld;
                        break;
                    };
                    cout << std::string(pOld->pBuf->buf, pOld->pBuf->len) <<endl;
4

3 に答える 3

4

私はAcceptExIOCPと協力してきましたが、そのような問題は見たことがありません。

あなたのコードについて。完全ではないので、何が悪いのかを正確に言うのは難しいです。しかし、私は問題がそこにあるとかなり確信しています。

私が見ている問題の1つは、指定する3番目のパラメーターがローカルバッファーAcceptExであるということです。このバッファはaccept操作の間有効である必要があるため、これは誤りです。あなたがしたことは、スタックメモリの破損につながる可能性があります。

しかし、あなたの「偽りの受け入れ」の問題はおそらく他の何かによって引き起こされています。そして、私は何が問題なのか知っているのではないかと思います。私に当てさせてください:

  1. リスニングソケットと受け入れられた(クライアント)ソケットの両方に同じIOCPを使用します。これは合理的であり、1つ以上のIOCPを持つ必要はありません。
  2. IOCPから完了をデキューすると、自動的ににキャストされWORK_ACCEPT、が呼び出されますHandleAcceptedConnection。そうでしょう?

もしそうなら-問題は明らかです。WSARecvクライアントソケットでを呼び出します。完了し、完了はIOCPのキューに入れられます。あなたはそれをフェッチします、しかしあなたはそれを完全な受け入れとして扱います。あなたはそれをにキャストしましたWORK_ACCEPT、それはジャンクに見えます(単にそれが構造ではないためWORK_ACCEPT)。

この場合、さまざまな完了タイプを区別する方法を追加する必要があります。たとえば、基本構造体(すべての補完が継承される)を宣言できます。これには、補完タイプを識別するタイプメンバーが含まれます。

于 2011-09-06T11:10:52.550 に答える
0

コードにバグがあると思いますが、基盤となるAPIへの実際の呼び出しを表示していないため、どこにあるかを判断するのは困難です。ラッパーコードが何をしているのかわかりません...

動作しているAcceptEx()ベースのサーバーを確認すると便利な場合があります。私の無料のIOCPサーバーフレームワークで利用できるものが1つあります:http ://www.serverframework.com/products---the-free-framework.html

于 2011-09-06T10:19:38.740 に答える
0

コードの発行された部分をクリーンアップして書き直しました:今すぐ動作します...。

しかし、面白いのは、2つの「コード」を互いに比較している間...呼び出しなどはまだ同じです...

于 2011-09-11T10:51:16.263 に答える