Search code examples
c#binaryformatter

Why I get a SerializationException with BinaryFormatter?


I am working on a .NET application where the server sends JPG compressed images from a webcam to the client via TCP/IP. For serialization/deserialization I am using the BinaryFormatter class. When testing between my Desktop computer (client/Windows 10) and my Laptop (server/Windows 10), everything works fine in the long run. When using a LattePanda (server/Windows 7), I get the following error after approximately 3-5 minutes runtime (I send/receive 30 frames per second):

An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll

Additional information: The input stream is not a valid binary format. The starting contents (in bytes) are: 00-00-00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00 ...

Here's a snippet from the server code:

    private void distribute(Camera camera, Mat frame) {
        if(clientSockets!=null) {
            if(clientSockets.Count > 0) {
                if(camera.Streaming) {
                    // compress and encapsulate raw image with jpg algorithm
                    CameraImage packet = new CameraImage(camera.Id, frame, camera.CodecInfo, camera.EncoderParams);
                    packet.SystemId = Program.Controller.Identity.Id;
                    packet.SequenceNumber = curSeqNum;
                    byte[] content;
                    using(MemoryStream ms = new MemoryStream()) {
                        BinaryFormatter bf = new BinaryFormatter();
                        bf.Serialize(ms, packet);
                        content = ms.ToArray();
                    }
                    byte[] payload = new byte[content.Length+4];
                    // prefix with packet length
                    Array.Copy(BitConverter.GetBytes(content.Length), 0, payload, 0, 4);
                    // append payload after length header
                    Array.Copy(content, 0, payload, 4, content.Length);
                    // distribute to connected clients
                    this.distribute(payload);
                }
            }
        }
    }

    private void distribute(byte[] bytes) {
        if(Program.Launched) {
            lock(syncobj) {
                // distribute to connected clients
                for(int i=clientSockets.Count-1; i>=0; i--) {
                    try {
                        clientSockets[i].Send(bytes, bytes.Length, SocketFlags.None);
                    } catch(SocketException) {
                        clientSockets.RemoveAt(i);
                    }
                }
            }
        }
    }

Here's a snippet from the client code:

    private void receive() {
        try {
            while(running) {
                if((available = clientSocket.Receive(buffer, 4, SocketFlags.None)) > 0) {
                    // receive bytes from tcp stream
                    int offset = 0;
                    int bytesToRead = BitConverter.ToInt32(buffer, 0);
                    byte[] data = new byte[bytesToRead];
                    while(bytesToRead > 0) {
                        int bytesReceived = clientSocket.Receive(data, offset, bytesToRead, SocketFlags.None);
                        offset += bytesReceived;
                        bytesToRead -= bytesReceived;
                    }
                    // deserialize byte array to network packet
                    NetworkPacket packet = null;
                    using(MemoryStream ms = new MemoryStream(data)) {
                        BinaryFormatter bf = new BinaryFormatter();
                        packet = (NetworkPacket)bf.Deserialize(ms);
                    }
                    // deliver network packet to listeners
                    if(packet!=null) {
                        this.onNetworkPacketReceived?.Invoke(packet);
                    }
                    // update network statistics
                    NetworkStatistics.getInstance().TotalBytesRtpIn += data.Length;
                }
            }
        } catch(SocketException ex) {
            onNetworkClientDisconnected?.Invoke(agent.SystemId);
        } catch(ObjectDisposedException ex) {
            onNetworkClientDisconnected?.Invoke(agent.SystemId);
        } catch(ThreadAbortException ex) {
            // allows your thread to terminate gracefully
            if(ex!=null) Thread.ResetAbort();
        }
    }

Any ideas why I get the SerializationException only on one machine and not on the other? Different mscorlib.dll libraries installed? How to check the version of the relevant (?) libraries?


Solution

  • Here is a tweaked version of your answer. Right now if clientSocket.Available < 4 and running == true you have a empty while(true) { } loop. This is going to take up 100% of one cpu core doing no useful work.

    Instead of looping doing nothing till you have 4 bytes in the system I/O buffer use the same kind of loop you did for the payload for your message and load it to your byte array for the header. (I have also simplified the loop of reading the payload data to the loop I unusually use.)

    private void receive() {
        try {
            while(running) {
                int offset = 0;
                byte[] header = new byte[4];
    
                // receive header bytes from tcp stream
                while (offset < header.Length) {
                    offset += clientSocket.Receive(header, offset, header.Length - offset, SocketFlags.None);
                }
                    int bytesToRead = BitConverter.ToInt32(header, 0);
                    // receive body bytes from tcp stream
                    offset = 0;
                    byte[] data = new byte[bytesToRead];
                    while(offset < data.Length) {
                        offset += clientSocket.Receive(data, offset, data.Length - offset, SocketFlags.None);
                    }
                    // deserialize byte array to network packet
                    NetworkPacket packet = null;
                    using(MemoryStream ms = new MemoryStream(data)) {
                        BinaryFormatter bf = new BinaryFormatter();
                        packet = (NetworkPacket)bf.Deserialize(ms);
                    }
                    // deliver network packet to listeners
                    if(packet!=null) {
                        this.onNetworkPacketReceived?.Invoke(packet);
                    }
                    // update network statistics 
                    NetworkStatistics.getInstance().TotalBytesRtpIn += data.Length;
                }
            }
        } catch(SocketException ex) {
            onNetworkClientDisconnected?.Invoke(agent.SystemId);
        } catch(ObjectDisposedException ex) {
            onNetworkClientDisconnected?.Invoke(agent.SystemId);
        } catch(ThreadAbortException ex) {
            // allows your thread to terminate gracefully
            if(ex!=null) Thread.ResetAbort();
        }
    }
    

    However, if you switched from the System.Net.Socket class to the System.Net.TcpClient class you could simplify your code a lot. First, if you don't need TotalBytesRtpIn to be updating you can stop sending the header, it is not needed for the deserialization as BinaryFormatter already has it's length stored as a internal field of the payload. Then all you need to do is get the NetworkStream from the TcpClient and process the packets as they come in.

    private TcpClient _client; // Set this wherever you had your original Socket set up.
    
    private void receive() {
        try {
            using(var stream = _client.GetStream()) {
                BinaryFormatter bf = new BinaryFormatter();
                while(running) {
    
    
    #region This part is not needed if you are only going to deserialize the stream and not update TotalBytesRtpIn, make sure the server stops sending the header too!
                        int offset = 0;
                        byte[] header = new byte[4];
    
                        // receive header bytes from tcp stream
                        while (offset < header.Length) {
                            offset += stream.Read(header, offset, header.Length - offset);
                        }
                        int bytesToRead = BitConverter.ToInt32(header, 0);
    #endregion
                        packet = (NetworkPacket)bf.Deserialize(stream);
    
                        // deliver network packet to listeners
                        if(packet!=null) {
                            this.onNetworkPacketReceived?.Invoke(packet);
                        }
                        // update network statistics
                        NetworkStatistics.getInstance().TotalBytesRtpIn += bytesToRead;
                    }
                }
            }
        //These may need to get changed.
        } catch(SocketException ex) {
            onNetworkClientDisconnected?.Invoke(agent.SystemId);
        } catch(ObjectDisposedException ex) {
            onNetworkClientDisconnected?.Invoke(agent.SystemId);
        } catch(ThreadAbortException ex) {
            // allows your thread to terminate gracefully
            if(ex!=null) Thread.ResetAbort();
        }
    }