[java] Java 소켓 API : 연결이 닫혔는지 확인하는 방법은 무엇입니까?

Java 소켓 API에 몇 가지 문제가 있습니다. 현재 내 게임에 연결된 플레이어의 수를 표시하려고합니다. 플레이어가 언제 연결되었는지 쉽게 확인할 수 있습니다. 그러나 소켓 API를 사용하여 플레이어의 연결이 끊어진시기를 확인하는 것은 불필요하게 어려워 보입니다.

isConnected()원격으로 연결이 끊어진 소켓에 대한 호출 은 항상 반환되는 것처럼 보입니다 true. 마찬가지로 isClosed()원격으로 닫힌 소켓을 호출 하면 항상 false. 소켓이 닫혔는지 여부를 실제로 확인하기 위해 데이터를 출력 스트림에 기록하고 예외를 포착해야한다는 것을 읽었습니다. 이것은이 상황을 처리하는 정말 불결한 방법 인 것 같습니다. 우리는 소켓이 언제 닫혔는지 알기 위해 네트워크를 통해 쓰레기 메시지를 지속적으로 스팸해야합니다.

다른 해결책이 있습니까?



답변

현재 연결 상태를 알려주는 TCP API는 없습니다. isConnected()그리고 isClosed()당신에게 현재 상태에게 당신의 소켓을 . 같은 것이 아닙니다.

  1. isConnected()이 소켓 을 연결 했는지 여부 알려줍니다 . 그래서 그것은 사실을 반환합니다.

  2. isClosed()이 소켓 을 닫았 는지 여부 알려줍니다 . 당신이 가질 때까지 그것은 거짓을 반환합니다.

  3. 피어 가 순서대로 연결 을 닫은 경우

    • read() -1 반환
    • readLine() 보고 null
    • readXXX()EOFException다른 XXX에 대해 던졌습니다 .

    • 쓰기는 IOException결국 버퍼링 지연에 따라 ‘피어에 의한 연결 재설정’ 을 throw합니다 .

  4. 다른 이유로 연결이 끊어진 경우 쓰기는 IOException결국 위와 같이를 던지고 읽기는 동일한 작업을 수행 할 수 있습니다.

  5. 피어가 여전히 연결되어 있지만 연결을 사용하지 않는 경우 읽기 제한 시간을 사용할 수 있습니다.

  6. 다른 곳에서 읽을 수있는 것과는 달리 ClosedChannelException이것을 말하지 마십시오. [도 안함 SocketException: socket closed.] 채널 닫은 다음 계속 사용 했음을 나타냅니다. 즉, 귀하의 프로그래밍 오류입니다. 닫힌 연결을 나타내지 않습니다 .

  7. Windows XP에서 Java 7을 사용한 몇 가지 실험 결과 다음과 같은 경우에도 나타납니다.

    • 당신은 선택하고 있습니다 OP_READ
    • select() 0보다 큰 값을 반환합니다.
    • 연결된 항목 SelectionKey이 이미 잘못되었습니다 ( key.isValid() == false).

    피어가 연결을 재설정했음을 의미합니다. 그러나 이것은 JRE 버전 또는 플랫폼에 고유 할 수 있습니다.


답변

다양한 메시징 프로토콜에서 서로 하트 비트를 유지하는 것이 일반적입니다 (핑 패킷을 계속 전송). 패킷이 매우 클 필요는 없습니다. 프로브 메커니즘을 사용하면 TCP가 일반적으로 파악하기 전에도 연결이 끊어진 클라이언트를 감지 할 수 있습니다 (TCP 시간 초과가 훨씬 더 높음). 2-3에 대한 응답이 표시되지 않으면 프로브를 보내고 응답을 5 초 동안 기다립니다. 후속 프로브, 플레이어 연결이 끊어집니다.

또한 관련 질문


답변

방금 게시 된 다른 답변을 보지만 게임을하는 클라이언트와 상호 작용한다고 생각하므로 다른 접근 방식을 취할 수 있습니다 (BufferedReader는 어떤 경우에는 확실히 유효합니다).

