2012-04-15 116 views
0

我遇到了一個問題,那就是當我嘗試從我的服務器寫入到我的多個客戶端時,它不斷寫入空字符串。我分享我的代碼和它的多客戶端服務器應用程序,客戶端可以寫入服務器,而服務器可以寫入所有正在運行的客戶端。客戶寫入服務器安靜罰款。但服務器是不是:(Tcp客戶端服務器窗體應用程序問題

Server代碼:

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(); 
     } 
    } 
} 

客戶端代碼:

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(); 
      } 
     } 
    } 
} 

回答

2

什麼用 'textBox1的', 'TextBox2中', 'Button1的' 在那裏似乎不到風度在這段代碼中成爲一個特定於應用程序的組件名 - 你知道,這些東西使得經驗豐富的開發人員可以快速地瀏覽帖子,並且很容易理解。爲什麼在發佈之前你沒有將組件名稱編輯成有意義的內容?你的英語不好,(我可以理解) ,你的問題很清楚。

無論如何,你的作者並沒有調用()ing textbox2 read,然後第一個清除textBox2的作者贏得了比賽,然後塞滿了所有其他的作家,然後他們什麼都不讀。

..和在CheckKeys你發出的信號的MRE,然後在一個結算運行TextBox2中比賽,獲獎作家甚至得到一個機會讀它。

我不能看到你重置MRE它,反正是錯誤的同步對象使用。

你不能吝嗇多線程代碼。你必須正確地做。

您的作家線程有一些可能的通信。

1)使用鎖保護,引用計數的對象,直到所有的作家都用它做從TextBox2中堅持自己的傳出文本。這意味着你知道有多少作家 - 目前你還沒有追蹤的東西。與此相關的問題是編寫者不得退出,而不會退出refCount被初始化爲包含的所有'hold'實例的refCount。我懷疑你會得到這個權利。

2)使用對象來保存到您從TextBox2中傳出的文本。另外,將這個實例提交給超時隊列,該超時隊列保存在一個引用上,比如10分鐘,屆時所有編寫者肯定會使用它。這是一個合理的方法。

3)使用對象從TextBox2中堅持自己的傳出文本每個作家和生產者 - 消費者隊列每個作家提交不同的複製。這意味着你需要一個寫作者向量,隨着客戶端連接和斷開,必須保持最新。

4)有,我認爲寫其他三個時的第四個辦法,但我現在已經忘了。

我會與(2) - 最不可能的東西不工作。

哦 - 忘了,發信號通知所有的客戶端線程。如果您使用MRE,您將在哪裏重置?你怎麼知道所有的客戶什麼時候閱讀過東西,並且又要等待?你不能,我懷疑這就是爲什麼你的代碼中沒有resetEvent - 你不知道該把它放在哪裏。

因此,考慮到你有一個GC語言,給每個作者自己的BlockingCollection並將每個文本對象的引用排列到所有作者可能是最簡單和最安全的。同樣,我們又回到了一個(線程安全的)作家集合,這些作者集合中有新的條目放在連接上並在斷開連接時被刪除。即使使用線程安全的集合,您也必須期望並捕獲奇怪的異常,因爲主線程嘗試發出文本對象ref。對剛剛斷開連接的作家來說,還沒有完全擺脫收藏的束縛。

此外,每個作家的線程有點矯枉過正。最大。客戶端,您最好使用threadPool進行寫入。您只需要一個'SeverClientSocket'類,它將擁有自己的讀取線程,要添加到主線程的queueUpForWrite()方法以及要調用的線程池線程的writeQueue()方法。

+0

好朋友,在閱讀完整答案之前,我得告訴你這是我在stackoverflow.com上的第二個問題。我不知道規則,我不知道如何正確格式化代碼。我試圖儘快擺脫問題,所以我沒有任何修改就複製了整個代碼。我對此表示歉意。感謝您閱讀我的問題並回答問題。希望我能在這裏得到我的解決方案。如果我問你你的Facebook,你似乎是一位專家嗎? – 2012-04-15 12:08:24