C#网络编程:Socket通信与HttpClient进阶
C# 网络编程:Socket 通信
Socket 通信基础概念
在 C# 网络编程中,Socket(套接字)是一种跨网络进程间通信的机制。它起源于 Unix 系统,后被广泛应用于各类操作系统平台。Socket 为应用程序提供了一种与网络交互的抽象层,使得不同主机上的进程能够进行数据传输。
从本质上讲,Socket 是 IP 地址与端口号的组合。IP 地址用于标识网络中的主机,而端口号则用于标识主机上的特定进程。这种组合允许数据在网络中准确地传输到目标应用程序。Socket 通信基于传输层协议,常见的有 TCP(传输控制协议)和 UDP(用户数据报协议)。
TCP 是一种面向连接的协议,它提供可靠的数据传输。在进行数据传输之前,TCP 会在客户端和服务器之间建立一条虚拟的连接,通过三次握手来确保连接的可靠性。数据在传输过程中会进行编号和确认,若有数据丢失或错误,TCP 会自动重传,保证数据的完整性和顺序性。
UDP 则是一种无连接的协议,它不保证数据的可靠传输。UDP 直接将数据报发送到目标地址,不进行连接的建立和确认。虽然 UDP 不具备 TCP 的可靠性,但它的优点是传输速度快,开销小,适用于对实时性要求高但对数据准确性要求相对较低的场景,如实时视频流、音频流传输等。
使用 TCP Socket 进行通信
- 服务器端实现
首先创建一个简单的 TCP 服务器端示例。在 C# 中,我们使用
System.Net.Sockets
命名空间来操作 Socket。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class TcpServer
{
private const int BufferSize = 1024;
private TcpListener _tcpListener;
public TcpServer(int port)
{
_tcpListener = new TcpListener(IPAddress.Any, port);
}
public void Start()
{
_tcpListener.Start();
Console.WriteLine($"服务器已启动,正在监听端口 {_tcpListener.LocalEndpoint}");
while (true)
{
TcpClient client = _tcpListener.AcceptTcpClient();
Console.WriteLine($"客户端 {client.Client.RemoteEndPoint} 已连接");
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[BufferSize];
int bytesRead = stream.Read(buffer, 0, BufferSize);
string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"收到客户端消息: {message}");
string response = "消息已收到";
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
stream.Write(responseBytes, 0, responseBytes.Length);
stream.Close();
client.Close();
}
}
public void Stop()
{
_tcpListener.Stop();
Console.WriteLine("服务器已停止");
}
}
在上述代码中,我们创建了一个 TcpServer
类。构造函数中初始化 TcpListener
并绑定到指定端口。Start
方法启动服务器并进入一个无限循环,等待客户端连接。当有客户端连接时,接受连接并获取 NetworkStream
,通过 Stream
读取客户端发送的消息,然后向客户端发送响应消息。最后关闭 Stream
和 TcpClient
。
- 客户端实现 接下来实现对应的 TCP 客户端。
using System;
using System.Net.Sockets;
using System.Text;
class TcpClientApp
{
private const string ServerIp = "127.0.0.1";
private const int ServerPort = 12345;
public void SendMessage()
{
using (TcpClient client = new TcpClient(ServerIp, ServerPort))
{
NetworkStream stream = client.GetStream();
string message = "Hello, Server!";
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
stream.Write(messageBytes, 0, messageBytes.Length);
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"收到服务器响应: {response}");
}
}
}
在客户端代码中,我们使用 TcpClient
连接到指定的服务器 IP 和端口。创建 NetworkStream
后,向服务器发送消息,并读取服务器的响应消息。
使用 UDP Socket 进行通信
- 服务器端实现 UDP 服务器端的实现与 TCP 有所不同,因为 UDP 不需要建立连接。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class UdpServer
{
private const int BufferSize = 1024;
private UdpClient _udpClient;
public UdpServer(int port)
{
_udpClient = new UdpClient(port);
}
public void Start()
{
IPEndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
byte[] receiveBytes = _udpClient.Receive(ref clientEndPoint);
string message = Encoding.UTF8.GetString(receiveBytes);
Console.WriteLine($"收到客户端消息: {message} 来自 {clientEndPoint}");
string response = "消息已收到";
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
_udpClient.Send(responseBytes, responseBytes.Length, clientEndPoint);
}
}
public void Stop()
{
_udpClient.Close();
Console.WriteLine("服务器已停止");
}
}
在 UDP 服务器端代码中,我们创建 UdpClient
并绑定到指定端口。通过 Receive
方法接收客户端发送的数据,同时获取客户端的 IPEndPoint
。接收到消息后,向客户端发送响应消息。
- 客户端实现
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class UdpClientApp
{
private const string ServerIp = "127.0.0.1";
private const int ServerPort = 12345;
public void SendMessage()
{
using (UdpClient client = new UdpClient())
{
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(ServerIp), ServerPort);
string message = "Hello, Server!";
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
client.Send(messageBytes, messageBytes.Length, serverEndPoint);
IPEndPoint receiveEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] receiveBytes = client.Receive(ref receiveEndPoint);
string response = Encoding.UTF8.GetString(receiveBytes);
Console.WriteLine($"收到服务器响应: {response}");
}
}
}
在 UDP 客户端代码中,创建 UdpClient
并向指定的服务器 IPEndPoint
发送消息。然后通过 Receive
方法接收服务器的响应消息。
C# 网络编程:HttpClient 进阶
HttpClient 基础使用
HttpClient
是 .NET 中用于发送 HTTP 请求和接收 HTTP 响应的强大工具。它位于 System.Net.Http
命名空间下。在使用 HttpClient
之前,需要先创建一个 HttpClient
实例。
using System;
using System.Net.Http;
using System.Threading.Tasks;
class HttpClientExample
{
private readonly HttpClient _httpClient;
public HttpClientExample()
{
_httpClient = new HttpClient();
}
public async Task<string> GetStringAsync(string url)
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
在上述代码中,我们创建了一个 HttpClientExample
类,在构造函数中初始化 HttpClient
。GetStringAsync
方法使用 HttpClient
的 GetAsync
方法发送 GET 请求,获取 HttpResponseMessage
。通过 EnsureSuccessStatusCode
方法检查响应状态码,如果状态码不是 2xx 系列,会抛出异常。最后通过 ReadAsStringAsync
方法读取响应内容并返回字符串。
设置请求头
在实际应用中,常常需要设置请求头来满足不同的服务器要求或携带额外信息。
public async Task<string> GetStringWithHeadersAsync(string url)
{
_httpClient.DefaultRequestHeaders.Add("User - Agent", "MyApp/1.0");
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
HttpResponseMessage response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
在 GetStringWithHeadersAsync
方法中,我们通过 DefaultRequestHeaders
属性添加了 User - Agent
和 Accept
请求头。User - Agent
用于标识客户端应用程序,Accept
用于告诉服务器客户端能够接受的响应内容类型。
发送 POST 请求
发送 POST 请求时,通常需要在请求体中携带数据。
public async Task<string> PostDataAsync(string url, string content)
{
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.PostAsync(url, httpContent);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
在 PostDataAsync
方法中,我们创建了一个 StringContent
实例,将需要发送的数据以 JSON 格式包装,并设置编码为 UTF - 8。然后使用 PostAsync
方法发送 POST 请求,同时将 HttpContent
作为请求体传递。
处理响应
- 解析 JSON 响应
当服务器返回 JSON 格式的数据时,我们可以使用
System.Text.Json
或Newtonsoft.Json
库来解析。
using System.Text.Json;
public async Task<T> GetJsonDataAsync<T>(string url)
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<T>(json);
}
在 GetJsonDataAsync
方法中,我们通过 JsonSerializer.Deserialize
方法将 JSON 字符串反序列化为指定类型 T
的对象。
- 处理错误响应 除了检查状态码,还可以根据响应内容来处理错误。
public async Task<string> HandleErrorResponseAsync(string url)
{
try
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
string errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"错误状态码: {response.StatusCode},错误内容: {errorContent}");
return null;
}
}
catch (HttpRequestException ex)
{
Console.WriteLine($"请求异常: {ex.Message}");
return null;
}
}
在 HandleErrorResponseAsync
方法中,我们首先检查响应状态码。如果状态码表示成功,读取响应内容。否则,读取错误内容并打印状态码和错误信息。同时捕获 HttpRequestException
异常并处理。
使用 HttpClientFactory
在 ASP.NET Core 应用程序中,推荐使用 HttpClientFactory
来创建 HttpClient
实例。这有助于管理 HttpClient
的生命周期,避免资源泄漏等问题。
- 注册 HttpClientFactory
在
Startup.cs
文件中,添加如下代码:
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
}
- 使用 HttpClientFactory
在控制器或其他服务中,可以通过依赖注入获取
HttpClient
。
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using System.Threading.Tasks;
public class MyController : ControllerBase
{
private readonly HttpClient _httpClient;
public MyController(HttpClient httpClient)
{
_httpClient = httpClient;
}
[HttpGet]
public async Task<string> GetData()
{
HttpResponseMessage response = await _httpClient.GetAsync("https://example.com/api/data");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
在上述代码中,HttpClient
通过构造函数注入到控制器中。这样可以确保 HttpClient
的正确管理和复用。
高级特性:自定义 HttpMessageHandler
HttpMessageHandler
是 HttpClient
处理请求和响应的核心组件。通过自定义 HttpMessageHandler
,可以实现诸如日志记录、请求重试等高级功能。
- 创建自定义 HttpMessageHandler
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
public class LoggingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine($"发送请求: {request.Method} {request.RequestUri}");
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
Console.WriteLine($"收到响应: {response.StatusCode}");
return response;
}
}
在 LoggingHandler
类中,我们继承自 DelegatingHandler
,并重写 SendAsync
方法。在方法中,打印请求的方法和 URL,然后调用基类的 SendAsync
方法发送请求,并在收到响应后打印响应状态码。
- 使用自定义 HttpMessageHandler
public class CustomHttpClientExample
{
private readonly HttpClient _httpClient;
public CustomHttpClientExample()
{
HttpMessageHandler handler = new LoggingHandler();
_httpClient = new HttpClient(handler);
}
public async Task<string> GetStringAsync(string url)
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
在 CustomHttpClientExample
类中,我们创建了 LoggingHandler
实例,并将其传递给 HttpClient
的构造函数。这样,在使用 HttpClient
发送请求时,就会执行 LoggingHandler
中的日志记录逻辑。
性能优化与注意事项
- 复用 HttpClient 实例
避免在频繁的操作中每次都创建新的
HttpClient
实例。HttpClient
内部维护了连接池,复用实例可以提高性能并减少资源消耗。 - 设置合理的超时时间
通过
HttpClient.Timeout
属性设置合理的请求超时时间,避免长时间等待无响应的请求,浪费资源。
_httpClient.Timeout = TimeSpan.FromSeconds(10);
- 处理大响应数据 对于大响应数据,避免一次性读取整个响应内容到内存中。可以使用流的方式逐步处理响应数据,例如:
public async Task ProcessLargeResponseAsync(string url)
{
HttpResponseMessage response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
using (Stream stream = await response.Content.ReadAsStreamAsync())
{
// 在这里对流进行处理,例如写入文件等
}
}
在上述代码中,通过 HttpCompletionOption.ResponseHeadersRead
选项,在接收到响应头后就开始处理,而不是等待整个响应内容下载完成。然后通过 ReadAsStreamAsync
方法获取响应流,进行后续处理。
- 安全考虑
在发送敏感数据时,确保使用 HTTPS 协议。同时,验证服务器的证书,防止中间人攻击。可以通过
HttpClientHandler.ServerCertificateCustomValidationCallback
来实现自定义的证书验证逻辑。
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
// 自定义证书验证逻辑,返回 true 或 false
return true;
};
HttpClient httpClient = new HttpClient(handler);
通过深入理解和掌握 C# 中的 Socket 通信和 HttpClient 的进阶用法,开发者能够更加高效地构建健壮、高性能的网络应用程序,满足不同场景下的网络通信需求。无论是开发实时交互的应用,还是与各种 Web 服务进行数据交互,这些知识和技能都将发挥重要作用。