티스토리 뷰

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로만 판단이 가능함. 메소드에 맞는 예외추상화하기

 

개선은 다음 편에

 

 

반응형

'Java' 카테고리의 다른 글

ClientSocketUtil 개선버전  (0) 2023.04.12
자바 동기화 처리 - volatile 와 synchronized  (1) 2020.11.25
synchronized 와 Double-checked locking  (0) 2020.11.20
Optional에 대해....  (0) 2020.10.22
ThreadPoolExecutor  (0) 2017.01.09
댓글