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
-
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
-
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!