Я много читал о том, как реализовать перфорирование отверстий UDP, но по какой-то причине я не могу заставить его работать.
Для тех, кто не знаком с тем, что здесь происходит с отверстием дыры udp, является мое собственное определение:
Цель состоит в том, чтобы иметь возможность передавать данные между двумя клиентами (Клиент А и клиент B) с помощью сервера. Таким образом, клиент A подключается к серверу и отправляет его информацию. Клиент B делает то же самое. Сервер имеет необходимую информацию, чтобы клиент A мог отправлять данные клиенту B и наоборот. Поэтому сервер предоставляет эту информацию обоим клиентам. Как только у обоих клиентов есть эта информация друг о друге, можно начать отправку и получение данных между этими клиентами без помощи сервера.
Моя цель состоит в том, чтобы иметь возможность делать то, что я только что описал (ударопробивание отверстий). Прежде чем это сделать, я думаю, что будет полезно подключиться с сервера к клиенту. Для этого я планирую отправить серверу информацию о клиенте. Как только сервер получит эту информацию, попытайтесь подключиться к клиенту с нуля. Как только я смогу выполнить это, у меня должно получиться все, что мне нужно, чтобы начать реалистичную перфорирование дыры.
Вот как я создал вещи:
Верхний маршрутизатор имеет сервер и нижний маршрутизатор, подключенные к портам локальной сети. Нижний маршрутизатор (NAT) подключается к верхнему маршрутизатору через порт WAN. И клиентский компьютер подключается к нижнему маршрутизатору к одному из его LAN-портов.
Таким образом, в этом случае клиент может видеть сервер, но сервер не может видеть клиента.
Итак, алгоритм, который я сделал в псевдокоде:
- Клиент подключается к серверу.
- Клиент отправляет на сервер некоторые пакеты UDP, чтобы открыть некоторые порты в NAT
- Отправлять на сервер информацию о том, какие порты клиент прослушивает.
- Как только сервер получит эту информацию, попытайтесь подключиться к клиенту с нуля.
Вот реализация в коде:
Сервер:
static void Main()
{
/* Part 1 receive data from client */
UdpClient listener = new UdpClient(11000);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 11000);
string received_data;
byte[] receive_byte_array = listener.Receive(ref groupEP);
received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
// get info
var ip = groupEP.Address.ToString();
var port = groupEP.Port;
/* Part 2 atempt to connect to client from scratch */
// now atempt to send data to client from scratch once we have the info
Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint endPointClient = new IPEndPoint(IPAddress.Parse(ip), port);
sendSocket.SendTo(Encoding.ASCII.GetBytes("Hello"), endPointClient);
}
Клиент:
static void Main(string[] args)
{
/* Part 1 send info to server */
Socket sending_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress send_to_address = IPAddress.Parse("192.168.0.132");
IPEndPoint sending_end_point = new IPEndPoint(send_to_address, 11000);
sending_socket.SendTo(Encoding.ASCII.GetBytes("Test"), sending_end_point);
// get info
var port = sending_socket.LocalEndPoint.ToString().Split(':')[1];
/* Part 2 receive data from server */
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, int.Parse(port));
byte[] buffer = new byte[1024];
sending_socket.Receive(buffer);
}
По какой-то причине он работал несколько раз! Он работает, когда клиент успешно получает данные на линии: sending_socket.Receive(buffer);
Примечания:
Если на сервере во второй части я использовал переменную экземпляра listner
вместо создания новой переменной sendSocket
и отправил байты через эту переменную, клиент сможет принимать отправленные данные. Помните, что вторая часть сервера будет реализована вторым клиентом B, поэтому я снова инициализирую переменные с нуля...
Изменить:
Вот другой способ взглянуть на ту же проблему. Когда я инициализирую новый объект вместо того, чтобы использовать тот же объект, клиент не получает ответ.
У меня есть объект типа UdpClient. Я могу отправить данные с этим объектом другому партнеру. Если я создаю другой объект того же типа с теми же свойствами и попытаюсь отправить данные, это не сработает! Мне может быть недостает инициализации некоторых переменных. Я могу установить частные переменные с отражением, поэтому у меня не должно быть проблем. в любом случае здесь находится код сервера:
public static void Main()
{
// wait for client to send data
UdpClient listener = new UdpClient(11000);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 11000);
byte[] receive_byte_array = listener.Receive(ref groupEP);
// connect so that we are able to send data back
listener.Connect(groupEP);
byte[] dataToSend = new byte[] { 1, 2, 3, 4, 5 };
// now let atempt to reply back
// this part does not work!
UdpClient newClient = CopyUdpClient(listener, groupEP);
newClient.Send(dataToSend, dataToSend.Length);
// this part works!
listener.Send(dataToSend, dataToSend.Length);
}
static UdpClient CopyUdpClient(UdpClient client, IPEndPoint groupEP)
{
var ip = groupEP.Address.ToString();
var port = groupEP.Port;
var newUdpClient = new UdpClient(ip, port);
return newUdpClient;
}
клиентский код в основном отправляет данные на сервер, а затем ждет ответа:
string ipOfServer = "192.168.0.132";
int portServerIsListeningOn = 11000;
// send data to server
Socket sending_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress send_to_address = IPAddress.Parse(ipOfServer);
IPEndPoint sending_end_point = new IPEndPoint(send_to_address, portServerIsListeningOn);
sending_socket.SendTo(Encoding.ASCII.GetBytes("Test"), sending_end_point);
// get info
var port = sending_socket.LocalEndPoint.ToString().Split(':')[1];
// now wait for server to send data back
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, int.Parse(port));
byte[] buffer = new byte[1024];
sending_socket.Receive(buffer); // <----- keeps waiting in here :(
обратите внимание, что клиент находится за маршрутизатором (NAT), иначе у меня не будет этой проблемы. Причина, по которой я хочу скопировать udpClient, заключается в том, что я могу отправить эту переменную на другой компьютер, другой компьютер для отправки данных клиенту.
Итак, мой вопрос, почему исходный объект listener
способен отправлять данные, но newClient
не может? Клиент продолжает ждать в строке sending_socket.Receive(buffer);
даже после того, как сервер выполнит строку: newClient.Send(dataToSend, dataToSend.Length);
. клиент успешно получает данные, когда слушатель отправляет данные, но не newClient. Почему это происходит, если обе переменные имеют одинаковый IP-адрес и порт назначения? как отличаются переменные?
Примечание:
Если сервер и клиент находятся в одной сети, то копия работает, а переменная newClient
может отправлять данные клиенту. Чтобы имитировать эту проблему, клиент должен находиться за NAT (маршрутизатором). Пример такой сети может состоять из двух маршрутизаторов. позвольте называть их маршрутизатором X и маршрутизатором Y. Вам также нужен серверный вызов, который S. и клиент C., поэтому S может быть подключен к одному из LAN-портов X. C может быть подключен к одному из LAN-портов Y. Наконец, подключите WAN-порт Y к одному из портов LAN X.