Бинарная сериализация файла. BinaryFormatter

220
07 мая 2018, 21:59

У меня есть приложение клиент и сервер. Сервер отправляет файл по протоколу TCP, клиент принимает. Вот только после того как сервер отправил, клиент не может сделать десериализацию. Вот функция отправки файл.

public string SendFileName = null;
public void SendData()
{
    // Состав отсылаемого универсального сообщения
    // 1. Заголовок о следующим объектом класса подробной информации дальнейших байтов
    // 2. Объект класса подробной информации о следующих байтах
    // 3. Байты непосредственно готовых к записи в файл или для чего-то иного.
    SendInfo si = new SendInfo();
    //  Если нет отсылаемого файла продолжать процедуру отправки нет смысла.
    if (String.IsNullOrEmpty(SendFileName) == true) return;
    if (SendFileName != null)
    {
        FileInfo fi = new FileInfo(SendFileName);
        if (fi.Exists == true)
        {
            si.filesize = (int)fi.Length;
            si.filename = fi.Name;
        }
        fi = null;
    }
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();
    bf.Serialize(ms, si);
    ms.Position = 0;
    byte[] infobuffer = new byte[ms.Length];
    int r = ms.Read(infobuffer, 0, infobuffer.Length);
    ms.Close();
    byte[] header = GetHeader(infobuffer.Length);
    byte[] total = new byte[header.Length + infobuffer.Length + si.filesize];
    Buffer.BlockCopy(header, 0, total, 0, header.Length);
    Buffer.BlockCopy(infobuffer, 0, total, header.Length, infobuffer.Length);
    // Если путь файла указан, добавим его содержимое в отправляемый массив байтов
    if (si.filesize > 0)
    {
        FileStream fs = new FileStream(SendFileName, FileMode.Open, FileAccess.Read);
        fs.Read(total, header.Length + infobuffer.Length, si.filesize);
        fs.Close();
        fs = null;
    }
    try
    {
        // Отправим данные подключенным клиентам
        NetworkStream ns = _tcpClient.tcpClient.GetStream();
        // Так как данный метод вызывается в отдельном потоке рациональней использовать синхронный метод отправки
        ns.Write(total, 0, total.Length);
        // Обнулим все ссылки на многобайтные объекты и попробуем очистить память
        header = null;
        infobuffer = null;
        total = null;
        SendFileName = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        // Подтверждение успешной отправки
        Parent.ShowReceiveMessage("Данные успешно отправлены!");
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }  
}

Вот функция принятия файла на Клиентской стороне:

public void ReadCallback(IAsyncResult ar)
{
    if (modeNetwork == Mode.indeterminately) return;
    TcpClientData myTcpClient = (TcpClientData)ar.AsyncState;
    try
    {
        NetworkStream ns = myTcpClient.tcpClient.GetStream();
        int r = ns.EndRead(ar);
        if (r > 0)
        {
            // Из главного заголовка получим размер массива байтов информационного объекта
            string header = Encoding.Default.GetString(myTcpClient.buffer);
            int leninfo = int.Parse(header);
            // Получим и десериализуем объект с подробной информацией о содержании получаемого сетевого пакета
            MemoryStream ms = new MemoryStream(leninfo);
            byte[] temp = new byte[leninfo];
            r = ns.Read(temp, 0, temp.Length);
            ms.Write(temp, 0, r);
            BinaryFormatter bf = new BinaryFormatter();
            ms.Position = 0;
            SendInfo sc = (SendInfo)bf.Deserialize(ms);
            ms.Close();
            if (sc.filesize > 0)
            {
                // Создадим файл на основе полученной информации и массива байтов следующих за объектом информации
                FileStream fs = new FileStream(sc.filename, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, sc.filesize);
                do
                {
                    temp = new byte[global.MAXBUFFER];
                    r = ns.Read(temp, 0, temp.Length);
                    // Записываем строго столько байтов сколько прочтено методом Read()
                    fs.Write(temp, 0, r);
                    // Как только получены все байты файла, останавливаем цикл,
                    // иначе он заблокируется в ожидании новых сетевых данных
                    if (fs.Length == sc.filesize)
                    {
                        fs.Close();
                        fs = null;
                        break;
                    }
                }
                while (r > 0);
                temp = null;
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }


            if (Receive != null)
                Receive(this, new ReceiveEventArgs(sc));
            myTcpClient.buffer = new byte[global.LENGTHHEADER];
            ns.BeginRead(myTcpClient.buffer, 0, myTcpClient.buffer.Length, new AsyncCallback(ReadCallback), myTcpClient);
        }
        else
        {
            DeleteClient(myTcpClient);
            // Событие клиент отключился
            if (Disconnected != null)
                Disconnected.BeginInvoke(this, "Клиент отключился!", null, null);
        }
    }
    catch (Exception e)
    {
        MessageBox.Show(e.ToString());
        DeleteClient(myTcpClient);

        // Событие клиент отключился
        if (Disconnected != null)
        {
            Disconnected.BeginInvoke(this, "Произошла ошибка во время обновления Базы Даных. Просьба повторить попытку.", null, null);
        }
        SoundError();
    }
}

Вот это класс, который есть и на Клиенте и на Сервере:

[Serializable]
class SendInfo
{
    public string message;
    public string filename;
    public int filesize;
}

Вот на этой строчке

SendInfo sc = (SendInfo)bf.Deserialize(ms);

и происходит ошибка, а именно пишет:

{"Не удалось привести тип объекта \"SERVER.SendInfo\" к типу \"KLIENT.SendInfo\"."} System.Exception {System.InvalidCastException}

Answer 1

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

Существует два варианта решения проблемы:

  • реализовать explicit оператор;
  • вынесли сериализуемый класс в отдельную сборку (библиотека классов) и подключить ее для обоих проектов.

Ссылки по теме:

  • Ключевые слова преобразований: explicit
  • Двоичная сериализация
READ ALSO
Редактирование таблицы excel

Редактирование таблицы excel

Подскажите, как можно на c# редактировать таблицу? То есть, имеется xlsx файл с готовой таблицейИ на c# нужно как-то эту таблицу отредактировать...

172
Разработка обновляемых приложении на WPF

Разработка обновляемых приложении на WPF

Изучаю C# и WPFПодскажите пожалуйста какие темы необходимо прочитать для реализации удаленного обновления приложения у конечного пользователя

194
Внедрить dll в exe

Внедрить dll в exe

Подскажите пожалуйста, как исправить код ниже (c# winforms vs2010)Сейчас выдает ошибку "Не удалось загрузить файл Newtonsoft

200