reuse DatagramSocket in UDP (already use)

WindSekirun (wind.seo)·2022년 4월 26일
0

이 글은 기존 운영했던 WordPress 블로그인 PyxisPub: Development Life (pyxispub.uzuki.live) 에서 가져온 글 입니다. 모든 글을 가져오지는 않으며, 작성 시점과 현재 시점에는 차이가 많이 존재합니다.

작성 시점: 2018-07-19

UDP 로 패킷을 받아 작업을 진행하는 기능이 있었다. 이 때 사용하는 클래스는 DatagramSocket 인데, 가끔 already use 라는 로그가 뜨면서 앱이 종료되는 일이 있다. close 하면 괜찮다고 생각했지만, 어느 시점에서 계속 발생하는 것 같았다.

결국에는, close() 말고도 다른 작업을 했어야 하는데, 좀 더 찾아보니 DatagramSocket 에 setReuseAddress 라는 메서드가 있어 이 것을 설정해주면 문제는 해결된다는 것이다. 문제는, DatagramSocket 의 바인딩 시점은 '생성자' 에서 한다는 것이다. 즉, setReuseAddress 를 사용하기 위해서는 DatagramSocket 의 인스턴스가 필요한데 이 시점에서 바인드가 되니 설정할 수 없는 것이다.

즉, setReuseAddress 를 사용하기 위해서는 바인딩 시점을 피해 인스턴스를 생성하는 것인데, 결론으로 말하자면 DatagramSocket 의 생성자 파라미터에 null를 넘기는 것이다.

이렇게 하면 문제가 해결될 것 같지만, 한 가지 더 문제가 발생했다. DatagramSocket 의 인스턴스를 생성하고 bind 메서드를 부르려 하니 bind 메서드는 InetAddress 클래스만 받고 있었다. 필요한 기능은 루프백 IP + 특정 Port 조합이므로 원래라면 DatagramSocket(port) 만 넘기고 있었으나, bind 메서드를 사용하기 위해서는 InetAddress 객체를 만들 필요가 있었다.

일반적으로 루프백 IP 라 함은 127.0.0.0 ~ 127.255.255.255 를 의미하니 InetSocketAddress("127.0.0.1", port) 를 넘기면 되겠다 싶었지만 작동이 안 되었다.

결국엔 DatagramSocket 의 자바독 문서를 보고 답을 찾았는데, 'If the IP address is 0.0.0.0, the socket will be bound to the wildcard address, an IP address chosen by the kernel. ' 라는 문구이다.

이 문구는 DatagramSocket 의 수 많은 생성자들 중 두 개에 있었는데, 먼저 DatagramSocket(port, InetAddress) 의 설명을 보면 아래와 같이 작성되어있다.

Creates a datagram socket, bound to the specified local address. The local port must be between 0 and 65535 inclusive. If the IP address is 0.0.0.0, the socket will be bound to the wildcard address, an IP address chosen by the kernel.

Datagram socket 를 구성하고 특정 로컬 주소로 바인드 합니다. 포트는 0 과 65535 내에 있어야 합니다. IP Address 가 0.0.0.0 이면, 커널에 의해 IP address 가 결정되는 와일드카드 주소로 바인드 됩니다.

그리고 원래 사용하던 DatagramSocket(port) 의 설명에도 비슷한 말이 작성되어 있었다.

Constructs a datagram socket and binds it to the specified port on the local host machine. The socket will be bound to the wildcard address, an IP address chosen by the kernel.

Datagram socket 를 구성하고 로컬 호스트 머신에 있는 특정 포트에 바인드 합니다. 이 소켓은 커널에 의해 IP address 가 결정되는 와일드카드 주소로 바인드 됩니다.

즉, InetSocketAddress("127.0.0.1", port) 가 아닌 InetSocketAddress("0.0.0.0", port) 를 넘기면 원래 사용하던 DatagramSocket(port) 와 같은 효과를 낼 수 있다는 것이다.

이렇게까지 해서 완성된 메서드는 다음과 같다.

/**
 * original DatagramSocket(port) will be bound to the wildcard address, an IP address chosen by the kernel.
 * So, we can bound to the wildcard address using 0.0.0.0 into hostname
 *
 * See original document [https://docs.oracle.com/javase/6/docs/api/java/net/DatagramSocket.html#DatagramSocket(int,%20java.net.InetAddress)]
 *
 * @param port Int
 * @return Observable<DatagramSocket>
 * @see DatagramSocket
 */
fun createReuseSocket(port: Int): Observable<DatagramSocket> {
    return Observable.create<DatagramSocket> {
        val socket = DatagramSocket(null)
        socket.reuseAddress = true
        socket.bind(InetSocketAddress("0.0.0.0", port))
        it.onNext(socket)
    }
}
profile
Android Developer @kakaobank

0개의 댓글