AMcoder - javascript, python, java, html, php, sql

Winsock. Overlapped AcceptEx-ը ցույց է տալիս նոր կապ, մինչդեռ հաճախորդը չի միանում

Իմ ծրագրում ես օգտագործում եմ AcceptEx()-ի համընկնող տարբերակը՝ նոր կապեր ընդունելու համար: Նոր կապի ընդունումից հետո ծրագիրը նախաձեռնում է ևս մեկ համընկնող զանգ AcceptEx()ին՝ ավելի շատ կապեր ընդունելու համար: Սա լավ է աշխատում, և ես կարող եմ մի քանի հաճախորդներ հաջողությամբ միացնել սերվերին:

Բայց եթե ես պարզապես միացնեմ մեկ հաճախորդ և թույլ տամ, որ սերվեր-հավելվածը կանչի WSARecv (համընկնող) այս վարդակից, AcceptEx() կախարդական կերպով ընդունում է նոր «ուրվական» միացում (Առաջին հաճախորդը աշխատում է և ոչինչ չի անում): Երբ ես զանգում եմ WSARecv-ին դրա վրա, իհարկե, սխալ է տալիս:

Ծրագիրը ներառում է I/O-Completion Portբոլոր համընկնող զանգերի համար:

Ես չգիտեմ, թե որտեղից է գալիս կեղծ կապը: Բայց դա կարծես սխալ է իմ կոդի մեջ, որը ես չեմ կարողանում գտնել:

Այն, ինչ ես կարող եմ միանշանակ բացառել սխալների պատճառ լինելուց. 2. IOCP-wrapper դասը:

Հետևյալը համապատասխան ծածկագիրն է (իմ կարծիքով) - եթե ավելին է պետք, ասեք, խնդրում եմ :)

//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;

Պատասխանները:


1

Ես աշխատել եմ AcceptEx-ի և IOCP-ի հետ, և երբեք նման խնդիր չեմ տեսել:

Ձեր կոդի մասին։ Դժվար է ասել, թե կոնկրետ ինչն է դրանում սխալ, քանի որ այն ամբողջական չէ: Բայց ես բավականին վստահ եմ, որ խնդիրն այնտեղ է:

Մի խնդիր, որը ես տեսնում եմ, այն է, որ 3-րդ պարամետրը, որը դուք տրամադրում եք AcceptEx-ին, տեղական բուֆեր է: Սա սխալ է, քանի որ այս բուֆերը պետք է վավեր մնա ընդունման գործողության ընթացքում: Այն, ինչ դուք արեցիք, կարող է հեշտությամբ հանգեցնել հիշողության փչացմանը, ինչ էլ որ լինի:

Բայց ձեր «կեղծ ընդունման» խնդիրը հավանաբար այլ բանով է պայմանավորված։ Եվ ես կասկածում եմ, որ գիտեմ, թե որն է խնդիրը։ Թույլ տվեք գուշակել.

  1. Դուք օգտագործում եք նույն IOCP-ն և՛ լսողական, և՛ ընդունված (հաճախորդի) վարդակից: Սա ողջամիտ է, կարիք չկա ունենալ ավելի քան 1 IOCP:
  2. Երբ լրացնում եք IOCP-ից, դուք ավտոմատ կերպով այն ուղարկում եք WORK_ACCEPTին և զանգում HandleAcceptedConnection: չէ՞

Եթե ​​այո, ապա խնդիրն ակնհայտ է: Դուք զանգահարում եք WSARecv-ին հաճախորդի վարդակից: Այն ավարտվում է, և ավարտը հերթագրվում է IOCP-ում: Դուք վերցնում եք այն, սակայն այն վերաբերվում եք որպես ավարտված ընդունելության: Դուք այն գցում եք WORK_ACCEPT-ին, որը անպիտան տեսք ունի (ուղղակի այն պատճառով, որ դա ոչ WORK_ACCEPT կառույց է):

Եթե ​​դա այդպես է, դուք պետք է ավելացնեք լրացման տարբեր տեսակներ տարբերելու միջոց: Օրինակ, դուք կարող եք հայտարարել բազային կառուցվածք (որից կժառանգվեն բոլոր լրացումները), որը կունենա տիպի անդամ, որը կբացահայտի ավարտի տեսակը:

