два сокета (один асинхронный) для тестов mDNS

Я делаю несколько тестов mDNS и использую два сокета: один для отправки запросов mDNS и один для прослушивания сети (этот асинхронный). Иногда я вижу ответы, которые не получены запрашивающим сокетом просто потому, что адрес назначения — 224.0.0.251. Дело в том, что когда время ожидания синхронного сокета истекает, асинхронный сокет получает перехваченные данные только после истечения времени ожидания другого. Почему это? Спасибо.

Для запросов у меня есть функция FindIP(), а для прослушивания у меня есть класс ClassSniffer. См. код ниже.

    private void FindIP( int idx_target )
    {
        string TargetName = textBox_Name_array[ idx_target ].Text.Trim();
        string TargetSuffix = "local";
        if( TargetName.Length > 0xFF ) TargetName = TargetName.Substring( 0, 0xFF );

        Socket sock = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );
        sock.SendTimeout = 3000;
        sock.ReceiveTimeout = Convert.ToInt32( numericUpDown_RxTimeOut.Value ) * 1000;

        IPEndPoint endPoint = new IPEndPoint( IPAddress.Parse( mDNS_addr ), mDNS_port );

        byte[] buffer_send, buffer_recv = new byte[ 1024 ];

        buffer_send = new byte[ 0 ]
        .Concat( BitConverter.GetBytes( cnt_trans ).Take( 2 ).Reverse().ToArray() ) // Transaction ID
        .Concat( BitConverter.GetBytes(     0     ).Take( 2 ).Reverse().ToArray() ) // Flags
        .Concat( BitConverter.GetBytes(     1     ).Take( 2 ).Reverse().ToArray() ) // Number of questions
        .Concat( BitConverter.GetBytes(     0     ).Take( 2 ).Reverse().ToArray() ) // Number of answers
        .Concat( BitConverter.GetBytes(     0     ).Take( 2 ).Reverse().ToArray() ) // Number of authority resource records
        .Concat( BitConverter.GetBytes(     0     ).Take( 2 ).Reverse().ToArray() ) // Number of additional resource records
        .Concat( new byte[] { Convert.ToByte( TargetName.Length   ) } ).Concat( Encoding.ASCII.GetBytes( TargetName   ) )
        .Concat( new byte[] { Convert.ToByte( TargetSuffix.Length ) } ).Concat( Encoding.ASCII.GetBytes( TargetSuffix ) )
        .Concat( new byte[] { 0 } ) // Terminator
        .Concat( BitConverter.GetBytes(     1     ).Take( 2 ).Reverse().ToArray() ) // Type (A record)
        .Concat( BitConverter.GetBytes(     1     ).Take( 2 ).Reverse().ToArray() ) // Class IN
        .ToArray();
        // TX
        sock.SendTo( buffer_send, endPoint );
        // RX
        try
        {
            sock.Receive( buffer_recv );
            ...
        }
        ...
    }

    private class ClassSniffer
    {
        public ClassSniffer( Form_Main main_instance )
        {
            this.main_instance = main_instance;
            buff_data = new byte[ 1024 ];
            recv_done = false;
            timer_socket = new System.Timers.Timer();
            timer_socket.SynchronizingObject = main_instance;
            timer_socket.Elapsed += TimerAttenElapsed;
            timer_socket.Interval = 100;
            timer_socket.Start();

            sock_conn = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );
            sock_conn.SetSocketOption( SocketOptionLevel.Udp, SocketOptionName.NoDelay, 1 );
            sock_conn.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1 );
            sock_conn.Bind( new IPEndPoint( IPAddress.Any, Form_Main.mDNS_port ) );
            sock_conn.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 0 ); // 0
            sock_conn.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.AddMembership
                                     , new MulticastOption( IPAddress.Parse( Form_Main.mDNS_addr ) ) );
            SnifferRecieve();
        }
        private Form_Main main_instance;
        private System.Timers.Timer timer_socket;
        private Mutex socket_mutex = new Mutex();
        private Socket sock_conn;
        private byte[] buff_data = new byte[ 1024 ];
        private int recv_size = 0;
        private bool recv_done = false;

        private void SnifferRecieve()
        {
            try
            {
                IPEndPoint LocalIPEndPoint = new IPEndPoint( IPAddress.Any, Form_Main.mDNS_port );
                EndPoint LocalEndPoint = (EndPoint)LocalIPEndPoint;
                StateObject state = new StateObject();
                state.socket_work = sock_conn;
                sock_conn.BeginReceiveFrom( state.buffer, 0, state.buffer.Length, 0, ref LocalEndPoint, new AsyncCallback( SnifferReceiveCallback ), state );
            }
            catch( Exception excp )
            {
                string err_msg = excp.ToString();
            }
        }

        private void SnifferReceiveCallback( IAsyncResult async_res )
        {
            IPEndPoint LocalIPEndPoint = new IPEndPoint( IPAddress.Any, Form_Main.mDNS_port );
            EndPoint LocalEndPoint = (EndPoint)LocalIPEndPoint;
            StateObject async_state = (StateObject)async_res.AsyncState;
            Socket socket_client = async_state.socket_work;
            int async_recv_size = socket_client.EndReceiveFrom( async_res, ref LocalEndPoint );
            socket_client.BeginReceiveFrom( async_state.buffer, 0, async_state.buffer.Length, 0, ref LocalEndPoint
                                          , new AsyncCallback( SnifferReceiveCallback ), async_state );
            socket_mutex.WaitOne();
            recv_size = async_recv_size;
            async_state.buffer.CopyTo( buff_data, 0 );
            recv_done = true;
            socket_mutex.ReleaseMutex();
        }

        private void TimerAttenElapsed( object source, ElapsedEventArgs e )
        {
            socket_mutex.WaitOne();
            if( recv_done )
            {
                main_instance.SnifferWriteData( buff_data, recv_size ); // displays data in a textBox
                recv_done = false;
            }
            socket_mutex.ReleaseMutex();
        }
    }

person Gigi    schedule 09.03.2018    source источник


Ответы (1)


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

person Gigi    schedule 10.03.2018