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

时间:2018-03-08 12:27:11

标签: java bufferedreader serversocket printwriter resource-leak

我正在尝试实现java命令行聊天服务器和客户端。我使用4个类,Server.java设置ServerSocket并接受所有连接。 Client.java可以连接到Server,然后使用SendMessages.java和ReceiveMessages.java创建客户端和服务器的2个线程。线程负责从stdin获取输入并将其发送到套接字的输出流,并将传入的输入流打印到stdout。启动服务器并连接客户端时,一切正常,聊天也有效。但是,当客户端或服务器终止时,会导致资源泄漏。我希望SendMessages类和ReceiveMessages类能够检测客户端或服务器的连接何时终止,并关闭所有资源以避免资源泄漏。这是代码:

  

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

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

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

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){}
    }
}
}

谢谢!

1 个答案:

答案 0 :(得分:1)

所以我最近已经解决了这个问题。

问题是,通过关闭连接的一端,仍然存在"half-open"连接(这不再是真正的连接)。

如果合作的服务/设备仍然存在,则Socket API没有检查选项。

那该怎么办?我自己更喜欢心跳的概念。 每隔10秒(或任何其他时间帧,由您决定),心跳将发送到套接字的输出流。当流不再可用时,此操作将抛出IOException。因此,您可以捕获异常并处理catch块中的所有“关闭”操作。

示例:

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