My suggestion would be to use I/O ports in conjunction with overlapped I/O. You can queue buffers for sending and receiving using WSASend/WSARecv, then when the operation completes a
completion packet is sent to the completion port, which can be obtained using
GetQueuedCompletionStatus.
Quote: "-When sending using the WSASend command, the buffer supplied is cleared when the actual send has occurred"
No, the buffer is never cleared. You use the
GetOverlappedResult function to determine when an overlapped operation completes. The pages on
WSASend and
WSARecv detail this, so read them carefully.
Quote: "I don't understand the purpose of threads. What are their advantages and when should they be used?"
Threads allow you to asynchronously perform multiple operations at the same time. I would personally say they are most important because they allow you to use blocking, which is when a function waits for an event to occur. Generally you should never constantly poll on a function when waiting for something to occur, as this is a huge waste of CPU time that can be spent doing something else. This is why blocking strategies are so much more efficient. If you use overlapped I/O with completion ports, you can have a single thread block on GetQueuedCompletionStatus and handle every operation that completes (or fails for that matter), while you fire off requests in other threads. While there is no activity, this thread uses zero CPU time.
Quote: "Is 'Overlapped I/O' only useful when dealing with multiple sockets as we are doing with TCP?"
Yes. Using overlapped I/O for a single socket is overkill. In most cases simply using a non-blocking socket is sufficient. Also, you can do overlapped I/O on both TCP and UDP sockets. It still makes sense to use it on UDP sockets because the requirements are still the same - you want to be able to queue multiple sends, and generally you'll definitely want to queue multiple recvs as the volume of activity on that socket will be quite high.
Before you get into this, you should probably research some more. I'm sure there is still a lot you need to understand about using sockets, and trying to learn these things while trying to code something so complex will be quite a headache. I'll give you a couple of examples...
1. Due to the fact TCP is a streaming protocol, when a block of data is sent it isn't guaranteed to arrive all in one piece at the other end. What I mean by this is, the second part of the data may be received at a later time than the first. In fact the minimum amount of data you are guaranteed to receive in one read is a single byte. When coding a message processing algorithm you must remember this. If you use a prefix of two bytes or more for messages to denote the size, never rely on the entire prefix to be received at one time. A partial receive may only occur rarely but in order to code a robust application you must respect this.
2. Handling data that can't be sent at the current time. The default socket buffer size is 8KB. If you issue sends that add up to 8KB, then until the endpoint receives this data any more sends will fail. This is an important consideration.