원한다면 … “등록”책임을 클라이언트에게 위임 할 수 있습니다. 즉, 각각에서받은 마지막 메시지에 타임 스탬프가있는 연결된 사용자 모음이있을 것입니다. 클라이언트가 시간 초과되면 클라이언트를 강제로 다시 등록해야하지만 아래의 인용문과 아이디어로 이어집니다.

소켓이 닫혔는지 여부를 실제로 확인하기 위해 데이터를 출력 스트림에 기록해야하고 예외를 잡아야한다는 것을 읽었습니다. 이것은이 상황을 처리하는 정말 불결한 방법 인 것 같습니다.

Java 코드가 소켓을 닫거나 연결 해제하지 않은 경우 원격 호스트가 연결을 닫았 음을 어떻게 알릴 수 있습니까? 궁극적으로 try / catch는 ACTUAL 소켓에서 이벤트를 수신하는 폴러가 수행하는 것과 거의 동일한 작업을 수행합니다. 다음을 고려하세요:

  • 로컬 시스템은 알리지 않고 소켓을 닫을 수 있습니다. 이는 소켓의 구현 일뿐입니다 (즉, 상태 변경을 위해 하드웨어 / 드라이버 / 펌웨어 / 무엇이든 폴링하지 않습니다).
  • new Socket (Proxy p) … 여러 당사자 (정말 6 개의 끝점)가 연결을 끊을 수 있습니다 …

추상화 된 언어의 특징 중 하나는 당신이 사소한 부분에서 추상화된다는 것입니다. SqlConnection에 대한 C # (try / finally)의 using 키워드를 생각해보십시오 … 그것은 단지 비즈니스를 수행하는 비용 일뿐입니다. try / catch / finally는 소켓 사용을위한 허용되고 필요한 패턴이라고 생각합니다.


답변

저는 이것이 TCP 연결의 특성이라고 생각합니다. 표준에서 송신 연결이 끊어 졌다고 결론을 내리기까지 약 6 분 동안 무음이 전송됩니다! 따라서이 문제에 대한 정확한 해결책을 찾을 수 없다고 생각합니다. 아마도 더 나은 방법은 서버가 사용자 연결이 닫혀 있다고 가정해야하는시기를 추측하는 편리한 코드를 작성하는 것입니다.


답변

@ user207421이 말했듯이 TCP / IP 프로토콜 아키텍처 모델 때문에 현재 연결 상태를 알 수있는 방법이 없습니다. 따라서 서버는 연결을 종료하기 전에 사용자를 인식해야하거나 사용자가 직접 확인해야합니다.
다음은 서버가 소켓을 닫는 방법을 보여주는 간단한 예입니다.

sockAdr = new InetSocketAddress(SERVER_HOSTNAME, SERVER_PORT);
socket = new Socket();
timeout = 5000;
socket.connect(sockAdr, timeout);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream());
while ((data = reader.readLine())!=null)
      log.e(TAG, "received -> " + data);
log.e(TAG, "Socket closed !");


답변

비슷한 문제에 직면했습니다. 제 경우에는 클라이언트가 주기적으로 데이터를 보내야합니다. 동일한 요구 사항이 있기를 바랍니다. 그런 다음 지정된 시간이 만료되면 socket.setSoTimeout(1000 * 60 * 5);throw되는 SO_TIMEOUT 을 설정합니다 java.net.SocketTimeoutException. 그러면 죽은 클라이언트를 쉽게 감지 할 수 있습니다.


답변

그게 내가 처리하는 방법

 while(true) {
        if((receiveMessage = receiveRead.readLine()) != null ) {

        System.out.println("first message same :"+receiveMessage);
        System.out.println(receiveMessage);

        }
        else if(receiveRead.readLine()==null)
        {

        System.out.println("Client has disconected: "+sock.isClosed());
        System.exit(1);
         }    }

result.code == null 인 경우