[Java] 소켓 통신에서, 동일한 주소와 포트를 사용한 두 번의 접속 관련 질문입니다.

조회수 780회

자바로 소켓 통신을 이용해 다른 host로 데이터를 보내는 프로그램을 만들고 있습니다.   데이터의 크기는 약 1만~100만 줄의 텍스트파일로 되어 있으며, 한 줄씩 전송하는 방식 A입니다.   A 루틴에서 어떤 데이터가 생기며, 이를 후에 처리해야 하는 루틴 B이 존재해서, A 루틴과 B 루틴은 동일한 객체 내에서 두 개의 스레드로 동작합니다.   (A와 B 둘 다 ScheduledExecutorService 내의 Runnable 객체로 수행)   두 스레드는 서로 synchronized(this) 블록으로 싸여 있어 동시에 수행되지 않습니다.   로직 수행은 다음과 같습니다

  • 접속 대상 Host는 1.2.3.4, Port는 1234라고 가정합니다(A로직, B로직의 소켓 모두 동일)
  • 아래 진행 방식에 있는 소켓 및 Stream 변수들은 모두 각 로직 내의 지역 변수입니다.
 1. A 로직 수행 시 소켓 생성 이후 InputStream, OutputStream 변수를 각각 할당합니다.
 2. A 로직을 수행합니다.
 3. A 로직이 끝난 후 소켓 및 stream 변수를 정리합니다(close, null 할당)

이후 바로 B 로직이 시작됩니다.

 5. B 로직 수행 시 소켓 생성 이후 InputStream, OutputStream변수를 각각 할당합니다.
 6. B 로직을 수행합니다.
 7. B 로직이 끝난 후 소켓 및 stream 변수를 정리합니다(close, null 할당)

테스트 환경은 A 루틴을 먼저 실행시키고, 1초 뒤에 B 루틴이 실행되도록 구성되어 있습니다.   (synchronize 블록으로 각각 묶여 있어 A 루틴이 실행 중이라면 B 루틴은 1초가 지났다 하더라도 A 루틴이 끝날 때가지는 대기했다가 끝나면 바로 수행되겠죠.)

문제는 테스트 데이터의 개수보다 대상 host로 전송된 데이터 개수가 적다는 것입니다.   (100만 건을 보냈는데 98만 건만 들어왔다든지..)  

왜 A 루틴에서 전송되는 로그가 누락되는지 잘 모르겠습니다. read/send 직전까지의 데이터는 모두 잘 읽히는 것을 확인했으므로 tcp 통신에서 문제가 있는 것 같은데... 이유를 아시는 분... 도와주세요...ㅠ


정영훈님 말씀에 따라 관련 코드를 추가합니다.

메인 함수 루틴

    public void run() {

        // A 루틴
        ScheduledExecutorService syncService = Executors
                .newSingleThreadScheduledExecutor();

        syncService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                A();
            }
         }, 0, syncSchedulingTime, TimeUnit.MINUTES);

        // B 루틴
        ScheduledExecutorService timeOutService = Executors
                .newSingleThreadScheduledExecutor();

        timeOutService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                B();
            }
         }, 1, 70, TimeUnit.SECONDS);
    }

A 루틴

synchronized (this) {
    Socket aSock = null;
    InputStream aIn = null;
    OutputStream aOut = null;
    try {
        aSock = new Socket(hostIp, servicePort);
    } catch (UnknownHostException e) {
        logger.log(Level.SEVERE, String.format(
                "Unknown Host [%s:%d].",
                hostIp, servicePort), e);
        is_connection_ok = false;
    } catch (IOException e) {
        logger.log(Level.SEVERE, String.format(
                "Socket IO Exception [%s:%d].Check Network Connection.",
                hostIp, servicePort), e);
        is_connection_ok = false;

    }

    if (!is_connection_ok) {
        cleanUpSocketVariable(aSock, aIn, aOut);
        return;
    }

    try {
        aIn = aSock.getInputStream();
        aOut = aSock.getOutputStream();

        // 데이터 전송
    } catch (IOException e) {
        logger.log(Level.SEVERE,
                "Failed sending data", e);
    } finally {
        cleanUpSocketVariable(aSock, aIn, aOut);
    }
}

B 루틴

synchronized(this) {
    boolean is_connection_ok = true;
    Socket bSock=null;
    InputStream bIn=null;
    OutputStream bOut=null;

    try {
        bSock = new Socket(hostIp, servicePort);
    } catch (UnknownHostException e) {
        logger.log(Level.SEVERE, String.format(
                "Unknown Host [%s:%d].",
                hostIp, servicePort), e);
        is_connection_ok = false;
    } catch (IOException e) {
        logger.log(Level.SEVERE, String.format(
                "Socket IO Exception [%s:%d].",
                hostIp, servicePort), e);
        is_connection_ok = false;
    }

    if (!is_connection_ok) {
        cleanUpSocketVariable(bSock, bIn, bOut);
        return;
    }

    try {
        bIn = bSock.getInputStream();
        bOut = bSock.getOutputStream();
        // B 루틴 처리
    } catch (IOException e) {
        logger.log(Level.SEVERE,
                "Failed to send data.", e);
    } finally {
        cleanUpSocketVariable(bSock, bIn, bOut);
    }
}           

자원 반환 및 null 처리

private void cleanUpSocketVariable(Socket sock, InputStream in, OutputStream out) {
    if (in != null) {
        try {
            in.close();
        } catch (IOException e) {
            // nothing to do.
        }
        in = null;
    }

    if (out != null) {
        try {
            out.close();
        } catch (IOException e) {
            // nothing to do.
        }
        out = null;
    }

    if (sock != null) {
        try {
            sock.close();
        } catch (IOException e) {
            // nothing to do.
        }
        sock = null;
    }
}
  • (•́ ✖ •̀)
    알 수 없는 사용자
  • 질문은 길고 상세하나...제작된 프로그램의 설명만 있을뿐 오류를 유추하기 위한 내용은 전혀 없습니다. 즉 "문제 없이 코딩했는데 오류가 발생한다" 정도 밖엔 되지 않습니다. 오류를 재현할 수 있는 코드를 올려주세요. 정영훈 2018.10.28 04:48
  • @정영훈 감사합니다. 수정했습니다. 알 수 없는 사용자 2018.10.28 05:50
  • 문제가 무엇인가하면 동적인 오류는 수행해보지 않는 이상 문제 파악을 할 수 없다는데 있습니다. 질문자가 올리는 코드는 문제가 없을 것 같은 부분을 발췌해서 올리신 것 같고요 동적인 오류는 실행 및 디버깅을 해봐야 합니다. 제가 "오류를 재현할 수 있는 코드를 올려주세요. " 라고 한 이유가 그것입니다. 즉 컴파일 해서 실행해볼 수 있는 코드를 올려달라는 이야깁니다. 정영훈 2018.10.28 07:01

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)