Sign in to follow this  

polling a socket and receiving data [previously titled as 'ioctlsocket']

This topic is 3855 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Is there anything wrong with using ioctlsocket/FIONREAD to poll a socket for incoming data (TCP)? It's of course for a game, so it's gonna be done very often (once a multiple of frames, or even on each frame).. [Edited by - arithma on May 25, 2007 5:44:51 AM]

Share this post


Link to post
Share on other sites
OK, so after select asserts there is actually data, how should I continue to know how big the buffer I should supply, or should I just: select, recv, select, recv, select, no data...?

Share this post


Link to post
Share on other sites
The socket should be non-blocking, so you can just keep doing recv() till it tells you that the operation would block.

From my code:

void PNetworkClient::OnRead(int nError)
{
if(nError)
{
ELog::Get().DebugFormat(L"NETWORK : Read from %s failed. Error: %s\n",
GetRemoteIP().ToString().c_str(), PNetworkMgr::FormatWindowsError(nError).c_str());
CleanupSocket();
return;
}

int nRet;
char szBuff[256];
do {
nRet = recv(m_sock, szBuff, sizeof(szBuff), 0);
if(nRet == SOCKET_ERROR)
{
int nError = WSAGetLastError();
if(nError != WSAEWOULDBLOCK)
{
ELog::Get().DebugFormat(L"NETWORK : Read from %s failed. Error: %s\n",
GetRemoteIP().ToString().c_str(), PNetworkMgr::FormatWindowsError(nError).c_str());
CleanupSocket();
}
}
else if(nRet == 0)
{
// Graceful close
CleanupSocket();
}
else
{
size_t nReadSize = (size_t)nRet;
if(m_nMaxRecvBufferSize > 0 && m_vRecvBuffer.size() + nReadSize > m_nMaxRecvBufferSize)
nReadSize = m_nMaxRecvBufferSize - m_vRecvBuffer.size();
if(nReadSize > 0)
{
size_t nOldLen = m_vRecvBuffer.size();
m_vRecvBuffer.resize(nOldLen + nReadSize);
memcpy(&m_vRecvBuffer[nOldLen], szBuff, nReadSize);
}
}
} while(nRet == sizeof(szBuff));
}



That code is called when select() says there's data to read.

Share this post


Link to post
Share on other sites
Quote:
Original post by arithma
Are you absolutely sure about passing std::vectors to the Winsock2 DLL?


Where does it say that (memcpy is not part of the WS library)? Or do you want to do that yourself?

Share this post


Link to post
Share on other sites
I just guessed from the .size()...
I was asking "Evil Steve" whether he was using std::vectors as buffers and passing them to Winsock.. that's all

Share this post


Link to post
Share on other sites
As far as I can see, he's not. He's pulling data into a temporary buffer allocated on the stack, and then proceeds by copying it to a UDT using memcpy. It does look like an stl-UDT though, yes.

Share this post


Link to post
Share on other sites
WinSock doesn't care where the buffer is coming from; it just wants an array of bytes to write into. '&vector[0]' is as good as '&stackvar' or 'mallocblock'.

However, the original question was: how do I know how much to read?

The answer is that you should try to read as much as possible in the first recv() call. 256 bytes is typically way too little. Try 65k at a time. When select() returns that there is data, the very next call to recv() is guaranteed to not block, even if the socket is blocking. That's because the specification for recv() is that the call may return a smaller amount of data than requested, if that's what it has to do to avoid blocking. recv() on a blocking socket only blocks if there is no data in the buffer when you call recv().

Btw: instead of using memcpy() to put things at the end of a vector, you can use insert.

Share this post


Link to post
Share on other sites
Yeah, I'm [ab]using the fact that vectors are guaranteed to be contiguous [smile]

Quote:
Original post by hplus0603
The answer is that you should try to read as much as possible in the first recv() call. 256 bytes is typically way too little. Try 65k at a time. When select() returns that there is data, the very next call to recv() is guaranteed to not block, even if the socket is blocking. That's because the specification for recv() is that the call may return a smaller amount of data than requested, if that's what it has to do to avoid blocking. recv() on a blocking socket only blocks if there is no data in the buffer when you call recv().

Btw: instead of using memcpy() to put things at the end of a vector, you can use insert.
Do you really think 65k is nessecary? I can see why you'd want to (To make sure the socket buffer gets emptied ASAP), but 64k is too big to have on the stack isn't it? I suppose it could be a static buffer, or allocated with new.

About the memcpy(), I was aware of insert, but I'm still not comfortable using pointers as iterators for some reason. And I would have thought memcpy() would be faster (Admittedly, probably not with a 256 byte buffer)

Share this post


Link to post
Share on other sites
It's hard to make speed of CPU matter much in networking.
Your DSL modem gives you, what, 160 kB per second?
Modern memory bandwidth is 12 GB/s, for about 5 orders of magnitude difference :-)

std::vector is guaranteed to be contiguous, just so that you can use them as buffers.

Share this post


Link to post
Share on other sites
And, as a plus, pointers are iterators :-). More so, iterators are a generalization of pointers. So, pointers are iterators, but not all iterators are pointers!

Share this post


Link to post
Share on other sites

This topic is 3855 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this