gpt4 book ai didi

c++ - 为什么 WSASend 返回 0 但仍调用完成例程?

转载 作者:可可西里 更新时间:2023-11-01 10:36:41 24 4
gpt4 key购买 nike

文档清楚地表明,如果 WSASend 立即完成,您将获得 WSA_IO_PENDING,但这从未发生过。我总是得到 0,并且 dwBytesTransferred 总是匹配我发送的字节。但是,似乎有时会调用我的完成例程,有时却不会。我为发送分配了缓冲区,因此如果不调用完成例程,我需要释放缓冲区。

我有三个临时计数器,m_dwAsyncSend、m_dwSyncSend 和 m_dwCompletions。 m_dwAsycSend 始终为零,并且 m_dwSyncSend 和 m_dwCompletions 始终相距很远,因为一个 m_dwSyncSend 可能是 750,而 m_dwCompletions 是 2。有很多次线程是可警报的,所以我认为我不会那样饿死它。

这让我发疯了!现在是午夜过后,我整天都在做这个。如果其中任何一个不连贯,我都会责怪它!

这是代码,我认为您不需要类文件就可以看到我在做什么。

void
CALLBACK
SendCompletion(
DWORD dwError,
DWORD cbTransferred,
LPOVERLAPPED pOvl,
DWORD dwFlags
)
{
LPWSABUF pBuffer = (LPWSABUF)((DWORD_PTR)pOvl + sizeof(OVERLAPPED));
CNetAsyncSocket *pSock = (CNetAsyncSocket *)pOvl->hEvent;
pSock->m_dwCompletions++;
if(dwError != NO_ERROR)
{
// If things didn't go as planned, ring the bell and disconnect.
pSock->Disconnect();
tracef(TT_REGULAR, 1,
"SOCKET_ERROR in CNetAsyncSocket::Send(), disconnecting, error code: %ld, on socket: %s:%ld",
dwError, pSock->GetIP(), pSock->GetPort());
free(pOvl);
}
else
{
// If we sent less than we meant to, queue up the rest.
if(cbTransferred < pBuffer->len)
{
DWORD dwRemaining = pBuffer->len - cbTransferred;
memmove(pBuffer->buf, (PVOID)((DWORD_PTR)pBuffer->buf + dwRemaining), dwRemaining);
pBuffer->len = dwRemaining;
}
else
{
free(pOvl);
}
}
}
void CNetAsyncSocket::SendAsync(PBYTE pData, DWORD dwLength)
{
// We want to minimize heap churn, so let's do this in one allocation.
// Also, having this in one chunk of memory makes it easier to interpret
// it on the other side.
DWORD dwAllocSize =
sizeof(OVERLAPPED) + // The OVERLAPPED struct.
sizeof(WSABUF) + // The buffer info.
dwLength; // The actual buffer we're copying.
LPOVERLAPPED pOvl = (LPOVERLAPPED)malloc(dwAllocSize);

if(pOvl == NULL)
{
// Out of memory.
}

// Initialize the allocation.
ZeroMemory(pOvl, dwAllocSize);


// Build the pointers.
LPWSABUF pBuffer = (LPWSABUF)((DWORD_PTR)pOvl + sizeof(OVERLAPPED));
pBuffer->len = dwLength;
assert(pBuffer->len < 1000000);
pBuffer->buf = (PCHAR)((DWORD_PTR)pBuffer + sizeof(WSABUF));
// When you have a completion routine, the hEvent member is ignored, so we
// can use it to pass our this pointer to the completion routine.
pOvl->hEvent = (PVOID)this;

// Copy the data to the buffer.
CopyMemory(pBuffer->buf, pData, dwLength);

// Send the data.
DWORD dwSent = 0;
int iResult = WSASend(
m_hSocket, // The socket.
pBuffer, // The WSABUF struct.
1, // Number of structs (1).
&dwSent, // Bytes sent. Updated if it happens synchronously.
0, // No flags.
pOvl, // The overlapped struct.
SendCompletion); // Completion routine.

if(iResult == NO_ERROR)
{
// If the send happened synchronously, we can go ahead and delete the
// memory that we allocated.

// TODO: look at bytes transferred, and if they're less than the total
// then issue another send with the remainder.

if(HasOverlappedIoCompleted(pOvl))
{
// If I actually free this here, the completion routine gets garbage.
//free(pOvl);
m_dwSyncSend++;
}
else
{
m_dwAsyncSend++;
}
}
else
{
// If we got WSA_IO_PENDING, then that just means the completion routine
// will take care of it.
if(iResult != WSA_IO_PENDING)
{
Disconnect();
tracef(TT_REGULAR, 1,
"SOCKET_ERROR in CNetAsyncSocket::Send(), disconnecting, error code: %ld, on socket: %s:%ld",
iResult, GetIP(), GetPort());
// Don't need the payload anymore.
free(pOvl);
}
else
{
m_dwAsyncSend++;
}
}
}

最佳答案

文档说,如果操作可以立即完成,您将得到 0,如果不能立即完成,您将得到 SOCKET_ERROR(最后一个错误代码为 WSA_IO_PENDING)。无论哪种方式,对完成例程的调用都将排队。

因此,您所描述的行为符合预期,并且您应该释放缓冲区的唯一情况是发生 WSA_IO_PENDING 以外的错误。

关于c++ - 为什么 WSASend 返回 0 但仍调用完成例程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22434024/

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