【发布时间】: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($"[L{socket.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[L{socket.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($"[P{socket.RemoteEndPoint}, L{socket.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没有为不同的地址使用不同的端口呢?更多详情请见stackoverflow.com/questions/2443471/…;您不太可能取得任何成功,这是一种在 90 年代 / 2000 年代初期的某些时候才勉强可行的技术,并且随着 NAT 技术的进步,它变得越来越不可靠。 UPnP 现在得到广泛支持,而且几乎总是更好的选择。
标签: c# tcp nat hole-punching