Я пишу программу, которая использует TcpListener
в качестве сервера. К нему подключаются несколько клиентов и отправляют/принимают пакеты.
Эти Packets
представляют собой просто сериализованные пользовательские классы, содержащие данные. Я использую BinaryFormatter
для сериализации и десериализации пакетов:
[Serializable]
public class Packet
{
public static implicit operator byte[](Packet p)
{
using (var memoryStream = new MemoryStream())
{
new BinaryFormatter().Serialize(memoryStream, p);
return memoryStream.ToArray();
}
}
protected byte[] Serialize(object o)
{
using (var memoryStream = new MemoryStream())
{
new BinaryFormatter().Serialize(memoryStream, o);
return memoryStream.ToArray();
}
}
public static object Deserialize(byte[] data)
{
using (var memoryStream = new MemoryStream(data))
return new BinaryFormatter().Deserialize(memoryStream);
}
}
Вот пример пакета, который я отправляю:
[Serializable]
public class Message : Packet
{
public string Text { get; set; }
public Message(string text)
{
Text = text;
}
public byte[] Serialize()
{
return Serialize(this);
}
}
Теперь, как я отправляю эти данные, я сначала создаю Header
, который указывает длину пакета, который мы сериализуем, а затем отправляю этот заголовок в NetworkStream
, а затем, после этого, он отправляет фактический пакет.
Я написал класс расширения для таких задач:
public static class StreamExtension
{
public static async Task WritePacket(this NetworkStream stream, byte[] packet)
{
var header = BitConverter.GetBytes(packet.Length); //Get packet length.
await stream.WriteAsync(header, 0, header.Length); //Write packet length as header.
await stream.WriteAsync(packet, 0, packet.Length); //Write actual packet.
Console.WriteLine($"Writing packet with length {packet.Length}");
}
}
Теперь, когда я пытаюсь подключиться, он отлично работает с первым подключающимся клиентом, он отправляет и получает все необходимые пакеты. Но когда присоединяется второй клиент, он пытается десериализовать пакеты, которые даже не отправляются.
Я создал попытку, чтобы увидеть, в чем проблема, и я получаю исключение, говорящее:
двоичный поток '0' не содержит допустимого двоичного заголовка
Это метод, который получает данные в клиенте:
private async void ReceiveData()
{
var header = new byte[4]; //Indicator for how big our actual packet is.
var stream = Server.GetStream();
while (await stream.ReadAsync(header, 0, header.Length) != 0)
{
var packetLength = BitConverter.ToInt32(header, 0);
var buffer = new byte[packetLength];
await stream.ReadAsync(buffer, 0, buffer.Length); //Read packet.
try {
var receivedPacket = Packet.Deserialize(buffer);
lbLog.Items.Add($"Received packet: {receivedPacket.GetType()}");
if (receivedPacket is SetPiece)
{
var setPiece = receivedPacket as SetPiece;
MyPiece = setPiece.Piece;
lblPiece.Text = $"You are {MyPiece}";
}
else if (receivedPacket is StartNew)
{
foreach (var box in Controls.OfType<PictureBox>())
box.Image = null;
}
else if (receivedPacket is SetTurn)
{
var setTurn = receivedPacket as SetTurn;
IsTurn = setTurn.IsMine;
lblTurn.Text = setTurn.IsMine ? "Your Turn" : $"{setTurn.Name}'s Turn";
}
else if (receivedPacket is Log)
{
var log = receivedPacket as Log;
lbLog.Items.Add(log.Message);
}
}
catch
{
File.AppendAllLines("errors.txt", new[] { $"Error occurred with packet. Length of {buffer.Length}" });
}
}
}
Вот лог консоли сервера:
А вот журнал ошибок:
Error occurred with packet. Length of 162
Error occurred with packet. Length of 256
Error occurred with packet. Length of 1952797486
Как может возникнуть ошибка с пакетом длиной 1952797486, если он даже не отправляется?
Любые идеи о том, что я делаю неправильно?
Read
(или здесьReadAsync
) возвращает сколько байтов было фактически прочитано, что может быть где-то между 1 и количеством байтов, которое вы запросили. Нет никакой гарантии, что вы получите полный набор байтов, который вызовWrite
принял на другом конце. Возможно, вам придется вызыватьRead
несколько раз. Если вам нужен обмен сообщениями, вы должны реализовать это поверх потока байтов, который дает вам TCP, или перейти на протокол более высокого уровня, который уже обеспечивает обмен сообщениями. . - person Damien_The_Unbeliever   schedule 29.03.2017while
после чтения заголовка, который считывает пакет только тогда, когда длина больше или равна тому, что мы запрашиваем? - person ThePerplexedOne   schedule 29.03.2017