简体   繁体   中英

Deserialization Exception when decoding a message

I'm working on a project that utilizes simple Socket connections to pass small variables between a client and a server using a Packet.cs object as the "mode of travel". The client and the server are part of different projects, but the same solution, and the Packet.cs is a separate "shared project" as well.

This is my packet.cs file:

using System;
using System.Net;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace ConnectionData
{
    [Serializable]
    public class Packet
    {
        // these are all the different types of things we can send
        public List<string> gData;
        public string packetString;
        public int packetInt;
        public bool packetBool;
        public PacketType packetType;

        // senderID is going to be the unique GUID that we generated
        public string senderID;


        public Packet (PacketType type, string senderID)
        {
            gData = new List<String> ();
            this.senderID = senderID;
            this.packetType = type;
        }

        public Packet(byte[] packetBytes)
        {
            // deconstructs the bytes we received into packet form
            BinaryFormatter bf = new BinaryFormatter ();
            MemoryStream ms = new MemoryStream (packetBytes);

            Packet p = (Packet)bf.Deserialize (ms);
            ms.Close ();

            // assigns all the values from the packet info we received in byte form
            this.gData = p.gData;
            this.packetInt = p.packetInt;
            this.packetBool = p.packetBool;
            this.senderID = p.senderID;
            this.packetType = p.packetType;
        }

        // this converts the whole packet object into a byte array to send through the socket
        public byte[] ToBytes()
        {
            BinaryFormatter bf = new BinaryFormatter ();
            MemoryStream ms = new MemoryStream ();

            bf.Serialize (ms, this);
            byte[] bytes = ms.ToArray ();
            ms.Close ();
            return bytes;
        }

        // this function will return the active IP address of the system. if it
        // cant find one, it returns default local address
        public static string GetIP4Address()
        {
            // this lists all addresses shown in IPConfig
            IPAddress[] ips = Dns.GetHostAddresses (Dns.GetHostName ());

            foreach(IPAddress i in ips)
            {
                // if there's an IP4 address in the list, return it
                if (i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) 
                {
                    return i.ToString ();
                }       
            }

            // else return local address
            return "127.0.0.1"; 
        }

        // enum makes it so we can define different strings, makes the packetType really easy to work with
        // allows us to define what kind of packet it is
        public enum PacketType
        {
            Registration, CloseConnection, Command
        }
    }
}

This is what I'm using to send the data. When I create a message on the server to send to the client, I simply call Packet p = new Packet(Packet.PacketType.Command); and socket.Send(p.ToBytes());

Receiving the packets (on both projects) uses:

public void Data_IN (object cSocket)
        {
            Socket clientSocket = (Socket)cSocket;

            byte[] buffer;
            int readBytes;

            while (Server.listening) {
                // sets our buffer array to the max size we're able to receive
                buffer = new byte[clientSocket.SendBufferSize];

                // gets the amount of bytes we've received
                readBytes = clientSocket.Receive (buffer);

                // if we actually recieve something, then sort through it
                if (readBytes > 0) {
                    // handle data
                    Packet packet = new Packet (buffer);
                    DataManager (packet); // handles the packet data
                }
            }
        }

The issue arises when I try to send the registration packet on the first connect. I start up my Server project and have it execute just waiting, everything working fine. The client IS able to connect, and the server sends the registration packet which is created here:

Packet p = new Packet (Packet.PacketType.Registration, Server.guid);
p.packetString = HeartCore.commandKey; // commandKey is a static string variable
clientSocket.Send (p.ToBytes ());

The server shows that it's been sent successfully, and then the client throws an exception. YAY! The client handles the receipt of the packet as shown above with Data_IN . The exception occurs right after it creates the new packet in

if (readBytes > 0) {
    // handle data
    Packet packet = new Packet (buffer); // this is where it stops
    DataManager (packet); // handles the packet data
}

and

