Implementation and Analysis of Sending and Receiving Data on the Same UDP Socket

Nov 23, 2025 · Programming · 7 views · 7.8

Keywords: UDP Protocol | C# Network Programming | Socket Communication

Abstract: This article provides an in-depth exploration of implementing client-server communication using UDP protocol in C#, focusing on the technical challenges of sending and receiving data on the same socket. Through analysis of a typical communication exception case, it reveals the root cause of the "An existing connection was forcibly closed by the remote host" error when UDP clients attempt to receive data after establishing connection. The paper thoroughly explains how UDP's connectionless nature affects communication patterns, the mechanism requiring servers to explicitly specify target endpoints for proper response delivery, and solutions for port conflicts in local testing environments. By reconstructing code examples, it demonstrates correct implementation of UDP request-response patterns, offering practical guidance for developing reliable UDP-based communication protocols.

UDP Communication Fundamentals and Problem Context

The User Datagram Protocol (UDP) serves as a connectionless transport layer protocol with distinct advantages in network programming. Unlike TCP, UDP does not guarantee packet ordering, reliability, or duplicate detection, making it more efficient in certain scenarios. When developing request-response protocols based on UDP, developers frequently encounter a common issue: clients failing to successfully receive server responses from the same socket after sending data.

Problem Manifestation and Root Cause Analysis

In the initial client code implementation, the developer created a UdpClient instance and connected to the server endpoint:

var client = new UdpClient();
IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000);
client.Connect(ep);
client.Send(new byte[] { 1, 2, 3, 4, 5 }, 5);
var receivedData = client.Receive(ref ep); // Exception: An existing connection was forcibly closed by the remote host

The server-side code also contained issues:

UdpClient udpServer = new UdpClient(UDP_LISTEN_PORT);
while (true)
{
    var groupEP = new IPEndPoint(IPAddress.Any, 11000);
    var data = udpServer.Receive(ref groupEP);
    udpServer.Send(new byte[] { 1 }, 1); // No target endpoint specified

The core issue stems from UDP's connectionless nature. When a client calls the Connect method, the socket becomes bound to a specific remote endpoint, and all send operations are automatically directed to that endpoint. However, on the server side, the Receive method reassigns the endpoint parameter to the actual data source address, but the subsequent Send call fails to explicitly specify the destination endpoint, preventing the server from properly replying to the client.

Solution and Code Refactoring

The correct implementation requires ensuring the server explicitly specifies the client endpoint when replying. Here is the corrected server code:

UdpClient udpServer = new UdpClient(11000);
while (true)
{
    var remoteEP = new IPEndPoint(IPAddress.Any, 11000);
    var data = udpServer.Receive(ref remoteEP);
    Console.Write("receive data from " + remoteEP.ToString());
    udpServer.Send(new byte[] { 1 }, 1, remoteEP); // Explicitly specify reply endpoint

The client code remains unchanged but requires understanding of its operational principles:

var client = new UdpClient();
IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000);
client.Connect(ep);
client.Send(new byte[] { 1, 2, 3, 4, 5 }, 5);
var receivedData = client.Receive(ref ep);
Console.Write("receive data from " + ep.ToString());

Considerations for Local Testing Environments

When clients and servers run on the same machine, special attention must be paid to port configuration. Although UDP allows multiple processes to bind to the same port (via SO_REUSEADDR), conflicts may still occur in some systems. It is recommended to use different ports for testing during development or ensure proper socket configuration.

Protocol Selection: UDP vs TCP Considerations

The user mentioned that the primary reason for choosing UDP over TCP is the convenience of NAT traversal. UDP hole punching is indeed simpler and more reliable than TCP hole punching, which is particularly important in P2P applications and real-time communication systems. However, developers must handle packet loss, duplication, and reordering issues themselves, or implement necessary reliability mechanisms at higher protocol layers.

Best Practices and Performance Optimization

When implementing UDP request-response patterns, the following best practices are recommended: set reasonable timeout mechanisms to prevent infinite waiting, implement simple sequence number mechanisms to detect duplicate requests, and consider using asynchronous operations to improve concurrency performance. For high-concurrency scenarios, BeginReceive/EndReceive or task-based asynchronous patterns can be utilized.

Conclusion

By correctly understanding UDP's connectionless nature and endpoint management mechanisms, developers can build stable and reliable request-response communication systems. The key is to ensure servers explicitly specify client endpoint addresses when replying and properly handle port configuration issues in local testing environments. This pattern provides a flexible and efficient communication foundation for various network applications, demonstrating unique advantages particularly in scenarios requiring NAT traversal.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.