开发者

c# XmlReader with base64 innertext

Ok here is my problem. I am trying to take a screenshot, add it to an xmldocument, send it over a socket, and read it with a XmlReader.

Here's some code...

Server Side

private void SendRandomData(object data)
{
    XMLShitSock sock = data as XMLShitSock;
    if(sock != null)
    {
        int incnum = 0;
        while(sock.Connected)
        {
            XmlDocument doc = new XmlDocument();
            XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
            doc.AppendChild(docNode);

            XmlNode productsNode = doc.CreateElement("image");

            productsNode.InnerText = Convert.ToBase64String(Program.CaptureImageToBytes(new Point(0, 0), new Rectangle(0, 0, Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height), ImageFormat.Png));
            doc.AppendChild(productsNode);
            string s = XMLShitSock.GetXmlString(doc);
            doc.Save("temp.xml");
            sock.WriteXMLMessage(doc);
            incnum++;
            Thread.Sleep(5000);
        }
    }
}

sock.WriteXMLMessage

public bool WriteXMLMessage(XmlDocument doc)
{
    try
    {
        ShittySocket.Client.Send(Encoding.ASCII.GetBytes(GetXmlString(doc)));
        return true;
    }
    catch(SocketException se)
    {
        this.Close();
        return false;
    }
}

To Read input

private void doInput()
{
    MemoryStream ms = new MemoryStream();
    NetworkStream ns = new NetworkStream(ShittySocket.Client, false);
    while(_connected)
    {
        if(ns.DataAvailable)
        {
            StreamReader sr = new StreamReader(ns);
            char[] b = new char[512];
            int nread = sr.Read(b, 0, 512);

            ms.Write(System.Text.Encoding.ASCII.GetBytes(b, 0, nread), 0, nread);
            ms.Seek(0, System.IO.SeekOrigin.Begin);

            XmlReaderSettings xrs = new XmlReaderSettings();

            XmlReader reader = XmlReader.Create(ms);
            if(reader.Read())
            {
                XmlDocument objXmlDocument = new XmlDocument();
                objXmlDocument.Load(reader);
                onInput(this, objXmlDocument);
                ms.Close();
                ms = new MemoryStream();
            }
        }
        Thread.Sleep(100);
    }
}

Problem I'm having is I get an error in doInput() saying the end of the document was reached without find the tag. I saved the xml file and looked through it and it exists, and I didn't even find any < characters beside开发者_StackOverflows those in the actual tags, so I'm not sure what's going on here. I'm obviously missing something.

Also, if you have concerns with the semantics or coding style, or anything that doesn't actually answer the question, please leave it as a comment, since it's not an answer.

Also I've look through this ->> http://msdn.microsoft.com/en-us/library/system.xml.xmltextreader.readbase64.aspx and I'm hoping that there is a better way to deal with base64 data than separating it from the XMLDocument itself. Thanks.


It looks like you're assuming the entire data will be read in one 512-byte chunk. Therefore, if the actual data is, say, 513 bytes long then you won't get the closing angle bracket > at the end of the document, thus the XML parser fails.

I don't know exactly what type of network stream you're using but if it's plain vanilla sockets then you need some kind of delimiter to know when you've reached the end of one "send" and the start of the next, because vanilla sockets are just a pipeline of data. With one read you could be receiving half, one, two, three-and-a-bit actual blocks.

Handily, you have a delimiter in the form of the closing tag.


Two things regarding doInput():

  1. XmlReaderSettings xrs = new XmlReaderSettings(); is not needed since you don't use it in subsequent XmlReader.Create(). And there would be no need to unless you set some properties of it first.

  2. You can't assume that reader.Read() read the whole xml right there. With XmlReader you have to do Read() until you get false in response.

It's also a good idea to using(var reader = XmlReader.Create(...)) { ... } so everything is nicely wrapped up when it's done reading.


try

ms.Position = 0; 

instead of

ms.Seek(0, System.IO.SeekOrigin.Begin);

in doInput()

also you can try to replace

Encoding.ASCII

with

Encoding.UTF8

or just replace a lot of code with just:

    private void doInput()
    {
        NetworkStream ns = new NetworkStream(ShittySocket.Client, false);
        while (_connected)
        {
            if (ns.DataAvailable)
            {
                using (StreamReader sr = new StreamReader(ns, System.Text.Encoding.UTF8))
                {
                        XmlDocument objXmlDocument = new XmlDocument();
                        objXmlDocument.LoadXml(sr.ReadToEnd());
                        onInput(this, objXmlDocument);
                }
            }
            Thread.Sleep(100);
        }
    }


Ok so I've came up with a solution. Here's the function

public static XmlDocument GrabXmlDocFromStream(StreamReader reader)
{
    using(MemoryStream ms = new MemoryStream())
    {
        byte b = 0;
        while(!reader.EndOfStream)
        {
            b = (byte)reader.Read();
            ms.WriteByte(b);
            ms.Seek(0, System.IO.SeekOrigin.Begin);

            using(XmlReader xmlreader = XmlReader.Create(ms))
            {
                XmlDocument doc = new XmlDocument();
                try
                {
                    if(Encoding.ASCII.GetChars(new byte[1] { b })[0] == '>')
                    {
                        if(xmlreader.Read())
                        {
                            XmlDocument objXmlDocument = new XmlDocument();
                            objXmlDocument.Load(xmlreader);
                            return objXmlDocument;
                        }
                    }
                }
                catch(XmlException xe) { }
            }
        }
    }
    return null;
}

You use it like this

            using(NetworkStream ns = new NetworkStream(ShittySocket.Client, false))
{
            using(StreamReader sr = new StreamReader(ns))
            {
                XmlDocument xdoc = new XmlDocument();
                while(xdoc != null)
                {
                    xdoc = Program.GrabXmlDocFromStream(sr);
                    if(xdoc != null)
                        Program.Handle_Document(xdoc);
                }
            }
}

Here are the other two function mentioned in the code

    public static string GetXmlString(XmlDocument doc)
    {
        StringWriter sw = new StringWriter();
        XmlTextWriter xw = new XmlTextWriter(sw);
        doc.WriteTo(xw);
        return sw.ToString();
    }

    public static void Handle_Document(XmlDocument doc)
    {
        MessageBox.Show(GetXmlString(doc));
    }

This probably isn't the best solution, but I used the line if(Encoding.ASCII.GetChars(new byte[1] { b })[0] == '>') so that I don't get an exception every character. From what I understand using try-catch in this manner isn't exactly the "right" way to solve problems, but it works, and I think I can suffer the minor time penalty involved. Any ideas would be nice, if you see something wrong with this let me know. Thanks.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