使用BeginXXX重写套接字和流代码到XXXAsync

时间:2014-02-25 21:49:01

标签: c# .net sockets asynchronous

我正在尝试将使用AsyncCallbacks的BeginXXX方法重写为使用async / await和XXXAsync方法的应用程序。但是我在性能方面遇到了一些麻烦。例如,这里是原始代码的片段,用于初始化一堆连接:

...
    for (int i = 1; i <= _maxTcpClients; i++) {
        TcpClientState tt = new TcpClientState(new TcpClient(), i);
        try {
            tt.TcpClient.BeginConnect(Host, Port, ConnectCallback, tt);
        } catch (Exception ex) {
            Log.Debug(
                "Error on BeginConnect on RequestHandler (" + tt.HandlerId + ") request (" + tt.RequestId + ")",
                ex);
            CloseRequest(tt);
        }
    }
...
    private static void ConnectCallback(IAsyncResult ar) {
        TcpClientState tt = (TcpClientState)ar.AsyncState;
        Log.Debug("ConnectCallback on TcpClient (" + tt.TcpClientId + ")");

        try {
            tt.TcpClient.EndConnect(ar);
        } catch (Exception ex) {
            Log.Debug("Error on EndConnect on TcpClient (" + tt.TcpClientId + ")", ex);
            CloseRequest(tt);
            Interlocked.Decrement(ref _maxTcpClients);
            return;
        }

        tt.SslStream = new SslStream(tt.TcpClient.GetStream(), false, Helper.ValidateServerCertificate, null);
        try {
            tt.SslStream.BeginAuthenticateAsClient(Host, SslAuthenticateCallback, tt);
        } catch (Exception ex) {
            Log.Debug("Error on BeginAuthenticateAsClient on TcpClient (" + tt.TcpClientId + ")", ex);
            CloseRequest(tt);
            Interlocked.Decrement(ref _maxTcpClients);
        }
    }

我已将此重写为以下内容:

...
    for (int i = 1; i <= _maxTcpClients; i++) {
            TcpClientState tt = new TcpClientState(new TcpClient(), i);
            try {
                tt.TcpClient.ConnectAsync(Host, Port).ContinueWith(t => ConnectCallback(tt));
            } catch (Exception ex) {
                Log.Debug("Error on ConnectAsync on TcpClient (" + tt.TcpClientId + ")", ex);
                CloseRequest(tt);
                Interlocked.Decrement(ref _maxTcpClients);
                return;
            }
        }
...
    private static void ConnectCallback(TcpClientState tt) {
        Log.Debug("ConnectCallback on TcpClient (" + tt.TcpClientId + ")");

        tt.SslStream = new SslStream(tt.TcpClient.GetStream(), false, Helper.ValidateServerCertificate, null);
        try {
            tt.SslStream.AuthenticateAsClientAsync(Host).ContinueWith(t => SslAuthenticateCallback(tt));
        } catch (Exception ex) {
            Log.Debug("Error on AuthenticateAsClientAsync on TcpClient (" + tt.TcpClientId + ")", ex);
            CloseRequest(tt);
            Interlocked.Decrement(ref _maxTcpClients);
            return;
        }
    }

TcpClients的初始化速度(连接,ssl握手和其他)的速度似乎存在巨大的性能差异。通过最初的方式,我可以在几秒钟内完成并初始化100个连接。在重写之后,可以花费30秒的时间来完成同样的事情。我可以在日志中看到各种回调函数是异步执行的,但一切都需要更长时间。我不确定我做错了什么?

另外我知道在这种情况下,围绕Async方法的try catch不会做任何事情,但现在不是问题。

举一个速度差异的例子,这是一个调试日志片段,当原始代码循环10次时:

2014-02-25 22:37:06,076 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (1)
2014-02-25 22:37:06,076 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (3)
2014-02-25 22:37:06,077 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (6)
2014-02-25 22:37:06,077 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (10)
2014-02-25 22:37:06,077 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (7)
2014-02-25 22:37:06,077 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (4)
2014-02-25 22:37:06,077 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (8)
2014-02-25 22:37:06,078 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (9)
2014-02-25 22:37:06,079 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (5)
2014-02-25 22:37:06,082 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (2)

Async版本:

2014-02-25 22:37:51,569 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (1)
2014-02-25 22:37:51,583 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (2)
2014-02-25 22:37:51,936 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (5)
2014-02-25 22:37:51,969 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (3)
2014-02-25 22:37:52,133 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (4)
2014-02-25 22:37:52,311 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (6)
2014-02-25 22:37:52,382 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (8)
2014-02-25 22:37:52,452 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (9)
2014-02-25 22:37:52,466 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (7)
2014-02-25 22:37:52,856 [] DEBUG CPT.Client (null) - ConnectCallback on TcpClient (10)

1 个答案:

答案 0 :(得分:0)

尝试以下操作,看看它是否与您的BeginXXX / EndXXX版本基准进行比较。

TcpClientState[] ConnectAll(string host, int port)
{
    var states = new List<TcpClientState>();

    for (int i = 1; i <= _maxTcpClients; i++)
    {
        TcpClientState tt = new TcpClientState(new TcpClient(), i);

        Func<Task> connectAsync = async () =>
        {
            try
            {
                // note ConfigureAwait(false)
                await tt.TcpClient.ConnectAsync(host, port).ConfigureAwait(false);
                tt.SslStream = new SslStream(tt.TcpClient.GetStream(), false, Helper.ValidateServerCertificate, null);
                await tt.SslStream.AuthenticateAsClientAsync(host);

                // move here the code from SslAuthenticateCallback
                // and so on ...  
            }
            catch (Exception ex)
            {
                // you really want to do --_maxTcpClients ?
                Interlocked.Decrement(ref _maxTcpClients);

                Debug.Print(ex.ToString());
                throw; // re-throw or handle
            }
        };

        tt.ConnectionTask = connectAsync();
        states.Add(tt);
    }

    return states.ToArray();
}