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

中国地区同事使用C#桌面应用调用欧洲服务器API异常求助

问题描述

我们有一款内部使用的C#桌面应用,用户分布在美国、欧洲、中国,通过WebSocket(SignalR)和REST API连接位于欧洲的服务器。目前仅中国地区用户使用时会出现两类错误:

  • System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by
  • Hub connection error System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout of 120 seconds elapsing. ---> System.TimeoutException: The operation was canceled by the remote host

HttpClient配置代码如下:

public class HttpConfiguration
{
    private HttpClient _client;       

    public HttpClient HttpClient => _client;

    public HttpConfiguration(string baseUrl)
    {
        _client = new HttpClient();
        _client.Timeout = TimeSpan.FromMinutes(3);
        _client.BaseAddress = new Uri(baseUrl);
        _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));            
    }
 }

示例API调用代码:

public async Task<ApiResponse> SendMessage(MessageDto message)
{
    var connectionMessage = await _connectivityService.IsServerAliveAndInternetConnected();
    if (string.IsNullOrEmpty(connectionMessage))
    {
        _config.HttpClient.DefaultRequestHeaders.ClearHeader().AddToken();
    
        var serialisedObject = JsonConvert.SerializeObject(message);
        var bodyContent = new StringContent(serialisedObject, UnicodeEncoding.UTF8, ApiConstants.MEDIA_TYPE);
    
        HttpResponseMessage response = await _config.HttpClient.PostAsync(ApiConstants.SYNC_SEND_MESSAGE, bodyContent);
    
        return await ResponseFactory.GenerateResponse<SyncResponseModel>(response);
    }
    else
    {
        _config.HttpClient.DefaultRequestHeaders.Clear();
        return ResponseFactory.GenerateDirectMessageResponse(connectionMessage, false);
    }
}

已知欧洲到中国物理距离导致延迟,但API数据包很小,且已设置3分钟超时仍无效,求无需搭建网关服务器的解决办法。


无需搭建网关的解决方案建议

1. 优化HttpClient连接配置

跨洲际网络不稳定,默认连接池参数可能不适用,可手动调整:

  • 启用HTTP/2:利用多路复用特性减少连接开销,提升稳定性
    var handler = new SocketsHttpHandler
    {
        EnableMultipleHttp2Connections = true,
        PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),
        KeepAlivePingDelay = TimeSpan.FromSeconds(30),
        KeepAlivePingTimeout = TimeSpan.FromSeconds(10)
    };
    _client = new HttpClient(handler);
    
  • 拆分全局超时与单请求超时:避免单个慢请求阻塞整个HttpClient实例,在请求时单独指定超时
    using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
    HttpResponseMessage response = await _config.HttpClient.PostAsync(ApiConstants.SYNC_SEND_MESSAGE, bodyContent, cts.Token);
    

2. SignalR WebSocket连接优化

针对跨洲际网络的WebSocket断开问题,调整连接策略:

  • 启用自动重连:配置阶梯式重试间隔,提升连接恢复能力
    var connection = new HubConnectionBuilder()
        .WithUrl("your-hub-url")
        .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) })
        .Build();
    
  • 调整WebSocket心跳:缩短心跳间隔,避免因延迟被判定为连接断开
    var connection = new HubConnectionBuilder()
        .WithUrl("your-hub-url", options =>
        {
            options.Transports = HttpTransportType.WebSocket;
            options.WebSocketConfiguration = ws =>
            {
                ws.KeepAliveInterval = TimeSpan.FromSeconds(15);
            };
        })
        .Build();
    

3. 添加请求重试机制

针对网络波动导致的连接关闭、超时,实现幂等重试逻辑,可借助Polly库简化:

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .Or<TaskCanceledException>()
    .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

var response = await retryPolicy.ExecuteAsync(async () =>
{
    using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
    return await _config.HttpClient.PostAsync(ApiConstants.SYNC_SEND_MESSAGE, bodyContent, cts.Token);
});

注意:需确保API接口是幂等的,避免重复请求导致数据异常

4. 优化DNS解析

中国地区DNS解析可能存在延迟或节点选择问题,可手动调整:

  • 预解析域名到IP,直接使用IP发起请求并手动添加Host头
    var ipAddresses = Dns.GetHostAddresses("your-server-domain");
    var ipAddress = ipAddresses.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
    if (ipAddress != null)
    {
        var baseUri = new UriBuilder("https", ipAddress.ToString(), 443) { Path = "/api" }.Uri;
        _client.BaseAddress = baseUri;
        _client.DefaultRequestHeaders.Host = "your-server-domain";
    }
    
  • 客户端系统层面切换至可靠公共DNS(如1.1.1.1或国内合规公共DNS)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:07:46