2016-12-02 163 views
-1

我試圖將一些數據發佈到Web服務並在控制檯應用程序中接收HttpClient的響應。如果我嘗試重用一個HttpClient實例,PostAsJsonAsync的第一個調用按預期工作,但第二個只是等待。如果我爲每個呼叫創建一個新的HttpClient,那麼一切都可以。第二次調用HttpClient的PostAsJsonAsync stucks

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); 

「服務器」 是一個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?

更新: 我改變了。結果到異步/ 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(); 
} 
+1

使用async/await時,不要等待使用.Result或.Wait()的異步方法時,請始終使用'await'。根據環境環境。結果和.Wait可能導致死鎖。 –

+0

我重寫了我的代碼以使用await而不是.Result無處不在,但沒有任何更改。我也試過.PostAsJsonAsync(...)。ConfigureAwait(false),但第二次調用它仍然掛起。 – gberes

+0

顯示你如何重寫代碼,以便我們看到你做了什麼。 – Nkosi

回答

1

我不禁注意到,你所提供的代碼甚至不進行編譯。像你主要方法中的myObj從哪裏來的?你怎麼開始http偵聽器,因爲方法StartListener被聲明爲私有? 這些類型的問題讓我懷疑你發佈的代碼不是具有所描述問題的實際代碼。

另外,你把什麼放入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是否在單獨的(控制檯)應用程序中運行?也許服務器進程在第一次調用後終止,例如由於錯誤。

如果上面的代碼在新的控制檯應用程序中工作,請嘗試查找與您的代碼的區別。

+0

myObj是一個簡單的DTO,但現在我也使用了一個整數。你的代碼也適用於我。本來我的StartListener是公開的,可能在格式化時意外刪除了它。我的DirectHandler和你的完全一樣,也是主要的方法。服務器在第一次調用後不會停止(它在Unity3D應用程序中(可能是這裏的東西?),StartListener在啓動時調用),如果我重新啓動客戶端,它會再次響應第一個請求,但OnRequestReceive不會由第二個PostAsJsonAsync。正如我傷心,如果我在每個SendToPlayer調用中創建一個新的HttpClient,那麼它的工作原理。 – gberes

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

希望這兩個建議幫助!