gpt4 book ai didi

c# - 使用异步套接字的 TCP 端口转发

转载 作者:可可西里 更新时间:2023-11-01 02:33:05 25 4
gpt4 key购买 nike

我正在尝试在 C# 中实现 TCP 转发器。具体应用:

  1. 监听一个TCP端口并等待客户端,
  2. 当客户端连接时,连接到远程主机,
  3. 在两个连接上等待传入数据并在两个端点之间交换数据(充当代理),
  4. 在另一个连接被端点关闭时关闭一个连接。

我已经改编了Simple TCP Forwader (加西亚)转发一系列端口,以便

TCPForwarder.exe 10.1.1.1 192.168.1.100 1000 1100 2000

将在 10.1.1.1 端口 1000-1100 上接收到的任何数据包转发到远程主机 192.168.1.100 端口 2000-2100。我已经使用它来公开 NAT 后面的 FTP 服务器。

通过运行上述命令,客户端能够连接到 FTP 服务器,并在预期的控制台中输出以下模式(请参阅代码):

0 StartReceive: BeginReceive
1 StartReceive: BeginReceive
1 OnDataReceive: EndReceive
1 OnDataReceive: BeginReceive
1 OnDataReceive: EndReceive
1 OnDataReceive: Close (0 read)
0 OnDataReceive: EndReceive
0 OnDataReceive: Close (exception)

但是在多次成功连接后(在 Filezilla 中按 F5),没有从 TCPForwarder(和 FTP 服务器)收到进一步的响应。

我的实现似乎有两个无法调试的问题:

  1. 在这种情况下,StartReceive 方法中的BeginReceive 被调用,但没有从 FTP 服务器接收到数据。我不认为这可能是 FTP 服务器问题(它是 ProFTPD 服务器),因为它是众所周知的 FTP 服务器。

  2. 每次建立和关闭连接时,线程数都会增加 1。我认为垃圾回收无法解决这个问题。线程数一直在增加,强制垃圾收集器运行也不会减少。我认为我的代码中存在一些泄漏,这也导致了问题 #1。

编辑:

  • 重新启动 FTP 服务器并没有解决问题,因此 TCPForwarder 中肯定存在错误。

  • @jgauffin 指出的一些问题已在以下代码中修复。

完整代码如下:

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

namespace TCPForwarder
{
class Program
{
private class State
{
public int ID { get; private set; } // for debugging purposes
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(int id, Socket source, Socket destination)
{
ID = id;
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}

public class TcpForwarder
{
public void Start(IPEndPoint local, IPEndPoint remote)
{
Socket MainSocket;
try
{
MainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
MainSocket.Bind(local);
MainSocket.Listen(10);
}
catch (Exception exp)
{
Console.WriteLine("Error on listening to " + local.Port + ": " + exp.Message);
return;
}

while (true)
{
// Accept a new client
var socketSrc = MainSocket.Accept();
var socketDest = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

try
{
// Connect to the endpoint
socketDest.Connect(remote);
}
catch
{
socketSrc.Shutdown(SocketShutdown.Both);
socketSrc.Close();
Console.WriteLine("Exception in connecting to remote host");
continue;
}

// Wait for data sent from client and forward it to the endpoint
StartReceive(0, socketSrc, socketDest);

// Also, wait for data sent from endpoint and forward it to the client
StartReceive(1, socketDest, socketSrc);
}
}

private static void StartReceive(int id, Socket src, Socket dest)
{
var state = new State(id, src, dest);

Console.WriteLine("{0} StartReceive: BeginReceive", id);
try
{
src.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
catch
{
Console.WriteLine("{0} Exception in StartReceive: BeginReceive", id);
}
}

private static void OnDataReceive(IAsyncResult result)
{
State state = null;
try
{
state = (State)result.AsyncState;

Console.WriteLine("{0} OnDataReceive: EndReceive", state.ID);
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);

Console.WriteLine("{0} OnDataReceive: BeginReceive", state.ID);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
else
{
Console.WriteLine("{0} OnDataReceive: Close (0 read)", state.ID);
state.SourceSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
catch
{
if (state!=null)
{
Console.WriteLine("{0} OnDataReceive: Close (exception)", state.ID);
state.SourceSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
}
}

static void Main(string[] args)
{
List<Socket> sockets = new List<Socket>();

int srcPortStart = int.Parse(args[2]);
int srcPortEnd = int.Parse(args[3]);
int destPortStart = int.Parse(args[4]);

List<Thread> threads = new List<Thread>();
for (int i = 0; i < srcPortEnd - srcPortStart + 1; i++)
{
int srcPort = srcPortStart + i;
int destPort = destPortStart + i;

TcpForwarder tcpForwarder = new TcpForwarder();

Thread t = new Thread(new ThreadStart(() => tcpForwarder.Start(
new IPEndPoint(IPAddress.Parse(args[0]), srcPort),
new IPEndPoint(IPAddress.Parse(args[1]), destPort))));
t.Start();

threads.Add(t);
}

foreach (var t in threads)
{
t.Join();
}
Console.WriteLine("All threads are closed");
}
}
}

最佳答案

第一个问题是代码将在目标套接字(接受循环)上的连接失败时继续。在 try/catch 中使用 continue;。当您调用第一个 BeginReceive 时,也无法保证套接字仍然可用。这些调用也需要包装。

始终将回调方法包装在 try/catch 中,否则您的应用程序可能会失败(在本例中为 OnDataRecieve)。

解决这个问题并开始写出异常。他们肯定会提示您哪里出了问题。

关于c# - 使用异步套接字的 TCP 端口转发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32347257/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com