异步TCP客户端连接挂起

本文关键字:挂起 连接 客户端 TCP 异步 | 更新日期: 2023-09-27 18:04:03

我正在使用异步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();
    }

测试是尝试连接一个远程服务器,但它必须是不可达的(PC打开,但服务停止)。

当我运行TryConnect函数时,它立即正确返回"连接正在进行中…",然后显示一个异常,因为远程端点已关闭。太好了!

问题是它需要几秒钟来返回异常,我想给用户机会取消正在进行的操作。根据MSDN关于BeginConnect方法的规范,如果您希望中止异步操作,只需在工作套接字上调用Close。

因此,我尝试在末尾添加几行(如上所述注释掉),以模拟用户在2秒后取消。结果看起来像是应用程序的一个悬挂(沙漏)。通过暂停IDE,它停在最后一行t.Result.Client.Close()上。但是,通过停止IDE,一切正常关闭,没有任何异常。

我也试过直接关闭客户端作为t.Result.Close(),但它是完全相同的。

是我,还是连接过程中出了什么问题?

异步TCP客户端连接挂起

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

要取消你必须等待两个任务:tcp和一个定时器。
使用异步tcp语法:

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

尝试在对象上调用Dispose() -它比Close()更具侵略性。您可以查看TcpClient类上的各种Timeout成员,并将它们设置为更合适的值(例如,在局域网环境中1秒可能就足够了)。你也可以看看。net 4.0中的CancellationTokenSource功能。这允许您向Task发出信号,表示您希望它停止-我找到了一篇文章,可能会让您开始。

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