最近用网CSDN学院中李军老师的IOCP代码做了一个服务端,但是在后期测试的时候遇到了如下问题:
自己做了一个客户端,主动往IOCP服务器发送报文(服务器不回复),一切正常,客户端主动断开链接也没啥问题。
但是我又在服务端增加了一个功能,将收到的客户端发来的报文原封不动的再回复给客户端,回复后客户端也能够正常收到,但是当客户端主动断开链接以后,IOCP的GetQueuedCompletionStatus会收到两次请求,
GetQueuedCompletionStatus(
In HANDLE CompletionPort,
Out LPDWORD lpNumberOfBytesTransferred,
Out PULONG_PTR lpCompletionKey,
Out LPOVERLAPPED* lpOverlapped,
In DWORD dwMilliseconds
);
并且第二个参数lpNumberOfBytesTransferred都为0,我想知道为啥客户端主动断开后会触发两次GetQueuedCompletionStatus,导致粗体部门会执行两次导致程序崩溃(如果工作线程数设置为1 则不会出现奔溃,如IOCP服务器不只接收不回复报文也不会崩溃)。工作线程代码如下:
DWORD WINAPI ServerWorkerThread(LPVOID p)
{
assert(p);
auto workers = (Workers*)p;
workers->ThreadProc();
return 0;
}
Workers::Workers(IocpServer* server)
{
_iocpServer = const_cast<IocpServer*>(server);
}
void Workers::Start()
{
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);
// Create worker threads based on the number of processors available on the
// system. Create two worker threads for each processor.
for (int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
{
HANDLE ThreadHandle;
// Create a server worker thread and pass the completion port to the thread.
DWORD ThreadID;
if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, this,
0, &ThreadID)) == NULL)
{
printf("CreateThread() failed with error %d\n", GetLastError());
return;
}
// Close the thread handle
CloseHandle(ThreadHandle);
}
_iocpServer->Accept();
}
void Workers::ThreadProc()
{
DWORD bytes_transferred;
ULONG_PTR completion_key;
DWORD Flags = 0;
Overlapped* overlapped = NULL;
while (1)
{
BOOL bRet = GetQueuedCompletionStatus(_iocpServer->_completion_port,
&bytes_transferred,
&completion_key,
reinterpret_cast<LPOVERLAPPED*>(&overlapped),
INFINITE);
if (bRet == FALSE)
{
if (GetLastError() == WAIT_TIMEOUT || GetLastError() == ERROR_NETNAME_DELETED)
{
if (overlapped != NULL)
{
if (overlapped->connection != NULL)
{
//客户端断开,调用回调函数
//WriteStrLog(overlapped, 2);
if (_iocpServer->OnDisconnectedUnusual)
_iocpServer->OnDisconnectedUnusual(overlapped->connection);
//CancelIo((HANDLE)overlapped->connection->GetSocket());
delete overlapped->connection;
continue;
}
}
}
}
else
{
if (overlapped->type == Overlapped::Accept_type)
{
_iocpServer->Accept();
if (_iocpServer->OnConnected)
_iocpServer->OnConnected(overlapped->connection);
//WriteStrLog(overlapped,0);
continue;
}
if (bytes_transferred <= 0)
{
if (overlapped->connection != NULL)
{
//客户端正常断开
if (_iocpServer->OnDisconnected)
_iocpServer->OnDisconnected(overlapped->connection);
//bool br=CancelIoEx((HANDLE)overlapped->connection->GetSocket(), &overlapped->overlapped);
delete overlapped->connection;
}
continue;
}
if (overlapped->type == Overlapped::Type::Read_type)
{
// 异步读完成
if (_iocpServer->OnRead)
{
_iocpServer->OnRead(overlapped->connection, overlapped->connection->GetReadBuffer(), bytes_transferred);
}
continue;
}
if (overlapped->type == Overlapped::Type::Write_type)
{
auto conn = overlapped->connection;
conn->SetSentBytes(conn->GetSentBytes() + bytes_transferred);
//判断是否只发送了一部分
if (conn->GetSentBytes() < conn->GetTotalBytes())
{
//将剩余部分再发送
overlapped->wsa_buf.len = conn->GetTotalBytes() - conn->GetSentBytes();
overlapped->wsa_buf.buf = reinterpret_cast<CHAR*>(conn->GetWriteBuffer()) + conn->GetSentBytes();
auto send_result = WSASend(conn->GetSocket(),
&overlapped->wsa_buf, 1, &bytes_transferred,
0, reinterpret_cast<LPWSAOVERLAPPED>(overlapped),
NULL);
if (!(send_result == NULL || (send_result == SOCKET_ERROR && WSAGetLastError() == WSA_IO_PENDING)))
fprintf(stderr, "发送数据失败\n");
}
else
{
//全部字节发送完成,此时再投递一个写操作
//AsyncRead(overlapped->connection);
if (_iocpServer->OnWrite)
_iocpServer->OnWrite(overlapped->connection, bytes_transferred);
}
}
}
}
}
你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,技术专家团超时未为您做出解答
本次提问扣除的有问必答次数,将会以问答VIP体验卡(1次有问必答机会、商城购买实体图书享受95折优惠)的形式为您补发到账户。
因为有问必答VIP体验卡有效期仅有1天,您在需要使用的时候【私信】联系我,我会为您补发。