通过网络发送自定义结构-SerializationException

本文关键字:自定义 结构 -SerializationException 网络 | 更新日期: 2023-09-27 17:57:50

早上,我有一个TCP服务器和一个TCP客户端。这是服务器的代码:

public static class Server
{
    private static IPEndPoint endPoint;
    private static TcpListener tcpServer;
    private static List<Client> clients;
    private static Thread threadListen;
    private static ASCIIEncoding encoding;
    public static void Initialize(IPAddress allowedIPAddress, int port)
    {
        endPoint = new IPEndPoint(allowedIPAddress, port);
        tcpServer = new TcpListener(endPoint);
        clients = new List<Client>();
        threadListen = new Thread(new ThreadStart(Listen));
        encoding = new ASCIIEncoding();
    }
    public static void Start()
    {
        threadListen.Start();
    }
    public static byte[] PacketToArray(Packet packet)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream();
        formatter.Serialize(stream, packet);
        byte[] packetArray = stream.GetBuffer();
        stream.Close();
        return packetArray;
    }
    public static Packet ArrayToPacket(byte[] array)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream(array);
        Packet packet = new Packet();
        packet = (Packet) formatter.Deserialize(stream);
        stream.Close();
        return packet;
    }
    public static void Send(Client target, Packet packet)
    {
        byte[] packetArray = PacketToArray(packet);
        target.networkStream.Write(packetArray, 0, packetArray.Length);
        target.networkStream.Flush();
        OnSend(packet);
    }
    private static void Listen()
    {
        tcpServer.Start();
        while (true)
        {
            try
            {
                Client client = new Client();
                client.tcpClient = tcpServer.AcceptTcpClient();
                client.networkStream = client.tcpClient.GetStream();
                client.thread = new Thread(new ParameterizedThreadStart(HandleCommunication));
                clients.Add(client);
                client.thread.Start(client);
                OnJoin(client);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                break;
            }
        }
    }
    private static void HandleCommunication(object client)
    {
        Client handleClient = (Client) client;
        byte[] messageBuffer = new byte[4096];
        int bytesRead = 0;
        while (true)
        {
            bytesRead = 0;
            try
            {
                bytesRead = handleClient.networkStream.Read(messageBuffer, 0, messageBuffer.Length);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                break;
            }
            if (bytesRead == 0)
            {
                clients.Remove(handleClient);
                OnLeave(handleClient);
                break;
            }
            Packet packet = ArrayToPacket(messageBuffer);
            OnReceive(packet);
        }
    }
    private static void OnStart()
    {
        Console.WriteLine("Server started on port .");
    }
    private static void OnJoin(Client client)
    {
        Console.WriteLine("Client [ID] connected.");
        Send(client, PacketGenerator.Generate(OpCodes.opHandshake, null));
    }
    private static void OnLeave(Client client)
    {
        Console.WriteLine("Client [ID] disconnected.");
    }
    private static void OnSend(Packet packet)
    {
        Console.WriteLine("Server: " + packet.opcode.ToString() + " | " + packet.message);
    }
    private static void OnReceive(Packet packet)
    {
        Console.WriteLine("Client [ID]: " + packet.opcode.ToString() + " | " + packet.message);
    }
    public struct Client
    {
        public TcpClient tcpClient;
        public NetworkStream networkStream;
        public Thread thread;
    }
}

这是客户端的代码:

