2015-12-24 95 views
2

我一直試圖讓我的服務器應用程序在客戶端與其斷開連接時在標籤中顯示消息。TCPClient斷開連接時顯示消息

現在,標籤顯示客戶端啓動時連接的客戶端的IP地址,但當客戶端關閉時,IP地址仍顯示在標籤中。

我到目前爲止已經試過這些方法沒有任何的運氣:

  // DETECT IF CLIENT DISCONNECTED 

//METHOD 1 
      if (bytesRead == 0) 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
       break; 
      } 

//METHOD 2 
      if (!tcpListener.Pending()) 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      } 

//METHOD 3 
      if (tcpClient.Client.Poll(0, SelectMode.SelectRead)) 
      { 
       byte[] buff = new byte[1]; 
       if (tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0) 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }     

我敢肯定,很可能一些簡單的我失蹤,我只是無法弄清楚它是什麼。


編輯:我發現有些地方,當我點擊關閉我的客戶端應用程序的按鈕,客戶端發送消息到服務器關閉之前的一種解決方法。服務器知道,如果它收到此特定消息(在本例中爲「SHUTDOWN CLIENT」)以將「ClientIPLabel.text」中的文本更改爲「(無客戶端已連接)」

這對大多數情況,但它有點破解,並且如果客戶端由於錯誤或崩潰等原因而關閉,服務器就不知道它已經斷開連接,所以它仍然會顯示它發送的最後一個已知IP。

第二編輯:因此看起來解決方法不適用於我。在將其添加到我的項目中後,出於某種原因,無論客戶端向服務器發送什麼消息都會顯示「(無客戶端已連接)」消息。

我的客戶仍然是實際連接和接收消息,但「ClientIPLabel」的標籤不正確



3日編輯:這是表示我有一個片段我客戶端設置發送郵件服務器爲每詳見下面我的意見之一:

private void SendMessage(string msg) 
    { 
     NetworkStream clientStream = ConsoleClient.GetStream(); 

     ASCIIEncoding encoder = new ASCIIEncoding(); 
     byte[] buffer = encoder.GetBytes(msg); 

     clientStream.Write(buffer, 0, buffer.Length); 
     clientStream.Flush(); 
    } 

4日編輯:我的服務器代碼,與我直接從客戶端關機按鈕來獲取客戶端斷開消息暫時的解決辦法:

public partial class Server : Form 
{ 
    private TcpListener tcpListener; 
    private Thread listenThread;  
    private delegate void WriteMessageDelegate(string msg); 

    public Server() 
    { 
     InitializeComponent(); 
     Server(); 
    } 


    // WAIT FOR CLIENT 

    private void Server() 
    { 
     this.tcpListener = new TcpListener(IPAddress.Any, 8888); 
     this.listenThread = new Thread(new ThreadStart(ListenForClients)); 
     this.listenThread.Start(); 
    } 

    private void ListenForClients() 
    { 
     this.tcpListener.Start(); 


    // GET CLIENT IP ADDRESS 

     while (true) 
     { 
      TcpClient client = this.tcpListener.AcceptTcpClient();     
      string clientIPAddress = "" + IPAddress.Parse(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString()); 
      ClientIPLabel.Text = clientIPAddress; 
      Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm)); 
      clientThread.Start(client);    
     } 
    } 


    // COMMUNICATE WITH CLIENT 

    private void HandleClientComm(object client) 
    { 
     TcpClient tcpClient = (TcpClient)client; 
     NetworkStream clientStream = tcpClient.GetStream(); 

     byte[] message = new byte[4096]; 
     int bytesRead; 

     while (true) 
     { 
      bytesRead = 0; 

      try 
      { 
       bytesRead = clientStream.Read(message, 0, 4096); 
      } 
      catch 
      { 
       break; 
      } 


      ASCIIEncoding encoder = new ASCIIEncoding(); 


    // CHECK FOR CLIENT DISCONNECT 

      string msg = encoder.GetString(message, 0, bytesRead); 
      WriteMessage(msg); 

      if (msg.Equals("Client Disconnected (" + DateTime.Now + ")")) 
      { 
       ClientIPLabel.Text = ("(No Client Connected)"); 
      }    
     } 

     tcpClient.Close(); 
    } 


