You must provide a buffer to WSARecvFrom(), just like with any read operation regardless of whether you use IOCP or not. You must ensure the buffer stays valid in memory until the IOCP operation is complete. IOCP fills the buffer you provide and then notifies the completion port when finished.
UDP cannot transfer more than 65535 bytes in a single datagram, so you can use that as your max buffer size.
In your example, your code is written to run synchronously (defeating the purpose of using IOCP at all), so you can use a local buffer:
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...
}
However, this defeats the purpose of IOCP. If you really want to be synchronous, you could just use recvfrom() instead and let it block the calling thread until data arrives. IOCP works best when you have a pool of threads servicing the completion port. Call WSARecvFrom() and let it work in the background, don't wait on it. Let a separate thread call GetQueuedCompletionPort() and process the data when it is received, eg:
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...
}
...
}
select(),WSAAsyncSelect()orWSAEventSelect()to detect when the socket is readable (has data pending), THEN you can read from it using a buffer. In your example, you are trying to use IOCP, which performs an actual read in the background and then notifies you when the read is complete, so you need a real buffer for it to read into. - Remy Lebeau