gpt4 book ai didi

c++ - Winsock: Overlapped AcceptEx 指示新连接,而没有客户端连接

转载 作者:行者123 更新时间:2023-11-30 02:07:56 25 4
gpt4 key购买 nike

在我的程序中,我使用 AcceptEx() 的重叠版本来接受新连接。接受新连接后,程序启动另一个重叠调用 AcceptEx() 以接受更多连接。这工作正常,我可以将多个客户端成功连接到服务器。

但是,如果我只连接一个客户端并让服务器应用程序在此套接字上调用 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;

最佳答案

我一直在使用 AcceptEx 和 IOCP,但我从未见过这样的问题。

关于您的代码。很难说它到底出了什么问题,因为它还不完整。但我很确定问题就在那里。

我看到的一个问题是,您提供给 AcceptEx 的第三个参数是一个本地 缓冲区。这是错误的,因为这个缓冲区在接受操作期间应该保持有效。你所做的很容易导致堆栈内存损坏。

但是您的“虚假接受”问题可能是由其他原因引起的。我怀疑我知道问题出在哪里。让我猜猜:

  1. 您对监听和接受(客户端)套接字使用相同的 IOCP。这是合理的,不需要超过 1 个 IOCP。
  2. 当您从 IOCP 中取出完成时 - 您会自动将其转换为 WORK_ACCEPT 并调用 HandleAcceptedConnection。不是吗?

如果是这样 - 问题很明显。您在客户端套接字上调用 WSARecv。它完成,并且完成在 IOCP 中排队。您获取它,但将其视为已完成的接受。您将其转换为 WORK_ACCEPT,这看起来很垃圾(因为它不是 WORK_ACCEPT 结构)。

如果是这种情况 - 您必须添加一种方法来区分不同的完成类型。例如,您可以声明一个基本结构(所有完成都将从中继承),它有一个类型成员,它将标识完成类型。

关于c++ - Winsock: Overlapped AcceptEx 指示新连接,而没有客户端连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7318260/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com