2014-01-29 27 views
2

好吧,我正在嘗試通過TCP/IP爲我的一羣在線玩DnD的朋友創建一個簡單的聊天應用程序。最終我想添加更多功能,但現在我只想聊天工作!爲什麼我的閱讀器和書寫器突然停止工作?

這裏是我對主服務器

class MainServer 
{ 
    IPAddress m_address = IPAddress.Parse("127.0.0.1"); 
    Int32 m_port = 5550; 
    public static Hashtable userNicknames = new Hashtable(50); 
    public static Hashtable connectionToNick = new Hashtable(50); 

    public MainServer() 
    { 
     TcpListener listener = new TcpListener(m_address, m_port); 
     Thread listenThread = new Thread(new ParameterizedThreadStart(StartListening)); 
     listenThread.Start(listener); 
     Console.WriteLine("Listening for incoming connection requests..."); 
    } 

    private void StartListening(Object listener) 
    { 
     TcpListener server = (TcpListener)listener; 
     ClientCommCenter commC; 

     server.Start(); 

     while (true) 
     { 
      if (server.Pending()) 
      { 
       TcpClient client = server.AcceptTcpClient(); 
       Console.WriteLine("Client has connected..."); 
       commC = new ClientCommCenter(client); 
      } 
     } 
    } 

    public static void SendSystemMessage(string msg) 
    { 
     StreamWriter writer; 
     TcpClient[] connectedClients = new TcpClient[MainServer.userNicknames.Count]; 
     MainServer.userNicknames.Values.CopyTo(connectedClients, 0); 

     for (int ii = 0; ii < connectedClients.Length; ii++) 
     { 
      try 
      { 
       if (msg.Trim().Equals(String.Empty)) 
        continue; 

       writer = new StreamWriter(connectedClients[ii].GetStream());      
       writer.WriteLine("Message from server: " + msg); 
       writer.Flush(); 
       writer = null; 
      } 
      catch (Exception e) 
      { 
       MainServer.userNicknames.Remove(MainServer.connectionToNick[connectedClients[ii]]); 
       MainServer.connectionToNick.Remove(connectedClients[ii]); 
      } 
     } 
    } 

    public static void SendMessageToAll(string nickname, string msg) 
    { 
     StreamWriter writer; 
     TcpClient[] connectedClients = new TcpClient[MainServer.userNicknames.Count]; 
     MainServer.userNicknames.Values.CopyTo(connectedClients, 0); 

     for (int ii = 0; ii < connectedClients.Length; ii++) 
     { 
      try 
      { 
       if (msg.Trim().Equals(String.Empty)) 
        continue; 

       writer = new StreamWriter(connectedClients[ii].GetStream());     

       writer.WriteLine(nickname + ": " + msg); 
       writer.Flush(); 
       writer = null; 
      } 
      catch (Exception e) 
      { 
       String user = (string)MainServer.connectionToNick[connectedClients[ii]]; 
       SendSystemMessage("ATTENTION: " + user + " has disconnected from chat"); 

       MainServer.userNicknames.Remove(user); 
       MainServer.connectionToNick.Remove(connectedClients[ii]); 
      } 
     } 
    } 
} 

這裏是主要的通信類,

class ClientCommCenter 
{ 
    TcpClient m_client; 
    StreamReader m_reader; 
    StreamWriter m_writer; 
    String m_nickname; 

    public ClientCommCenter(TcpClient client) 
    { 
     m_client = client; 
     Thread chatThread = new Thread(new ThreadStart(StartChat)); 
     chatThread.Start(); 
    } 

    private String GetNick() 
    { 
     m_writer.WriteLine("Enter a nickname to begin."); 
     m_writer.Flush(); 

     return m_reader.ReadLine(); 
    } 

    private void StartChat() 
    { 
     m_reader = new StreamReader(m_client.GetStream()); 
     m_writer = new StreamWriter(m_client.GetStream()); 

     m_writer.WriteLine("Connected to DnD Chat!!"); 
     m_nickname = GetNick(); 

     while (MainServer.userNicknames.Contains(m_nickname)) 
     { 
      m_writer.WriteLine("ERROR!!! Username already in use"); 
      m_nickname = GetNick(); 
     } 

     MainServer.userNicknames.Add(m_nickname, m_client); 
     MainServer.connectionToNick.Add(m_client, m_nickname); 

     MainServer.SendSystemMessage("****** " + m_nickname + " ****** has joined the chat!"); 
     m_writer.WriteLine("Now connected...."); 
     m_writer.Flush(); 

     Thread startChatting = new Thread(new ThreadStart(runChat)); 
     startChatting.Start(); 
    } 

    private void runChat() 
    { 
      try 
      { 
       String clientMessage = String.Empty; 

       while(true){ 
        clientMessage = m_reader.ReadLine(); 
        MainServer.SendMessageToAll(m_nickname, clientMessage); 
       } 
      } 
      catch(Exception e) 
      { 
       Console.WriteLine(e); 
      } 
    } 
} 

,最後由每個客戶單獨使用的代碼,在這裏是Client類的代碼:

public partial class MainForm : Form 
{ 
    [DllImport("kernel32.dll")] 
    private static extern void ExitProcess(int a); 

    TcpClient client; 
    StreamReader m_reader; 
    StreamWriter m_writer; 

    public MainForm() 
    { 
     InitializeComponent(); 
    } 