public Packet(byte[] packetBytes)
{
    // deconstructs the bytes we received into packet form
    BinaryFormatter bf = new BinaryFormatter ();
    MemoryStream ms = new MemoryStream (packetBytes);

    Packet p = (Packet)bf.Deserialize (ms); //EXCEPTION OCCURS HERE
    ms.Close ();
    ...

If you recall, the packet class has a constructor that takes the byte[] buffer and converts it into an object, and then copies the values from the temp packet object to the actual object being used.

The exception being thrown is

System.Runtime.Serialization.SerializationException: Couldn't find assembly 'Heart' ---> System.Exception: Could not load file or assembly 'Heart' or one of its dependencies. The system cannot find the file specified.
  at System.AppDomain.Load (System.String assemblyString, System.Security.Policy.Evidence assemblySecurity, Boolean refonly) [0x00000] in <filename unknown>:0
  at System.AppDomain.Load (System.String assemblyString) [0x00000] in <filename unknown>:0
  at at (wrapper remoting-invoke-with-check) System.AppDomain:Load (string)
  at System.Reflection.Assembly.Load (System.String assemblyString) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetDeserializationType (Int64 assemblyId, System.String className, Boolean throwOnError) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetDeserializationType (Int64 assemblyId, System.String className, Boolean throwOnError) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetDeserializationType (Int64 assemblyId, System.String className) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadTypeMetadata (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectInstance (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (BinaryElement element, System.IO.BinaryReader reader) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) [0x00000] in <filename unknown>:0
  at ConnectionData.Packet..ctor (System.Byte[] packetBytes) [0x00016] in /home/austin/Programming/C#/Crystal-Home-Systems/ConnectionData/Packet.cs:63
  at Shard.Client.Data_IN () [0x0002f] in /home/austin/Programming/C#/Crystal-Home-Systems/Shard/Client.cs:90
  at System.Threading.Thread.StartInternal () [0x00000] in <filename unknown>:0

Now, I'm semi-new to C# itself, but I have a decent amount of experience with Java and have done this type of server-client work with that before. I've spent the past 5 hours or so changing things around, using Google, even asking a couple programming friends and we have been unable to figure out what's going on. Couldn't find assembly 'Heart' refers I THINK to the project Heart which is the server class that sends the packet that causes the crash.

Does anyone have any idea what might be causing the crash? I'm getting irritated and feel like I'm missing something super obvious. I tried to be as specific as I could, please let me know if I forgot to put anything into the question or if you need more details!

Thank you so much in advance for any help!

EDIT: Just to clarify, the server and client are able to connect without a problem, the error occurs when trying to decode a registration packet that is sent by the server to the client. Also, this runs on a Linux system Kubuntu using mono. I dont think that'd make a difference, but it might

By using bf.Serialize (ms, this) to send a packet, your client needs the assembly that contains typeof(this) , being the Heart assembly. The client then needs that assembly to deserialize it.

Don't serialize an internal structure ( Packet ), serialize a Payload class that you move into an assembly and share that with the client.

The problem though is that this code:

// gets the amount of bytes we've received
readBytes = clientSocket.Receive (buffer);

// if we actually recieve something, then sort through it
if (readBytes > 0) {
    // handle data
    Packet packet = new Packet (buffer);
    DataManager (packet); // handles the packet data
}

Might work on localhost in a small number of calls, but it will fail when any serious traffic is achieved, as buffer can then contain half a message, or three and a half messages.

You need to implement a framing protocol to detect separate messages.

But while you're doing that, you seem to be recreating Protocol Buffers from scratch. Use existing libraries for stuff like this.

I found an answer to my problem. The Packet.cs file was inside of a shared project. As far as I can tell, a shared project uses resources from all other projects inside the solution which is NOT what I wanted to do.

I tried all sorts of things, including the amazing answer that CodeCaster gave, however nothing worked.

To solve the Exception, I remade the Packet.cs file into a Shared Library project, instead of a Shared Project. That allowed for the Packet.cs file to be put as a .dll file into the build path, letting it not have resources from other projects.

Solution built and executed perfectly! I hope this helps someone in the future!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM