2015-12-24 177 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

相关问题