/// <summary>
        /// Raise the QueryFinished event with the specified parameters
        /// </summary>
        /// <param name="response">Response to the query (success, failure, etc)</param>
        /// <param name="queryType">Type of query that finished</param>
        /// <param name="payload">Received data packet</param>
        private void OnQueryFinished(UDPServerQueryState queryState, UDPPacket payload)
        {
            UDPServerQueryResponseHandler queryFinished = this.QueryFinished;

            if (queryFinished != null)
            {
                queryFinished(this, queryState.QueryResponse, queryState.QueryType, payload);
            }
        }
        /// <summary>
        /// Start an asynchronous query operation
        /// </summary>
        /// <param name="timeout">Timeout (in seconds) to wait for a response from the server or zero for no timeout</param>
        public void BeginQuery(int timeout, UDPServerQueryType queryType, IPAddress address, int queryPort)
        {
            remoteEndpoint = new IPEndPoint(address, queryPort);

            // Start the query thread
            UDPServerQueryState queryState = new UDPServerQueryState(new ParameterizedThreadStart(QueryThreadProc), new TimerCallback(QueryTimeoutCallback), queryType);

            lock (activeQueryLock)
            {
                activeQueries.Add(queryState);
            }

            queryState.Start(timeout);
        }
        /// <summary>
        /// Callback function for when the timeout timer expires
        /// </summary>
        /// <param name="state"></param>
        private void QueryTimeoutCallback(object state)
        {
            UDPServerQueryState queryState = (UDPServerQueryState)state;

            // Flag the timeout condition
            queryState.QueryResponse = UDPServerQueryResponse.Timeout;

            queryState.End();
            OnQueryFinished(queryState, null);

            lock (activeQueryLock)
            {
                activeQueries.Remove(queryState);
            }
        }
        /// <summary>
        /// Thread function where the query is actually executed. The call to Receive() is blocking and the
        /// separate timer thread takes care of the timeout behaviour (if specified)
        /// </summary>
        private void QueryThreadProc(object oQueryState)
        {
            UDPServerQueryState queryState = (UDPServerQueryState)oQueryState;

            try
            {
                // Send the query to the server
                OutboundPacket query = new OutboundPacket(true);
                query.Append((byte)queryState.QueryType);
                Send(queryState.QueryClient, query);

                // Wait for a response from the server
                UDPPacket queryResponse = Receive(queryState.QueryClient);

                // Stop the timeout timer
                queryState.End();

                if (queryState.QueryResponse == UDPServerQueryResponse.None)
                {
                    queryState.QueryResponse = UDPServerQueryResponse.Success;
                    OnQueryFinished(queryState, queryResponse);
                }
            }
            catch (ThreadAbortException) { }
            catch (Exception)
            {
                if (queryState.QueryResponse != UDPServerQueryResponse.Timeout)
                {
                    queryState.QueryResponse = UDPServerQueryResponse.Error;

                    // Stop the timeout timer
                    queryState.End();
                    OnQueryFinished(queryState, null);
                }
            }

            lock (activeQueryLock)
            {
                activeQueries.Remove(queryState);
            }
        }