    private void MainForm_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     e.Cancel = false; 
     Application.Exit(); 

     if (m_reader != null) 
     { 
      m_reader.Dispose(); 
     } 

     ExitProcess(0);   

    } 

    private void MainForm_KeyUp(object sender, KeyEventArgs e) 
    { 
     if (e.KeyCode == Keys.Enter) 
     { 
      SendChat(); 
     } 
    } 

    private void SendChat() 
    { 
     TextBox txtChat = (TextBox)chatEntry; 

     if (chatEntry.Lines.Length >= 1) 
     { 
      m_writer.WriteLine(txtChat.Text); 
      m_writer.Flush(); 
      chatEntry.Text = String.Empty; 
      chatEntry.Lines = null; 
     } 
    } 

    private void RunChat() 
    { 
     StreamReader reader = new StreamReader(client.GetStream()); 

     while (true) 
     { 
      Application.DoEvents(); 

      if (this.InvokeRequired) 
      { 
       this.Invoke(new MethodInvoker(delegate{ 
        RunChat(); 
       })); 
      } 

      if (reader.Peek() > 0) 
      { 
       chatDisplay.AppendText(reader.ReadLine() + "\r\n"); 
       chatDisplay.SelectionStart = chatDisplay.Text.Length; 
      } 

     } 
    } 

    private void toolstripConnectButton_Click(object sender, EventArgs e) 
    { 
     client = new TcpClient("127.0.0.1", 5550); 
     m_writer = new StreamWriter(client.GetStream()); 
     m_reader = new StreamReader(client.GetStream()); 

     Thread chatThread = new Thread(new ThreadStart(RunChat));    

     chatThread.Start(); 

     while (true) 
     { 
      Application.DoEvents(); 
     } 
    } 

    private void sendButton_Click(object sender, EventArgs e) 
    { 
     SendChat(); 
    } 
} 

我對上述代碼的問題是這樣的:我可以很好地連接到正在運行的服務器,並且我已正確提示服務器已連接,然後提示我輸入暱稱。

我在文本框中鍵入暱稱,然後按發送。但是,在發生這種情況後,我不再一起接收來自服務器的消息。毫無意義。我什至垃圾的連接按鈕,並不斷地顯示了兩個相同的消息:

「已連接」 「輸入一個綽號」

我一直在努力,現在摸不着頭腦了近5個小時,而我根本不知道發生了什麼。我覺得這是非常簡單的事情,因爲解決方案總是很簡單。

所以,慷慨的人,你能解決我的問題嗎?爲什麼我的StreamReader和Streamwriter突然停止工作?!?!?!

+1

您是否收到任何錯誤或僅收到沒有數據? –

+0

它看起來像你正在閱讀從TCP連接暱稱而不是客戶端控制檯/文本框 – mao47

+0

我的壞 - 我誤解了代碼的一部分。您是否曾嘗試在客戶端和/或服務器上進行調試,以檢查代碼是否正在發送暱稱並接收暱稱?您是否使用wireshark或fiddler來檢查網絡流量,看看您是否實際上正在發送數據包? – mao47

回答

1

兩件事情:

首先,跳過if (reader.Peek() > 0)。請致電reader.ReadLine();這將阻塞,直到你有一條線路可用。我不知道爲什麼,但即使在發送消息之後,Peek返回-1,但是ReadLine在該點返回一行,從而修復了問題。無論如何,在Application.DoEvents()上旋轉都沒有幫助。

(同樣,您可以跳過if (server.Pending()))。

二,您對Invoke的使用有問題;您不應該「調用」RunChat(),因爲這是反覆輪詢流以獲取新數據的方法。這意味着您將在UI線程上運行整個方法,這正是您想要避免的的。 UI正在忙於抽取Windows消息隊列。您應該「調用」僅修改控件屬性的代碼。

(我懷疑這就是爲什麼你認爲有必要無論如何使用Application.DoEvents()。你不應該需要它,如果你是正確處理您的線程。)

(此外,第一件事你應該做的是檢查InvokeRequired。現在你的方法是創建一個你永遠不會使用的StreamReader,還有其他的地方你可以這樣做,但這不是主題。)

這裏有兩個建議:

private void RunChat() 
{ 
    StreamReader reader = new StreamReader(client.GetStream()); 

    Delegate invoker = new Action<string>(AppendChatText); 

    while (true) 
     Invoke(invoker, reader.ReadLine()); 
} 

,或者使用更經典的 「調用」 圖案:

private void RunChat() 
{ 
    StreamReader reader = new StreamReader(client.GetStream()); 

    while (true) 
     AppendChatText(reader.ReadLine()); 
} 

private void AppendChatText(string text) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke((Action<string>)AppendChatText, text); 
     return; 
    } 

    chatDisplay.AppendText(text + "\r\n"); 
    chatDisplay.SelectionStart = chatDisplay.Text.Length; 
} 

第一具有僅創建一個Delegate對象的優點;第二個每次創建一個新的。

最後,這是一個非常C#1.2的方法來解決這個問題。更新的方法會使用異步/等待來避免創建所有這些線程(更不用說System.Collections.Generic.Dictionary<,>而不是HashTable)。

+0

非常感謝。我沒有得到改變代碼的機會,但我想我會用你建議的技術(異步和等待)來重製程序。 我發現的教程一定是非常古老的,我應該在將來查看這些論壇帖子的日期:X – TGreg