2012-04-15 9 views
0

Ich habe mit einem Problem fest und das ist, wenn ich versuche, von meinem Server auf meine mehrere Clients schreiben, schreibt es weiterhin die leere Zeichenfolge. Ich teile meinen Code und seine multiple Client-Server-App, in der Clients auf den Server schreiben können, während der Server auf alle laufenden Clients schreiben kann. Clients schreiben auf den Server ruhig gut. aber Server ist nicht :(Tcp Client Server Formular App Problem

Server Code:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Net; 
using System.IO; 
using System.Net.Sockets; 
using System.Threading; 
using System.Collections; 

namespace ServerGui 
{ 
    public partial class Form1 : Form 
    { 
     TcpListener tcpListen; 
     Thread listenThread; 
     String read = ""; 
     ArrayList collect = new ArrayList(); 
     int counter = 0; 
     ArrayList NS = new ArrayList(); 
     TcpClient general = null; 
     readonly ManualResetEvent mre = new ManualResetEvent(false); 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void connectServerToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
      tcpListen = new TcpListener(IPAddress.Any, 3000); 
      listenThread = new Thread(new ThreadStart(ListenForClients)); 
      listenThread.Start(); 
      connectServerToolStripMenuItem.Enabled = false; 
     } 

     public void ListenForClients() 
     { 
      tcpListen.Start(); 

      while (true) 
      { 

       TcpClient client = tcpListen.AcceptTcpClient(); 
       collect.Insert(counter, client); 
       general = (TcpClient)collect[counter]; 
       if (textBox1.InvokeRequired) 
       { 
        textBox1.Invoke(new MethodInvoker(delegate { textBox1.AppendText(client.Client.LocalEndPoint.ToString() + " Connected."); 
        textBox1.AppendText(Environment.NewLine); })); 
       } 
       else 
       { 
        textBox1.AppendText(client.Client.LocalEndPoint.ToString() + " Connected."); 
        textBox1.AppendText(Environment.NewLine); 
       } 
       Thread clientThread = new Thread(new ParameterizedThreadStart(Reader)); 
       clientThread.Start(collect[counter]); 
       Thread clientThread1 = new Thread(new ParameterizedThreadStart(Writer)); 
       clientThread1.Start(collect[counter]); 
       counter++; 
      } 
     } 
     public void Reader(object client) 
     { 
      TcpClient tcpClient = (TcpClient)client; 
      NetworkStream clientStream = tcpClient.GetStream(); 
      StreamReader sr = new StreamReader(clientStream); 
      while (true) 
      { 
       try 
       { 
        read = sr.ReadLine(); 
       } 
       catch 
       { 
        if (textBox1.InvokeRequired) 
        { 
         textBox1.Invoke(new MethodInvoker(delegate 
         { 
           textBox1.AppendText(tcpClient.Client.LocalEndPoint.ToString() + 
" disconnected."); 
          textBox1.AppendText(Environment.NewLine); 
         })); 
        } 
        else 
        { 
         textBox1.AppendText(tcpClient.Client.LocalEndPoint.ToString() + " disconnected."); 
         textBox1.AppendText(Environment.NewLine); 
        } 
        return; 
       } 
       if (textBox1.InvokeRequired) 
       { 
        textBox1.Invoke(new MethodInvoker(delegate 
        { 
         textBox1.AppendText("<" +   tcpClient.Client.LocalEndPoint.ToString() + "> saying: " + read); 
         textBox1.AppendText(Environment.NewLine); 
        })); 
       } 
       else 
       { 
        textBox1.AppendText("<" + tcpClient.Client.LocalEndPoint.ToString() + "> saying: " + read); 
        textBox1.AppendText(Environment.NewLine); 
       } 
      } 

      tcpClient.Close(); 
     } 

     public void Writer(object client) 
     { 
      TcpClient tcpClient = (TcpClient)client; 
      NetworkStream ns = tcpClient.GetStream(); 
      mre.WaitOne(); 
      while (true) 
      { 
       StreamWriter sw = new StreamWriter(ns); 

        string str = textBox2.Text; 
        sw.WriteLine(str); 
        sw.Flush(); 

       if (textBox2.InvokeRequired) 
       { 
        textBox2.Invoke(new MethodInvoker(delegate 
         { 
          textBox2.Clear(); 
         })); 
       } 
       else 
       textBox2.Clear(); 
       } 
      tcpClient.Close(); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      mre.Set(); 
      textBox1.AppendText(textBox2.Text); 
      textBox1.AppendText(Environment.NewLine); 
      textBox2.Clear(); 
     } 

     private void CheckKeys(object sender, System.Windows.Forms.KeyPressEventArgs e) 
     { 
      if (e.KeyChar == (char)13) 
      { 
       mre.Set(); 
       textBox1.AppendText(textBox2.Text); 
       textBox1.AppendText(Environment.NewLine); 
       textBox2.Clear(); 
      } 
     } 

     private void exitToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
      Application.Exit(); 
     } 
    } 
} 

