c# - 将套接字和流代码从使用 BeginXXX 重写为 XXXAsync

标签 c# .net sockets asynchronous

我正在尝试将一个应用程序从使用 BeginXXX 方法和 AsyncCallbacks 重写为使用 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;
        }
    }

在 TcpClient 的初始化速度(连接、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)

以及异步版本:

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)

最佳答案

尝试以下操作,看看它是否与您的 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();
}

关于c# - 将套接字和流代码从使用 BeginXXX 重写为 XXXAsync,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22027006/

相关文章:

c# - "Input string was not in a correct format."

c - 使用 Select 读取时超时

c# - 重叠 Socket.BeginSend/EndSend 调用的可预测行为

python - python 中的多人服务器套接字

c# - 如何正确有效地使用 WorkbookBeforeClose 事件?

c# - 返回方法调用之前更新本地 iqueryable 列表

.net - Visual Studio 提供了 Monodevelop 所缺乏的哪些功能?

c# - 在 ASP.NET 4.5 中的 AutoCompleteExtender 中添加滚动条

c# - 如何声明一个以元组列表作为参数的 f# 方法?

c# - 如果在DataGridView单元格上存在