You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
4.1 KiB
129 lines
4.1 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Security;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace Apewer.Network
|
|
{
|
|
|
|
/// <summary>TCP 端口代理。</summary>
|
|
public class TcpProxy
|
|
{
|
|
|
|
IPEndPoint _local;
|
|
Socket _listen;
|
|
Thread _thread;
|
|
int _backlog;
|
|
int _port;
|
|
|
|
Func<IPEndPoint, IPEndPoint> _remote;
|
|
|
|
/// <summary>本地端口已连接。</summary>
|
|
public bool Connected { get => _listen == null ? false : _listen.Connected; }
|
|
|
|
/// <summary>本地已监听的端口。</summary>
|
|
public int Port { get => _port; }
|
|
|
|
/// <summary>监听本地端口以启动代理。</summary>
|
|
/// <param name="local">本地监听端口。</param>
|
|
/// <param name="remote">获取要连接的远程端口,无法获取远程端口时应返回 NULL 值。</param>
|
|
/// <param name="backlog">挂起连接队列的最大长度</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
/// <exception cref="SocketException"></exception>
|
|
/// <exception cref="SecurityException"></exception>
|
|
public TcpProxy(IPEndPoint local, Func<IPEndPoint, IPEndPoint> remote, int backlog = 10000)
|
|
{
|
|
Start(local, remote, backlog);
|
|
}
|
|
|
|
void Start(IPEndPoint local, Func<IPEndPoint, IPEndPoint> remote, int backlog = 10000)
|
|
{
|
|
if (local == null) throw new ArgumentNullException(nameof(local));
|
|
if (remote == null) throw new ArgumentNullException(nameof(remote));
|
|
if (backlog < 1) throw new ArgumentOutOfRangeException(nameof(backlog));
|
|
|
|
_local = local;
|
|
_remote = remote;
|
|
_backlog = backlog;
|
|
|
|
var server = new Socket(_local.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
server.Bind(_local);
|
|
server.Listen(_backlog);
|
|
_listen = server;
|
|
if (server.LocalEndPoint is IPEndPoint lep) _port = lep.Port;
|
|
|
|
_thread = new Thread(Listen);
|
|
_thread.IsBackground = true;
|
|
_thread.Start();
|
|
}
|
|
|
|
/// <summary>停止代理。</summary>
|
|
public void Stop()
|
|
{
|
|
Close(_listen);
|
|
}
|
|
|
|
void Listen()
|
|
{
|
|
while (true)
|
|
{
|
|
if (_listen == null) break;
|
|
Socket socket1 = null;
|
|
IPEndPoint remote = null;
|
|
try
|
|
{
|
|
socket1 = _listen.Accept();
|
|
remote = _remote(socket1.RemoteEndPoint as IPEndPoint);
|
|
}
|
|
catch { }
|
|
if (socket1 == null || remote == null) continue;
|
|
|
|
Socket socket2 = null;
|
|
try
|
|
{
|
|
socket2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
socket2.Connect(remote);
|
|
ThreadPool.QueueUserWorkItem(Handler, new Socket[] { socket1, socket2 });
|
|
ThreadPool.QueueUserWorkItem(Handler, new Socket[] { socket2, socket1 });
|
|
}
|
|
catch
|
|
{
|
|
Close(socket1);
|
|
Close(socket2);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Handler(object obj)
|
|
{
|
|
var tuple = (Socket[])obj;
|
|
var src = tuple[0];
|
|
var dst = tuple[1];
|
|
var buffer = new byte[1024];
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
int count = src.Receive(buffer, buffer.Length, SocketFlags.None);
|
|
if (count < 1) break;
|
|
dst.Send(buffer, count, SocketFlags.None);
|
|
}
|
|
catch { break; }
|
|
}
|
|
Close(src);
|
|
Close(dst);
|
|
}
|
|
|
|
static void Close(Socket socket)
|
|
{
|
|
if (socket == null) return;
|
|
try { socket.Close(); } catch { }
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|