Client-Code.

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Net; 
using System.Net.Sockets; 
using System.Threading; 
using System.IO; 

namespace ClientGcui 
{ 
    public partial class Form1 : Form 
    { 
     NetworkStream general = null; 
     public Form1() 
     { 
      InitializeComponent(); 

     } 

     private void exitToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
      Application.Exit(); 
     } 

     private void connectToServerToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
      String str = ""; 
      TcpClient client = new TcpClient(); 
      IPEndPoint server = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000); 
      client.Connect(server); 
      textBox1.AppendText("Connected to server"); 
      textBox1.AppendText(Environment.NewLine); 
      NetworkStream clientStream = client.GetStream(); 
      general = clientStream; 
      StreamReader sr = new StreamReader(clientStream); 
      StreamWriter sw = new StreamWriter(clientStream); 
      Thread reader = new Thread(new ParameterizedThreadStart(readserver)); 
      reader.Start(sr); 
     } 
     public void readserver(object client) 
     { 
      StreamReader sr = (StreamReader)client; 
      string read = ""; 
      while (true) 
      { 
       try 
       { 
        read = sr.ReadLine(); 
       } 
       catch 
       { 
        if (textBox1.InvokeRequired) 
        { 
         textBox1.Invoke(new MethodInvoker(delegate 
         { 
          textBox1.AppendText("Disconnected from Server."); 
          textBox1.AppendText(Environment.NewLine); 
         })); 
        } 
        else 
        { 
         textBox1.AppendText("Disconnected from Server."); 
         textBox1.AppendText(Environment.NewLine); 
        } 
       } 
       if (textBox1.InvokeRequired) 
       { 
        textBox1.Invoke(new MethodInvoker(delegate 
        { 
         textBox1.AppendText(sr.ReadLine()); 
         textBox1.AppendText(Environment.NewLine); 
        })); 
       } 
       else 
       { 
        textBox1.AppendText(sr.ReadLine()); 
        textBox1.AppendText(Environment.NewLine); 
       } 
       } 
      sr.Close(); 
      } 


     private void button1_Click(object sender, EventArgs e) 
     { 
      StreamWriter sw = new StreamWriter(general); 
      sw.WriteLine(textBox2.Text); 
      sw.Flush(); 
      textBox1.AppendText(textBox2.Text); 
      textBox1.AppendText(Environment.NewLine); 
      textBox2.Clear(); 
     } 

     private void CheckKeys(object sender, System.Windows.Forms.KeyPressEventArgs e) 
     { 
      if (e.KeyChar == (char)13) 
      { 
       StreamWriter sw = new StreamWriter(general); 
       sw.WriteLine(textBox2.Text); 
       sw.Flush(); 
       textBox1.AppendText(textBox2.Text); 
       textBox1.AppendText(Environment.NewLine); 
       textBox2.Clear(); 
      } 
     } 
    } 
} 

Antwort

2

Was ist mit 'textBox1', 'TextBox2', 'button1' Es dosn't scheinen In diesem Code ist nur ein anwendungsspezifischer Komponentenname enthalten - Sie wissen, was es für erfahrene Entwickler einfach und schnell verständlich macht, wenn sie schnell in Posts suchen. Warum haben Sie die Komponentennamen vor dem Posten nicht auf etwas Sinnvolles geändert? dein Englisch ist schlecht, (was ich dann verstehen könnte) Deine Frage ist klar genug.

Wie auch immer, Ihr Schreiber ist nicht Invoke() ing die Textbox2 lesen und dann der erste Schreiber zu löschen textBox2 gewinnt das Rennen und stopft alle anderen Autoren, die dann nichts lesen.