[Serializable]
public struct Packet
{
    public int opcode;
    public string message;
}
public static class Client
{
    private static IPEndPoint endPoint;
    private static TcpClient tcpClient;
    private static NetworkStream networkStream;
    private static Thread threadCommunication;
    private static ASCIIEncoding encoding;
    public static void Initialize(string ipAddress, int port)
    {
        endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
        tcpClient = new TcpClient();
        networkStream = null;
        threadCommunication = new Thread(new ThreadStart(HandleCommunication));
        encoding = new ASCIIEncoding();
    }
    public static void Connect()
    {
        tcpClient.Connect(endPoint);
        threadCommunication.Start();
        Console.WriteLine("Connected to server.");
        networkStream = tcpClient.GetStream();
    }
    public static byte[] PacketToArray(Packet packet)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream();
        formatter.Serialize(stream, packet);
        byte[] packetArray = stream.GetBuffer();
        stream.Close();
        return packetArray;
    }
    public static Packet ArrayToPacket(byte[] array)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream(array);
        Packet packet = new Packet();
        packet = (Packet)formatter.Deserialize(stream);
        stream.Close();
        return packet;
    }
    public static void Send(Packet packet)
    {
        byte[] packetArray = PacketToArray(packet);
        networkStream.Write(packetArray, 0, packetArray.Length);
        networkStream.Flush();
        OnSend(packet);
    }
    private static void HandleCommunication()
    {
        byte[] messageBuffer = new byte[4096];
        int bytesRead = 0;
        while (true)
        {
            bytesRead = 0;
            try
            {
                bytesRead = networkStream.Read(messageBuffer, 0, messageBuffer.Length);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                break;
            }
            if (bytesRead == 0)
            {
                Console.WriteLine("Connection closed.");
                break;
            }
            Packet packet = ArrayToPacket(messageBuffer);
            OnReceive(packet);
        }
    }
    private static void OnSend(Packet packet)
    {
        Console.WriteLine("Client [ID]: " + packet.opcode.ToString() + " | " + packet.message);
    }
    private static void OnReceive(Packet packet)
    {
        Console.WriteLine("Server: " + packet.opcode.ToString() + " | " + packet.message);
        switch (packet.opcode)
        {
            case OpCodes.opHandshake:
                Send(PacketGenerator.Generate(OpCodes.opHandshake, null));
                break;
        }
    }
}

但是,当我从服务器向客户端发送数据包(握手)时,客户端在"packet=(packet)formatter.Deserialize(stream);"确切的消息是:找不到程序集"ComDee,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"。(ComDee是运行服务器的程序集)。

为什么?客户端的数据包结构与服务器的结构相同。

更新

我已经编辑了Server和Client类,并使用了protobuf-net。但是客户端类中的"OnReceive"不会被调用。问题出在哪里?服务器类别:

[ProtoContract]
public struct Packet
{
    [ProtoMember(1)] public int opcode;
    [ProtoMember(2)] public string message;
}
public static class Server
{
    private static IPEndPoint endPoint;
    private static TcpListener tcpServer;
    private static List<Client> clients;
    private static Thread threadListen;
    private static ASCIIEncoding encoding;
    public static void Initialize(IPAddress allowedIPAddress, int port)
    {
        endPoint = new IPEndPoint(allowedIPAddress, port);
        tcpServer = new TcpListener(endPoint);
        clients = new List<Client>();
        threadListen = new Thread(new ThreadStart(Listen));
        encoding = new ASCIIEncoding();
    }
    public static void Start()
    {
        threadListen.Start();
    }
    public static byte[] PacketToArray(Packet packet)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream();
        Serializer.Serialize<Packet>(stream, packet);
        byte[] packetArray = stream.GetBuffer();
        stream.Close();
        return packetArray;
    }
    public static Packet ArrayToPacket(byte[] array)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream(array);
        Packet packet = new Packet();
        packet = Serializer.Deserialize<Packet>(stream);
        stream.Close();
        return packet;
    }
    public static void Send(Client target, Packet packet)
    {
        byte[] packetArray = PacketToArray(packet);
        target.networkStream.Write(packetArray, 0, packetArray.Length);
        target.networkStream.Flush();
        OnSend(packet);
    }
    private static void Listen()
    {
        tcpServer.Start();
        OnStart();
        while (true)
        {
            try
            {
                Client client = new Client();
                client.tcpClient = tcpServer.AcceptTcpClient();
                client.networkStream = client.tcpClient.GetStream();
                client.thread = new Thread(new ParameterizedThreadStart(HandleCommunication));
                clients.Add(client);
                client.thread.Start(client);
                OnJoin(client);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                break;
            }
        }
    }
    private static void HandleCommunication(object client)
    {
        Client handleClient = (Client) client;
        byte[] messageBuffer = new byte[4096];
        int bytesRead = 0;
        while (true)
        {
            bytesRead = 0;
            try
            {
                bytesRead = handleClient.networkStream.Read(messageBuffer, 0, messageBuffer.Length);
            }
            catch
            {
                clients.Remove(handleClient);
                OnLeave(handleClient);
                break;
            }
            if (bytesRead == 0)
            {
                Packet packet = ArrayToPacket(messageBuffer);
                OnReceive(packet);
            }
        }
    }
    private static void OnStart()
    {
        Console.WriteLine("Server started.");
    }
    private static void OnJoin(Client client)
    {
        Console.WriteLine("Client [ID] connected.");
        Send(client, PacketGenerator.Generate(OpCodes.opHandshake, null));
    }
    private static void OnLeave(Client client)
    {
        Console.WriteLine("Client [ID] disconnected.");
    }
    private static void OnSend(Packet packet)
    {
        Console.WriteLine("Server: " + packet.opcode.ToString() + " | " + packet.message);
    }
    private static void OnReceive(Packet packet)
    {
        Console.WriteLine("Client [ID]: " + packet.opcode.ToString() + " | " + packet.message);
    }
    public struct Client
    {
        public TcpClient tcpClient;
        public NetworkStream networkStream;
        public Thread thread;
    }
}

