2014-08-28 4 views
4

Привет, я хочу загрузить некоторый динамически сгенерированный контент в свой веб-api. На клиенте я использую HttpWebRequest. Данные должны быть загружены синхронизации, и я хочу писать в поток ПОСЛЕ (!) Я выполнил HTTP-запрос.HttpWebRequest chunked/async POST

(От сервера к клиенту он работает нормально, но от клиента к серверу я получаю некоторые исключения).

реализация клиента выглядит следующим образом:

HttpWebRequest httpWebRequest = HttpWebRequest.Create(myUrl) as HttpWebRequest; 
httpWebRequest.Method = "POST"; 
httpWebRequest.Headers["Authorization"] = "Basic " + ... ; 
httpWebRequest.PreAuthenticate = true; 

httpWebRequest.SendChunked = true; 
//httpWebRequest.AllowWriteStreamBuffering = false; //does not help... 
httpWebRequest.ContentType = "application/octet-stream"; 
Stream st = httpWebRequest.GetRequestStream(); 

Task<WebResponse> response = httpWebRequest.GetResponseAsync(); 

// NOW: Write after GetResponse() 

var b = Encoding.UTF8.GetBytes("Test1"); 
st.Write(b, 0, b.Length); 

b = Encoding.UTF8.GetBytes("Test2"); 
st.Write(b, 0, b.Length); 

b = Encoding.UTF8.GetBytes("Test3"); 
st.Write(b, 0, b.Length); 

st.Close(); 

var x = response.Result; 
Stream resultStream = x.GetResponseStream(); 
//do some output... 

Я получаю исключение (NotSupportedException:. Поток не поддерживает одновременную IO операцию чтения или записи) на stream.write().

Почему у меня есть исключения. Несколько раз первая пишет worke, а поздняя запись генерирует исключение. Вначале свойство stream.CanWrite имеет значение true, но после первого или второго или третьего написания оно становится ложным ... И тогда исключение бросается на следующую запись.

Edit: Изменение AllowWriteStreamBuffering не помог

Приложения: я нашел мою проблему. Эта проблема вызвана порядком моего кода. я должен назвать его в таком порядке:

  • GetRequestStream (написание асинхронной к потоку) (запрос отправляется на сервер после первой записи) затем:
  • GetResponseAsync()
  • GetResponseStream()

Я подумал: «GetResponseAsync» запускает клиент для отправки запроса (на текущий момент только заголовки). Но это не обязательно, потому что запрос уже отправлен после того, как я напишу первые бит в поток.

Вторая причина моих проблем: Скрипач. (Fiddler в настоящее время поддерживает только потоки ответов, а не запросы)

ответ

6

Я нашел свою проблему.

Порядок моего кода вызвал проблему.

Решения состоит в том, чтобы назвать его в таком порядке:

  • GetRequestStream (написание асинхронного к потоку) (запрос отправляется на сервер после первой записи), то:
  • GetResponseAsync()
  • GetResponseStream()

Я понимаю, что «GetResponseAsync» запускает клиент для отправки запроса (на текущий момент только заголовки), но я обнаружил, что это будет uneccesary поскольку запрос уже отправлен после того, как первые несколько бит были записаны в поток.

Вторая причина моих проблем - Fiddler, но Fiddler поддерживает только потоки ответов, а не запросы.

Код дружнее ссылки класс '' HttpWebRequest:

HttpWebRequest httpWebRequest = HttpWebRequest.Create("http://xxx") as HttpWebRequest; 
httpWebRequest.Method = "POST"; 
httpWebRequest.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("user:pw")); 
httpWebRequest.PreAuthenticate = true;  
httpWebRequest.SendChunked = true; 
httpWebRequest.AllowWriteStreamBuffering = false; 
httpWebRequest.AllowReadStreamBuffering = false;  
httpWebRequest.ContentType = "application/octet-stream"; 

Stream st = httpWebRequest.GetRequestStream(); 

Console.WriteLine("Go"); 

