我目前正在托管C ++中编写一个winsock服务器端套接字。在创建LPWSAOVERLAPPED对象并将其传递给WSASend函数之后,当操作完成非阻塞时,我看不到将其删除的位置(WSASend返回SOCKET_ERROR并且WSAGetLastError()返回WSA_IO_PENDING)。我目前的解决方案是创建一个System :: Threading :: WaitHandle,获取指向等待句柄的不安全指针,并将其传递给LPWSAOVERLAPPED对象下的hEvent。但是,这会导致无关的对象创建,因为我并不关心发送操作何时完成。另一方面,我需要一个LPWSAOVERLAPPED对象,以使操作完全无阻塞。有没有人有更好的解决方案来解决这个问题?这是我目前的代码:
void Connectivity::ConnectionInformation::SendData(unsigned char data[], const int length)
{
if (isClosed || sendError)
return;
Monitor::Enter(this->sendSyncRoot);
try
{
LPWSAOVERLAPPED overlapped = OverlappedObjectPool::GetOverlapped();
WaitHandle ^ handle = gcnew ManualResetEvent(false);
IntPtr handlePointer = handle->SafeWaitHandle->DangerousGetHandle();
sendInfo->buf = (char*)data;
sendInfo->len = length;
overlapped->Internal = 0;
overlapped->InternalHigh = 0;
overlapped->Offset = 0;
overlapped->OffsetHigh = 0;
overlapped->Pointer = 0;
overlapped->hEvent = (void*)handlePointer; //Set pointer
if (WSASend(connection, sendInfo, 1, NULL, 0, overlapped, NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() == WSA_IO_PENDING)
{
ThreadPool::UnsafeRegisterWaitForSingleObject(handle, sentCallback, (IntPtr)((void*)overlapped), -1, true);
}
else
{
this->sendError = true;
//The send error bool makes sure that the close function doesn't get called
//during packet processing which could lead to a lot of null reffernce exceptions.
OverlappedObjectPool::GiveObject(overlapped);
}
}
else
{
handle->Close();
sentData((IntPtr)((void*)overlapped), false);
}
}
finally
{
Monitor::Exit(this->sendSyncRoot);
}
}
答案 0 :(得分:1)
对于异步I / O,通过调用完成例程或将IOCP完成消息排队到IOCP完成队列来通知完成。在这两种情况下,应该注意OVL结构的生命周期至少应该是整个异步操作,但如果方便的话可以更长:)
在完成例程的情况下,OVL中未使用的hEvent参数可用于将指针传递到包含数据缓冲区,WSABUF数组和OVL结构作为成员的“IOrequest”类实例(并且肯定是指向已为其发出I / O的套接字对象的指针。 OVL指针作为参数提供给完成例程,因此可以检索hEvent并转换为类类型,因此检索完整的类实例 - OVL,数据缓冲区等。当数据处理完毕后,(或立即执行) WSASend的情况下完成例程,并且这个IOrequest最终被破坏,(或重新执行),OVL将继续使用它。这听起来有点乱,但工作正常,不需要任何令人讨厌的宏或其他技巧。
类似的方法可以与完整IOCP一起使用,或者,OVL作为lpCompletionKey'spare'参数传递。
哦 - 如果操作完成,你确实关心 - 你至少需要检查错误。