Проблемы с копированием потока памяти в сетевой поток

У меня проблема с этим кодом здесь.

using (MemoryStream ms = new MemoryStream())
{
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(ms,SerializableClassOfDoom);
    ms.Position = 0;
    byte[] messsize = BitConverter.GetBytes(ms.Length);
    ms.Write(messsize, 0, messsize.Length);
    NetworkStream ns = Sock.GetStream();
    ms.CopyTo(ns);
    //ms.Close();
}

Я не могу понять, что здесь происходит или почему это не работает. Вроде то ли не копирует, то ли сетевой поток закрывает, то ли еще что.

Извините, я пытался его отладить, но если кто-нибудь увидит здесь какую-либо очевидную проблему, я был бы признателен.

Кстати, класс сериализуется нормально, а MemoryStream содержит данные, но по какой-то причине выполнение ms.CopyTo(ns) просто не работает?

По сути, я хочу сериализовать класс в сетевой поток с предшествующим ему размером сериализованных данных. Если у кого-то есть лучший способ сделать это, дайте мне знать!


person Kelly Elton    schedule 29.11.2011    source источник
comment
я предполагаю, что после того, как вы покинете блок использования, NetworkStream удаляется, потому что он выходит за рамки. вы можете попробовать сбросить его до конца использования блока. Или ваша проблема где-то в другом?   -  person Davide Piras    schedule 29.11.2011
comment
Пробовал сбрасывать как поток памяти, так и сетевой поток до окончания использования, похоже, это тоже не помогло.   -  person Kelly Elton    schedule 29.11.2011
comment
Вам нужен еще один ms.Position = 0; после ms.Write. Редактировать: на самом деле первый ms.Position = 0; кажется неправильным, поскольку вы перезаписываете данные, которые вы только что написали.   -  person leppie    schedule 29.11.2011
comment
@leppie О боже, чувствую себя глупо. Я просто думал, что это сместит данные, которые там есть, вперед (по какой-то причине), но вместо этого, вы правы, это просто перезаписывает то, что уже было записано. Это должно быть проблемой.   -  person Kelly Elton    schedule 29.11.2011
comment
@kelton52: на всякий случай добавлю ответ :)   -  person leppie    schedule 29.11.2011


Ответы (3)


Вы сбрасываете позицию потока в неправильное время.

В вашем случае вы пишете «длину» в начало потока.

Следующее должно работать:

using (MemoryStream ms = new MemoryStream())
{
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(ms,SerializableClassOfDoom);
    byte[] messsize = BitConverter.GetBytes(ms.Length);
    ms.Write(messsize, 0, messsize.Length);
    ms.Position = 0;
    NetworkStream ns = Sock.GetStream();
    ms.CopyTo(ns);
}

Обновление:

Для записи «длины» в начало используйте временный поток/байт[].

Пример:

using (MemoryStream ms = new MemoryStream())
{
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(ms,SerializableClassOfDoom);
    byte[] data = ms.ToArray();
    byte[] messsize = BitConverter.GetBytes(ms.Length);
    ms.Position = 0;
    ms.Write(messsize, 0, messsize.Length);
    ms.Write(data, 0, data.Length);
    ms.Position = 0; // again!
    NetworkStream ns = Sock.GetStream();
    ms.CopyTo(ns);
}

Обновление 2:

Более эффективный способ.

using (MemoryStream ms = new MemoryStream())
{
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(ms,SerializableClassOfDoom);
    byte[] messsize = BitConverter.GetBytes(ms.Length);
    NetworkStream ns = Sock.GetStream();
    ns.Write(messsize, 0, messsize.Length);
    ms.Position = 0; // not sure if needed, doc for CopyTo unclear
    ms.CopyTo(ns); 
}
person leppie    schedule 29.11.2011
comment
Я хочу, чтобы размер сообщения был в начале потока, это сработает? Кажется, что это отбросило бы его за данные. - person Kelly Elton; 29.11.2011
comment
Прошу прощения, опять мало подробностей. По сути, это код, с которого я начал, но я хотел переписать его, чтобы мне не нужно было иметь какие-либо массивы за пределами messsize. Я пытаюсь сделать это очень быстро, и данные каждый раз превышают 1 МБ (для подключений по локальной сети). Я надеялся, что смогу манипулировать потоками таким образом, что это избавит меня от необходимости помещать поток в массив, что было самым дорогим вызовом. - person Kelly Elton; 29.11.2011
comment
@ kelton52: использование 2 потоков памяти, вероятно, было бы правильным, тогда, в качестве альтернативы, НЕ используйте Stream.CopyTo. Я обновлю ответ (снова). - person leppie; 29.11.2011
comment
Да, обновление 2 — это то, что я только что придумал, но почему вы говорите не использовать Stream.CopyTo? - person Kelly Elton; 29.11.2011
comment
@ kelton52: Из-за отсутствия однозначной документации. См. комментарий для установки позиции. В конце концов, под ним просто Stream.Read и Stream.Write. - person leppie; 29.11.2011

Возможно, вам нужно перемотать поток памяти в начало перед вызовом CopyTo (используйте ms.Seek(0, SeekOrigin.Start))

person nightwatch    schedule 29.11.2011

Я не вижу реальной проблемы, но я вижу здесь небольшую проблему рефакторинга.

Попробуйте использовать

Stream.Flush()
Stream.Close()

перед завершением любого потока.

Также окружите его подобными операторами try-catch, чтобы обеспечить закрытие и очистку, даже если есть исключение!

Stream s = new Stream();
try{
    s.Write();
}catch(Exception ex){
    //Oh god, we are doomed!
}finally{
    s.Flush();
    s.Close();
}

Ваш код будет выглядеть так

NetworkStream ns = null;
using (MemoryStream ms = new MemoryStream())
{
    try{
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(ms,SerializableClassOfDoom);
        ms.Position = 0;
        byte[] messsize = BitConverter.GetBytes(ms.Length);
        ms.Write(messsize, 0, messsize.Length);
        ns = Sock.GetStream();
        ms.CopyTo(ns);
        //ms.Close();
    }catch(Exception ex){
        throw;
    }finally{
        ms.Flush();
        if(ns != null)
            ns.Flush();

        ms.Close();
    }
}

После этого вы сможете найти свою проблему.

person oberfreak    schedule 29.11.2011
comment
Две вещи, которые я вижу неправильными в этом: ns не разрешается в блок finally. Кроме того, я не хочу закрывать сетевой поток. Я изменю его и попробую. - person Kelly Elton; 29.11.2011
comment
Обновляется с ns снаружи и не закрывается. - person oberfreak; 29.11.2011
comment
Это нормально, я, вероятно, не дал достаточно подробностей или что-то в этом роде. - person Kelly Elton; 29.11.2011