..und in den CheckKeys signalisieren Sie die Mere und laufen dann auf einer Lichtung die Textbox2, bevor der Rennsieger sogar eine Chance bekommt, sie zu lesen.

Ich kann nicht sehen, wo Sie die MRE zurücksetzen, die, obwohl, ist das falsche Synchro-Objekt zu verwenden.

Sie können nicht auf Multithread-Code sparen. Du musst es richtig machen.

Es gibt einige Möglichkeiten für die Kommunikation mit Ihrem Writer-Thread.

1) Verwenden Sie ein gesperrtes Referenzobjekt, um den ausgehenden Text von textbox2 zu behalten, bis alle Autoren damit fertig sind. Dies bedeutet, dass Sie wissen, wie viele Autoren es gibt - etwas, das Sie derzeit nicht im Auge behalten. Das Problem dabei ist, dass Writer nicht beendet werden dürfen, ohne den refCount aller 'hold'-Instanzen zu dekomprimieren, deren refCount initialisiert wurde. Ich bezweifle, dass du das richtig machst.

2) Verwenden Sie ein Objekt, um Ihren ausgehenden Text von textbox2 zu halten. Übergeben Sie diese Instanz außerdem an eine Timeout-Warteschlange, die eine Referenz für beispielsweise 10 Minuten enthält, zu welcher Zeit alle Autoren sie definitiv verwendet haben. Dies ist ein vernünftiger Ansatz.

3) Verwenden Sie ein Objekt, um Ihren ausgehenden Text aus textbox2 für jeden Writer zu behalten, und senden Sie eine andere Kopie in einer Producer-Consumer-Warteschlange an jeden Writer. Dies bedeutet, dass Sie einen Vektor von Writern benötigen, die beim Verbinden und Trennen von Clients auf dem neuesten Stand gehalten werden müssen.

4) Es gab einen vierten Weg, an den ich beim Schreiben der anderen drei gedacht habe, aber ich habe es jetzt vergessen.

Ich würde mit (2) gehen - am wenigsten Chance, dass etwas nicht funktioniert.

Oh - vergessen, Signalisierung aller Client-Threads. Wenn Sie ein MRE verwenden, wo werden Sie es zurücksetzen? Wie können Sie feststellen, wann alle Kunden etwas gelesen haben und warten wollen? Sie können nicht, und ich vermute, dass das ist, warum Sie kein resetEvent in Ihrem Code haben - Sie wissen nicht, wo man es setzt.

Dank der Tatsache, dass Sie eine GC-Sprache haben, ist es am einfachsten und sichersten, jedem Schreiber eine eigene BlockingCollection zu geben und den Verweis auf jedes Textobjekt an alle Schreiber zu hängen. Wir sind wieder bei einer (thread-sicheren) Sammlung von Writern, die neue Einträge in connect einfügen und bei Trennung entfernen. Auch bei der threadsicheren Auflistung müssen Sie die ungerade Ausnahme erwarten und abfangen, da der Hauptthread versucht, eine Textobjekt-Referenz auszugeben. zu einem Schreiber, der gerade von sich getrennt hat, ist noch nicht ganz dazu gekommen, sich aus der Sammlung zu entfernen.

Auch ein Thread pro Writer ist ein bisschen Overkill. Für max. Clients, wäre es besser, den threadPool für Schreibvorgänge zu verwenden. Sie benötigen dann nur eine "SeverClientSocket" -Klasse mit einem eigenen Lese-Thread, einer queueUpForWrite() -Methode für den hinzuzufügenden Haupt-Thread und einer writeQueue() -Methode für einen aufzurufenden Pool-Thread.

+0

Nun Kumpel, vor dem Lesen Ihrer vollständigen Antwort, ich muss Ihnen sagen, das ist meine zweite Frage auf stackoverflow.com. Ich kenne die Regeln nicht, ich weiß nicht, wie man den Code richtig formatiert. Ich habe versucht, das Problem so schnell wie möglich loszuwerden, also habe ich den ganzen Code ohne irgendwelche Modifikationen kopiert. Ich entschuldige mich dafür. Danke, mein Problem gelesen und beantwortet. Ich hoffe, ich würde meine Lösung darin finden. Du scheinst ein Experte zu sein, wenn ich dich nach deinem Facebook frage? –