Il POST asincrono non riesce su WP7 e F #


87

Quando lo faccio let! read = from.AsyncRead bufin F #, si blocca e non ritorna finché il socket TCP non è morto. Perché? E come lo aggiusto?

Il suo codice:

module StreamUtil

open System.IO

/// copy from 'from' stream to 'toStream'
let (|>>) (from : Stream) (toStream : Stream) =
  let buf = Array.zeroCreate<byte> 1024
  let rec doBlock () =
    async {
      let! read = from.AsyncRead buf
      if read <= 0 then
        toStream.Flush()
        return ()
      else
        do! toStream.AsyncWrite(buf, 0, read)
        return! doBlock () }
  doBlock ()

Viene chiamato da questo codice:

use fs = new FileStream(targPath, FileMode.CreateNew, FileAccess.ReadWrite)
do! req.InputStream |>> fs

e richiesto su HTTP con questo codice dall'emulatore di Windows Phone 7.1:

public void Send()
{
    var b = new UriBuilder(_imageService.BaseUrl) {Path = "/images"};

    var req = WebRequest.CreateHttp(b.Uri);
    req.ContentType = "image/jpeg";
    req.Method = "POST";
    var imgLen = SelectedImage.ImageStream.Length;
    req.Headers[HttpRequestHeader.ContentLength] = imgLen.ToString(CultureInfo.InvariantCulture);
    req.Accept = "application/json";
    req.BeginGetRequestStream(RequestReady, new ReqState(req, imgLen));
}

void RequestReady(IAsyncResult ar)
{
    var state = (ReqState)ar.AsyncState;
    var req = state.Request;

    var reqStream = req.EndGetRequestStream(ar);

    SmartDispatcher.BeginInvoke(() =>
        {
            using (var sw = new StreamWriter(reqStream))
            using (var br = new BinaryReader(SelectedVoucher.ImageStream))
            {
                var readBytes = br.ReadBytes(state.ImgLen);

                // tried both 2
                sw.Write(readBytes);
                //sw.Write(Convert.ToBase64String(readBytes));
                sw.Flush();
                sw.Close();
            }
            req.BeginGetResponse(ResponseReady, req);
        });
}

// WHY IS IT YOU ARE NOT CALLED???
void ResponseReady(IAsyncResult ar)
{
    try
    {
        var request = (HttpWebRequest)ar.AsyncState;
        var response = request.EndGetResponse(ar);

        SmartDispatcher.BeginInvoke(() =>
            {
                var rdr = new StreamReader(response.GetResponseStream());
                var msg = rdr.ReadToEnd();

                var imageLocation = response.Headers["Location"];

                Debug.WriteLine(msg);
                Debug.WriteLine(imageLocation);
            });
    }
    catch (WebException ex)
    {
        Debug.WriteLine(ex.ToString());
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.ToString());
    }
}

Senza successo. La ResponseReadyrichiamata non viene mai raggiunta.

Nel frattempo, questo codice funziona in modo eccellente:

open System
open System.Net.Http // WebAPI nuget

let sync aw = Async.RunSynchronously aw

let postC<'a> (c : HttpClient) (r : Uri) (cont : HttpContent) =
  let response = sync <| Async.AwaitTask( c.PostAsync(r, cont) )
  let struc:'a = sync <| deserialize<'a> response
  response, struc

let withContent<'a> (fVerb : (HttpClient -> Uri -> HttpContent -> _ * 'a))=
  let c = new HttpClient()
  fVerb c

[<Test>]
let ``POST /images 201 + Location header`` () =
  let post = withContent<MyImage> postC
  let bytes = IO.File.ReadAllBytes("sample.jpg")
  let hash = SHA1.Create().ComputeHash(bytes) |> Convert.ToBase64String
  let pic = new ByteArrayContent(bytes)
  pic.Headers.Add("Content-Type", "image/jpeg")
  pic.Headers.Add("X-SHA1-Hash", hash)
  let resp, ri = (resource "/images", pic) ||> post

  resp.StatusCode =? Code.Created
  ri.sha1 =? hash
  mustHaveHeaders resp

Non sono riuscito a far funzionare Fiddler2 con WP7.

EDIT: Benvenuto in uno yak. Sono passato io stesso a pascoli più verdi;)


9
Se si from.AsyncReadblocca, significa che il server remoto non invia alcun byte.
qehgt

Sono uscito dal problema che il flusso non si chiude correttamente, ma sto ancora ricevendo solo file di dimensioni 40 byte sul lato ricevente e su WP molte operazioni che bloccano lanciano NotSupportedException invece di non essere disponibili , quindi è davvero doloroso eseguire il debug. Pubblicherò una soluzione completa quando arriverò.
Henrik

Non ho dimenticato questa domanda; solo molto da fare in questo momento, posterò fix a breve.
Henrik

5
Questo può esserti utile se stai cercando di far funzionare il violinista con dispositivi mobili: diaryofaninja.com/blog/2010/11/09/…
Alpha

2
Non è questo il comportamento previsto quando richiedi una quantità infinita di byte da un socket TCP?
jyoung

Risposte:


1

È necessario inserire i byte nell'output di BufferStream INput prima di inviare e utilizzare

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.