简体   繁体   English

关闭后Java客户端/服务器资源泄漏

[英]Java Client/Server resource leak after closing

I am trying to implement a java command line chat server and client. 我正在尝试实现Java命令行聊天服务器和客户端。 I am using 4 classes, the Server.java sets up a ServerSocket and accepts all connections. 我正在使用4个类,Server.java设置了ServerSocket并接受所有连接。 The Client.java can connect to the Server, then 2 threads for both client and server are created using the SendMessages.java and ReceiveMessages.java. Client.java可以连接到服务器,然后使用SendMessages.java和ReceiveMessages.java为客户端和服务器创建2个线程。 The threads are responsible for taking the input from stdin and sending it to the output stream of the socket, and taking the incoming input stream to print it to stdout. 线程负责从stdin中获取输入并将其发送到套接字的输出流,并使用传入的输入流将其打印到stdout。 Everything works when launching the server and connecting the client, the chat also works. 启动服务器并连接客户端时,一切正常,聊天也有效。 However, when either client or server is terminated a resource leak is caused. 但是,当客户端或服务器终止时,会导致资源泄漏。 I want the SendMessages class and ReceiveMessages class to be able to detect when the connection of client or server is terminated and close all resources to avoid resource leaks. 我希望SendMessages类和ReceiveMessages类能够检测到客户端或服务器的连接何时终止,并关闭所有资源以避免资源泄漏。 Here is the code: 这是代码:

Server.java Server.java

import java.net.*;
import java.io.*;
import java.util.*;
public class Server{
public static void main(String[] args) throws SocketException,IOException{
    ServerSocket s = new ServerSocket(8000);
    s.setSoTimeout(10000000);
    while(true){
        Socket clientSocket = s.accept();
    handle(clientSocket);

}  
}

   static void handle(Socket clientSocket) throws IOException{
    System.out.println("connection accepted from " + clientSocket.getRemoteSocketAddress());
    receiveMessages(clientSocket);
    sendMessages(clientSocket);
  }

  static void receiveMessages(Socket clientSocket) throws IOException{
    (new Thread(new ReceiveMessages(clientSocket))).start();;

  }

  static void sendMessages(Socket clientSocket)throws IOException{
    (new Thread(new SendMessages(clientSocket))).start();
  }
}

Client.java Client.java

import java.net.*;
import java.io.*;
import java.util.*;

public class Client{


public static void main(String[] args) throws IOException, UnknownHostException, ConnectException{
    String hostname = args[0];
    int port = Integer.parseInt(args[1]);
    Socket s = null;
    s = connect(hostname, port);
    handle(s);
}


public static Socket connect(String hostname, int port) throws UnknownHostException, IOException, ConnectException{
    Socket s = null;
    try{
    s = new Socket(hostname, port);
    } catch(ConnectException e){
        System.out.println("Connect Exception caught!");
    }
return s;
}

static void handle(Socket clientSocket) throws IOException, UnknownHostException{
receiveMessages(clientSocket);
sendMessages(clientSocket);
}

static void receiveMessages(Socket clientSocket) throws IOException, NullPointerException{
    (new Thread(new ReceiveMessages(clientSocket))).start();
  }

static void sendMessages(Socket clientSocket)throws IOException, NullPointerException{
  (new Thread(new SendMessages(clientSocket))).start();
}

}

SendMessages.java SendMessages.java

import java.io.*;
import java.net.*;
import java.util.*;
public class SendMessages implements Runnable{
Socket clientSocket;

public SendMessages(Socket clientSocket){
    this.clientSocket = clientSocket;
}

public void run(){
    System.out.println("SendMessages thread has started.");
    Scanner sc = new Scanner(System.in);
    PrintWriter out = null;
    try{
    out = new PrintWriter(clientSocket.getOutputStream(), true);
    }
    catch(IOException | NullPointerException e){}

    String message;

    while(true){
        message = sc.nextLine();
        out.println(message);
        if(out.checkError()){
            out.close();
            sc.close();
            System.out.println("SendMessages closed");
        }
    }

}
}

ReceiveMessages.java ReceiveMessages.java

import java.io.*;
import java.net.*;
import java.util.*;
public class ReceiveMessages implements Runnable{
    Socket clientSocket;
    public ReceiveMessages(Socket clientSocket){
        this.clientSocket = clientSocket;
    }
    public void run(){
        System.out.println("ReceiveMessages thread has started.");
        BufferedReader in = null;
        try{
        in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        }catch(IOException|NullPointerException e){}
        String message;
        try{
            while(true){
            while((message = in.readLine())!= null){
                System.out.println(message);
            }

    }
    } catch(IOException|NullPointerException e){
        System.out.println("ReceiveMessages resources closed.");
        try{  
        in.close();
            clientSocket.close();
            }
            catch(IOException f){}
    }
}
}

Thank you! 谢谢!

So I have recently delt myself with such an issue. 所以我最近遇到了这样的问题。

The problem is, that by shutting one side of the connection down, a "half-open" connection remains (which isn't a real connection anymore). 问题是,通过关闭连接的一侧,将保留“半开”连接(不再是真正的连接)。

The Socket API does not have an option to check, if the partnered service/device is still alive. 如果配对的服务/设备仍处于活动状态,则Socket API没有检查选项。

So what to do about that? 那么该怎么办呢? I myself prefer the concept of heartbeats. 我本人更喜欢心跳的概念。 Every 10 seconds (or any other timeframe, it's up to you) a heartbeat is send to the output stream of the socket. 每10秒钟(或任何其他时间范围,由您决定),心跳将发送到套接字的输出流。 This operation throws a IOException, when the stream is no longer avaiable. 当流不再可用时,此操作将引发IOException。 Therefore you can catch the the exception and handle all "close" operations within the catch block. 因此,您可以捕获异常并处理catch块中的所有“关闭”操作。

Example: 例:

    public class Heartbeater
    implements Runnable
{
  private OutputStream os;
  private ServerHandler servhand;
  private Logger logger = Logger.getLogger( Logger.GLOBAL_LOGGER_NAME );

  //NOTE: ServerHandler is my class which handles the socket of the server after ServerSocket.accept();
  public Heartbeater( OutputStream os, ServerHandler servhand )
  {
    this.servhand = servhand;
    this.os = os;
  }

  @Override
  public void run()
  {
    try
    {
      Thread.currentThread().setName( "Heartbeater" );
      // while the handler's connection is alive
      while ( servhand.isConnectionAlive() )
      {

        Thread.sleep( 10000 );

        // dummy write to trigger the exception if the client does not respond properly
        os.write( "_HEARTBEAT_".getBytes() );

      }
    }
    catch ( InterruptedException e )
    {
      logger.log( Level.SEVERE, "HEARTBEATER GOT INTERRUPTED", e );
      Thread.currentThread().interrupt();
    }
    catch ( IOException e )
    {
      logger.log( Level.INFO, "The connection to the client has been lost." );
      // Here you would define the resource close operation.
      servhand.setConnectionAlive( false );
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM