You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用System.Net.Http.HttpClient获取解压前的HTTP请求大小以监控用量

如何用System.Net.Http.HttpClient追踪解压前的HTTP数据用量

一、统计发送的HTTP请求总传输大小(含请求头+请求体)

如果你需要统计每次请求实际通过网络发送的字节数(包括请求头和请求体,无论请求体是否压缩),可以通过自定义DelegatingHandler拦截请求并计算大小:

  1. 实现自定义追踪Handler:
public class RequestSizeTrackingHandler : DelegatingHandler
{
    // 累计已发送的总字节数
    public long TotalSentBytes { get; private set; }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 计算请求行+请求头的大小
        var headerSize = CalculateHeadersSize(request);

        // 计算请求体大小
        long bodySize = 0;
        if (request.Content != null)
        {
            if (request.Content is ByteArrayContent byteContent)
            {
                bodySize = byteContent.Headers.ContentLength ?? byteContent.ReadAsByteArrayAsync().Result.Length;
            }
            else if (request.Content is StringContent stringContent)
            {
                var encoding = stringContent.Headers.ContentType?.CharSet ?? "utf-8";
                bodySize = Encoding.GetEncoding(encoding).GetByteCount(await stringContent.ReadAsStringAsync(cancellationToken));
            }
            else
            {
                // 其他类型Content先缓冲到内存计算大小,再重新设置回请求
                var buffer = await request.Content.ReadAsByteArrayAsync(cancellationToken);
                bodySize = buffer.Length;
                request.Content = new ByteArrayContent(buffer);
                // 复制原请求体头部信息
                foreach (var header in request.Content.Headers)
                {
                    request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
                }
            }
        }

        // 累计总发送量并检查阈值
        var requestTotalSize = headerSize + bodySize;
        TotalSentBytes += requestTotalSize;

        if (TotalSentBytes >= YourThresholdValue)
        {
            throw new OperationCanceledException("已达到数据发送阈值,停止请求");
        }

        return await base.SendAsync(request, cancellationToken);
    }

    private long CalculateHeadersSize(HttpRequestMessage request)
    {
        // 计算请求行大小:请求方法 + 路径 + HTTP版本
        var requestLine = $"{request.Method} {request.RequestUri.PathAndQuery} HTTP/{request.Version}\r\n";
        long size = Encoding.ASCII.GetByteCount(requestLine);

        // 计算所有请求头的大小
        foreach (var header in request.Headers)
        {
            foreach (var value in header.Value)
            {
                size += Encoding.ASCII.GetByteCount($"{header.Key}: {value}\r\n");
            }
        }

        // 头部末尾的空行
        size += Encoding.ASCII.GetByteCount("\r\n");
        return size;
    }
}
  1. 初始化HttpClient并使用:
var trackingHandler = new RequestSizeTrackingHandler();
var httpClient = new HttpClient(trackingHandler)
{
    BaseAddress = new Uri("https://your-api-domain.com/")
};

try
{
    while (true)
    {
        var response = await httpClient.GetAsync("/target-endpoint");
        // 处理响应逻辑...
    }
}
catch (OperationCanceledException ex)
{
    Console.WriteLine(ex.Message);
}

二、统计接收的HTTP响应压缩前传输大小(网络实际接收字节数)

HttpClient默认会自动解压gzip/deflate格式的响应,若要统计服务器发送的原始压缩大小,有两种实现方式:

方法1:禁用自动解压,直接读取响应头的Content-Length

var httpClient = new HttpClient();
// 清空Accept-Encoding,禁用自动解压
httpClient.DefaultRequestHeaders.AcceptEncoding.Clear();

long TotalReceivedBytes = 0;
const long Threshold = 1024 * 1024; // 1MB阈值

while (true)
{
    var response = await httpClient.GetAsync("/target-endpoint");
    // 累加响应大小
    if (response.Content.Headers.ContentLength.HasValue)
    {
        TotalReceivedBytes += response.Content.Headers.ContentLength.Value;
    }
    else
    {
        // 无Content-Length时,读取整个响应流计算大小
        var contentBytes = await response.Content.ReadAsByteArrayAsync();
        TotalReceivedBytes += contentBytes.Length;
    }

    if (TotalReceivedBytes >= Threshold)
    {
        Console.WriteLine("已达到数据接收阈值,停止请求");
        break;
    }

    // 手动解压响应内容(示例为gzip格式)
    var decompressedContent = await DecompressGzipContent(response.Content);
    // 处理解压后内容...
}

// 手动解压gzip响应的方法
async Task<string> DecompressGzipContent(HttpContent content)
{
    using var stream = await content.ReadAsStreamAsync();
    using var gzipStream = new GZipStream(stream, CompressionMode.Decompress);
    using var reader = new StreamReader(gzipStream);
    return await reader.ReadToEndAsync();
}

方法2:保留自动解压,用Handler拦截获取原始大小

这种方式不需要禁用自动解压,通过Handler在响应到达时统计原始传输大小:

public class ResponseSizeTrackingHandler : DelegatingHandler
{
    public long TotalReceivedBytes { get; private set; }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        // 计算响应原始大小
        long responseSize = 0;
        if (response.Content.Headers.ContentLength.HasValue)
        {
            responseSize = response.Content.Headers.ContentLength.Value;
        }
        else
        {
            var buffer = await response.Content.ReadAsByteArrayAsync(cancellationToken);
            responseSize = buffer.Length;
            // 重新设置Content,避免后续处理无内容
            response.Content = new ByteArrayContent(buffer);
            foreach (var header in response.Content.Headers)
            {
                response.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
        }

        TotalReceivedBytes += responseSize;

        if (TotalReceivedBytes >= YourThresholdValue)
        {
            throw new OperationCanceledException("已达到数据接收阈值,停止请求");
        }

        return response;
    }
}

// 使用示例
var trackingHandler = new ResponseSizeTrackingHandler();
var httpClient = new HttpClient(trackingHandler)
{
    BaseAddress = new Uri("https://your-api-domain.com/")
};

try
{
    while (true)
    {
        var response = await httpClient.GetAsync("/target-endpoint");
        // HttpClient自动解压,直接处理内容
        var content = await response.Content.ReadAsStringAsync();
    }
}
catch (OperationCanceledException ex)
{
    Console.WriteLine(ex.Message);
}

三、统计请求体解压前的原始大小(若请求体被压缩发送)

如果你会压缩请求体后再发送,只需在压缩前记录原始内容大小并累计即可:

long TotalRequestRawSize = 0;
const long Threshold = 10 * 1024 * 1024; // 10MB阈值

var httpClient = new HttpClient();

while (true)
{
    // 原始请求内容
    var rawContent = "待发送的大请求内容";
    var rawSize = Encoding.UTF8.GetByteCount(rawContent);
    TotalRequestRawSize += rawSize;

    if (TotalRequestRawSize >= Threshold)
    {
        Console.WriteLine("已达到请求原始数据阈值,停止请求");
        break;
    }

    // 压缩请求体
    using var memoryStream = new MemoryStream();
    using var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress);
    using var writer = new StreamWriter(gzipStream);
    await writer.WriteAsync(rawContent);
    await writer.FlushAsync();
    gzipStream.Close();

    var compressedContent = new ByteArrayContent(memoryStream.ToArray());
    compressedContent.Headers.ContentEncoding.Add("gzip");
    compressedContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

    // 发送请求
    var response = await httpClient.PostAsync("/target-endpoint", compressedContent);
    // 处理响应...
}

内容的提问来源于stack exchange,提问作者AlgoRythm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 13:10:16