요즘들어 자바 관련 글들을 열심히 퍼다 나르고 있습니다.
대 부부은 네이버블로그에서 발췌 하는데, 그것도 이상한게 다른곳에서 같은 내용을 찾다 보면 토시 하나
틀리지 않은 글이 수두룩 합니다. ( 저 와 같은 사람들이 많다는 것이겠지요…)
그래서 정확한 출처를 알기가 어렵다는 변명을 합니다. ㅡㅡ;
아래의 자바로 소켓을 구현 할 경우 타임 아웃에 대한 글 입니다.
소켓 연결에 대한 타임아웃
작성자 : 무니 <moonyL@orgio.net>
자바 1.4 에서는 여러 가지 네트웍 기능이 추가되었다. 대표적인 것으로 IPv6 프로토콜 지원이 추가되었고, C/C++에서는 제공되었지만, 이전 자바 버젼에서는 제공되지 않은 기능들도 사용이 가능해졌다. 그러한 것들 중에서 타임 아웃에 대해서 추가된 기능이 있는데, 소켓 연결 시의 타임아웃을 설정할 수 있게 되었다. 자바 1.1 부터 소켓으로부터 읽기 작업을 실행할 시에 타임아웃 처리 기능은 지원되었다. 이 글에서는 소켓 연결과 읽기 작업에서의 타임아웃 처리 방법에 대해서 소개하고자 한다.
소켓 연결 시의 타임아웃 설정
자바 1.4 부터, Socket 클래스의 메소드인 connect()에서 두번째 인자로 타임아웃값을 넘겨줄 수 있다. 타임아웃값은 msec 단위이다. 소켓의 연결에 허용되는 시간이 타임아웃값으로 설정되며, 이 시간을 넘어서도 연결이 되지 않는다면 SocketTimeoutException이 발생된다. 다음 코드에서 소켓 연결 시의 타임아웃 처리 설정 방법을 확인할 수 있다.
int timeout = 500;
SocketAddress socketAddress = new InetSocketAddress(host, port);
Socket socket = new Socket();
socket.connect(socketAddress, timeout);
소켓 읽기 실행 시의 타임아웃 설정
이 기능은 자바 1.1부터 지원되어온 기능이다. Socket 클래스의 메소드인 setSoTimeout()을 통해서 타임아웃값을 설정할 수 있다. 여기에서 설정되는 타임아웃은 단지 소켓 읽기 작업에서만 사용되는 것이며, 연결 시의 타임아웃과는 관계가 없다. 그러므로, 연결 시와 읽기 작업 모두 타임아웃을 설정해주기 위해서는 새로 지원되는 connect() 메소드(인자 2개를 요구)와 setSoTimeout() 모두 사용해줘야 한다. 다음 코드는 소켓 읽기 작업의 타임아웃 처리 방법을 설명한다.
int timeout = 1000;
SocketAddress socketAddress = new InetSocketAddress(host, port);
Socket socket = new Socket();
socket.setSoTimeout(timeout);
소켓에서의 타임아웃 처리
두 가지 타임아웃 설정 방법을 이용해서 간단한 예제를 작성해본다. 클래스 이름은 EchoClientTest 이며, 프로그램 실행 시에 명령행에서 설정하는 파라메터로 소켓 연결이나 읽기에서의 타임 아웃 값을 조절할 수 있다.
이 시점에서 부가적인 설명이 하나 필요하다. 네트웍 서비스에서 서버 쪽에 포트 7번으로 접속하는 경우에 대한 것이다. 아마 네트웍을 다뤄본 사람들은 쉽게 ping 이라는 프로그램을 실행해봤을 것이다. ping 이라는 프로그램은 파라메터로 입력받는 IP 주소의 포트 7로 접속을 해서 응답을 받아 해당하는 컴퓨터의 네트웍이 동작하는지 그렇지 않은지를 확인하도록 해준다. 일반적으로 네트웍에 연결된 컴퓨터에서는 이 포트 7에 접속하는 프로그램에 서비스를 제공해주는 Echo Server가 동작하고 있다. 이것은 단순히 클라이언트에서 보내는 메시지를 단순히 재전송해주는 역할을 한다.
다시 본론으로 돌아와서 EchoClientTest 프로그램은 이 Echo Server에 접속을 시도하는 클라이언트 쪽의 프로그램이다. 실행 시에 파라메터에 따라 다음과 같이 동작이 달라진다.
- 파라메터가 없는 경우 : 로컬 컴퓨터의 Echo Service에 접속한다. (예 : java EchoClientTest)
- 하나의 파라메터가 있는 경우 : 파라메터에서 지정한 컴퓨터의 Echo Service에 접속한다. (예 : java EchoClientTest rtlab.knu.ac.kr)
- 두 개의 파라메터가 있는 경우 : 두 번째 파라메터에 특정 포트를 지정한다. (예 : java EchoClientTest rtlab.knu.ac.kr 9000)
- 세 개의 파라메터가 있는 경우 : 세 번째 파라메터에 연결 시 타임아웃 값을 지정한다. (예 : java EchoClientTest rtlab.knu.ac.kr 7 5000)
- 네 개의 파라메터가 있는 경우 : 네 번째 파라메터에 플래그가 들어가고, 이 경우에 세 번째 파라메터는 읽기 실행 시에 타임아웃 값이 된다. (예 : java EchoClientTest rtlab.knu.ac.kr 9000 1 x)
다음은 EchoClientTest 의 소스이다.
import java.io.*;
import java.net.*;
import java.util.*;public class EchoClientTest {
BufferedReader reader;
PrintWriter writer;
int port;
int timeout;
Socket socket;
SocketAddress socketAddress;
String host;private static final int ECHO_PORT = 7;
public EchoClientTest() {
this(“localhost”, ECHO_PORT);
}public EchoClientTest(String host) {
this(host, ECHO_PORT);
}public EchoClientTest(String host, int port) {
this.host = host;
this.port = port;
if (getSocketNoTimeout()) {
doWork();
}
}public EchoClientTest(
String host, int port, int timeout) {
this.host = host;
this.port = port;
this.timeout = timeout;
if (getSocketConnectTimeout()) {
doWork();
}
}public EchoClientTest(
String host, int port, int timeout, boolean b) {
this.host = host;
this.port = port;
this.timeout = timeout;
if (getSocketSetTimeout()) {
doWork();
}
}public boolean getSocketNoTimeout() {
try {
System.out.println(“Not connected, waiting…”);
socket = new Socket(host, port);
System.out.println(
“Connected. Enter ‘quit’ to end.”);
} catch (UnknownHostException e) {
System.err.println(
“Don’t know about host: ” + host + “.”);
return false;
} catch (IOException ioe) {
System.err.println(
“Connect: ” + ioe.getMessage());
return false;
}
return true;
}public boolean getSocketConnectTimeout() {
try {
System.out.println(“Not connected, waiting…”);
socketAddress =
new InetSocketAddress(host, port);
socket = new Socket();
socket.connect(socketAddress, timeout);
System.out.println(
“Connected. Enter ‘quit’ to end.”);
} catch(UnknownHostException e) {
System.err.println(“Don’t know about host: ” +
host + “.”);
return false;
} catch(SocketTimeoutException ste) {
System.err.println(“Timeout: ” +
ste.getMessage());
return false;
} catch(IOException ioe) {
System.err.println(“Connect: ” +
ioe.getMessage());
return false;
}
return true;
}public boolean getSocketSetTimeout() {
try {
socketAddress =
new InetSocketAddress(host, port);
socket = new Socket();
try {
socket.setSoTimeout(timeout);
} catch (SocketException se) {
System.err.println(“Could not set timeout: ” +
se.getMessage());
return false;
}
System.out.println(“Not connected, waiting…”);
socket.connect(socketAddress);
System.out.println(
“Connected. Enter ‘quit’ to end.”);
} catch (UnknownHostException e) {
System.err.println(“Don’t know about host: ” +
host + “.”);
return false;
} catch (SocketTimeoutException ste) {
System.err.println(“Timeout: ” +
ste.getMessage());
return false;
} catch (IOException ioe) {
System.err.println(“Connect: ” +
ioe.getMessage());
return false;
}
return true;
}public void doWork() {
try {
writer = new PrintWriter(
socket.getOutputStream(), true);
reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
System.err.println(
“Couldn’t get I/O for the connection to: ” +
host + “.”);
return;
}try {
BufferedReader stdIn = new BufferedReader(
new InputStreamReader(System.in));
String userInput;while ((userInput = stdIn.readLine()) != null) {
writer.println(userInput);
System.out.println(“echo: ” +
reader.readLine());
if (userInput.equals(“quit”)) {
return;
}
}writer.close();
reader.close();
stdIn.close();
socket.close();
} catch(IOException ioe) {
System.err.println(“IOException: ” +
ioe.getMessage());
return;
}
}public static void main(String args[]) {
int thePort = 0;
int theTimeout = 0;if (args.length == 1 && args[0].equals(“?”)) {
System.out.println(
“No args: localhost, port 7, no timeout.”);
System.out.println(
“1 arg: arghost, port 7, no timeout.”);
System.out.println(
“2 args: argHost, argPort, no timeout.”);
System.out.println(
“3 args: argHost, argPort, argTimeout, immediate connect.”);
System.out.println(
“4 args: argHost, argPort, argTimeout, argAny – set, then connect.”);return;
}System.out.println(“Start time: ” + new Date());
if (args.length == 0) {
new EchoClientTest();
} else {
String theHost = args[0];
//set up port, timeout, if sent
if (args.length > 1) {
try {
thePort = Integer.parseInt(args[1]);
if (args.length > 2) {
theTimeout = Integer.parseInt(args[2]);
}
} catch(NumberFormatException nfe) {
System.out.println(
“Invalid port or timeout value.”);
return;
}
}if (args.length == 1) {
new EchoClientTest(theHost);
} else if (args.length == 2) {
new EchoClientTest(theHost, thePort);
} else if (args.length == 3) {
new EchoClientTest(
theHost, thePort, theTimeout);
} else if (args.length == 4) {
new EchoClientTest(
theHost, thePort, theTimeout, true);
}
}
System.out.println(“End time: ” + new Date());
}
}
소켓 연결 시에 타임 아웃 값을 유연성 있게 설정하게 된다면, 네트웍 검색과 같은 프로그램에서 보다 효율적인 작업을 수행할 수 있을 것이다.