我已经在 C# 中实现了 TCP 打孔。我得到的是,客户端正在相互发送 SYN 消息,但它们被丢弃在对面的路由器上。场景是这样的:Client:A--------Router:A--------Server--------Router:B--------Client :B
如果客户端:A 向客户端:B 发送 SYN 数据包,它会在路由器:B 被丢弃,这是明显的 NAT 行为。但是,根据打孔场景,现在如果客户端:B 向客户端:A 发送 SYN,它应该通过路由器:A 的 NAT。在我的例子中, router:A 也丢弃了这个数据包。在 netstat 中,它将与 B 的连接显示为 SYN_SENT,几秒钟后,我收到一条消息,例如“客户端没有响应。连接失败”
根据我在这方面的研究,放弃的原因可能是无声放弃或主动拒绝。如果是静默丢弃,则应打洞,B 的 SYN 应通过 A 的 NAT。如果是主动拒绝,路由器:A 将收到路由器:B 关于丢弃的通知消息,A 将关闭洞。
问题是我在 A 没有收到任何通知消息。(通过路由器日志检查)。我还尝试了一个从 https://github.com/jasonpang/tcp-holepunching获取的已经实现的 TCP 孔 pucnhing 示例
但我得到了类似的结果。帮助我找出解决方案。我也怀疑路由器打孔的超时。
命名空间 HolePunching { 公共类 HolePunchingClient { 私有 Socket _clientSocket;
private Socket _serverSocket;
private Socket _connectSocket;
//private Socket _serverSocket;
private Socket _holePunchedSocket;
public HolePunchingClient (IPEndPoint localEndPoint)
{
/*For the moment, only TCP Hole Punching is supported*/
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_clientSocket.Bind(localEndPoint);
//_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//_serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
//_serverSocket.Bind(localEndPoint);
}
private void ProcessClientRequest (object sender, DoWorkEventArgs eventArgs)
{
try
{
IPEndPoint iep = (IPEndPoint)eventArgs.Argument;
_connectSocket.Connect(iep);
Console.WriteLine("Connected correctly with: " + iep);
_serverSocket.Close();
_holePunchedSocket = _connectSocket;
Console.WriteLine("\n\n\nException check clientRequest\n\n\n");
}
catch (Exception e)
{
Console.WriteLine("In ProcessClientRequest: " + e.Message);
return;
}
}
private void ProcessServerRequest (object sender, DoWorkEventArgs eventArgs)
{
try
{
_serverSocket.Listen(10);
Socket s = _serverSocket.Accept();
Console.WriteLine("Received connection from: " + s.RemoteEndPoint);
_holePunchedSocket = s;
Console.WriteLine("\n\n\nException check serverRequest\n\n\n");
}
catch (Exception e)
{
Console.WriteLine("In ProcessServerRequest: " + e.Message);
return;
}
}
private void ProcessRequest (object sender, DoWorkEventArgs eventArgs)
{
while (true)
{
byte[] bytes = new byte[2048];
_clientSocket.Receive(bytes);
MessageType messageType = (MessageType) bytes[0];
Console.WriteLine("MessageType received: " + messageType);
switch (messageType)
{
case MessageType.ConnectClient:
byte[] byteAddress = new byte[4];
byte[] bytePort = new byte[2];
Buffer.BlockCopy(bytes, 1, byteAddress, 0, 4);
Buffer.BlockCopy(bytes, 5, bytePort, 0, 2);
IPEndPoint remoteEndPoint = new IPEndPoint(new IPAddress(byteAddress), BitConverter.ToUInt16(bytePort, 0));
Console.WriteLine("HP will be done towards this address: " + remoteEndPoint);
_connectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_connectSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_connectSocket.Bind(_clientSocket.LocalEndPoint);
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_serverSocket.Bind(_clientSocket.LocalEndPoint);
BackgroundWorker bwClient = new BackgroundWorker();
bwClient.DoWork += ProcessClientRequest;
BackgroundWorker bwServer = new BackgroundWorker();
bwServer.DoWork += ProcessServerRequest;
bwClient.RunWorkerAsync(remoteEndPoint);
bwServer.RunWorkerAsync();
break;
}
}
}
public void Connect (IPEndPoint serverEndPoint)
{
_clientSocket.Connect(serverEndPoint);
_clientSocket.Send(BitConverter.GetBytes((byte)MessageType.Register));
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += ProcessRequest;
bw.RunWorkerAsync();
}
public Socket HolePunch (IPAddress otherAddress)
{
byte[] bytes = new byte[5];
Buffer.BlockCopy(BitConverter.GetBytes((byte)MessageType.RequestClient), 0, bytes, 0, 1);
Buffer.BlockCopy(otherAddress.GetAddressBytes(), 0, bytes, 1, 4);
_clientSocket.Send(bytes);
while (_holePunchedSocket == null)
{
System.Threading.Thread.Sleep(1500);
}
return _holePunchedSocket;
}
}
}
这是我从上面的链接中获取的客户端代码。我在连接到客户端 B/A 时收到异常消息“其他客户端没有响应。连接失败”。
客户端 A 和 B 都运行该程序的相同副本。他们中的任何一个都会向服务器发送请求以与另一个客户端连接。服务器将向两者发送 IP-Port 组合。并且客户端代码将通过相互发送连接请求来继续。(最终超时)