locked
Doesn't read all content from socket in local http server RRS feed

  • Question

  • Hi,

    I'm using a local server in a Windows Store App (http://blog.jsolutions.co.uk/?p=492) for serve html files.

    The program reads the input from socket:

    StringBuilder inputRequestBuilder = new StringBuilder();
    
    // Read all the request data.
    // (This is assuming it is all text data of course)
    using (var input = socket.InputStream)
    {
        var data = new Windows.Storage.Streams.Buffer(BUFFER_SIZE);
        uint dataRead = BUFFER_SIZE;
    
        while (dataRead == BUFFER_SIZE)
        {
            await input.ReadAsync(data, BUFFER_SIZE, InputStreamOptions.Partial);
    
            var dataArray = data.ToArray();
            var dataString = Encoding.UTF8.GetString(dataArray, 0, dataArray.Length);
    
            inputRequestBuilder.Append(dataString);
            dataRead = data.Length;
        }
    }

    But sometimes, when the user submit a html form, the inputRequestBuilder has not all the content, for example:

    "POST /LMS/ajax.php?op=commitSCORM HTTP/1.1\r\nAccept: */*\r\nContent-Type: application/x-www-form-urlencoded\r\nReferer: http://localhost:8080/xxxxxxx.htm\r\nAccept-Language: es-ES,es;q=0.8,en-GB;q=0.5,en;q=0.3\r\nAccept-Encoding: gzip, deflate\r\nUser-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; WebView/1.0)\r\nHost: localhost:8080\r\nContent-Length: 2537\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\nCookie: port=8080\r\n\r\n"

    The content-length is 2537, but there is no content. So, I don't know what's the problem. Maybe the webview sends two packet (http header and form content) and... I don't know.

    I tried to read from socket again, when all the content is not ready, but VS throws: Error 0x80072746 The remote host closed the connection.

    Thanks!

    Thursday, November 7, 2013 9:40 AM

Answers

  • I urge everyone to use the HttpClient classes when possible; there are a number of compatibility "hacks" to handle a surprising number of "broken" web sites and services.


    Network Developer Experience Team (Microsoft)

    Tuesday, November 12, 2013 11:19 PM

All replies

  • Are you confident that all of the content is sent from the server?  You'll want to use NetMon or Wireshark to confirm this.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Thursday, November 7, 2013 8:09 PM
    Moderator
  • To read all of the data from a stream, you need to read until the stream returns zero and not until it's not returning some pre-set buffer size.

    The reason for this is that whatever packets were sent by the server were certainly broken up into smaller packets (sometimes in surprising ways!), very possibly reordered by the network, and then reassembled (also, sometimes in very surprising ways -- did you know that some network adapters will automatically coalesce incoming packets?) and passed up through some very sophisticated network code. (Some of my network customers read hundreds of thousands of packets per second)

    All this means that when you do the partial read, you might get all of the bytes you expect, and sometimes you don't.  It will vary based on the timing of packets and the timing of the rest of your app.  The real guarantee we make is that zero bytes == the stream is fully drained.


    Network Developer Experience Team (Microsoft)

    Saturday, November 9, 2013 12:15 AM
  • I realize I've another, more important question (I'm the PM for the Windows.Web.Http code) -- why not just use the nifty new HttpClient code?  It's designed to read data from web servers directly, and provides more functionality and is also easier to use than trying to pull the data from sockets.

    Indeed, our standard recommendation to customers that want to do HTTP with sockets is, "don't".  The protocol is trickier than it looks, and that causes problems with internationalization and security (among others).


    Network Developer Experience Team (Microsoft)

    Saturday, November 9, 2013 12:19 AM
  • I checked that the client sends the content with Wireshark.

    If I read until the stream returns 0, when the stream returns less than the buffer, the program doesn't pass the next readAsync(), because... it waits something? But the input socket should be closed? I don't know.

    I will try the HttpClient.

    Thanks!
    Monday, November 11, 2013 5:08 PM
  • Solved reading the content-length from header.

    The ugly script that works:

    StringBuilder inputRequestBuilder = new StringBuilder();
    List<byte> bytes = new List<byte>();
    List<byte> last4bytes = new List<byte>();
    string request = null;
    using (var input = socket.InputStream.AsStreamForRead())
    {
        int b;
        while (true)
        {
            try
            {
                b = input.ReadByte();
            }
            catch(Exception e) {
                FileManager.WriteToDebug(e.ToString());
                break;
            }
            bytes.Add((byte)b);
            if (last4bytes.Count < 4)
            {
                last4bytes.Add((byte)b);
            }
            else
            {
                last4bytes.RemoveAt(0);
                last4bytes.Add((byte)b);
                if (last4bytes.ElementAt(0) == 13 && last4bytes.ElementAt(1) == 10
                    && last4bytes.ElementAt(2) == 13 && last4bytes.ElementAt(3) == 10)
                {
                    //after \r\n\r\n there is the content
    				byte[] byteArray = bytes.ToArray();
                    request = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
                    inputRequestBuilder.Append(request);
                    string contentLength = getValueFromHeader("Content-Length", request);
                    if (!contentLength.Equals(""))
                    {
                        int length = Convert.ToInt32(contentLength);
                        if (length > 0)
                        {
                            byteArray = new byte[length];
                            input.Read(byteArray, 0, length);
                            var dataString = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
                            inputRequestBuilder.Append(dataString);
                        }
                    }
                    break;
                }
            }
        }
    }
    request = inputRequestBuilder.ToString();

    • Edited by JoniJnm Tuesday, November 12, 2013 11:34 AM
    Tuesday, November 12, 2013 11:31 AM
  • I urge everyone to use the HttpClient classes when possible; there are a number of compatibility "hacks" to handle a surprising number of "broken" web sites and services.


    Network Developer Experience Team (Microsoft)

    Tuesday, November 12, 2013 11:19 PM