You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NAT后客户端的TCP主动传输实现:求带源码的示例项目

Got it, let’s break this down and give you a working code example since you already have the UDP caching part sorted. The core trick here is using NAT hole punching to let your server initiate TCP connections to clients behind NAT—here’s a complete, tested setup in C# that you can adapt to your language of choice:

Server Implementation

This server listens for UDP handshakes to cache client NAT-mapped endpoints, then periodically initiates TCP connections to send files.

using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Threading.Tasks;

class TcpNATServer
{
    // Cache to store client IDs and their NAT-mapped IPEndPoints
    private static Dictionary<string, IPEndPoint> _clientEndpoints = new Dictionary<string, IPEndPoint>();
    private static UdpClient _udpHandshakeListener;

    static async Task Main(string[] args)
    {
        // Start UDP listener to capture client NAT mappings
        _udpHandshakeListener = new UdpClient(12345);
        _ = Task.Run(ListenForClientHandshakes);

        Console.WriteLine("Server running. Waiting for client handshakes...");

        // Loop to periodically send files to cached clients
        while (true)
        {
            await Task.Delay(60000); // Send every minute (adjust as needed)
            await PushFilesToClients();
        }
    }

    private static async Task ListenForClientHandshakes()
    {
        while (true)
        {
            var handshakeResult = await _udpHandshakeListener.ReceiveAsync();
            string clientId = System.Text.Encoding.ASCII.GetString(handshakeResult.Buffer);
            
            // Update or add the client's NAT-mapped endpoint to cache
            if (_clientEndpoints.ContainsKey(clientId))
                _clientEndpoints[clientId] = handshakeResult.RemoteEndPoint;
            else
                _clientEndpoints.Add(clientId, handshakeResult.RemoteEndPoint);

            Console.WriteLine($"Cached endpoint for client {clientId}: {handshakeResult.RemoteEndPoint}");
        }
    }

    private static async Task PushFilesToClients()
    {
        // Iterate over a copy of the cache to avoid modification during loop
        foreach (var clientEntry in new Dictionary<string, IPEndPoint>(_clientEndpoints))
        {
            string clientId = clientEntry.Key;
            IPEndPoint clientEndpoint = clientEntry.Value;

            try
            {
                // Initiate TCP connection to the client's NAT-mapped address
                using (TcpClient tcpClient = new TcpClient())
                {
                    await tcpClient.ConnectAsync(clientEndpoint.Address, clientEndpoint.Port);
                    Console.WriteLine($"Established TCP connection to client {clientId}");

                    // Send sample file content (replace with actual file read logic)
                    string fileData = "Sample file content from server - " + DateTime.Now.ToString();
                    byte[] dataBytes = System.Text.Encoding.UTF8.GetBytes(fileData);
                    NetworkStream stream = tcpClient.GetStream();
                    await stream.WriteAsync(dataBytes, 0, dataBytes.Length);

                    Console.WriteLine($"Sent file to client {clientId}");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Failed to send to client {clientId}: {ex.Message}");
                // Remove stale endpoints that can't be reached
                _clientEndpoints.Remove(clientId);
            }
        }
    }
}
Client Implementation

The client sends a UDP handshake to punch a hole in its NAT, then listens for incoming TCP connections from the server.

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

class TcpNATClient
{
    static async Task Main(string[] args)
    {
        string clientId = "Client_" + Guid.NewGuid().ToString().Substring(0, 8);
        IPAddress serverPublicIp = IPAddress.Parse("YOUR_SERVER_PUBLIC_IP"); // Replace with your server's public IP
        int serverUdpPort = 12345;

        // Send UDP handshake to server to create NAT mapping
        using (UdpClient udpClient = new UdpClient())
        {
            byte[] clientIdBytes = System.Text.Encoding.ASCII.GetBytes(clientId);
            await udpClient.SendAsync(clientIdBytes, clientIdBytes.Length, new IPEndPoint(serverPublicIp, serverUdpPort));
            Console.WriteLine($"Sent handshake to server. Client ID: {clientId}");
        }

        // Start TCP listener on a random port (NAT will map this to a public port)
        TcpListener tcpListener = new TcpListener(IPAddress.Any, 0);
        tcpListener.Start();
        Console.WriteLine("Client TCP listener started. Waiting for server connection...");

        while (true)
        {
            using (TcpClient incomingConnection = await tcpListener.AcceptTcpClientAsync())
            {
                NetworkStream stream = incomingConnection.GetStream();
                byte[] buffer = new byte[4096];
                int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                string receivedFile = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead);
                
                Console.WriteLine("\nReceived file from server:");
                Console.WriteLine(receivedFile);
            }
        }
    }
}

Key Notes to Make This Work:

  • NAT Hole Punching: The UDP handshake tells the client's NAT device to allow incoming TCP traffic from the server's IP. This works for most home/corporate cone NATs (the most common type).
  • Cache Management: The server removes stale endpoints if connections fail—this handles cases where a client's NAT mapping changes (common with dynamic NAT).
  • Firewall Rules: Ensure your server allows incoming UDP (port 12345) and outgoing TCP traffic. Clients don't need inbound firewall rules since the NAT hole allows the server's connection.

内容的提问来源于stack exchange,提问作者jbbarnes77

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:37:40