Winsock: Overlapped AcceptEx表示在没有客户端连接的情况下有一个新的连接
Winsock: Overlapped AcceptEx indicates a new connection while no client connecting
在我的程序中,我使用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
的第三个参数是本地缓冲区。这是错误的,因为这个缓冲区应该在accept操作期间保持有效。你所做的可能很容易导致堆栈内存损坏。
但你的"虚假接受"问题可能是由其他原因引起的。我想我知道问题出在哪里了。让我猜猜:
- 你使用相同的IOCP来监听和接收(客户端)套接字。这是合理的,不需要超过1个IOCP。
- 当你从IOCP中取出完成队列时-你自动将其强制转换为
WORK_ACCEPT
并调用HandleAcceptedConnection
。你不?
如果是,问题很明显。调用客户端套接字上的WSARecv
。它完成了,并在IOCP中排队完成。您获取它,但是您将其视为已完成的接受。您将其强制转换为WORK_ACCEPT
,这看起来很垃圾(仅仅因为它是而不是 WORK_ACCEPT
结构)。
如果是这种情况,必须添加一种方法来区分不同的完成类型。例如,你可以声明一个基结构(所有的完成都将从它继承),它将有一个类型成员,它将标识完成类型。
我预计你的代码中有一个bug,但很难说在哪里,因为你没有显示对底层API的实际调用;我不知道你的包装代码在做什么…
您可能会发现查看一个基于AcceptEx()的工作服务器是很有用的。有一个可用在我的免费IOCP服务器框架:http://www.serverframework.com/products---the-free-framework.html清理并重写了发布的部分代码:现在可以工作了....
但有趣的是:当比较两个"代码"时…
- C++套接字客户端到 Python 服务器未创建连接
- 计算出有多少客户端可以连接到我正在使用的一些tcp服务器代码
- 当客户端在 write() 期间终止连接时,由对等套接字错误重置连接
- 在 QNX 中,如何管理服务器和客户端之间的 IPC 连接?
- C++ Winsock2 客户端未通过远程 IP 连接到服务器
- 客户端 gRPC 连接,C++重新连接?
- 如何在 c++ 中确定 tcp 客户端是否连接
- 检测grpc服务器中关闭的客户端连接
- 仅通过建立一次TCP连接将Recv从客户端发送到服务器套接字
- 如何将请求的客户端连接的 IP 与 QTcpSocket 类中识别的 IP 之一进行比较?
- 为什么信号连接会使套接字客户端对象失效?
- 为什么我的客户端无法连接到服务器?
- 我正在编写一个简单的客户端套接字应用程序,但在连接后服务器收到一个空缓冲区
- gRPC C++尝试在无法访问的 IP 上连接通道时阻止客户端
- C++ 套接字接受,连接的客户端列表
- 多连接客户端套接字应用程序C++
- C [UDP]如何跟踪服务器上的所有连接(客户端)套接字连接
- 使用CORBA-org.omg.CORBA.BAD_PARAM:连接客户端(在VirtualBox上)和服务器(在loc
- 如何连接客户端(在VirtualBox上)和服务器(在localhost上)?我使用CORBA和C++/Java
- 服务器设置在编译时保持打印"connected"即使没有连接客户端