Нет подтверждения от клиента в дыроколе TCP
Я реализовал TCP дырокол в C#. Я получаю то, что клиенты посылают друг другу сообщения SYN, но они отбрасываются на противоположных маршрутизаторах. Сценарий выглядит примерно так: Клиент:A-------- Маршрутизатор:A-------- Сервер -------- Маршрутизатор:B-------- Клиент: B
Если клиент: A отправляет пакет SYN клиенту:B, он отбрасывается на маршрутизаторе:B, что является очевидным поведением NAT. Но, согласно сценариям дырокола, теперь, если клиент: B отправляет SYN клиенту:A, он должен проходить через NAT на маршрутизаторе:A. Вот в моём случае роутер: А тоже отбрасывает этот пакет. В netstat он показывает соединение с B как SYN_SENT, и через пару секунд я получаю сообщение типа "Клиент не отвечает. Ошибка подключения"
Согласно моим исследованиям, причиной падения может быть тихое падение или активное отклонение. Если это бесшумное отбрасывание, то дыра должна быть пробита, и SYN от B должен быть передан через NAT в A. И если это активное отклонение, маршрутизатор: A получит уведомление от маршрутизатора: B о сбрасывании и A закроется дыра.
Проблема в том, что я не получаю никаких уведомлений в A. (проверено через журнал роутера). Я также попытался использовать уже реализованный пример TCP-дырки, полученный по https://github.com/jasonpang/tcp-holepunching
Но я получаю аналогичные результаты. Помогите мне найти решение для этого. Я также сомневаюсь в тайм-ауте для пробитых отверстий на маршрутизаторах.
пространство имен HolePunching {открытый класс HolePunchingClient {private 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-портов обоим. и код клиента будет продолжен, посылая друг другу запрос на соединение (который в итоге истекает)