C Async Socket Server - BeginReceive in Callback
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In the classic .NET socket asynchronous pattern, it is normal to call BeginReceive again inside the receive callback after you finish processing the bytes from the previous receive. The crucial rule is that every asynchronous receive must be completed with EndReceive, and a return value of 0 means the remote side has closed the connection.
The basic APM pattern
The older socket API uses the Asynchronous Programming Model, often called APM:
- '
BeginAccept/EndAccept' - '
BeginReceive/EndReceive' - '
BeginSend/EndSend'
A receive loop in this model is callback-driven. You begin one receive, the callback fires, you process data, and then you start the next receive.
That is why calling BeginReceive in the callback is not a bug by itself. It is the intended continuation pattern.
Example pattern
That is the normal receive loop shape in the old API.
Why EndReceive is mandatory
You must call EndReceive inside the callback for the matching BeginReceive. That call:
- completes the asynchronous operation
- reports how many bytes arrived
- surfaces socket errors
Skipping EndReceive breaks the APM contract.
Handling partial reads
TCP is a stream, not a message protocol. One BeginReceive callback does not guarantee that a full logical message arrived.
That means production code often needs:
- a per-connection buffer
- framing logic such as length prefix or delimiter parsing
- accumulation across multiple callbacks
So the callback loop is only the transport layer. Message reconstruction is a separate problem.
Do not treat callback chaining as recursion panic
Developers sometimes worry that calling BeginReceive inside ReceiveCallback causes dangerous recursion. In the ordinary APM model, this is not synchronous recursion in the usual stack-growth sense. You are scheduling the next async receive, not directly re-entering the method immediately as a normal function call.
So the pattern itself is correct.
Error handling and disconnects
A robust callback should handle:
Those are not optional details. In long-running servers, connection churn and partial failures are the normal case, not the exception.
- '
bytesRead == 0as remote disconnect' - socket exceptions
- object disposal and shutdown races
For example, if the client disconnects abruptly, the next receive cycle may fail and should close the socket cleanly.
Modern note
For new .NET code, SocketAsyncEventArgs or async/await-style APIs are usually easier to maintain. But if you are working in the BeginReceive / EndReceive world, the callback-loop pattern is still the right mental model.
Understanding that older model is still useful because a lot of production socket code was written before the newer async abstractions became standard.
Common Pitfalls
A common mistake is forgetting to call EndReceive.
Another mistake is assuming each receive callback contains a complete application message.
A third mistake is not handling bytesRead == 0, which means the peer has closed the connection.
Summary
- Calling
BeginReceiveagain inside the callback is normal in the APM socket model. - Always complete each async receive with
EndReceive. - Treat
0bytes as a closed connection. - Build message framing separately from the raw receive loop.
- Prefer newer async APIs for new code, but use the callback loop correctly in legacy code.

