2013-07-21 3 views
0

java 서버 소켓을 만들고 소켓이 연결되면 소켓 입력 및 출력 스트림에 액세스 할 수있는 새 스레드를 만들고이 스레드는 입력 라인을 차단하고 처리합니다. in.자바 소켓 처리

BufferedReader의 readln 메서드는 입력 스트림이 끝날 때 null을 반환한다는 것을 알고 있습니다. 그렇다고해서 반드시 소켓이 닫혔다는 의미는 아닙니다. 이것은 무엇을 의미 하는가? 그래서 소켓을 닫으려면 close 메서드를 실행하고 싶습니다.

readln 메서드는 IOException을 throw 할 수 있으며 현재 차단중인 소켓에서 close 메서드를 호출하면 throw됩니다. 그 밖에 언제 던질 수 있습니까? 소켓이 여전히 던져진 후에도 여전히 열리거나 가비지 수집 등을 위해 항상 닫히고 닫힐 수 있습니까?

이것은 현재 제가 가지고있는 코드이며, 올바르게 연결 해제를 처리하는 방법을 알지 못합니다. 소켓에 disconnect이 소켓에서 close을 호출 할 것이므로 disconnect 메서드가 호출되면 소켓이 회선을 기다리는 동안 이것이 교착 상태에 빠질 수 있다고 생각합니다. 그러면 readLineIOException이 던져지며 그러면 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

내가 교착 상태가 될 수 있다고 말했을 때 나는 틀렸다고 생각한다.

어떤 일이 일어날 것은 :

  • socket.close() 실행을 차단 in.readLine() 동안이라고

    1. 분리는().
    2. in.readline() throws IOException.

    나는 그 예외가 끝날 때까지 분리가 대기하는 동안 SocketThread에서 예외 핸들러가 분리를 호출 것이라고 생각했다. 두 스레드가 다르기 때문에 disconnect()의 코드는 예외가 SocketThread에서 catch되는 동안 계속됩니다. SocketThread는 disconnect()를 호출하지만 disconnect()의 첫 번째 인스턴스가 완료 될 때까지 기다려야합니다. 그런 다음 disconnect()가 다시 실행되지만 SocketThread에서 catch 된 NotConnectedException이 발생하여 아무 일도 일어나지 않습니다. SocketThread가 종료되고 원하는 결과가 나타납니다.

    그러나 I는 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

    소켓과 관련된 모든 리소스가 relased되도록하려면 해당 소켓 작업을 완료 할 때 close() 메서드를 호출해야합니다. 일반적인 IO 예외 처리 패턴은 catch를 catch 한 다음 close() 메서드를 호출하는 모든 것을 정리하는 최선의 노력을 수행하는 것입니다.

    그래서해야 할 일은 평생 동안 모든 소켓에서 close()를 호출하는 것입니다.

    0

    당신은 올바른 길을 가고 있습니다. 나는 "readline"을 사용하지 않고 원시 읽기 만 "do stuff" 은 수신 된 데이터의 큐를 생성하도록 제한되어야한다. 마찬가지로 답장을 쓰려면 은 전송할 데이터 큐를 비우는 별도의 스레드 여야합니다.

    소켓의 무결성 보장에도 불구하고 물건이 잘못되어 때때로 이해할 수없는 데이터가 수신됩니다. "읽기"및 "쓰기"아래에 물건이 가득차 있으며 완벽한 시스템이나 버그가없는 시스템은 없습니다. 읽고 쓰는 수준에서 체크섬이있는 래퍼를 추가하면 보내려는 내용을 확실히받을 수 있습니다.