繁体   English   中英

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

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

在使用异步套接字的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

是否有必要再次从已经在单独线程中执行的处理程序进行异步调用? 可以在这个处理程序中循环使用Receive吗? 就像是:

while (bytesRead) {

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

APM模式的主要目标是避免在等待异步操作的结果时阻塞调用线程,从而提高服务器应用程序的可伸缩性。

如果在AsyncCallback继续同步循环调用Receive ,您仍将阻止初始BeginReceive已完成的IOCP线程。 这对于客户端UI应用程序可能没问题,在那里您可能不关心ThreadPool饥饿,但对于服务器端应用程序来说当然不是一个好主意,否则被阻止的线程可能会服务于其他传入的客户端请求。

请注意,在C#5.0 / .NET 4.5及更高版本中,APM被认为是遗留的。 您可以使用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();
}

如果由于某种原因您不想使用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