@cebugdev2 You should carefully read the manual page for recv()
and recvfrom()
Specifically, these functions will read whatever is available from the socket, up to the given size, and immediately return if there is any data to return. This means that they may return less than you asked for, or up to the amount you asked for.
The function of the select()
call is to return “readable” if there is at least one byte to read, meaning that recv()
or recvfrom()
will not block.
From this, you can draw the conclusion, that if you make the buffer passed into recv()
as big as the kernel buffer for the socket (which you can set with SO_RCVBUFSIZ
) then you are guaranteed to drain whatever is in the buffer.
This, in turn, means that there are two “plain” ways to poll a number of sockets:
- Set all the sockets to non-blocking mode, and rip through the list of sockets you have open, and call
recv()
for each of them, once per main loop. The drawback here is that you won't have any good way to block until there is more data available, and you will also have to call into the kernel to ask for data even for sockets that don't have data. For “chat” servers and “web” servers and such, the traditional kind of server, this ends up being inefficient, but for game servers that need to simulate 60 times a second and where all clients will typically send data most of the time, it might be a fine way to do it. - Provide all sockets into the
select()
call, and then poll the sockets that select claims have data. This lets you wait until there is data, with a timeout, and it tells you which sockets to read from, so you don't waste CPU cycles entering the kernel to try to receive from sockets that don't have any data. The draw-back is that the select()
system interface is somewhat annoying to work with (it mutates its socket lists/bitmasks) and on Windows, there's an upper limit of 64 sockets that can be polled with select at once. It also doesn't scale to tens of thousands of sockets.
The third way, is to use one of many possible asynchronous mechanisms. Linux got this wrong several times (various flavors of poll) before they managed to get it right with uring. Windows did better with their I/O completion port system, which is still pretty good, but they also have IoRing these days. These APIs matter when your sockets count in the tens of thousands, or when you're trying to saturate a top-of-the-line network interface like 40 or 100 Gbps Ethernet or Fiber Channel or somesuch.
At this point, it's useful to understand the distinction between “user mode” and “kernel mode,” and the difference between “a library call” and “a system call,” and the various costs involved in those mechanisms – the various trade-offs won't make a lot of sense without a solid understanding of these systems programming fundamentals. If you're not there yet, I recommend just going with option 1 or 2, or perhaps using an existing wrapper library (like ASIO) which will be plenty good enough for most indie game systems.