2013-07-21 41 views
0

我在java中创建了一个到服务器的套接字,并且在套接字连接之后它创建了一个可以访问套接字输入和输出流的新线程,然后此线程在它们到来时阻止并处理输入行in。在java中处理套接字

据我所知,当输入流结束时,BufferedReader上的readln方法将返回null。这并不一定意味着套接字是封闭的,但它呢?这是什么意思?所以我想要在套接字上运行close方法来很好地关闭它。

我也明白,readln方法可以抛出一个IOException,并且在套接字上调用close方法(如果它当前被阻塞)后抛出。什么时候可以抛出?插入后仍然可以打开它,或者它会一直关闭并准备好进行垃圾回收等。

这是我现在的代码,我真的不知道如何正确处理断开连接。目前我认为如果在套接字正在等待线路时调用disconnect方法,则可能最终陷入死锁,因为disconnect将在套接字上调用close。然后这将把IOException放在readLine上,然后这将导致该catch块再次调用disconnect

public class SocketManager { 

    private Socket socket = null; 
    private PrintWriter out = null; 
    private BufferedReader in = null; 

    private String ip; 
    private int port; 

    private Object socketLock = new Object(); 

    public SocketManager(String ip, int port) { 
     this.ip = ip; 
     this.port = port; 
    } 

    public void connect() throws UnableToConnectException, AlreadyConnectedException { 
     synchronized(socketLock) { 
      if (socket == null || socket.isClosed()) { 
       throw (new AlreadyConnectedException()); 
      } 
      try { 
       socket = new Socket(ip, port); 
       out = new PrintWriter(socket.getOutputStream(), true); 
       in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
      } catch (IOException e) { 
       throw (new UnableToConnectException()); 
      } 
      new Thread(new SocketThread()).start(); 
     } 
    } 

    public void disconnect() throws NotConnectedException { 
     synchronized(socketLock) { 
      if (isConnected()) { 
       throw (new NotConnectedException()); 
      } 
      try { 
       socket.close(); 
      } catch (IOException e) {} 
     } 
    } 

    public boolean isConnected() { 
     synchronized(socketLock) { 
      return (socket != null && !socket.isClosed()); 
     } 
    } 

    private class SocketThread implements Runnable { 

     @Override 
     public void run() { 
      String inputLine = null; 
      try { 
       while((inputLine = in.readLine()) != null) { 
        // do stuff 
       } 
       if (isConnected()) { 
        try { 
         disconnect(); 
        } catch (NotConnectedException e) {} 
       } 
      } catch (IOException e) { 
       // try and disconnect (if not already disconnected) and end thread 
       if (isConnected()) { 
        try { 
         disconnect(); 
        } catch (NotConnectedException e1) {} 
       } 
      } 
     } 

    } 
} 

我基本上想知道达到以下的最佳方式:编写一个连接到插座,并启动一个单独的线程监听输入连接方法

  • 编写一个从套接字断开并终止正在侦听输入的线程的断开连接方法。
  • 处理与远程套接字连接中断的情况。

我已通读java tutorial on sockets,但在我看来,它并没有真正涵盖这些细节。

谢谢!

回答

2

当我说它最终可能会陷入僵局时,我认为我错了。

会出现什么情况是:

  1. 断开()呼吁,同时in.readLine()阻挡
  2. socket.close()执行。
  3. in.readline()抛出IOException。

我当时认为SocketThread中的异常处理程序会调用断开连接,而断开连接正在等待该异常完成。因为它们都是不同的线程,所以disconnect()中的代码会在SocketThread中捕获异常的同时继续执行。 SocketThread然后会调用disconnect(),但是必须等到disconnect()的第一个实例完成。然后disconnect()会再次执行,但会得到抛出的NotConnectedException,这将在SocketThread中被捕获,并且什么都不会发生。 SocketThread会退出,这就是想要的结果。

但是我已经调查了socket class和它也包含这些方法:

  • shutdownInput()
  • shutdownOutput()

shutdownInput()发送端EOF符号到输入流的含义in.readline()返回null并且循环干净地退出。 shutdownOutput()发送TCP终止序列,通知服务器它已断开连接。

socket.close()之前调用这两个函数更有意义,因为它意味着线程将很好地退出,而不是因抛出的异常而退出,这会导致更多开销。

因此,这是修改后的代码:

public class SocketManager { 

    private Socket socket = null; 
    private PrintWriter out = null; 
    private BufferedReader in = null; 

    private String ip; 
    private int port; 

    private Object socketLock = new Object(); 

    public SocketManager(String ip, int port) { 
     this.ip = ip; 
     this.port = port; 
    } 

    public void connect() throws UnableToConnectException, AlreadyConnectedException { 
     synchronized(socketLock) { 
      if (socket == null || socket.isClosed()) { 
       throw (new AlreadyConnectedException()); 
      } 
      try { 
       socket = new Socket(ip, port); 
       out = new PrintWriter(socket.getOutputStream(), true); 
       in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
      } catch (IOException e) { 
       throw (new UnableToConnectException()); 
      } 
      new Thread(new SocketThread()).start(); 
     } 
    } 

    public void disconnect() throws NotConnectedException { 
     synchronized(socketLock) { 
      if (isConnected()) { 
       throw (new NotConnectedException()); 
      } 
      try { 
       socket.shutdownInput(); 
      } catch (IOException e) {} 
      try { 
       socket.shutdownOutput(); 
      } catch (IOException e) {} 
      try { 
       socket.close(); 
      } catch (IOException e) {} 
     } 
    } 

    public boolean isConnected() { 
     synchronized(socketLock) { 
      return (socket != null && !socket.isClosed()); 
     } 
    } 

    private class SocketThread implements Runnable { 

     @Override 
     public void run() { 
      String inputLine = null; 
      try { 
       while((inputLine = in.readLine()) != null) { 
        // do stuff (probably in another thread) 
       } 

       // it will get here if socket.shutdownInput() has been called (in disconnect) 
       // or possibly when the server disconnects the clients 


       // if it is here as a result of socket.shutdownInput() in disconnect() 
       // then isConnected() will block until disconnect() finishes. 
       // then isConnected() will return false and the thread will terminate. 

       // if it ended up here because the server disconnected the client then 
       // isConnected() won't block and return true meaning that disconnect() 
       // will be called and the socket will be completely closed 

       if (isConnected()) { 
        try { 
         disconnect(); 
        } catch (NotConnectedException e) {} 
       } 
      } catch (IOException e) { 
       // try and disconnect (if not already disconnected) and end thread 
       if (isConnected()) { 
        try { 
         disconnect(); 
        } catch (NotConnectedException e1) {} 
       } 
      } 
     } 

    } 
} 
0

为了确保与套接字关联的所有资源都已关闭,您必须在完成对该套接字的工作时调用close()方法。 典型的IO异常处理模式是,你抓住它,然后尽最大努力清理所有调用close()方法。

所以你唯一要做的就是确保你在每个套接字的生命周期中调用close()。

0

你在正确的轨道上。我不会使用“readline”,只有原始读取,并且“do stuff” 应限于构建接收数据的队列。同样,写回复 应该是一个单独的线程,用于清空要发送的数据队列。

尽管套接字的完整性保证,东西会出错,有时您会收到无意义的数据。在“读”和“写”下面有一堆东西,没有系统是完美的或没有错误的。在阅读和书写的层面添加自己的包含校验和的包装,这样您就可以确定您正在接收准备发送的内容。