异步TCP客户端连接挂断

3
我正在使用异步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

t.Result.Close()会等待t任务完成。 t.ContinueWith()也会等待任务完成。

要取消,必须等待2个任务完成:tcp和定时器。
使用异步tcp语法:

await Task.WhenAny(t,Task.Delay(QueryTimeout));
if (!t.IsCompleted)
    tcpClient.Close(); //Cancel task

1

尝试在对象上调用Dispose() - 它比Close()更加强制。您可以查看TcpClient类上的各种超时成员,并将它们设置为更合适的值(例如,在局域网环境中,1秒可能已经足够)。您还可以查看 .Net 4.0 中的CancellationTokenSource功能。这允许您向Task发出信号,表示您希望它停止 - 我找到了一篇文章,可能会让您入门。

您还应该找出哪个线程实际上正在停顿(主线程可能只是在等待另一个停顿的线程),例如.ContinueWith(_ => _.Result.Close())可能是问题所在(您应该检查关闭套接字两次时的行为)。在调试时打开线程窗口(调试 -> 窗口 -> 线程),并查看每个线程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接