Socket关闭旧连接后接受新连接时ReceiveAsync抛出‘SocketAsyncEventArgs实例已存在异步操作’异常的问题求助
Let's break down exactly what's going wrong with your TCP echo server, then walk through step-by-step fixes to get it working reliably.
The Root Cause
Your code hits this error because you're reusing the same SocketAsyncEventArgs instance for multiple asynchronous operations—a strict no-no in .NET's SocketAsync model. Here's the play-by-play:
- When the first client connects, you create one
readEventArgsinAcceptCallbackand loop trying to callReceiveAsyncwith it. - When that client disconnects, you call
accept()to wait for a new connection—but the original infinitewhileloop inAcceptCallbackkeeps running, still trying to use that samereadEventArgsinstance. - When a new client connects, the old
readEventArgsmight still be tied to the dead client socket, or might still have an unfinished asynchronous operation in flight. Trying to reuse it triggers the exception.
On top of that, your loop logic for accepting connections and receiving data is broken:
- The
while(true)inaccept()is unnecessary and blocks the thread from handling async events properly. - The infinite loop in
AcceptCallbackkeeps trying to receive even after the client has disconnected.
Step-by-Step Fixes
Let's rewrite the problematic parts to follow async socket best practices.
1. Fix the accept() Method (Remove the Dead Loop)
AcceptAsync is designed to be asynchronous—you don't need to loop indefinitely. Instead, after handling a new connection, just re-initiate AcceptAsync to wait for the next one:
void accept() { SocketAsyncEventArgs e = new SocketAsyncEventArgs(); e.Completed += e_Completed; // Initiate async accept; if it completes synchronously, handle immediately bool raiseEvent = serverSocket.AcceptAsync(e); if (!raiseEvent) { AcceptCallback(e); } }
2. Use Independent SocketAsyncEventArgs Per Client
Never reuse an SocketAsyncEventArgs instance across multiple operations or clients. Create a new one for each client's receive/send operations, and attach the client socket to the UserToken property to track it in callbacks:
private void AcceptCallback(SocketAsyncEventArgs e) { // Immediately start waiting for the next connection (supports multiple concurrent clients) accept(); Socket clientSocket = e.AcceptSocket; if (clientSocket == null) return; // Create a unique args instance for this client's receive operations SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs(); byte[] buffer = new byte[1024]; readEventArgs.SetBuffer(buffer, 0, buffer.Length); readEventArgs.UserToken = clientSocket; // Attach client socket for later use readEventArgs.Completed += readEventArgs_Completed; // Start first receive operation bool raiseEvent = clientSocket.ReceiveAsync(readEventArgs); if (!raiseEvent) { ReceiveCallback(readEventArgs); } }
3. Update ReceiveCallback to Handle Echo and Cleanup
When data is received, send it back asynchronously, and when the client disconnects, clean up resources properly:
private void ReceiveCallback(SocketAsyncEventArgs e) { Socket clientSocket = e.UserToken as Socket; if (clientSocket == null) return; try { if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) { // Create args for the echo send operation SocketAsyncEventArgs sendEventArgs = new SocketAsyncEventArgs(); sendEventArgs.SetBuffer(e.Buffer, 0, e.BytesTransferred); sendEventArgs.UserToken = clientSocket; sendEventArgs.Completed += sendEventArgs_Completed; bool raiseEvent = clientSocket.SendAsync(sendEventArgs); if (!raiseEvent) { SendCallback(sendEventArgs); } } else { // Client disconnected; clean up resources clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); e.Dispose(); } } catch (Exception ex) { Console.WriteLine($"Receive error: {ex.Message}"); clientSocket?.Close(); e.Dispose(); } }
4. Implement SendCallback to Resume Receiving
After sending the echo, initiate a new receive operation so the client can send more data:
void sendEventArgs_Completed(object sender, SocketAsyncEventArgs e) { SendCallback(e); } private void SendCallback(SocketAsyncEventArgs e) { Socket clientSocket = e.UserToken as Socket; if (clientSocket == null) return; try { if (e.SocketError == SocketError.Success) { // Prepare for next receive SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs(); byte[] buffer = new byte[1024]; readEventArgs.SetBuffer(buffer, 0, buffer.Length); readEventArgs.UserToken = clientSocket; readEventArgs.Completed += readEventArgs_Completed; bool raiseEvent = clientSocket.ReceiveAsync(readEventArgs); if (!raiseEvent) { ReceiveCallback(readEventArgs); } } else { // Send failed; close the connection clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); } e.Dispose(); } catch (Exception ex) { Console.WriteLine($"Send error: {ex.Message}"); clientSocket?.Close(); e.Dispose(); } }
5. Remove the Unnecessary ManualResetEvent
The allDone event was used to sync the old infinite loop, but now we're using async callbacks to drive the flow—you can safely delete it from your code.
Final Notes
With these changes, your server will:
- Handle multiple concurrent clients correctly
- Allow clients to disconnect and reconnect without throwing errors
- Properly clean up resources when clients disconnect
内容的提问来源于stack exchange,提问作者Ristea Stefan

