XmlSerializer 不会通过 NetworkStream 反序列化
XmlSerializer Won't Deserialize over NetworkStream
我想实现一个简单的 Client/Server 设置,它可以将序列化的 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 没有任何经验。对于我如何改进程序的任何帮助或建议,一如既往,我将不胜感激。谢谢。
这里的问题是 XmlSerializer
无法知道没有更多数据要反序列化,因为客户端未能关闭套接字。
调用serializer.Serialize(stream,request);
后,需要添加这行代码:
client.Client.Shutdown(SocketShutdown.Send);
这会在客户端提示end-of-stream,让服务器知道数据传输已经完成。这允许 XmlSerializer
知道没有更多的东西可以尝试反序列化并且它可以 return 新对象。
此外,服务器也从不调用 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
class,它可以以更方便的方式处理数据(即它会自动缓冲数据并正确检测流的结尾)。
最后,在收到剩余数据后(按照上述),您还应该关闭客户端套接字:
client.Close();
我想实现一个简单的 Client/Server 设置,它可以将序列化的 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 没有任何经验。对于我如何改进程序的任何帮助或建议,一如既往,我将不胜感激。谢谢。
这里的问题是 XmlSerializer
无法知道没有更多数据要反序列化,因为客户端未能关闭套接字。
调用serializer.Serialize(stream,request);
后,需要添加这行代码:
client.Client.Shutdown(SocketShutdown.Send);
这会在客户端提示end-of-stream,让服务器知道数据传输已经完成。这允许 XmlSerializer
知道没有更多的东西可以尝试反序列化并且它可以 return 新对象。
此外,服务器也从不调用 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
class,它可以以更方便的方式处理数据(即它会自动缓冲数据并正确检测流的结尾)。
最后,在收到剩余数据后(按照上述),您还应该关闭客户端套接字:
client.Close();