private void SendConnectionUdpMessages()
        {
            _isRunning = true;

            var messageToServer = new InfoMessage
            {
                Id            = _id,
                LocalEndpoint = _localEndPoint.Convert()
            };
            var messageToPeer = new Udp_KeepaliveMessage();

            while (_isRunning)
            {
                // while we haven't got the partner's address, we will send messages to server
                if (_partnerPublicUdpEndPoint == null && _partnerLocalUdpEndPoint == null)
                {
                    byte[] bytes = messageToServer.ToByteArray();
                    _udpPuncher.Send(bytes, bytes.Length, _serverUdpEndPoint);
                    Console.WriteLine($" >>> Sent UDP to server {_serverUdpEndPoint.Address}:{_serverUdpEndPoint.Port}");
                }
                else
                {
                    // you can skip it. just a demonstration that you still can send messages to server
                    // _udpClient.Send(messageToServer.Data, messageToServer.Data.Length, _serverUdpEndPoint);
                    // Console.WriteLine($" >>> Sent UDP to server [ {_serverUdpEndPoint.Address} : {_serverUdpEndPoint.Port} ]");

                    // THIS is how we punch a hole! we expect the very first message to be dropped by the partner's NAT router
                    // i suppose that this is good idea to send this "keep-alive" messages to peer even if you have already connected,
                    // because AFAIK "hole" for UDP lives ~2 minutes on NAT. so "will we let it die? NEVER!" (c)
                    byte[] bytes = messageToPeer.ToByteArray();
                    _udpClient.Send(bytes, bytes.Length, _partnerPublicUdpEndPoint);
                    _udpClient.Send(bytes, bytes.Length, _partnerLocalUdpEndPoint);
                    Console.WriteLine($" >>> Sent UDP to peer.public [ {_partnerPublicUdpEndPoint.Address} : {_partnerPublicUdpEndPoint.Port} ]");
                    Console.WriteLine($" >>> Sent UDP to peer.local [ {_partnerLocalUdpEndPoint.Address} : {_partnerLocalUdpEndPoint.Port} ]");

                    // "connected" UdpClient sends data much faster,
                    // so if you have something that your partner cant wait for (voice, for example), send it this way
                    if (_extraUdpClientConnected)
                    {
                        _extraUdpClient.Send(bytes, bytes.Length);
                        Console.WriteLine($" >>> Sent UDP to peer.received EP");
                    }
                }

                Thread.Sleep(3000);
            }
        }