Вы должны предоставить буфер для WSARecvFrom()
, как и при любой операции чтения, независимо от того, используете ли вы IOCP или нет. Вы должны убедиться, что буфер остается действительным в памяти до завершения операции IOCP. IOCP заполняет предоставленный вами буфер, а затем уведомляет порт завершения о завершении.
UDP не может передавать более 65535 байтов в одной дейтаграмме, поэтому вы можете использовать это в качестве максимального размера буфера.
В вашем примере ваш код написан для синхронного запуска (что полностью исключает цель использования IOCP), поэтому вы можете использовать локальный буфер:
void foo() {
...
SOCKET sck = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (sck == INVALID_SOCKET)
{
// error, do something...
return;
}
....
bind(sck,(struct sockaddr *)&addr, sizeof(struct sockaddr_in));
HANDLE hPort = CreateIoCompletionPort((HANDLE)sck, NULL, 0, 0 );
if (!hPort)
{
// error, do something...
return;
}
WSAOVERLAPPED Overlapped = {0};
Overlapped.hEvent = WSACreateEvent();
BYTE buffer[0xFFFF];
DWORD dwBytesRecvd = 0;
DWORD dwFlags = 0;
sockaddr_in fromaddr = {0};
int fromaddrlen = sizeof(fromaddr);
WSABUF buf;
buf.len = sizeof(buffer);
buf.buf = buffer;
int iRet = WSARecvFrom(sck, &buf, 1, &dwBytesRecvd, &dwFlags, (sockaddr*)&fromaddr, &fromaddrlen, &Overlapped, NULL);
if (iRet == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
// error, do something...
return;
}
DWORD rBytes;
ULONG_PTR key;
LPOVERLAPPED pOverlapped = NULL;
if (!GetQueuedCompletionStatus(hPort, &rbytes, &key, &pOverlapped, INFINITE))
{
if (pOverlapped)
{
// WSARecvFrom() failed...
}
else
{
// GetQueuedCompletionStatus() failed...
}
// do something...
return;
}
}
// I/O complete, use buffer, dwBytesRecvd, dwFlags, and fromaddr as needed...
}
Однако это противоречит цели IOCP. Если вы действительно хотите быть синхронным, вы можете просто использовать recvfrom()
и позволить ему блокировать вызывающий поток до тех пор, пока не будут получены данные. IOCP работает лучше всего, когда у вас есть пул потоков, обслуживающих порт завершения. Позвоните WSARecvFrom()
и дайте ему поработать в фоновом режиме, не ждите. Пусть отдельный поток вызывает GetQueuedCompletionPort()
и обрабатывает данные при их получении, например:
struct MyOverlapped
{
WSAOVERLAPPED overlapped;
BYTE buffer[0xFFFF];
DWORD buflen;
DWORD flags;
sockaddr_storage fromaddr;
int fromaddrLen;
};
HANDLE hPort = NULL;
void foo() {
...
SOCKET sck = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (sck == INVALID_SOCKET)
{
// error, do something...
return;
}
....
bind(sck,(struct sockaddr *)&addr, sizeof(struct sockaddr_in));
hPort = CreateIoCompletionPort((HANDLE)sck, NULL, 0, 0 );
if (!hPort)
{
// error, do something...
return;
}
MyOverlapped *ov = new MyOverlapped;
ZeroMemory(ov, sizeof(*ov));
ov->overlapped.hEvent = WSACreateEvent();
ov->fromaddrlen = sizeof(ov->fromaddr);
WSABUF buf;
buf.len = sizeof(ov->buffer);
buf.buf = ov->buffer;
int iRet = WSARecvFrom(sck, &buf, 1, &ov->buflen, &ov->flags, (sockaddr*)&ov->fromaddr, &ov->fromaddrlen, (WSAOVERLAPPED*)ov, NULL);
if (iRet == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
// error, do something...
return;
}
// WSARecvFrom() is now operating in the background,
// the IOCP port will be signaled when finished...
}
else
{
// data is already available,
// the IOCP port will be signaled immediately...
}
...
}
...
// in another thread...
{
...
DWORD rbytes;
ULONG_PTR key;
MyOverlapped *ov = NULL;
if (!GetQueuedCompletionStatus(hPort, &rbytes, &key, (LPOVERLAPPED*)&ov, INFINITE))
{
if (ov)
{
// WSARecvFrom() failed...
// free ov, or reuse it for another operation...
}
else
{
// GetQueuedCompletionStatus() failed...
}
}
else
{
// use ov as needed...
// free ov, or reuse it for another operation...
}
...
}
person
Remy Lebeau
schedule
07.08.2015
select()
,WSAAsyncSelect()
илиWSAEventSelect()
, чтобы определить, когда сокет доступен для чтения (есть ожидающие данные), ЗАТЕМ вы можете читать из него, используя буфер. В вашем примере вы пытаетесь использовать IOCP, который выполняет фактическое чтение в фоновом режиме, а затем уведомляет вас, когда чтение завершено, поэтому вам нужен настоящий буфер для чтения. - person Remy Lebeau   schedule 07.08.2015