这是客户端类:

[ProtoContract]
public struct Packet
{
    [ProtoMember(1)]
    public int opcode;
    [ProtoMember(2)]
    public string message;
}
public static class Client
{
    private static IPEndPoint endPoint;
    private static TcpClient tcpClient;
    private static NetworkStream networkStream;
    private static Thread threadCommunication;
    private static ASCIIEncoding encoding;
    public static void Initialize(string ipAddress, int port)
    {
        endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
        tcpClient = new TcpClient();
        networkStream = null;
        threadCommunication = new Thread(new ThreadStart(HandleCommunication));
        encoding = new ASCIIEncoding();
    }
    public static void Connect()
    {
        tcpClient.Connect(endPoint);
        threadCommunication.Start();
        Console.WriteLine("Connected to server.");
        networkStream = tcpClient.GetStream();
    }
    public static byte[] PacketToArray(Packet packet)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream();
        Serializer.Serialize<Packet>(stream, packet);
        byte[] packetArray = stream.GetBuffer();
        stream.Close();
        return packetArray;
    }
    public static Packet ArrayToPacket(byte[] array)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream(array);
        Packet packet = new Packet();
        packet = Serializer.Deserialize<Packet>(stream);
        stream.Close();
        return packet;
    }
    public static void Send(Packet packet)
    {
        byte[] packetArray = PacketToArray(packet);
        networkStream.Write(packetArray, 0, packetArray.Length);
        networkStream.Flush();
        OnSend(packet);
    }
    private static void HandleCommunication()
    {
        byte[] messageBuffer = new byte[4096];
        int bytesRead = 0;
        while (true)
        {
            bytesRead = 0;
            try
            {
                bytesRead = networkStream.Read(messageBuffer, 0, messageBuffer.Length);
            }
            catch
            {
                Console.WriteLine("Connection closed.");
                break;
            }
            if (bytesRead == 0)
            {
                Packet packet = ArrayToPacket(messageBuffer);
                OnReceive(packet);
            }
        }
    }
    private static void OnSend(Packet packet)
    {
        Console.WriteLine("Client [ID]: " + packet.opcode.ToString() + " | " + packet.message);
    }
    private static void OnReceive(Packet packet)
    {
        Console.WriteLine("Server: " + packet.opcode.ToString() + " | " + packet.message);
        switch (packet.opcode)
        {
            case OpCodes.opHandshake:
                Send(PacketGenerator.Generate(OpCodes.opHandshake, null));
                break;
        }
    }
}

通过网络发送自定义结构-SerializationException

类型由其程序集确定作用域,BinaryFormatter序列化类型元数据(程序集限定名称等)。在两个位置拥有相同class的副本是不够的:它们不是相同的类型,除非它们来自同一程序集

还要注意,BinaryFormatter在线路上存储的内容非常冗长。如果你想要解决这两个问题而不需要手动进行所有序列化的东西,那么protobuf-net会有所帮助:

  • 它在电线上很密
  • 它不局限于特定的类型;只要A和B看起来相似,就可以用A序列化,用B反序列化

例如:

[ProtoContract]
public struct Packet {    
    [ProtoMember(1)] public int opcode;
    [ProtoMember(2)] public string message;
}

并且使用CCD_ 3而不是CCD_。