Developer Sang Guy

Socket is closed 본문

Java

Socket is closed

은크 2022. 12. 22. 13:27

SocketUtil 클래스를 생성하여 Socket 통신 테스트를 진행하였더니 "Socket is closed" 라는 에러를 만나게 되었다.

해당 에러는 소켓을 사용하려 할 때 이미 소켓이 닫혀 버린 경우 발생하는 에러이다.

 

Socket.class

    public OutputStream getOutputStream() throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!isConnected())
            throw new SocketException("Socket is not connected");
        if (isOutputShutdown())
            throw new SocketException("Socket output is shutdown");
        OutputStream out = this.out;
        if (out == null) {
            // wrap the output stream so that the close method closes this socket
            out = new SocketOutputStream(this, impl.getOutputStream());
            if (!OUT.compareAndSet(this, null, out)) {
                out = this.out;
            }
        }
        return out;
    }
    public InputStream getInputStream() throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!isConnected())
            throw new SocketException("Socket is not connected");
        if (isInputShutdown())
            throw new SocketException("Socket input is shutdown");
        InputStream in = this.in;
        if (in == null) {
            // wrap the input stream so that the close method closes this socket
            in = new SocketInputStream(this, impl.getInputStream());
            if (!IN.compareAndSet(this, null, in)) {
                in = this.in;
            }
        }
        return in;
    }

어떻게 소스 코드를 작성하여 해당 에러가 발생했는지와 어떻게 해결하였는지 기록하겠다.

 

Socket is closed 발생 Case

SocketUtil.class

	public static Socket getConnection(String ip, int port) throws Exception {
		
		SocketAddress socketAddress = new InetSocketAddress(ip, port);
		Socket socket = new Socket();
		socket.connect(socketAddress, 5 * 1000);
		socket.setSoTimeout(5 * 1000);
		return socket;
	}
	 
	public static int send(Socket socket, String msg, String charset) throws Exception {
		
		try (DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
			dos.write(msg.getBytes(charset));
			return dos.size();
		}
	}
	
	public static String recv(Socket socket, String charset) throws Exception {
		
		try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), charset))) {
			
			StringBuffer sb = new StringBuffer();
			String line;
			while ((line = br.readLine()) != null) {
				sb.append(line);
				sb.append("\n");
			}
			
			return sb.toString();
		}
	}

 

Main.class

	public static void main(String[] args) {

		String ip = "연결할 IP";
		int port = 25000; // 연결 할 Port
		String msg = "전달할 데이터";
		String charset = "UTF-8";

		try (Socket socket = SocketUtil.getConnection(ip, port)) {
			int sendLength = SocketUtil.send(socket, msg, charset);
			if (sendLength != msg.getBytes(charset).length) {
				throw new Exception("데이터가 올바르게 전달되지 않음");
			}

			String result = SocketUtil.recv(socket, charset);
			System.out.println("응답 데이터 : " + result);

		} catch (Exception e) {
			System.out.println("Exception 발생 : " + e);
		}
	}

 

위 main 메소드에 올바른 ip와 port를 입력하여 실행하면 "socket is closed"가 발생한다.

일단 원인은 SocketUtil.class의 send() Method에서 사용 된 try - with - resource AutoCloseable 기능 때문이다.

OutputStream을 상속받아 구현한 SocketOutputStream.class의 Override 된 close() Method가 try - with - resource에 의해 호출되어 Socket가 Close 되었다. 

 

Socket.class

    private static class SocketOutputStream extends OutputStream {
        private final Socket parent;
        private final OutputStream out;
        SocketOutputStream(Socket parent, OutputStream out) {
            this.parent = parent;
            this.out = out;
        }
        @Override
        public void write(int b) throws IOException {
            byte[] a = new byte[] { (byte) b };
            write(a, 0, 1);
        }
        @Override
        public void write(byte b[], int off, int len) throws IOException {
            out.write(b, off, len);
        }

        @Override
        public void close() throws IOException {
            parent.close();
        }
    }

 

데이터를 전송한 이 후에 recv() Method가 실행 될 때까지는 Socket이 열려있어야 하기때문에 아래와 같이 소스를 수정하여 해당 이슈를 해소하였다.

보완 Case

SocketUtil.class

	public static DataOutputStream send(Socket socket, String msg, String charset) throws Exception {
		
		DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
		dos.write(msg.getBytes(charset));
		return dos;
	}

 

Main.class

	public static void main(String[] args) {

		String ip = "연결할 IP";
		int port = 25000; // 연결 할 Port
		String msg = "전달할 데이터";
		String charset = "UTF-8";

		try {
			
			Socket socket = SocketUtil.getConnection(ip, port);
			try (DataOutputStream dos = SocketUtil.send(socket, msg, charset)) {
				
				int sendLength = dos.size();
				if (sendLength != msg.getBytes(charset).length) {
					throw new Exception("데이터가 올바르게 전달되지 않음");
				}

				String result = SocketUtil.recv(socket, charset);
				System.out.println("응답 데이터 : " + result);
			}

		} catch (Exception e) {
			System.out.println("Exception 발생 : " + e);
		}
	}

 

try (DataOutputStream dos = SocketUtil.send(socket, msg, charset)) 에 의하여 Socket가 Close되는 것을 확인하여 Socket socket = SocketUtil.send(socket, msg, charset) 해당 코드는 try - with - resource을 사용할 필요가 없어 제거하였다.

 

 

 

Comments