c#用TCP/IP协议打孔NAT网络
Posted
技术标签:
【中文标题】c#用TCP/IP协议打孔NAT网络【英文标题】:c # punch NAT network with TCP / IP protocol 【发布时间】:2020-04-26 11:44:10 【问题描述】:打通 UDP 网络可以正常工作。但是,说到TCP,可能我写错了,但我不是初学者,或者我不明白。
当然,我会缩短一点,我们假设我们已经有了一些微不足道的东西,比如连接到外部服务器 :) 线程之间的数据包同步以及它们的创建、排队、序列化、发送原始字节等等我跳过了,因为它是关于创建 TCP 套接字的元素,而不是什么有效。
我们将为服务器使用 TcpListener 类。
public void InitializeServer(IPAddress address, int port)
try
// 127.0.0.1 accept only local connections, 0.0.0.0 is open for whole internet connections
listener = new TcpListener(address, port);
socket = listener.Server;
// Enable NAT Translation
listener.AllowNatTraversal(true);
// Start listening for example 10 client requests.
listener.Start(listenQueue);
Debug.Log($"[Lsocket.LocalEndPoint]Server start... ", EDebugLvl.Log);
OnServerInitialize(true);
// Enter the listening loop.
StartListener();
catch (SocketException e)
Debug.LogError($"SocketException: e", EDebugLvl.Error);
OnServerInitialize(false);
我们开始倾听
private void StartListener()
Debug.Log("\nWaiting for a connection... ");
listener.BeginAcceptTcpClient(AcceptCallback, listener);
服务器收到连接后,我们创建一个新的socket
private void AcceptCallback(IAsyncResult ar)
TcpListener server = (TcpListener)ar.AsyncState;
TcpClient newClient = null;
try
newClient = server.EndAcceptTcpClient(ar);
catch (Exception e)
Debug.LogError(e.ToString());
if (newClient != null && newClient.Connected)
//...
client.StartRead();
//Loop
StartListener();
我们在客户端新建一个socket并尝试建立连接
public void Connect(IPEndPoint remote, IPEndPoint bind = null, bool reuseAddress = false)
if (bind == null)
client = new TcpClient();
else
client = new TcpClient(bind);
socket = client.Client;
if (reuseAddress)
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress);
//It throws me a error SocketOption so im comment this.
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, reuseAddress);
client.BeginConnect(remote.Address, remote.Port, ConnectCallback, null);
连接正常,数据传输没有任何问题。
不幸的是,正如我们所知,我们必须启动一个新的套接字,监听与连接服务器时创建的地址和端口相同的地址和端口。我为每个客户都这样做。
public void StartHost(Client server)
if (server != null && server.socket.Connected)
IPEndPoint localHost = (IPEndPoint)server.socket.LocalEndPoint;
InitializeHost(localHost.Address, localHost.Port);
public void InitializeHost(IPAddress address, int port, bool reuse = false)
try
listener = new TcpListener(address, port);
socket = listener.Server;
if (reuse)
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, true);
// Enable NAT Translation
listener.AllowNatTraversal(true);
// Start listening for example 10 client requests.
listener.Start(listenQueue);
Debug.Log($"\n[Lsocket.LocalEndPoint]Host start... ", EDebugLvl.Log);
OnServerInitialize(true);
// Enter the listening loop.
StartListener();
catch (SocketException e)
Debug.LogError($"SocketException: e", EDebugLvl.Error);
OnServerInitialize(false);
private void StartListener()
Debug.Log("\nWaiting for a connection... ");
listener.BeginAcceptTcpClient(AcceptCallback, listener);
private void AcceptCallback(IAsyncResult ar)
TcpListener server = (TcpListener)ar.AsyncState;
TcpClient newClient = null;
try
newClient = server.EndAcceptTcpClient(ar);
catch (Exception e)
Console.WriteLine(e.ToString());
if (newClient != null && newClient.Connected)
//...
client.StartRead();
//Loop
StartListener();
所以,当他们到处写...客户端“B”向服务器发送一个数据包想要建立连接时,服务器向客户端“A”发送有关客户端“B”的信息,反之亦然
然后他们都尝试连接新的套接字?没问题...
public void Connect(IPEndPoint remote, IPEndPoint bind = null, bool reuseAddress = false)
if (bind == null)
client = new TcpClient();
else
client = new TcpClient(bind);
socket = client.Client;
if (reuseAddress)
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress);
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, reuseAddress);
client.BeginConnect(remote.Address, remote.Port, ConnectCallback, null);
private void ConnectCallback(IAsyncResult ar)
try
client.EndConnect(ar);
catch (Exception e)
Debug.LogError(e.ToString(), EDebugLvl.ConnectionError);
if (client.Connected)
Debug.Log($"[Psocket.RemoteEndPoint, Lsocket.LocalEndPoint]Connected", EDebugLvl.ConnectionLog);
stream = new NetworkStream(socket, FileAccess.ReadWrite, true);
StartRead();
ConnectedComplete(this, socket.Connected);
无论我尝试多少次,连接都会被拒绝......地址到处都匹配但它不起作用,所以在这种情况下我没有什么可写的,特别是因为 UDP 对我有用。
我写的内容只适用于同一个 NAT 网络。不幸的是,我注意到在同一个 NAT 上创建了两个连接。一种是尝试将新的套接字 A 连接到 B 的结果,另一种是接收到从 B 到 A 的新连接的结果,因此每个客户端都有一个由本地地址连接的不必要的套接字。所以所有的 NAT TCP / IP Punch NAT 都对我不起作用。我实际上可以使用 UDP,但我真的需要 TCP。我在空闲时间已经坐了几个月,但我无法从代码中找到一个例子,而不是有很多理论。 8年积累了很多知识,2年开始用socket写应用,终于要打网了。
为什么我不使用现成的解决方案?我需要我自己的,它只使用 UDP 和 TCP 完全开放,因为一些目标设备只支持这些协议。我也使用了 Socket 类,但是这个没有给我一个工作副本。
也许有人能帮助我,对此我将不胜感激,当然这篇文章也会帮助其他人理解这一点。
问候 屋大维
【问题讨论】:
您假设两个 NAT 将重用相同的外部端口。你肯定知道吗?根据我的经验,这并不常见。一个或两个 NAT 也完全有可能防止打孔。 我知道端口的末端可能会改变 XXXX 1-9 但它并没有随着我改变它仍然是相同的至少我没有注意到任何东西。 90% 的路由器支持打孔,或者至少他们写:D 我假设如果我管理 UDP,那么为什么我不能通过 TCP 写错东西。因为如果它不起作用并且我不能使用 UPnP。那么如何获得稳定的连接呢?好的,我可以使用将接收和发送数据包的网关,但我真的只有一种解决方案可以将 10 个用户连接到 NAT 网络后面的另一台主机? :// 你知道你的服务器看到的端口没有改变,但是你怎么知道你的NAT没有为不同的地址使用不同的端口呢?更多详情请见***.com/questions/2443471/…;您不太可能取得任何成功,这是一种在 90 年代 / 2000 年代初期的某些时候才勉强可行的技术,并且随着 NAT 技术的进步,它变得越来越不可靠。 UPnP 现在得到广泛支持,而且几乎总是更好的选择。 【参考方案1】:NAT 打孔仅适用于 UDP,即使这样也是一种 hack。
NAT 防火墙实现将在看到初始 SYN 数据包离开网络时开始跟踪 TCP 流。要捕获传入的 TCP 流,您需要在路由器上创建传入规则,这是无法解决的。如果路由器支持 UPnP,您可以要求它为您动态创建该规则。
由于 UDP 没有等效的 SYN 数据包,路由器将开始跟踪任何传出数据包的流。这就是 NAT 打孔有效的原因。如果两个端点都在 NAT 之后,并且只是假设链接可以工作。两者都可以开始互相发送UDP数据包。路由器将添加连接状态,并将传入的数据包映射到每个端点。
【讨论】:
以上是关于c#用TCP/IP协议打孔NAT网络的主要内容,如果未能解决你的问题,请参考以下文章