网络编程是后端开发的核心能力。C# 提供了从高层级的 HttpClient 到低层级的 Socket 的完整网络编程栈。本文将深入探讨 HTTP 请求调优、gRPC 通信、Socket 编程、以及 ASP.NET Core 中的网络性能优化。
HttpClient 深入调优 正确使用 HttpClient 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class BadService { public async Task CallApi () { using var client = new HttpClient(); return await client.GetStringAsync("https://api.example.com" ); } } public class GoodService { private readonly HttpClient _httpClient; public GoodService (IHttpClientFactory factory ) { _httpClient = factory.CreateClient("Api" ); } public async Task<string > CallApi () { return await _httpClient.GetStringAsync("/data" ); } } services.AddHttpClient("Api" , client => { client.BaseAddress = new Uri("https://api.example.com" ); client.Timeout = TimeSpan.FromSeconds(30 ); client.DefaultRequestHeaders.Add("User-Agent" , "MyApp/1.0" ); }) .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { MaxConnectionsPerServer = 10 , PooledConnectionLifetime = TimeSpan.FromMinutes(5 ), PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2 ), EnableMultipleHttp2Connections = true , });
高级请求技术 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using Polly;var retryPolicy = HttpPolicyExtensions .HandleTransientHttpError() .WaitAndRetryAsync(3 , retryAttempt => TimeSpan.FromMilliseconds(100 * Math.Pow(2 , retryAttempt))); var response = await retryPolicy.ExecuteAsync(() => _httpClient.GetAsync("/unreliable-endpoint" )); using var response = await _httpClient.GetAsync( "/large-file" , HttpCompletionOption.ResponseHeadersRead); using var stream = await response.Content.ReadAsStreamAsync();await stream.CopyToAsync(File.OpenWrite("large.bin" ));using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5 ));try { var result = await _httpClient.GetAsync("/slow-api" , cts.Token); } catch (OperationCanceledException){ Console.WriteLine("请求超时" ); }
gRPC 通信 定义服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 service OrderService { rpc GetOrder (OrderRequest) returns (OrderResponse) ; rpc ListOrders (ListRequest) returns (stream OrderResponse) ; rpc PlaceOrder (stream OrderRequest) returns (OrderSummary) ; rpc Chat (stream ChatMessage) returns (stream ChatMessage) ; } message OrderRequest { int32 id = 1 ; } message OrderResponse { int32 id = 1 ; string customer = 2 ; double amount = 3 ; }
服务端实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class OrderServiceImpl : OrderService.OrderServiceBase { public override Task<OrderResponse> GetOrder ( OrderRequest request, ServerCallContext context ) { return Task.FromResult(new OrderResponse { Id = request.Id, Customer = "张三" , Amount = 99.9 }); } public override async Task ListOrders ( ListRequest request, IServerStreamWriter<OrderResponse> responseStream, ServerCallContext context ) { foreach (var order in GetOrders ()) { await responseStream.WriteAsync(order); await Task.Delay(100 ); } } public override async Task Chat ( IAsyncStreamReader<ChatMessage> requestStream, IServerStreamWriter<ChatMessage> responseStream, ServerCallContext context ) { await foreach (var msg in requestStream.ReadAllAsync()) { await responseStream.WriteAsync(new ChatMessage { Content = $"收到: {msg.Content} " }); } } }
客户端调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 var channel = GrpcChannel.ForAddress("https://localhost:5001" );var client = new OrderService.OrderServiceClient(channel);var response = await client.GetOrderAsync(new OrderRequest { Id = 1 });using var listCall = client.ListOrders(new ListRequest());await foreach (var order in listCall.ResponseStream.ReadAllAsync()){ Console.WriteLine(order.Customer); } using var chatCall = client.Chat();_ = Task.Run(async () => { await foreach (var reply in chatCall.ResponseStream.ReadAllAsync()) Console.WriteLine(reply.Content); }); await chatCall.RequestStream.WriteAsync( new ChatMessage { Content = "你好" }); await chatCall.RequestStream.CompleteAsync();
Socket 编程 TCP 客户端/服务器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 var listener = new TcpListener(IPAddress.Any, 8888 );listener.Start(); _ = Task.Run(async () => { while (true ) { var client = await listener.AcceptTcpClientAsync(); _ = HandleClientAsync(client); } }); async Task HandleClientAsync (TcpClient tcpClient ){ using (tcpClient) using (var stream = tcpClient.GetStream()) { byte [] buffer = new byte [1024 ]; int bytesRead = await stream.ReadAsync(buffer); string message = Encoding.UTF8.GetString(buffer, 0 , bytesRead); byte [] response = Encoding.UTF8.GetBytes($"已收到: {message} " ); await stream.WriteAsync(response); } } using var client = new TcpClient();await client.ConnectAsync("localhost" , 8888 );using var stream = client.GetStream();byte [] data = Encoding.UTF8.GetBytes("你好" );await stream.WriteAsync(data);byte [] buffer = new byte [1024 ];int read = await stream.ReadAsync(buffer);Console.WriteLine(Encoding.UTF8.GetString(buffer, 0 , read));
UDP 广播 1 2 3 4 5 6 7 8 9 10 11 12 13 14 using var udpClient = new UdpClient();byte [] message = Encoding.UTF8.GetBytes("发现服务" );await udpClient.SendAsync(message, message.Length, "255.255.255.255" , 9999 );using var listener = new UdpClient(9999 );while (true ){ var result = await listener.ReceiveAsync(); string text = Encoding.UTF8.GetString(result.Buffer); Console.WriteLine($"来自 {result.RemoteEndPoint} : {text} " ); }
Socket 多路复用(Select/Poll) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class AsyncSocketServer { private readonly Socket _listenSocket; private readonly BufferManager _bufferManager; private readonly SemaphoreSlim _maxClient; public async Task StartAsync (int port, int maxClients = 1000 ) { _listenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); _listenSocket.Bind(new IPEndPoint(IPAddress.Any, port)); _listenSocket.Listen(maxClients); while (true ) { var socket = await _listenSocket.AcceptAsync(); _ = ProcessClientAsync(socket); } } }
ASP.NET Core 网络性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 builder.WebHost.ConfigureKestrel(options => { options.Limits.MaxConcurrentConnections = 10 _000; options.Limits.MaxRequestBodySize = 10 * 1024 * 1024 ; options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2 ); }); builder.Services.AddResponseCompression(options => { options.EnableForHttps = true ; options.Providers.Add<BrotliCompressionProvider>(); options.Providers.Add<GzipCompressionProvider>(); }); builder.WebHost.ConfigureKestrel(options => { options.Listen(IPAddress.Any, 5001 , listenOptions => { listenOptions.Protocols = HttpProtocols.Http1AndHttp2; }); }); builder.Services.AddSingleton<ConnectionMultiplexer>( _ => ConnectionMultiplexer.Connect("localhost:6379" )); app.Use(async (context, next) => { await next(); });
网络编程常见陷阱 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var handler = new HttpClientHandler();try { await client.GetAsync("https://example.com" ); } catch (HttpRequestException ex){ } public async Task<string > BadPattern (){ var result = await client.GetStringAsync("url" ).ConfigureAwait(false ); return result; }
总结 :现代 C# 网络编程主要围绕 HttpClient(HTTP/REST)、gRPC(高性能 RPC)和原生 Socket(自定义协议)。使用 IHttpClientFactory 管理连接池、利用 HTTP/2 多路复用、引入重试策略和连接生命周期管理,是构建健壮网络服务的基础。gRPC 基于 HTTP/2 的双向流和 Protobuf 序列化,使其成为微服务间通信的首选。