c # сеть NAT punch с протоколом TCP / IP - PullRequest
0 голосов
/ 09 января 2020

Штамповка сети UDP работает и работает. Однако, когда дело доходит до 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);
        }

Когда сервер получает соединение, мы создать новый сокет

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();
        }

Мы создаем новый сокет на клиенте и пытаемся установить sh соединение

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);
        }

Соединение работает без проблем и передачи данных.

К сожалению, здесь, как мы знаем, мы должны запустить новый сокет, прослушивающий тот же адрес и порт, который был создан при подключении к серверу. RVer. Я делаю это для каждого клиента.

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" отправляет пакет на сервер, который хочет установить sh При соединении сервер отправляет информацию клиенту "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 я пишу приложения, используя сокеты, и, наконец, мне нужно пробить net.

Почему я не буду использовать готовое решение? Мне нужен мой собственный, который полностью открыт с использованием только UDP и TCP, потому что некоторые целевые устройства поддерживают только эти протоколы. Я также использовал класс Socket, но этот не дал мне рабочую копию.

Может быть, кто-то сможет мне помочь, за что я был бы очень благодарен, и, конечно, пост также поможет другим понять это.

С уважением Октавиан

1 Ответ

0 голосов
/ 09 января 2020

Пробивка NAT-дырок работает только с UDP, и даже это является хаком.

Реализации брандмауэра NAT начнут отслеживать TCP-поток, когда увидят исходный пакет SYN, покидающий сеть. Чтобы перехватить входящие потоки TCP, вам нужно создать правило входа на маршрутизаторе. Если маршрутизатор поддерживает UPnP, вы можете попросить его динамически создать это правило для вас.

Поскольку UDP не имеет эквивалента пакета SYN, маршрутизаторы начнут отслеживать поток для любого исходящего пакета. Вот почему NAT дырокол работает. Если обе конечные точки находятся за NAT и просто предположим, что ссылка будет работать. Оба могут просто начать отправлять UDP-пакеты друг другу. Маршрутизаторы добавят состояние соединения и отобразят входящие пакеты для каждой конечной точки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...