[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;
}
}
-
(•́ ✖ •̀)
알 수 없는 사용자
댓글 입력