Winsock: sovrapposta AcceptEx indica una nuova connessione mentre nessun client che si connette
-
26-10-2019 - |
Domanda
Nel mio programma sto usando la versione sovrapposta di AcceptEx()
per accettare nuove connessioni.
Dopo una nuova connessione è stata accettata, il programma inizia un'altra chiamata sovrapposta alla AcceptEx()
per accettare più connessioni. Questo funziona bene e posso collegare più client al server con successo.
Ma se ho appena Connect Client uno e lasciare che il WSARecv chiamata server di applicazione (sovrapposti) su questo socket, AcceptEx()
accetta magicamente una nuova -Connection "fantasma" (C'è il primo client che esegue non fare nulla). Quando chiamo WSARecv
su questo, ovviamente -. Dà un errore
Il programma è dotato di un I / O-porta di completamento per tutte le chiamate sovrapposte.
Non lo so dove la connessione falso proviene. Ma che sembra essere un bug nel mio codice che io sono in grado di trovare.
cose che posso sicuramente escludere dalla essere la ragione errori: 1.Le sovrapposti strutture che uso e il parametro per la fusione funziona correttamente. 2. La classe IOCP-wrapper .
In seguito è il codice in questione (a mio parere) - Se avete bisogno di più, mi dica, per favore:)
//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)
{
[...]
}
EDIT: Ha scritto un test server che funziona bene
//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;
Soluzione 3
Forse up pulito e riscrivere la parte rilasciato del codice: ora funziona ....
Ma la cosa divertente è: mentre confrontando i due "codici" con l'altro ... le chiamate ecc sono sempre gli stessi ...
Altri suggerimenti
I "ve lavorato con AcceptEx
e IOCP, e non ho mai mai visto un tale problema.
A proposito di codice. E 'difficile dire cosa c'è che non va esattamente in essa, dal momento che non è completa. Ma sono abbastanza sicuro che il problema è lì.
Un problema che vedo è che il 3 ° parametro che ci fornirete per AcceptEx
è un locali buffer. Questo è sbagliato, perché questo buffer dovrebbe rimanere valida per tutta la durata dell'operazione accettare. Quello che hai fatto può facilmente portare alla corruzione della memoria pila di qualsiasi altra cosa.
Ma il problema "spuria accettare" è probabilmente causato da qualcos'altro. E ho il sospetto che io so qual è il problema. Fammi indovinare:
- È possibile utilizzare lo stesso IOCP sia per l'ascolto e accettato presa (client). Questo è ragionevole, non c'è bisogno di avere più di 1 IOCP.
- Quando si Dequeue un completamento della IOCP - lanci automaticamente in
WORK_ACCEPT
e callHandleAcceptedConnection
. non è vero?
Se è così - il problema è evidente. Si chiama il WSARecv
sul socket client. Completa, e il completamento è in coda al l'IOCP. Si prenderlo, però lo trattano come un completamento accettare. La lanci a WORK_ACCEPT
, che sembra Junky (semplicemente becase è non una struttura WORK_ACCEPT
).
Se questo è il caso - si deve aggiungere un modo per distinguere i diversi tipi di completamento. Per esempio si può dichiarare una struct di base (da cui tutti i completamenti avrebbero ereditato), che avrebbe un membro tipo, che identificherà il tipo di completamento.
mi aspetto di avere un bug nel codice, ma è difficile dire dove, come non si mostra le chiamate effettivi alle API sottostante; Non ho idea di che cosa il vostro codice wrapper sta facendo ...
Si potrebbe trovare utile guardare un AcceptEx di lavoro () basato su server. Ce n'è uno disponibile nella mia libera quadro del server IOCP: http: //www.serverframework .com / prodotti --- the-libera-framework.html