【问题标题】:C# - Why HttpClient PostAsync blocks in .exe, but works with Visual Studio 2015 -> Debug?C# - 为什么 .exe 中的 HttpClient PostAsync 阻塞,但适用于 Visual Studio 2015 -> 调试?
【发布时间】:2017-09-14 16:21:13
【问题描述】:

我有一个 C# 控制台应用程序,它必须创建一个 HttpClient 并向我们的“3CX 电话系统管理”门户网站发出一些请求(我必须检索/更改我们员工的电话状态)。为此,我必须登录,将收到的 cookie 保存到 CookieContainer 中,然后继续处理必须使用相同 cookie 的其他请求。代码如下:

public static CookieContainer cookies = null;
public static HttpClientHandler handler = new HttpClientHandler();

static void Main(string[] args) {
    Task.Run(() => ExtListAsync());
    Console.ReadLine();
}

static async Task<bool> LoginAsync() {
    try {
        cookies = new CookieContainer();
        handler = new HttpClientHandler();
        handler.CookieContainer = cookies;

        using (HttpClient client = new HttpClient(handler, disposeHandler: false)) {
            client.BaseAddress = new Uri("https://my.uri.com:5001");
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            StringContent parameters = new StringContent(@"{""key"":""value"",""key2"":""value2""}", Encoding.UTF8, "application/json");

            // BLOCKS HERE....
            using (HttpResponseMessage response = await client.PostAsync("/api/other", parameters).ConfigureAwait(false)) {
                using (HttpContent content = response.Content) {
                    string responseContent = content.ReadAsStringAsync().Result;

                    var responseCookies = cookies.GetCookies(uri).Cast<Cookie>();

                    if (responseCookies.Count() > 0)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
        }
    } catch (HttpRequestException ex) {
        Console.WriteLine("\n\r EXCEPTION MESSAGE: " + ex.Message);
        Console.WriteLine("\n\r DATA: " + ex.Data);
        Console.WriteLine("\n\r InnerException: " + ex.InnerException);
        Console.WriteLine("\n\r Source: " + ex.Source);
        Console.WriteLine("\n\r StackTrace: " + ex.StackTrace);
        Console.WriteLine("\n\r TargetSite: " + ex.TargetSite);
        return true;
    }
}

static async Task ExtListAsync() {
    bool loggedIn = await LoginAsync();
    if (loggedIn) { ... }
}

我有这个简单的代码,用 Visual Studio 启动的“开始调试”可以正确执行。 如果我尝试从命令提示符启动 .exe(也尝试使用管理员模式),有时应用程序会在稍有延迟的情况下正确执行,但在大多数情况下,它仍会在“await client.PostAsync(...) "... ,然后抛出一个 HttpRequestException:

EXCEPTION MESSAGE: An error occurred while sending the request.

 DATA: System.Collections.ListDictionaryInternal

 InnerException: System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond xxx.xxx.xxx.xxx:5001
   at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
   at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)
   --- End of inner exception stack trace ---
   at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
   at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)

 Source: mscorlib

 StackTrace:    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at statusmng3cx.Program.<LoginAsync>d__15.MoveNext() in ...\myPath\Program.cs:line 254

 TargetSite: Void Throw()

有人对此有解释吗? 提前谢谢你。

更新:

我也尝试添加 SSL 证书:

WebRequestHandler whandler = new WebRequestHandler();
whandler.ClientCertificateOptions = ClientCertificateOption.Manual;

var pem = System.IO.File.ReadAllText("mycert.pem");
byte[] certBuffer = GetBytesFromPEM(pem, "CERTIFICATE");
X509Certificate2 certificate = new X509Certificate2(certBuffer);

whandler.ClientCertificates.Add(certificate);

using (var httpClient = new HttpClient(whandler)) { ... as above ... }

并且发生了同样的事情:通过调试它完美地工作: 状态码 200,ReasonPhrase: 'OK', responseContent: AuthSuccess, ...

通过提示,.exe 不会协作。但是如果我将 3CX 门户 URL 更改为另一个简单的网站,该网站检索静态网页(没有 SSL),.exe 可以完美运行。

【问题讨论】:

  • 您是否设置了任何必需的请求标头?服务器上是否有 SSL 转发?
  • 实际上没有,但是如果我尝试使用 Postman 复制相同的 POST/GET 请求,它可以在没有任何 SSL 标头或证书的情况下运行良好。我已经用 Chrome 网络检查器嗅探了这些请求。
  • 您在 Fiddler 中看到了什么?它在调试/邮递员中有效,但在通过 exe 运行时无效,这绝对是奇怪的。出于好奇,你是在为 Release 做准备吗?
  • 我还没有尝试过使用 Fiddler.. 但是我尝试在调试/发布中为 x64/任何 CPU 的任何组合构建它.. 前 2-3 次它可以工作,然后它抛出 HttpRequestException。服务器是我们的“3CX 电话系统管理”门户,我必须检索我们的员工状态..
  • @Jakasha 这是因为您在 async/await 中混合了阻塞调用 (.Result),这可能导致死锁。代码需要一直异步。

标签: c# post cookies visual-studio-2015 httpclient


【解决方案1】:

最后我用 Java 编写了程序,它可以在控制台上完美运行。

    try {
        TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        };

        SSLContextBuilder builder = new SSLContextBuilder();
        builder.loadTrustMaterial(null, acceptingTrustStrategy);
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());

        HttpClientContext context = HttpClientContext.create();
        CookieStore cookieStore = new BasicCookieStore();
        context.setCookieStore(cookieStore);

        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setDefaultRequestConfig(RequestConfig.custom()
                .setCookieSpec(CookieSpecs.STANDARD).build()).setDefaultCookieStore(cookieStore).build();


        HttpPost request = new HttpPost(mainUrl + loginUrl);
        StringEntity params = new StringEntity("myJson");
        request.addHeader("content-type", "application/json");
        request.setEntity(params);
        HttpResponse response = httpClient.execute(request, context);

        System.out.println("Response getStatusLine: " + 

        response.getStatusLine());
    } catch (...) { ... }

然后输出:

Response getStatusLine: HTTP/1.1 200 OK

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 2015-03-14
    • 1970-01-01
    相关资源
    最近更新 更多