06.09.2011
  • Այո, ես օգտագործում եմ միայն մեկ IOCP... 2. Ոչ, ես չեմ. այն ունի բազային կառուցվածք՝ բանալիով, որը ցույց է տալիս, թե որ WORK-Type-ն է այն... (այն սահմանված է ստացված դասի կոնստրուկտորում) և ես կանչում եմ: Switch-case-statement-ում ճիշտ տիպի փոխակերպում (դա արդեն ստուգված է)... բայց շնորհակալություն ձեր առաջարկների համար... Ես ուղղում եմ լոկալ բուֆերային խնդիրը... բայց նույնիսկ դրանով մի քանի կապեր ընդունելը լավ է աշխատում 06.09.2011
  • @Incubbus. Լավ, տեսնում եմ: Կարո՞ղ եք բացատրել, թե ինչ նկատի ունեք՝ ընդունելով միացում, երբ ոչ ոք միացված չէ: Ի՞նչ եք տեսնում կոնկրետ։ Նաև, հնարավո՞ր է, որ WSARecv-ին զանգահարելիս դուք սխալմամբ ցուցիչը փոխանցեք սխալ համընկնող կառույցի (որ տեսակն է ընդունվում): 06.09.2011
  • Ահ... դա պետք է լիներ. մինչ ոչ ոք չի փորձում կապվել: Ստուգեցի ցուցիչի/կառուցվածքի հետ կապված խնդիրները... խնդիրներ չկան... քայլ առ քայլ ինքս անցա վրիպազերծիչի միջոցով և թվում է(!) ճիշտ է: Ես տեսնում եմ այն, ինչ կարծում եմ, որ պետք է տեսնեմ, երբ այն պետք է աշխատի :/... վերադարձի կոդերը WSARecv-ից և AcceptEx-ից... կառուցվածքների անդամի տվյալները և այլն... Ես բազմիցս օգտագործել եմ IOCP-ը տարբեր ծրագրերում և այն լավ է աշխատում: ... (քանի որ դա պարզապես հեշտ փաթաթման դաս է IOCP API-գործառույթների շուրջ) (տեսակը WT_ACCEPT է ընդունման և WT_RECV ստանալու համար) 06.09.2011
  • ավելացրել է վերը նշված բոլոր անհրաժեշտ ծածկագիրը OP-ում և այստեղ (գուցե մի փոքր ավելի հեշտ է կարդալ)՝ codepad.org/nrTmfUjR< /ա> 06.09.2011

  • 2

    Ես ակնկալում եմ, որ ձեր կոդի մեջ սխալ կա, բայց դժվար է ասել, թե որտեղ, քանի որ դուք չեք ցույց տալիս իրական զանգերը հիմքում ընկած API-ին. Ես չգիտեմ, թե ինչ է անում ձեր փաթաթման կոդը...

    Հնարավոր է, որ օգտակար լինի նայել գործող AcceptEx() սերվերին: Իմ անվճար IOCP սերվերի շրջանակում կա մեկը՝ http://www.serverframework.com/products---the-free-framework.html

    06.09.2011
  • Սա շատ լավ հոդված է սովորելու համար: Եթե ​​կոդի մեջ ինչ-որ մաս համաժամանակյա է (ինչպես, օրինակ, որոշ տվյալներ ուղարկելը և recv պատասխանը), իսկ մնացած մասը ասինխրոն է, հնարավո՞ր է միայն այդ դեպքում օգտագործել AcceptEx()-ը: եթե այո, ապա կարո՞ղ եմ իմանալ, թե ինչպես: Շնորհակալություն 05.02.2020
  • Միշտ հնարավոր է համաժամանակյա ընթերցում կատարել, նույնիսկ եթե վարդակը կապված է IOCP-ի հետ, պարզապես զանգի մեջ մի մատակարարեք համընկնող կառուցվածք: Այսպիսով, այո, դուք կարող եք օգտագործել AcceptEx-ը async ընդունելու համար, այնուհետև կոդի որոշ մասերի համաժամեցում ուղարկել/recv: 05.02.2020

  • 3

    Մաքրել և վերաշարադրել է կոդի թողարկված մասը․ աշխատում է հիմա։

    Բայց զավեշտալին այն է, որ երկու «կոդերը» միմյանց հետ համեմատելիս... զանգերը և այլն նույնն են...

    11.09.2011
    Նոր նյութեր

    Օգտագործելով Fetch Vs Axios.Js-ը՝ HTTP հարցումներ կատարելու համար
    JavaScript-ը կարող է ցանցային հարցումներ ուղարկել սերվեր և բեռնել նոր տեղեկատվություն, երբ դա անհրաժեշտ լինի: Օրինակ, մենք կարող ենք օգտագործել ցանցային հարցումը պատվեր ներկայացնելու,..

    Տիրապետել հանգստության արվեստին. մշակողի ուղեցույց՝ ճնշման տակ ծաղկելու համար
    Տիրապետել հանգստության արվեստին. մշակողի ուղեցույց՝ ճնշման տակ ծաղկելու համար Ինչպե՞ս հանգստացնել ձեր միտքը և աշխատեցնել ձեր պրոցեսորը: Ինչպես մնալ հանգիստ և զարգանալ ճնշման տակ...

    Մեքենայի ուսուցում բանկային և ֆինանսների ոլորտում
    Բարդ, խելացի անվտանգության համակարգերը և հաճախորդների սպասարկման պարզեցված ծառայությունները բիզնեսի հաջողության բանալին են: Ֆինանսական հաստատությունները, մասնավորապես, պետք է առաջ մնան կորի..

    Ես AI-ին հարցրի կյանքի իմաստը, այն ինչ ասում էր, ցնցող էր:
    Այն պահից ի վեր, երբ ես իմացա Արհեստական ​​ինտելեկտի մասին, ես հիացած էի այն բանով, թե ինչպես է այն կարողանում հասկանալ մարդկային նորմալ տեքստը, և այն կարող է առաջացնել իր սեփական արձագանքը դրա..

    Ինչպես սովորել կոդավորումը Python-ում վագրի պես:
    Սովորելու համար ծրագրավորման նոր լեզու ընտրելը բարդ է: Անկախ նրանից, թե դուք սկսնակ եք, թե առաջադեմ, դա օգնում է իմանալ, թե ինչ թեմաներ պետք է սովորել: Ծրագրավորման լեզվի հիմունքները, դրա..

    C++-ի օրական բիթ(ե) | Ամենաերկար պալինդրոմային ենթաշարը
    C++ #198-ի ամենօրյա բիթ(ե), Ընդհանուր հարցազրույցի խնդիր. Ամենաերկար պալինդրոմային ենթատող: Այսօր մենք կանդրադառնանք հարցազրույցի ընդհանուր խնդրին. Ամենաերկար palindromic substring...

    Kydavra ICAReducer՝ ձեր տվյալների ծավալայինությունը նվազեցնելու համար
    Ի՞նչ է ICAReducer-ը: ICAReducer-ն աշխատում է հետևյալ կերպ. այն նվազեցնում է նրանց միջև բարձր փոխկապակցված հատկանիշները մինչև մեկ սյունակ: Բավականին նման է PCAreducer-ին, չնայած այն..