try 
{ 
    st.Write(buffer, 0, buffer.Length); //with the first write, the request will be send. 
    st.Write(buffer, 0, buffer.Length); 
    st.Write(buffer, 0, buffer.Length); 

    for (int i = 1; i <= 10; i++) 
    {   
     st.Write(buffer, 0, buffer.Length); //still writing while I can read on the stream at my ASP.NET web api 

    } 

} 
catch (WebException ex) 
{ 
    var y = ex.Response; 

} 
finally 
{ 
    st.Close(); 

} 

// Now we can read the response from the server in chunks 

Task<WebResponse> response = httpWebRequest.GetResponseAsync(); 

Stream resultStream = response.Result.GetResponseStream(); 

byte[] data = new byte[1028]; 

int bytesRead; 

while ((bytesRead = resultStream.Read(data, 0, data.Length)) > 0) 
{ 
    string output = System.Text.Encoding.UTF8.GetString(data, 0, bytesRead); 

    Console.WriteLine(output); 

} 

Код дружнее ссылки, 'HttpClient' класс:

HttpClientHandler ch = new HttpClientHandler(); 

HttpClient c = new HttpClient(ch);  
c.DefaultRequestHeaders.TransferEncodingChunked = true;  
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes("user:pw"))); 

Stream stream = new MemoryStream(); 

AsyncStream asyncStream = new AsyncStream(); // Custom implementation of the PushStreamContent with the method, "WriteToStream()". 

PushStreamContent streamContent = new PushStreamContent(asyncStream.WriteToStream); 

HttpRequestMessage requestMessage = new HttpRequestMessage(new HttpMethod("POST"), "http://XXX") { Content = streamContent }; 

requestMessage.Headers.TransferEncodingChunked = true; 

HttpResponseMessage response = await c.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead); 

// The request has been sent, since the first write in the "WriteToStream()" method. 

response.EnsureSuccessStatusCode(); 

Task<Stream> result = response.Content.ReadAsStreamAsync(); 

byte[] data = new byte[1028]; 

int bytesRead; 

while ((bytesRead = await result.Result.ReadAsync(data, 0, data.Length)) > 0) 
{ 
    string output = System.Text.Encoding.UTF8.GetString(data, 0, bytesRead); 

    Console.WriteLine(output); 

} 

Console.ReadKey(); 
+0

Я удивлен, что это работает и поддерживается. Можете ли вы опубликовать обновленный код? – usr

+0

здесь вы :-) – user437899

+0

Это работает только в * идеальных * ситуациях и, к сожалению, не является жизнеспособным вариантом. Клиент достаточно упрям, чтобы продолжить запись всего потока, даже если сервер давно ошибочно и отправил статус HTTP. Клиент не будет читать ничего, что отправил сервер, если он не завершит свой запрос, удивительно, даже после закрытия пула приложений, IIS и базового сокета тоже закрылись. Я попытался перейти на один уровень до HttpWebRequest, чтобы попытаться прочитать поток, пока клиент все еще пишет, однако эта операция не разрешена. – MoonStom

0

Вы используете объекты HttpWebRequest на нескольких потоках одновременно. response - это задача, выполняемая одновременно с вашими записями. Это явно небезопасно.

Кроме того, я не вижу, чего вы хотите достичь. HTTP имеет модель запроса-ответа. Сервер не может (обычно) отправлять один байт ответа перед получением всего запроса. Теоретически это возможно. Но это очень необычно и, вероятно, не поддерживается .NET BCL. Это будет поддерживаться только (очень необычным) настраиваемым вашим HTTP-сервером.

+0

Да, я знаю, что это unusal случай. :-) Объяснение моей цели: В основном я хочу только вернуть динамические сгенерированные данные с сервера клиенту. Для этого я использую «кодирование передачи: chunked». Клиент получает ответ, и сервер все еще может писать в поток. (С этим я могу предложить клиенту что-то вроде «yield return»). Моя следующая попытка состояла в том, чтобы сделать это от клиента к серверу (мой вопрос stackoverflow). Но: Конечно, генерация результата не может начаться до того, как клиент отправит последние бит. (BTW: я нашел свою проблему, см. Приложение) – user437899

Смежные вопросы