第二次调用HttpClient的PostAsJsonAsync卡住

时间:2016-12-02 14:14:03

标签: c# dotnet-httpclient

我正在尝试将一些数据发布到Web服务,并在控制台应用程序中使用HttpClient接收响应。如果我尝试重用HttpClient实例,第一次调用PostAsJsonAsync会按预期工作,但第二次只是等待。如果我为每个调用创建一个新的HttpClient,那么一切都还可以。

public class DirectHandler
{
    HttpClient httpClient;

    public string SendToPlayer(object message)
    {

        if (httpClient == null)
        {
            httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri("..url...");
        }

        HttpResponseMessage response = httpClient.PostAsJsonAsync("", message).Result;  // Blocking call!
        if (response.IsSuccessStatusCode)
        {
            var result = response.Content.ReadAsByteArrayAsync().Result;
            return System.Text.Encoding.Default.GetString(result);
        }
        else
        {
            throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
        }
    }
}

使用此类的代码:

DirectHandler dh = new DirectHandler();
var resp = dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
Thread.Sleep(500);
resp = dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);

“server”是一个HttpListener:

public class HTTPServer
{
    void StartListener()
    {
        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("my prefix");
        listener.Start();
        listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
    }

    private void OnRequestReceive(IAsyncResult result)
    {
        HttpListener listener = (HttpListener)result.AsyncState;

        HttpListenerContext context = listener.EndGetContext(result);
        HttpListenerResponse response = context.Response;
        HttpListenerRequest request = context.Request;

        using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
        {
            Console.WriteLine(reader.ReadToEnd());
        }

        string responseString = "{'a': \"b\"}";
        byte[] buffer = Encoding.UTF8.GetBytes(responseString);
        response.ContentLength64 = buffer.Length;
        response.OutputStream.Write(buffer, 0, buffer.Length);

        listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
    }
}

我如何重用HttpClient?

更新 我改变了.Result到async / await,现在SendToPlayer方法看起来像这样:

public async Task<string> SendToPlayer(object message)
{
    if (httpClient == null)
    {
        httpClient = new HttpClient();
        httpClient.BaseAddress = new Uri("..url...");
    }

    HttpResponseMessage response = await httpClient.PostAsJsonAsync("", message).ConfigureAwait(false); 
    if (response.IsSuccessStatusCode)
    {
        var result = await response.Content.ReadAsByteArrayAsync();
        return System.Text.Encoding.Default.GetString(result);
    }
    else
    {
        throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
    }
}

当多次调用时,它仍会在PostAsJsonAsync中永远等待。

UPDATE2:

测试代码挂在第二个SendToPlayer行:

public static void Main(string[] args)
{
    Task.Run(async () =>
    {
        DirectHandler dh = new DirectHandler();
        var resp = await dh.SendToPlayer(myObj);
        Console.WriteLine("Received: " + str);
        Thread.Sleep(500);
        resp = await dh.SendToPlayer(myObj);
        Console.WriteLine("Received: " + str);

    }).Wait();
}

2 个答案:

答案 0 :(得分:1)

我不禁注意到你提供的代码甚至没有编译。就像主方法中myObj的来源一样?而且,由于方法StartListener被声明为私有,你甚至如何启动http监听器? 这些问题让我怀疑您发布的代码不是具有所述问题的实际代码。

另外,您对myObj投入了什么?

我复制并调整了您的代码,使其在我的机器上运行。请在新的控制台应用程序中查看它是否也在您的计算机上运行(您需要管理员权限才能运行它)

public static void Main(string[] args)
{
    var s = new HTTPServer();
    s.StartListener();

    Task.Run(async () =>
    {
        var myObj = 8;

        DirectHandler dh = new DirectHandler();
        var resp = await dh.SendToPlayer(myObj);
        Console.WriteLine("Received: " + resp);
        Thread.Sleep(500);
        resp = await dh.SendToPlayer(myObj);
        Console.WriteLine("Received: " + resp);

    }).Wait();
}

public class HTTPServer
{
    public void StartListener()
    {
        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://*:8080/");
        listener.Start();
        listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
    }

    private void OnRequestReceive(IAsyncResult result)
    {
        HttpListener listener = (HttpListener)result.AsyncState;

        HttpListenerContext context = listener.EndGetContext(result);
        HttpListenerResponse response = context.Response;
        HttpListenerRequest request = context.Request;

        using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
        {
            Console.WriteLine(reader.ReadToEnd());
        }

        string responseString = "{'a': \"b\"}";
        byte[] buffer = Encoding.UTF8.GetBytes(responseString);
        response.ContentLength64 = buffer.Length;
        response.OutputStream.Write(buffer, 0, buffer.Length);

        listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
    }
}

public class DirectHandler
{
    HttpClient httpClient;

    public async Task<string> SendToPlayer(object message)
    {
        if (httpClient == null)
        {
            httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri("http://localhost:8080");
        }

        HttpResponseMessage response = await httpClient.PostAsJsonAsync("", message).ConfigureAwait(false); 
        if (response.IsSuccessStatusCode)
        {
            var result = await response.Content.ReadAsByteArrayAsync();
            return System.Text.Encoding.Default.GetString(result);
        }
        else
        {
            throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
        }
    }
}

此输出

8
Received: {'a': "b"}
8
Received: {'a': "b"}

现在,您可能已经简化了此处发布的代码。如果有任何代码你没有在这里发布,那将很难解决。

您的HttpListener是否在单独的(控制台)应用中运行?由于例如错误,可能在第一次调用后终止服务器进程。

如果上述代码适用于新的控制台应用,请尝试查找与您的代码的差异。

答案 1 :(得分:0)

我认为,有两个问题:

1)您的服务器需要等待处理多于一个事件,并且

2)您的客户端和服务器代码应驻留在单独的执行线程中。

根据您编写的方式,服务器将在启动后立即关闭,这就是为什么第一个请求可以正常工作但第二个请求挂起的原因。

我创建了一个例子: https://github.com/paulsbruce/StackOverflowExamples/tree/master/SOF40934218

您必须首先关闭服务器项目,等待传入请求(StartListening不保持服务器线程打开,因此有一段时间(IsRunning){Thread.Sleep(100);}循环作为示例。 https://github.com/paulsbruce/StackOverflowExamples/blob/master/SOF40934218/SOF40934218_Server/Server.cs

然后将客户端作为一个单独的进程启动,并观察它的控制台输出。它确实得到了第二个响应(为什么有5秒的延迟让你看到这个)。 https://github.com/paulsbruce/StackOverflowExamples/blob/master/SOF40934218/SOF40934218_Client/Client.cs

希望这两个建议有所帮助!