5日編輯:我已經更新我的代碼,我已經(幾乎)得到了心跳計時器的實現,但它仍然沒有設置完全正確...這是我的代碼:

// CHECK FOR CLIENT DISCONNECT 

      // SHUTDOWN BUTTON PRESSED 
      string msg = encoder.GetString(message, 0, bytesRead); 
      WriteMessage(msg); 

      if (msg.Equals("Client Disconnected (" + DateTime.Now + ")")) 
      { 
       ClientIPLabel.Text = ("(No Client Connected)"); 
      }    
     }       
     tcpClient.Close(); 
    } 

    // CHECK FOR CONNECTION FAILED 
    void heartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      HandleClientComm("Check for connection"); 
     } 
     catch (Exception) 
     {     
      Invoke((MethodInvoker)delegate 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }); 
     } 
    } 

無論我的客戶端是否已斷開連接,「(無客戶端已連接)」消息會在計時器後自動彈出,因此它沒有正確捕獲異常。

我試過在他寫這篇文章的時候實現了interceptwind的建議,但是由於某種原因,我應該有catch (Exception e)我只有在我擺脫e並將它寫爲catch (Exception)時才能構建它。如果我離開e那裏,我收到一條警告,說"The variable 'e' is declared but never used"。我知道interceptwind寫他的例子使用我的SendMessage()方法,但該方法只用於我的客戶端,所以我改變了代碼嘗試使用我的HandleClientComm()方法,所以我想知道如果這是這個工作不正常的原因。我嘗試過改變一些東西,但仍似乎無法得到它的工作。 5秒後,即使我的客戶端仍然連接並正常工作,我仍然收到「(無客戶端連接)」消息。


6日編輯:我試圖調節我的HandleClientComm()方法能夠發送郵件,但我顯然錯過了一些東西,因爲我的「心跳計時器」仍是我的切換到ClientIPLabel.text「 (無客戶端連接)「,即使我的客戶端仍然連接。

這裏是我的代碼:

// COMMUNICATE WITH CLIENT 

    private void HandleClientComm(object client) 
    { 
     TcpClient tcpClient = (TcpClient)client; 
     NetworkStream clientStream = tcpClient.GetStream(); 
     ASCIIEncoding encoder = new ASCIIEncoding(); 
     byte[] message = new byte[4096]; 
     byte[] buffer = encoder.GetBytes("Send Message"); 
     int bytesRead; 
     clientStream.Write(buffer, 0, buffer.Length); 
     clientStream.Flush(); 
     while (true) 
     { 
      bytesRead = 0; 

      try 
      { 
       bytesRead = clientStream.Read(message, 0, 4096); 
      } 
      catch 
      { 
       break; 
      }         


      // START HEARTBEAT TIMER 

      System.Timers.Timer heartbeatTimer = new System.Timers.Timer(); 
      heartbeatTimer.Interval = 5000; 
      heartbeatTimer.Elapsed += heartbeatTimer_Elapsed; 
      heartbeatTimer.Start(); 

      // CHECK FOR CLIENT DISCONNECT 

      // SHUTDOWN BUTTON PRESSED 
      string msg = encoder.GetString(message, 0, bytesRead); 
      WriteMessage(msg); 

      if (msg.Equals("Client Disconnected (" + DateTime.Now + ")")) 
      { 
       ClientIPLabel.Text = ("(No Client Connected)"); 
      }    
     }       
     tcpClient.Close(); 
    } 

    // CHECK FOR CONNECTION FAILED 
    void heartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      HandleClientComm("Check for connection"); 
     } 
     catch (Exception) 
     {     
      Invoke((MethodInvoker)delegate 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }); 
     } 
    } 
+0

您的服務器應用程序有一個GUI? –

+0

是的,它只是坐在系統托盤中,但它也有一個基本的圖形用戶界面,您可以打開它顯示我的客戶端發送給服務器的消息(類似日誌),但它也顯示當前連接的客戶端的IP在GUI的底部。 – Patrick

+0

您用於收聽客戶端的對象是否有事件,如「OnDisconnected」或您可以監控的事件? –

回答

1

