Practical Implementation of Asynchronous File Operations and HTTP Server in C#

Practical Implementation of Asynchronous File Operations and HTTP Server in C#

This article will introduce various implementations of asynchronous file read and write operations in C#, as well as how to build a simple asynchronous HTTP server and client.

Detailed Explanation of Asynchronous File Operations

Four Methods of Asynchronous File Writing

// Method 1: Default FileStream (without using I/O threads)
using (var stream = new FileStream("test1.txt", FileMode.Create, 
       FileAccess.ReadWrite, FileShare.None, BUFFER_SIZE))
{
    Console.WriteLine("1. Uses I/O Threads: {0}", stream.IsAsync);
    byte[] buffer = Encoding.UTF8.GetBytes(CreateFileContent());
    var writeTask = Task.Factory.FromAsync(stream.BeginWrite, stream.EndWrite, 
                     buffer, 0, buffer.Length, null);
    await writeTask;
}

// Method 2: Explicitly enable asynchronous FileStream
using (var stream = new FileStream("test2.txt", FileMode.Create, 
       FileAccess.ReadWrite, FileShare.None, BUFFER_SIZE, FileOptions.Asynchronous))
{
    Console.WriteLine("2. Uses I/O Threads: {0}", stream.IsAsync);
// ... Writing operation same as above
}

// Method 3: Create asynchronous file using simplified API
using (var stream = File.Create("test3.txt", BUFFER_SIZE, FileOptions.Asynchronous))
using (var sw = new StreamWriter(stream))
{
    Console.WriteLine("3. Uses I/O Threads: {0}", stream.IsAsync);
    await sw.WriteAsync(CreateFileContent());
}

// Method 4: StreamWriter wrapping (may not be true asynchronous I/O)
using (var sw = new StreamWriter("test4.txt", true))
{
    Console.WriteLine("4. Uses I/O Threads: {0}", 
                     ((FileStream)sw.BaseStream).IsAsync);
    await sw.WriteAsync(CreateFileContent());
}

Key Points Analysis

  1. I/O Thread Usage:

  • Only when explicitly specified<span>FileOptions.Asynchronous</span>or using the corresponding API will I/O threads be used
  • Methods 2 and 3 show<span>IsAsync</span>as true, others as false
  • Comparison of Asynchronous Modes:

    • <span>Task.Factory.FromAsync</span>: Converts APM mode to Task
    • <span>WriteAsync</span> method: Directly uses asynchronous method
    • It is recommended to use the simplified API of Method 3 for clearer code

    Asynchronous Reading and File Processing

    async static Task<long> SumFileContent(string fileName)
    {
        using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, 
               FileShare.None, BUFFER_SIZE, FileOptions.Asynchronous))
        using (var sr = new StreamReader(stream))
        {
            long sum = 0;
            while (sr.Peek() > -1)
            {
                string line = await sr.ReadLineAsync();
                sum += long.Parse(line);
            }
            return sum;
        }
    }
    
    // Parallel processing of multiple files
    Task<long>[] readTasks = new Task<long>[4];
    for (int i = 0; i < 4; i++)
    {
        readTasks[i] = SumFileContent($"test{i + 1}.txt");
    }
    long[] sums = await Task.WhenAll(readTasks);
    

    Implementation of Asynchronous HTTP Server

    Core Code of the Server

    class AsyncHttpServer
    {
        readonly HttpListener _listener;
        const string RESPONSE_TEMPLATE = 
    "<html><head><title>Test</title></head>" +
    "<body><h2>Test page</h2><h4>Today is: {0}</h4></body></html>";
    
        public AsyncHttpServer(int portNumber)
        {
            _listener = new HttpListener();
            _listener.Prefixes.Add($"http://+:{portNumber}/");
        }
    
        public async Task Start()
        {
            _listener.Start();
    
            while (true)
            {
                var ctx = await _listener.GetContextAsync();
                Console.WriteLine("Client connected...");
                string response = string.Format(RESPONSE_TEMPLATE, DateTime.Now);
    
                using (var sw = new StreamWriter(ctx.Response.OutputStream))
                {
                    await sw.WriteAsync(response);
                    await sw.FlushAsync();
                }
            }
        }
    }
    

    Asynchronous HTTP Client

    static async Task GetResponseAsync(string url)
    {
        using (var client = new HttpClient())
        {
            HttpResponseMessage responseMessage = await client.GetAsync(url);
            string responseHeaders = responseMessage.Headers.ToString();
            string response = await responseMessage.Content.ReadAsStringAsync();
    
            Console.WriteLine("Response headers:");
            Console.WriteLine(responseHeaders);
            Console.WriteLine("Response body:");
            Console.WriteLine(response);
        }
    }
    

    Usage Example

    static void Main(string[] args)
    {
        var server = new AsyncHttpServer(portNumber: 1234);
        var t = Task.Run(() => server.Start());
    
        Console.WriteLine("Listening on port 1234. Open http://localhost:1234 in your browser.");
    
    // Test client request
        GetResponseAsync("http://localhost:1234").GetAwaiter().GetResult();
    
        Console.WriteLine("Press Enter to stop the server.");
        Console.ReadLine();
    
        server.Stop().GetAwaiter().GetResult();
    }
    

    Execution Results

    1. Uses I/O Threads: False
    2. Uses I/O Threads: True  
    3. Uses I/O Threads: True
    4. Uses I/O Threads: False
    Starting parsing files in parallel
    

    Core Points Summary

    1. Asynchronous File I/O Selection:

    • Explicitly use<span>FileOptions.Asynchronous</span>to ensure true asynchronous I/O
    • Prefer using<span>File.Create</span>and other simplified APIs for cleaner code
  • Characteristics of HTTP Server:

    • Uses<span>HttpListener.GetContextAsync()</span><span>to asynchronously receive requests</span>
    • Each request is handled independently, without blocking thread pool threads
    • Response generation is completely asynchronous
  • Performance Advantages:

    • Asynchronous operations avoid thread blocking, improving concurrent processing capability
    • Suitable for I/O intensive operations, such as file reading and writing, network requests, etc.
  • Considerations:

    • Asynchronous file deletion requires<span>Task.Run</span><span>to simulate</span>
    • Resource release should use<span>using</span><span>statement or correctly call</span><code><span>Dispose</span>

    This asynchronous programming model is very important in modern C# applications, significantly enhancing application responsiveness and throughput.

    Follow my WeChat public account for more C# and .NET technical content!

    Leave a Comment