简体   繁体   English

C#套接字:异步调用内的同步调用

[英]C# Sockets: synchronous calls within asynchronous ones

In MSDN example of using asynchronous sockets, the receiving data in socket is done by repeatedly calling asynchronous BeginReceive from the callback handler which is called by BeginReceive: 在使用异步套接字的MSDN 示例中 ,套接字中的接收数据是通过从BeginReceive调用的回调处理程序重复调用异步BeginReceive来完成的:

private static void ReceiveCallback( IAsyncResult ar ) {
//...Skipped...
        if (bytesRead > 0) {
            // There might be more data, so store the data received so far.
            state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
                //  Get the rest of the data.
            client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
                new AsyncCallback(ReceiveCallback), state);
        } else {
// ...Skipped...
}

http://msdn.microsoft.com/en-us/library/bbx2eya8(v=vs.110).aspx http://msdn.microsoft.com/en-us/library/bbx2eya8(v=vs.110).aspx

Is there a necessity to make again an asynchronous call from a handler which is already executing in a separate thread? 是否有必要再次从已经在单独线程中执行的处理程序进行异步调用? Can one simply use Receive in a loop in this handler? 可以在这个处理程序中循环使用Receive吗? Something like: 就像是:

while (bytesRead) {

bytesRead = client.Receive(state.buffer, 0, client.Available, 
                                   SocketFlags.None);
// Etc...
}

The major goal of the APM pattern was to avoid blocking the calling thread while waiting for the result of asynchronous operation, and thus boost the scalability of the server applications. APM模式的主要目标是避免在等待异步操作的结果时阻塞调用线程,从而提高服务器应用程序的可伸缩性。

If in your AsyncCallback you continue calling Receive in the loop synchronously, you'll still be blocking the IOCP thread on which the initial BeginReceive has completed. 如果在AsyncCallback继续同步循环调用Receive ,您仍将阻止初始BeginReceive已完成的IOCP线程。 This may be OK for a client-side UI app, where you might not care about ThreadPool starvation, but this is certainly not a good idea for a server-side app, where the blocked thread could otherwise be serving other incoming client requests. 这对于客户端UI应用程序可能没问题,在那里您可能不关心ThreadPool饥饿,但对于服务器端应用程序来说当然不是一个好主意,否则被阻止的线程可能会服务于其他传入的客户端请求。

Note that with C# 5.0 / .NET 4.5 and on, APM is considered legacy. 请注意,在C#5.0 / .NET 4.5及更高版本中,APM被认为是遗留的。 You can use async/await and new Task-based Asynchronous Pattern (TAP) pattern, which greatly simplifies the asynchronous code development, eg: 您可以使用async/awaitnew Task-based Asynchronous Pattern (TAP)模式,这极大地简化了异步代码开发,例如:

async Task<string> ReadAllAsync()
{
    var sb = new StringBuffer();

    using (var tcp = new TcpClient())
    {
        await tcp.ConnectAsync(IPAddress.Parse("localhost"), 8080).ConfigureAwait(false);
        var buffer = new byte[1024];
        using (var stream = tcp.GetStream())
        {
            var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
            if (0 == bytesRead)
                break;
            sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
        }
    }

    return sb.ToString();
}

If for some reason you don't want to use NetworkStream.ReadAsync , you can wrap APM-style socket APIs as TAP with Task.FromAsync : 如果由于某种原因您不想使用NetworkStream.ReadAsync ,则可以使用Task.FromAsync将APM样式的套接字API包装为TAP:

public static class SocketsExt
{
    static public Task ReceiveDataAsync(
        this TcpClient tcpClient,
        byte[] buffer)
    {
        return Task.Factory.FromAsync(
            (asyncCallback, state) =>
                tcpClient.Client.BeginReceive(buffer, 0, buffer.Length, 
                    SocketFlags.None, asyncCallback, state),
            (asyncResult) =>
                tcpClient.Client.EndReceive(asyncResult), 
            null);
    }

    static public async Task<Int32> ReceiveInt32Async(
        this TcpClient tcpClient)
    {
        var data = new byte[sizeof(Int32)];
        await tcpClient.ReceiveDataAsync(data).ConfigureAwait(false);
        return BitConverter.ToInt32(data, 0);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM