การส่งโครงสร้างที่กำหนดเองผ่านเครือข่าย - SerializationException

ตอนเช้า ฉันมีเซิร์ฟเวอร์ 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;

        }
    }

}

แต่เมื่อฉันส่งแพ็คเก็ตจากเซิร์ฟเวอร์ไปยังไคลเอนต์ (แฮนด์เชค) ไคลเอนต์จะได้รับข้อยกเว้นในฟังก์ชัน "ArrayToPacket" ที่ "packet = (Packet) formatter.Deserialize(stream);" ข้อความที่แน่นอนคือ: ไม่พบชุดประกอบ "ComDee, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" (คอมดีคือชุดที่เซิร์ฟเวอร์ทำงาน)

ทำไม โครงสร้างแพ็กเก็ตของไคลเอนต์เหมือนกับโครงสร้างของเซิร์ฟเวอร์

อัปเดต

ฉันได้แก้ไขคลาส Server และ Client และใช้ protobuf-net แต่ "OnReceive" ใน Client-Class จะไม่ถูกเรียก ปัญหาอยู่ที่ไหน? ระดับเซิร์ฟเวอร์:

[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;

    }

}

และนี่คือ Client-Class:

[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;

        }
    }

}

person namespace    schedule 06.02.2013    source แหล่งที่มา
comment
ผู้อ่านของคุณจะต้องอ่านต่อไปจนกว่าจะได้ค่าศูนย์หรือข้อผิดพลาดก่อนที่จะเรียกใช้ ArrayToPacket มิฉะนั้น คุณกำลังพยายามแยกวิเคราะห์ส่วนของข้อความทั้งหมด   -  person David Schwartz    schedule 06.02.2013
comment
ขออภัย ฉันค่อนข้างใหม่ในการเขียนโปรแกรม tcp ฉันจะตรวจสอบได้อย่างไรว่าการอ่านเสร็จสมบูรณ์แล้ว?   -  person namespace    schedule 06.02.2013
comment
ตรรกะในการส่งของคุณดูเหมือนฉันเข้ารหัสข้อความ ส่งข้อความ จากนั้นปิดการเชื่อมต่อ หากเป็นเช่นนั้น ตรรกะการรับที่ตรงกันคือฉันได้รับอย่างต่อเนื่องจนกว่าการเชื่อมต่อจะปิด จากนั้นฉันจะแยกวิเคราะห์ทุกอย่างที่ฉันได้รับเป็นข้อความเดียว แต่รหัสรับของคุณไม่ทำเช่นนั้น โปรดดูคำตอบนี้เพื่อเรียนรู้บทเรียนที่ใหญ่กว่าและสำคัญกว่ามาก   -  person David Schwartz    schedule 06.02.2013
comment
โอเค ฉันเข้าใจสิ่งที่คุณหมายถึง คุณช่วยส่งตัวอย่างโค้ดสำหรับตรรกะการรับที่ถูกต้องให้ฉันได้ไหม   -  person namespace    schedule 06.02.2013
comment
@namespace เมื่อคุณโทร networkStream.Read มันจะส่งคืนจำนวนไบต์ที่อ่าน คุณต้องเรียกวิธีนี้ต่อไปจนกว่าหมายเลขที่ส่งคืนจะเป็น 0 ด้วยวิธีนี้ คุณจะมีข้อมูลทั้งหมด ดู msdn.microsoft.com/en-us /ไลบรารี/   -  person Daniel Kelley    schedule 06.02.2013
comment
ฉันแก้ไขแล้ว @David Schwartz บางทีอาจมีบางคนสามารถแก้ปัญหาที่แท้จริงของฉันได้   -  person namespace    schedule 06.02.2013
comment
@namespace ฉันอาจผิด แต่คุณแค่เขียนทับบัฟเฟอร์ของคุณในแต่ละวง คุณผ่าน offset เป็น 0 ในการวนซ้ำแต่ละครั้ง ควรตั้งค่าเป็น bytesRead แต่คุณรีเซ็ตเป็น 0 ในแต่ละลูป   -  person Daniel Kelley    schedule 06.02.2013
comment
โอ้ ขอบคุณ - แต่ฉันเดาว่าฉันมีปัญหาอื่น - bytesRead ไม่เคยเป็น 0 เมื่อฉันได้รับข้อความแรกมันเป็น 11 ดังนั้น OnSend จะไม่ถูกเรียก ฉันจะแก้ปัญหานั้นได้อย่างไร?   -  person namespace    schedule 06.02.2013
comment
@namespace - ชำระเงินไลบรารีเครือข่ายโอเพ่นซอร์ส networkcomms.net นอกจากนี้ยังใช้ตัวสร้างอนุกรม protobuf-net และปัญหาส่วนใหญ่ที่คุณพบอาจได้รับการแก้ไขแล้ว การส่งออบเจ็กต์แบบกำหนดเองจะแสดงอยู่ในตัวอย่าง AdvancedSend ที่รวมไว้ หากคุณเห็นส่วนการเริ่มต้นใช้งาน (networkcomms.net/ การเริ่มต้นใช้งาน) คุณควรจะสามารถเริ่มต้นและดำเนินการได้ค่อนข้างเร็ว   -  person MarcF    schedule 06.02.2013
comment
ฉันทำให้มันทำงาน ขอบคุณสำหรับความช่วยเหลือของคุณ   -  person namespace    schedule 06.02.2013


คำตอบ (1)


ประเภทจะถูกกำหนดขอบเขตตามแอสเซมบลี และ BinaryFormatter จะทำให้ข้อมูลเมตาของประเภทเป็นอนุกรม (ชื่อที่ผ่านการรับรองแอสเซมบลี ฯลฯ) การมีสำเนา class ที่เหมือนกันในสองแห่งนั้นไม่เพียงพอ เนื่องจากไม่ใช่ ประเภท เดียวกัน เว้นแต่จะมาจาก ชุดประกอบ เดียวกัน

โปรดทราบว่า BinaryFormatter ค่อนข้างละเอียดในสิ่งที่เก็บไว้บนสาย หากคุณต้องการบางสิ่งที่จัดการปัญหาทั้งสองนี้โดยคุณไม่จำเป็นต้องทำซีเรียลไลซ์เซชันทั้งหมดด้วยตนเอง protobuf-net จะช่วย:

  • บนเส้นลวดมีความหนาแน่นมาก
  • ไม่ผูกมัดกับประเภทเฉพาะ คุณสามารถซีเรียลไลซ์ด้วย A และดีซีเรียลไลซ์ด้วย B ตราบใดที่ A และ B ดูคล้ายกัน

ตัวอย่างเช่น:

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

และใช้ Serializer.Serialize แทน BinaryFormatter

person Marc Gravell    schedule 06.02.2013
comment
Serializerมาจากไหน? - person AkariKamigishi; 07.05.2014