Java
Socket Util 만들어 보기
개발자-김씨
2023. 4. 11. 18:09
반응형
socket connection timeout 랑 read timeout 설정 가능한 클라이언트용 Socket util만들어보기
connection time => 접속 대기 시간
socket.connect(new InetSocketAddress(host, port), 3000);
3초간 접속안되면 SocketTimeoutException 발생
read timeout ==> 응답 대기 시간
socket.setSoTimeout(3000);
3초동안 응답(패킷수신)이 없을 경우 SocketTimeoutException 발생
ClientSocketUtil
기본 connectionTimeout 3초, readTimeout 3초
public class ClientSocketUtils {
private static final int DEFAULT_CONNECT_TIMEOUT = 3000;
private static final int DEFAULT_READ_TIMEOUT = 3000;
public static byte[] request(String url, int port, byte[] sendBytes, int readByteLength) {
return request(url, port, sendBytes, readByteLength,DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT);
}
public static byte[] request(String url, int port, byte[] sendBytes, SocketReadFunction readHandler) {
return request(url, port, sendBytes, readHandler, DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT);
}
public static byte[] request(String url, int port, byte[] sendBytes, int readByteLength, int connectTimeout, int readTimeout) {
return request(url, port, sendBytes, (inputStream) -> {
byte[] readBytes = new byte[readByteLength];
inputStream.read(readBytes);
return readBytes;
}, connectTimeout, readTimeout);
}
public static byte[] request(String host, int port, byte[] sendBytes, SocketReadFunction readHandler, int connectTimeout, int readTimeout) {
Socket socket = new Socket();
BufferedOutputStream bufferedOutputStream = null;
BufferedInputStream bufferedInputStream = null;
try {
socket.connect(new InetSocketAddress(host, port), connectTimeout);
socket.setSoTimeout(readTimeout);
bufferedInputStream = new BufferedInputStream(socket.getInputStream());
return readHandler.read(bufferedInputStream);
} catch (SocketTimeoutException ste) {
throw new RuntimeException(ste);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
IOUtils.closeQuietly(bufferedOutputStream);
IOUtils.closeQuietly(bufferedInputStream);
IOUtils.closeQuietly(socket);
}
}
}
public interface SocketReadFunction {
byte[] read(BufferedInputStream input) throws IOException;
}
ClientSocketUtil을 사용하는 샘플 코드
소켓통신으로 국민은행 조회(search), 출금(withdraw), 입금(deposit) 요청 전문을 호출하는 샘플 코드
public class KBSocket {
private static String host = "127.0.0.1";
private static int port = 20009;
public byte[] search(ByteTranslatable request) {
byte[] response = ClientSocketUtils.request(host, port, request.toBytes(), 1000);
//request bytes를 보내고 1000바이트 응답값을 받는다.
return response;
}
public byte[] withdraw(ByteTranslatable request) {
byte[] head = new byte[4];
byte[] response = ClientSocketUtils.request(host, port, request.toBytes(), (input) -> {
input.read(head);
byte[] data = new byte[Integer.parseInt(new String(head))];
input.read(data);
return ArrayUtils.addAll(head, data);
});
//request bytes를 보내고 헤더4바이트 읽고, 헤더에 명시된 본문길이만큼 다시 읽어서 응답받음
return response;
}
public byte[] deposit(ByteTranslatable request) {
byte[] body = ClientSocketUtils.request(host, port, request.toBytes(), this::receiveDepositResponse, 1000, 30000);
//request bytes를 보내고 헤더4바이트 읽고, 헤더에 명시된 본문길이만큼 다시 읽어서 응답받음
//connection timeout은 1초, 입금처리는 오래걸릴수 있으므로 read timeout은 30초
return body;
}
public byte[] receiveDepositResponse(BufferedInputStream in) throws IOException {
byte[] head = new byte[4];
in.read(head);
byte[] data = new byte[Integer.parseInt(new String(head))];
in.read(data);
return data;
}
interface ByteTranslatable {
byte[] toBytes();
}
}
ClientSocketUtil 개선해보기
public byte[] receiveDepositResponse(BufferedInputStream in) throws IOException {
byte[] head = new byte[4];
in.read(head);
byte[] data = new byte[Integer.parseInt(new String(head))];
in.read(data);
return data;
}
1.일반적으로 소켓통신 시 응답데이터를 받는 로직이 다양해서(고정길이읽기, 종료문자받기, 고정읽기+가변읽기 등) 수신 구현 확장가능하도록 SocketReadFunction인터페이스 둠 -> 응답 데이터 받는 로직이 같을 때마다 코드 중복이 발생함 -> 일반적인 읽기 구현 제공
*SocketReadFunction인터페이스도 Function<T,R>대체 가능
2. ClientSocketUtil,request메소드는 내부적으로 connect, write, read 여러 동작을 함 -> SocketTimeoutException이 발생하면connectTimeout인지 readTimeout인지 exception message로만 판단이 가능함. 메소드에 맞는 예외추상화하기
개선은 다음 편에
반응형