2016-11-21 31 views
0

Ich habe meinen Code hier, ich kann nicht verstehen, was der Fehler in diesem ist. Jemand sagte mir, das Problem bestehe darin, das 'Net'-Objekt in der' Handle Client'-Methode zu deserialisieren. Weil ich es jedes Mal überschreibe, wenn ein neuer Kunde kommt. Kannst du mir helfen, das zu lösen? Ich kann immer noch nicht herausfinden, was ich tun soll.Wie löst man diese Serialisierungsausnahme?

Zuerst funktioniert es für 2-3 Nachrichten und dann stürzt es ab.

Die Ausnahme, die ich bekommen ist:

Serialisierung Ausnahme - Der Eingangsstrom ist kein gültiges binäres Format. Die Anfangsinhalte (in Bytes) sind: FF-FF-FF-FF-06-44-61-76-69-64-3A-20-66-75-63-6B-20 ....

Auch einmal habe ich die gleiche Serialisierungs-Ausnahme nur gesagt - auf Top-Objekt.

Der Code:

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

Stellen Sie sicher, dass Ihr Code Thread-sicher ist. –

+1

Ich würde 'BinaryFormatter' nicht verwenden, um' string' zu serialisieren, es fügt zusätzliche Nutzlast hinzu, ist schwieriger zu debuggen und ist nicht mit Nicht-.NET-Clients kompatibel. Sie können stattdessen 'Encoding.UTF8' verwenden. –

Antwort

1

Das Net Feld hält durch die Kunden ersetzt zu werden, die zuletzt verbunden ist, so dass, obwohl Sie pro Client einen HandleClient Thread haben, all diese Fäden aus den zuletzt erhaltenen NetworkStream lesen .

In ähnlicher Weise wird das Feld bei jedem Verbindungsaufbau durch einen Client und das Feld umsg.Message bei jeder eingehenden Nachricht überschrieben.

Sie diese Probleme beheben, indem Sie die NetworkStream der einzelnen Verbindung zu HandleClient, Lieferung und eine lokale Variable für die empfangene Nachricht zu erstellen:

public void HandleClient(NetworkStream client) 
{ 
    ... 
    string message = bf.Deserialize(client).ToString(); 
    ... 
} 

Ebenso müssen Sie den Namen des Kunden an die weitergeben LetSubsKnow Methode, anstatt sich auf das umsg Feld, das immer aktualisiert wird zu verlassen.

public void LetSubsKnow(string clientName) 
{ 
    .... 
} 

Des Weiteren, obwohl Sie Pass die clientsStream um als Parameter, sie sind alle gleich Bezug auf die Clients Feld, und wenn ein Client eine Verbindung während Sie Daten senden, wird die foreach werfen a Ausnahme "Die Sammlung wurde geändert".

Dies kann durch Zugriff auf das Feld Clients mit einem Schloss behoben werden. Ich würde nicht den ganzen foreach Block in einem Verriegelungsabschnitt setzen, sondern eine Momentaufnahme der aktuell verbundenen Clients nehmen statt:

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 
} 

Beachten Sie, dass einige Ausnahme um den NetworkStream Zugriff auf Code Umgang brauchen werden.

Last but not least glaube ich nicht BinaryFormatter ist thread-safe (siehe this answer). Anstatt zu sperren, können Sie besser neue Methoden in den Methoden HandleClient und erstellen.