如何解决这个序列化异常?

时间:2016-11-21 14:24:21

标签: c# .net serialization deserialization

我的代码在这里,我无法理解这里的错误是什么。 有人告诉我问题是在'Handle Client'方法中反序列化'Net'对象时。因为每次新客户进来时我都会覆盖它。你能帮我解决这个问题吗?我仍然无法弄清楚我该怎么做。

首先它适用于2-3条消息然后崩溃。

我得到的例外是:

序列化异常 - 输入流不是有效的二进制格式。起始内容(以字节为单位)为:FF-FF-FF-FF-06-44-61-76-69-64-3A-20-66-75-63-6B-20 ....

还有一次我得到了同样的序列化异常,只是它说 - 在顶层对象上。

代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using System.Net.Sockets;
using System.Net;
using Message;

namespace Chat
{
    public partial class ChatWindow : Form
    {
    uMessage umsg = new uMessage();
    BinaryFormatter bf = new BinaryFormatter();
    NetworkStream Net;
    TcpListener listener;
    TcpClient client;
    List<NetworkStream> Clients = new List<NetworkStream>();

    public ChatWindow(string ip, int port)
    {
        InitializeComponent();
        umsg.IP = ip;
        umsg.Port = port;
    }

    public void OpenNewThread()
    {
        listener = new TcpListener(IPAddress.Parse(umsg.IP), umsg.Port); 
        listener.Start();
        Thread a = new Thread(Listen);
        a.Start();
    }

    public void Listen()
    {
        do
        {
            client = listener.AcceptTcpClient();
            Net = client.GetStream();
            Clients.Add(Net);
            umsg.Name = bf.Deserialize(Net).ToString();
            lstboxCurrentUsers.Invoke(new Action(() =>
            {
                lstboxCurrentUsers.Items.Add($"{umsg.Name} connected at " + DateTime.Now);
                listboxHistory.Items.Add($"{umsg.Name} connected at " + DateTime.Now);
            }));
            LetSubsKnow(Clients);


                Thread b = new Thread(() => HandleClient(Clients));
                b.Start();


        } while (true);
    }

    public void HandleClient(List<NetworkStream> ClientsStream)
    {
        while (true)
        {
            umsg.Message = bf.Deserialize(Net).ToString();

            foreach (var client in ClientsStream)
                {
                   bf.Serialize(client, umsg.Message);
                }
        }
    }

    public void LetSubsKnow(List<NetworkStream> clientsStream)
    {           
        foreach (var client in clientsStream)
        {
            bf.Serialize(client, $"{umsg.Name} Has Connected.");
        }
    }

1 个答案:

答案 0 :(得分:1)

Net字段不断被最近连接的客户端取代,因此即使每个客户端有一个HandleClient个线程,所有这些线程都会从最近获得的{{1}读取}。

类似地,每当客户端连接时,NetworkStream字段都会被覆盖,而只要有消息到达,就会覆盖umsg.Name字段。

您可以通过向umsg.Message提供单个连接的NetworkStream并为收到的消息创建本地变量来解决这些问题:

HandleClient

同样,您需要将客户端的名称传递给public void HandleClient(NetworkStream client) { ... string message = bf.Deserialize(client).ToString(); ... } 方法,而不是依赖于不断更新的LetSubsKnow字段。

umsg

此外,即使您传递 public void LetSubsKnow(string clientName) { .... } 周围的参数,它们都是clientsStream字段的相同引用,如果客户端连接 您正在发送数据,Clients会抛出一个&#34;该集合已被修改&#34;例外。

可以通过使用锁访问foreach字段来解决此问题。我不会将整个Clients块放在锁定部分中,而是拍摄当前连接的客户端的快照:

foreach

请注意,您需要围绕private readonly object clientsLock = new object(); List<NetworkStream> Clients = new List<NetworkStream>(); ... NetworkStream[] currentClients; lock(clientsLock) { currentClients = Clients.ToArray(); } foreach (NetworkStream client in currentClients) { // send stuff } 访问代码进行一些异常处理。

最后但并非最不重要的是,我认为NetworkStream是线程安全的(请参阅this answer)。您可以在BinaryFormatterHandleClient方法中创建新的内容,而不是锁定。