【问题标题】:XmlSerializer Won't Deserialize over NetworkStreamXmlSerializer 不会通过 NetworkStream 反序列化
【发布时间】:2015-02-13 04:03:22
【问题描述】:

我希望实现一个简单的客户端/服务器设置,可以将序列化的EmailRequest 对象(使用XmlSerializer)从客户端传输到服务器,用于发送电子邮件。

服务器

class Program
{
    private static void Main()
    {
        try
        {
            TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8888);

            listener.Start();

            while (true)
            {
                Console.WriteLine("Waiting for request..");

                TcpClient client = listener.AcceptTcpClient();

                NetworkStream stream = client.GetStream();

                XmlSerializer serializer = new XmlSerializer(typeof(EmailRequest));

                EmailRequest request = (EmailRequest)serializer.Deserialize(stream); // This is where the problem is //

                bool success = SendGridUtility.SendEmail(request.Recipients, request.Subject,
                    request.BodyType == EmailRequest.Type.Plain ? request.PlainText : request.HTMLText,
                    request.BodyType).Result;

                byte[] response = Encoding.ASCII.GetBytes(success ? "Success" : "Failure");
                Console.WriteLine("Email Successfully Sent!");

                stream.Write(response, 0, response.Length);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Something went wrong.. :( :\n" + e.Message + "\n\n");
        }
        Console.ReadLine();
    }
}

客户

class Program
{
    static void Main(string[] args)
    {
        try
        {
            TcpClient client = new TcpClient("127.0.0.1", 8888);

            NetworkStream stream = client.GetStream();

            XmlSerializer serializer = new XmlSerializer(typeof(EmailRequest));

            EmailRequest request = new EmailRequest
            {
                BodyType = EmailRequest.Type.Plain,
                HTMLText = "not used",
                PlainText = "Email Body",
                Recipients = new List<string> {"johnsmith@example.com"},
                Subject = "Email Subject"
            };

            serializer.Serialize(stream,request);

            Byte[] data = new Byte[256];

            Int32 bytes = stream.Read(data, 0, data.Length);
            string responseData = Encoding.ASCII.GetString(data, 0, bytes);
            Console.WriteLine("Received: {0}", responseData);
            Console.ReadLine();

        }
        catch (Exception e)
        {
            Console.WriteLine(e.StackTrace + "\n\n");
            Console.WriteLine(e.Message);
            Console.ReadLine();
        }
    }
}

电子邮件请求模型

[Serializable]
public class EmailRequest
{
    public enum Type
    {
        Plain,
        HTML
    }
    public List<string> Recipients { get; set; }
    public string Subject { get; set; }
    public Type BodyType { get; set; }
    public string PlainText { get; set; }
    public string HTMLText { get; set; }
}

当程序到达Deserialize 方法时,应用程序不会挂起,而是等待,就好像它在等待用户输入,我不知道为什么。除了我今天所做的之外,我对 TCP/XmlSerialization/Streams 没有任何经验。与往常一样,任何关于我如何改进程序的帮助或建议都将不胜感激。谢谢。

【问题讨论】:

    标签: c# tcp deserialization xmlserializer networkstream


    【解决方案1】:

    这里的问题是XmlSerializer无法知道没有更多的数据需要反序列化,因为客户端关闭套接字失败。

    在调用serializer.Serialize(stream,request);之后,你需要添加这行代码:

    client.Client.Shutdown(SocketShutdown.Send);
    

    这将指示来自客户端的流结束,以便服务器知道数据传输已完成。这使XmlSerializer 知道没有什么可以尝试反序列化并且它可以返回新对象。

    此外,服务器也从不调用Shutdown()。您可以使用发布的代码摆脱它,因为您的客户端在接收时不会检查流的结尾。

    虽然这可能几乎在所有时间都有效,但它并不完全正确。由于您不检查接收值的长度,因此您无法知道客户端已收到服务器响应发送的所有数据。

    相反,您应该首先修复服务器以调用Shutdown(),方法是在将响应写入流之后添加以下语句:

    client.Client.Shutdown(SocketShutdown.Both);
    client.Close();
    

    那么,你还需要修复客户端……

    客户端处理响应的一种更简单、更可靠的方法是将调用Shutdown()之后的所有内容替换为:

    using (StreamReader reader = new StreamReader(stream))
    {
        string line;
    
        while ((line = reader.ReadLine()) != null)
        {
            Console.WriteLine("Received: " + line);
        }
    }
    

    这使您不必直接担心底层流 I/O,而是将其委托给 StreamReader 类,它可以以更方便的方式处理数据(即它自动缓冲数据并正确检测结束流)。

    最后,在接收到剩余数据后(如上),您还应该关闭客户端套接字:

    client.Close();
    

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 2012-02-24
      • 2015-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多