因为IPV4的地址很紧张。但很多时候确定有要访问内网的需求。所以frp就是以内网穿透而出名。虽然现在多了很多关于异地组网的项目和产品,但frp依然没有被替代。现在让GPT 生成一个c# 版的,亲测包可直接食用。
使用场景:你的公网机器VPS 为A,跑服务端代码。被访问的机器B跑客户端代码。 C为任意一台客户端使用mstsc 访问A的8848端口,实现访问B的3389远程桌面。
- 服务端代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace ProxyServerApp
{
public class ReverseProxyServer
{
private readonly int _listenPort;
private TcpClient _connectedClient;
private TcpListener _listener;
public ReverseProxyServer(int listenPort)
{
_listenPort = listenPort;
}
public async Task StartAsync()
{
// 使用同一个TcpListener实例来监听客户端和C机器的连接
_listener = new TcpListener(IPAddress.Any, _listenPort);
_listener.Start();
Console.WriteLine($"Server listening on port {_listenPort}.");
// 开启监听客户端连接的任务
_ = AcceptClientConnectionAsync();
while (true)
{
// 接收来自 C 机器的连接
var clientSocket = await _listener.AcceptSocketAsync();
Console.WriteLine("Accepted connection from C.");
if (_connectedClient == null)
{
Console.WriteLine("No client connected from B.");
clientSocket.Close();
continue;
}
// 通过连接的 B 客户端转发流量
var clientStream = _connectedClient.GetStream();
var serverStream = new NetworkStream(clientSocket);
await Task.WhenAny(
TransferDataAsync(serverStream, clientStream),
TransferDataAsync(clientStream, serverStream)
);
}
}
private async Task AcceptClientConnectionAsync()
{
while (true)
{
// 等待 B 客户端连接
_connectedClient = await _listener.AcceptTcpClientAsync();
Console.WriteLine("Client from B connected.");
}
}
private async Task TransferDataAsync(NetworkStream source, NetworkStream destination)
{
var buffer = new byte[8192];
try
{
int bytesRead;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await destination.WriteAsync(buffer, 0, bytesRead);
}
}
catch (IOException ex) when (ex.InnerException is SocketException socketEx &&
socketEx.SocketErrorCode == SocketError.ConnectionReset)
{
Console.WriteLine("Connection reset by peer.");
}
catch (Exception ex)
{
Console.WriteLine($"Data transfer error: {ex.Message}");
}
finally
{
source.Close();
destination.Close();
}
}
}
public class Program
{
public static async Task Main(string[] args)
{
int listenPort = 8848; // A 机器上的监听端口
var server = new ReverseProxyServer(listenPort);
await server.StartAsync();
}
}
}
- 客户端代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace ProxyClientApp
{
public class PersistentClient
{
private readonly string _serverIp;
private readonly int _serverPort;
private readonly int _localPort;
public PersistentClient(string serverIp, int serverPort, int localPort)
{
_serverIp = serverIp;
_serverPort = serverPort;
_localPort = localPort;
}
public async Task StartAsync()
{
while (true)
{
try
{
using (var clientSocket = new TcpClient())
{
Console.WriteLine("Connecting to server...");
await clientSocket.ConnectAsync(_serverIp, _serverPort);
Console.WriteLine("Connected to server. Tunneling traffic...");
var serverStream = clientSocket.GetStream();
// 打开本地端口的远程桌面服务连接
using (var localSocket = new TcpClient("127.0.0.1", _localPort))
{
var localStream = localSocket.GetStream();
// 双向数据传输
await Task.WhenAny(
TransferDataAsync(serverStream, localStream),
TransferDataAsync(localStream, serverStream)
);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Connection error: {ex.Message}. Retrying...");
await Task.Delay(5000); // 连接失败时的重试延迟
}
}
}
private async Task TransferDataAsync(NetworkStream source, NetworkStream destination)
{
var buffer = new byte[8192];
try
{
int bytesRead;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await destination.WriteAsync(buffer, 0, bytesRead);
}
}
catch (IOException ex) when (ex.InnerException is SocketException socketEx &&
socketEx.SocketErrorCode == SocketError.ConnectionReset)
{
Console.WriteLine("Connection reset by peer.");
}
catch (Exception ex)
{
Console.WriteLine($"Data transfer error: {ex.Message}");
}
finally
{
source.Close();
destination.Close();
}
}
}
public class Program
{
public static async Task Main(string[] args)
{
string serverIp = "VPS公网IP"; // A 机器 IP
int serverPort = 8848; // A 机器上的监听端口
int localPort = 3389; // B 机器上远程桌面的端口
var client = new PersistentClient(serverIp, serverPort, localPort);
await client.StartAsync();
}
}
}
运行日志
客户端
服务端