Короткая версия, по-видимому, при использовании XmlSerializer (или любого другого большого двоичного объекта) для выталкивания данных в NetworkStream просто удерживает строку открытой в течение неопределенного времени в ожидании записи дополнительной информации.Он сбрасывает соединение только после того, как вы его закроете.Это создает ситуацию, когда этот метод отлично подходит для отправки, но не получения.Или наоборот.Это становится односторонней связью и бесполезной для продолжения обратной и обратной связи по одному и тому же соединению.
Отчасти это дерьмо, что мне приходилось обходить что-то, что выглядело так элегантно на поверхности, но бросалоВозвращаясь к своим старым дням C, я сначала отправил пакет «количество байтов», а затем сам пакет.Это позволяет мне ПРОЧИТАТЬ на другом конце точное число байтов, поэтому я никогда не попаду в блокирующий шаблон.
Чтобы упростить свою жизнь, я создал класс, который содержит некоторые статические методы для отправки и получения.Этот класс может отправлять ЛЮБОЙ XML-сериализуемый класс по сети, поэтому он делает то, что мне нужно.
Если у кого-нибудь есть более элегантное решение, я буду открыт для его прослушивания.
public class PacketTransit
{
public static void SendPacket(TcpClient C, object Packet)
{
MemoryStream ms = new MemoryStream();
XmlSerializer xml = new XmlSerializer(Packet.GetType());
xml.Serialize(ms, Packet);
ms.Position = 0;
byte[] b = ms.GetBuffer();
ms.Dispose();
byte [] sizePacket = BitConverter.GetBytes(b.Length);
// Send the 4-byte size packet first.
C.Client.Send(sizePacket, sizePacket.Length, SocketFlags.None);
C.Client.Send(b, b.Length, SocketFlags.None);
}
/// The string is the XML file that needs to be converted.
public static string ReceivePacket(TcpClient C, Type PacketType)
{
byte [] FirstTen = new byte[1024];
int size = 0;
byte[] sizePacket = BitConverter.GetBytes(size);
// Get the size packet
int sp = C.Client.Receive(sizePacket, sizePacket.Length, SocketFlags.None);
if (sp <= 0) return "";
size = BitConverter.ToInt32(sizePacket, 0);
// read until "size" is met
StringBuilder sb = new StringBuilder();
while (size > 0)
{
byte[] b = new byte[1024];
int x = size;
if (x > 1024) x = 1024;
int r = C.Client.Receive(b, x, SocketFlags.None);
size -= r;
sb.Append(UTF8Encoding.UTF8.GetString(b));
}
return sb.ToString();
}
/// The XML data that needs to be converted back to the appropriate type.
public static object Decode(string PacketData, Type PacketType)
{
MemoryStream ms = new MemoryStream(UTF8Encoding.UTF8.GetBytes(PacketData));
XmlSerializer xml = new XmlSerializer(PacketType);
object obj = xml.Deserialize(ms);
ms.Dispose();
return obj;
}
public static RequestPacket GetRequestPacket(TcpClient C)
{
string str = ReceivePacket(C, typeof(RequestPacket));
if (str == "") return new RequestPacket();
RequestPacket req = (RequestPacket) Decode(str, typeof(RequestPacket));
return req;
}
public static ResponsePacket GetResponsePacket(TcpClient C)
{
string str = ReceivePacket(C, typeof(ResponsePacket));
if (str == "") return new ResponsePacket();
ResponsePacket res = (ResponsePacket)Decode(str, typeof(ResponsePacket));
return res;
}
}
Чтобы использовать этот класс, мне просто нужно вызвать PacketTransit.SendPacket(myTcpClient, SomePacket)
, чтобы отправить любой данный XML-Serializable объект.Затем я могу использовать PacketTransit.GetResponsePacket
или PacketTransit.GetRequestPacket
, чтобы получить его на другом конце.
Для меня это работает очень хорошо, но это было намного больше тренировки, чем первоначально ожидалось.