鑑於OP的限制,即服務器不能將消息發送到客戶端,另一種是用於客戶端發送心跳消息服務器每隔X秒(例如5秒)。

然後,服務器將檢查它是否在過去的Y秒(例如30秒)內收到來自客戶端的任何消息。如果不是這樣,那意味着客戶端斷開連接。

客戶端的代碼

public Client() 
    { 
     ... 

     //After connection, CALL ONCE ONLY 
     Timer heartbeatTimer = new System.Timers.Timer(); 
     heartbeatTimer.Interval = 5000; //5 seconds 
     heartbeatTimer.Elapsed += heartbeatTimer_Elapsed; 
     heartbeatTimer.Start(); 
    } 

    void heartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     SendMessage("Heartbeat"); 
    } 

服務器的代碼

bool receiveHeartBeat = false; 

    public Server() 
    { 
     ... 

     //After connection, CALL ONCE ONLY 
     Timer checkHeartbeatTimer = new System.Timers.Timer(); 
     checkHeartbeatTimer.Interval = 30000; //30 seconds 
     checkHeartbeatTimer.Elapsed += checkHeartbeatTimer_Elapsed; 
     checkHeartbeatTimer.Start(); 
    } 

    void checkHeartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     if(receiveHeartBeat) 
     { 
      Invoke((MethodInvoker)delegate //prevent cross-thread exception 
      { 
       ClientIPLabel.Text = "Connected"; 
      }); 
     } 
     else 
     { 
      Invoke((MethodInvoker)delegate //prevent cross-thread exception 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }); 
     } 
    } 

    void WriteMessage(string msg) 
    { 
     receiveHeartBeat = true; 
     // rest of your code 

    } 
+0

感謝您付出如此多的努力來幫助我。我想我會暫時休息一段時間,並暫時使用我的小解決方法;無論出於何種原因,這件事只是繼續在我的頭上......我還有一些項目的其他部分工作,這不會給我這麼多麻煩大聲笑。 – Patrick

+1

我會將其標記爲正確的答案,因爲我知道它是。我只是無法讓它工作,但這是我的技能水平,而不是你的答案。 – Patrick

1

您可能需要使用:

if (TcpClient.Connected){} 
+0

「== true」是多餘的/不必要的。 –

+0

不幸的是,這對我不起作用 – Patrick

1

威爾的評論是正確的。您可能需要實施Heartbeat機制,您可以定期向客戶端發送Heartbeat(或虛擬)消息。如果您在嘗試發送心跳信號時遇到錯誤,那麼您知道您的客戶端已斷開連接。

來實現,這將是這個樣子的最簡單方法:

//using System.Timers; 

    private void ListenForClients() 
    { 
     this.tcpListener.Start(); 

     Timer heartbeatTimer = new System.Timers.Timer(); 
     heartbeatTimer.Interval = 5000; //5 seconds 
     heartbeatTimer.Elapsed += heartbeatTimer_Elapsed; 
     heartbeatTimer.Start(); 

     //rest of your code 
    } 


    void heartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      SendMessage("Can be anything :D"); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e.Message); //Check what is the exception 

      //Fail to send message - client disconnected. 
      Invoke((MethodInvoker)delegate //prevent cross-thread exception 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }); 
     } 
    } 
+0

太棒了!這看起來會很好!我還沒有機會對它進行測試,但我有一個關於它的一部分的問題。在發送信息「HEARTBEAT」的地方,它看起來好像是作爲一個字符串「味精」發送的,這是正確的嗎?我只問,因爲我的應用程序的主要部分依賴於我的客戶端已經將消息作爲「msg」發送到服務器,所以我可以看到與我的應用程序的主要功能相沖突。如果情況並非如此,那很好。否則,我是否可以將「msg」更改爲其他內容,如「消息」或類似內容?或者我需要擔心這一點? – Patrick

+0

我已經更新了我的問題,包括我的客戶端已經發送消息到服務器的代碼位 – Patrick

+0

呃......我討厭這麼新鮮。我試着調整你的代碼,使它適合我項目中的幾個不同的地方,而我似乎無法讓它正常工作。如果有人想幫助我使用「心跳機制」,我已經用我的服務器代碼更新了我的問題。對不起,我很不好意思...... – Patrick

相關問題