2016-11-21 9 views
0

ここに自分のコードがありますが、このバグは何か分かりません。 「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

コードがスレッドセーフであることを確認してください。 –

+1

'BinaryFormatter'を' string'をシリアル化するために使用せず、ペイロードを追加し、デバッグするのが難しく、非.NETクライアントと互換性がありません。代わりに 'Encoding.UTF8'を使用したいかもしれません。 –

答えて

1

Netフィールドは最も最近接続したクライアントに置き換え取得し続けるので、あなたは、クライアントごとに1 HandleClientのスレッドを持っているにもかかわらず、これらすべてのスレッドが最近NetworkStream得たから読み取ります。

同様に、umsg.Nameフィールドは、クライアントが接続するたびに上書きされ、メッセージが到着するたびにumsg.Messageフィールドが上書きされます。

あなたはHandleClientへの個々の接続のNetworkStreamを供給することにより、これらの問題を修正することができ、および受信したメッセージのローカル変数を作成:

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

は同様にあなたは、クライアントの名前を渡す必要がありますumsgフィールドに依存するのではなく、LetSubsKnowメソッドを使用します。あなたはパスパラメータとして周りclientsStream、彼らはClientsフィールドへのすべての参照同じであり、あなたがデータを送っているながら、クライアントが接続している場合、foreachがスローされますさらににもかかわらず

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

"コレクションは変更されました"例外。

これは、ロックを使用してClientsフィールドにアクセスすることで修正できます。私は、ロック部の内側全体foreachブロックを置くが、その代わりに、現在接続しているクライアントのスナップショットを取得しません:あなたはNetworkStreamアクセスコードの周りに扱ういくつかの例外が必要になるだろう

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 
} 

注意を。

最後には、BinaryFormatterはスレッドセーフです(this answer参照)。ロックする代わりに、HandleClientLetSubsKnowの方法で新しいものを作成するほうが良いかもしれません。

関連する問題