Зависание при асинхронном соединении с TCP-клиентом - PullRequest
3 голосов
/ 01 августа 2011

Я практикуюсь с асинхронной структурой CTP, и в качестве упражнения я бы создал TCP-клиента, способного запрашивать сервер (используя произвольный протокол).Во всяком случае, я застрял на самой ранней стадии, потому что проблема с подключением.Либо я до сих пор не понял какой-то базовый момент, либо есть что-то странное.

Итак, вот асинхронный разъем:

public class TaskClient
{
    public static Task<TcpClient> Connect(IPEndPoint endPoint)
    {
        //create a tcp client
        var client = new TcpClient(AddressFamily.InterNetwork);

        //define a function to return the client
        Func<IAsyncResult, TcpClient> em = iar =>
        {
            var c = (TcpClient)iar.AsyncState;
            c.EndConnect(iar);
            return c;
        };

        //create a task to connect the end-point async
        var t = Task<TcpClient>.Factory.FromAsync(
            client.BeginConnect,
            em,
            endPoint.Address.ToString(),
            endPoint.Port,
            client);

        return t;
    }
}

Я имею в виду, чтобы вызывать эту функцию только один раз, затемвозвращение экземпляра TcpClient для использования в любом последующем запросе (код здесь не показан).

Где-то в моей форме я вызываю приведенную выше функцию следующим образом:

    //this method runs on the UI thread, so can't block
    private void TryConnect()
    {
        //create the end-point
        var ep = new IPEndPoint(
            IPAddress.Parse("192.168.14.112"), //this is not reachable: correct!
            1601);

        var t = TaskClient
            .Connect(ep)
            .ContinueWith<TcpClient>(_ =>
            {
                //tell me what's up
                if (_.IsFaulted)
                    Console.WriteLine(_.Exception);
                else
                    Console.WriteLine(_.Result.Connected);

                return _.Result;
            })
            .ContinueWith(_ => _.Result.Close());

        Console.WriteLine("connection in progress...");

        //wait for 2" then abort the connection
        //Thread.Sleep(2000);
        //t.Result.Client.Close();
    }

Тестэто попытка подключиться к удаленному серверу, но он должен быть недоступен (ПК включен, но служба остановлена).

Когда я запускаю функцию TryConnect, она возвращает правильно "соединение в процессе ...", как только, затем отображает исключение, потому что удаленная конечная точка выключена.Отлично!

Проблема в том, что для возврата исключения требуется несколько секунд, и я хотел бы дать пользователю возможность отменить выполняемую операцию.Согласно спецификации MSDN о методе BeginConnect, если вы хотите прервать асинхронную операцию, просто вызовите Close на рабочем сокете.

Итак, я попытался добавить пару строк в конце (закомментировано каквыше), поэтому для имитации отмены пользователей через 2 секунды.Результат выглядит как зависание приложения (песочные часы).При остановке IDE она останавливается на самой последней строке t.Result.Client.Close().Тем не менее, после остановки IDE все закрывается нормально, без каких-либо исключений.

Я также пытался закрыть клиент напрямую как t.Result.Close(), но это точно так же.

Это я,или что-то не работает в процессе подключения?

Заранее большое спасибо.

Ответы [ 2 ]

2 голосов
/ 11 мая 2012

t.Result.Close() будет ожидать завершения задачи t. t.ContinueWith() также будет ожидать завершения задачи.

Для отмены необходимо подождать 2 задания: tcp и таймер.
Использование асинхронного синтаксиса tcp:

await Task.WhenAny(t,Task.Delay(QueryTimeout));
if (!t.IsCompleted)
    tcpClient.Close(); //Cancel task
1 голос
/ 01 августа 2011

Попробуйте вызвать Dispose() на объекте - это немного более агрессивно, чем Close().Вы можете посмотреть на различные члены Timeout класса TcpClient и установить для них более подходящие значения (например, 1 секунда в среде LAN, вероятно, достаточно хороша).Вы также можете взглянуть на функциональность CancellationTokenSource в .Net 4.0.Это позволяет вам сообщить Task о том, что вы хотите прекратить - я нашел статью , которая может помочь вам начать.

Вы также должны выяснить, какой поток фактически останавливается (основной поток может просто ожидать другого потока, который остановился), например, .ContinueWith(_ => _.Result.Close()) может быть проблемой (вы должны проверить, каково поведение при закрытии сокета дважды).Во время отладки откройте окно Threads (Debug -> Windows -> Threads) и просмотрите каждый поток.

...