티스토리 로고

Wei의 잡다한 공간

home 홈 local_offer 태그 menu_book 방명록
search

    Wei756

    하고 싶은 거 하면서 사는 사람의 블로그

    최근 게시글 태그 방명록
    chevron_right 컴퓨터/개발

    안드로이드 파일 공유 앱 개발: 자바 소켓 통신

    Wei756
    2020. 6. 14. 00:24

    소켓 통신

    네트워크 소켓(Network Socket)은 컴퓨터 네트워크를 경유하는 프로세스 간 통신의 종착점이다. [각주:1] 바꿔 말하자면 특정 포트를 통해 들어오고 나가는 연결을 제어하는 객체를 의미한다.

    자바에서 소켓 통신은 Socket 클래스와 입출력 스트림을 이용한다.


    서버 측 코드

    서버는 아래 단 한 줄로 열린다.

    ServerSocket soc = new ServerSocket(port);

    port에는 1~65535 사이의 정수가 들어가며 다른 프로그램에서 해당 포트를 사용 중이면 BindException을 뿜으며 서버가 종료된다.



    서버가 열렸으면 다음 코드를 통해 클라이언트가 접속할 때까지 대기한다.

    Socket socket = soc.accept();



    클라이언트와 연결되면 스트림을 통해 데이터를 주고받을 수 있다. 아래 코드를 통해 데이터 스트림을 연다.

    InputStream in = socket.getInputStream();
    OutputStream out = socket.getOutputStream();
    DataInputStream din = new DataInputStream(in);
    DataOutputStream dout = new DataOutputStream(out);

    InputStream/OutputStream 만 있어도 되지만 DataStream을 쓰면 요청을 Data 단위로 알아서 끊어주기 때문에 사용했다.



    스트림이 열리면 아래 메소드를 이용해 데이터를 주고 받는다.

    String response = din.readUTF(); // data가 들어올 때까지 대기
    dout.writeUTF(str);              // data 전송

    readInt같이 자료형마다 주고 받는 메소드가 있는데 내가 사용하는 프로토콜은 자료형을 전부 String으로 통일했기 때문에 저 두 가지만 사용한다.



    위 내용을 정리한 서버 코드는 다음과 같다.

    class Server {
    
        ServerSocket soc;
        Socket socket;
    
        InputStream in;
        OutputStream out;
        DataInputStream din;
        DataOutputStream dout;
    
        public Server(int port) {
            try {
                soc = new ServerSocket(port);
                Log.i("Server", port + "번 포트로 서버 열림");
    
                int status;
                if ((status = readyForClient()) == 0) {
                    standBy();
                } else if (status == -2) { // TimeoutException
                    Log.w("Server", "클라이언트 연결 시간 초과");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (soc != null) {
                    try {
                        soc.close(); // 서버 종료
                    } catch (IOException ex) {
                    }
                }
            }
            Log.i("Server", "서버 종료");
        }
    
        /**
         * 클라이언트의 연결을 대기합니다.
         */
        public int readyForClient() {
            try {
                soc.setSoTimeout(7200000); // 타임아웃 설정
                socket = soc.accept(); // 클라이언트의 접속을 받습니다.
                Log.i("Server", "클라이언트 연결 수락");
    
                // 데이터 스트림 개통
                in = null;
                out = null;
                in = socket.getInputStream();
                out = socket.getOutputStream();
                din = new DataInputStream(in);
                dout = new DataOutputStream(out);
            } catch (IOException e) {
                if (e instanceof SocketTimeoutException) {
                    Log.w("Server", "오랫동안 기기 연결이 감지되지 않아 서버를 종료합니다.");
    
                    return -2;
                } else {
                    Log.e("Server", "클라이언트와 연결하는 중 에러가 발생하였습니다!");
                    e.printStackTrace();
    
                    return -1;
                }
            }
    
            return 0;
        }
    
        public void standBy() {
            try {
                while(true) {
                    String response = din.readUTF();
                    Log.v("Server", "Response: " + response);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }

    Log 는 안드로이드 호환성을 위해 임의로 만든 클래스이다. 소스는 여기서 확인할 수 있다.


    클라이언트 측 코드

    클라이언트에서 서버에 접속하는 코드는 다음과 같다.

    Socket socket = new Socket(address, port);

    address는 서버 주소, port는 접속할 포트이다.



    서버와 연결되면 마찬가지로 스트림을 열어 데이터를 주고받을 수 있도록 한다. 코드는 다음과 같다.

    InputStream in = socket.getInputStream();
    OutputStream out = socket.getOutputStream();
    DataInputStream din = new DataInputStream(in);
    DataOutputStream dout = new DataOutputStream(out);



    스트림이 열리면 아래와 같은 메소드를 이용해 데이터를 주고 받는다.

    String response = din.readUTF(); // data가 들어올 때까지 대기
    dout.writeUTF(str);              // data 전송



    위 내용을 정리한 클라이언트 코드는 다음과 같다.

    class Client {
    
        Socket socket;
    
        InputStream in;
        OutputStream out;
        DataInputStream din;
        DataOutputStream dout;
    
        boolean connected = false;
    
        public Client(String address, int port) {
            try {
                socket = new Socket(address, port);
                Log.i("Client", port + "번 포트로 클라이언트 시작됨");
    
                connected = readyForHost();
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
        /**
         * 서버에 연결합니다.
         *
         * @return
         */
        public boolean readyForHost() {
            try {
                socket.setSoTimeout(5000); // 타임아웃 설정
    
                // 스트림 개통
                in = socket.getInputStream();
                out = socket.getOutputStream();
                din = new DataInputStream(in);
                dout = new DataOutputStream(out);
                Log.i("Client", "서버 연결됨!");
    
            } catch (IOException e) {
                Log.e("Client", "서버에 연결하는 중 에러가 발생하였습니다!");
                e.printStackTrace();
                return false;
            }
    
            return true;
        }
    
        /*
         * 서버로 데이터를 전송합니다.
         *
         * @param str 전송할 데이터
         */
        public void send(String str) {
            try {
                if (connected)
                    dout.writeUTF(str);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    테스트

    위에서 작성한 코드를 테스트하기 위한 메인 클래스 소스이다.

    class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
    
            String cmd = sc.nextLine();
    
            // 서버/클라이언트 선택
            if("server".equals(cmd)) { // 서버
                Server server = new Server(12345); // 1~65535 사이 숫자 아무거나
    
            } else if("client".equals(cmd)) { // 클라이언트
                Client client = new Client("127.0.0.1", 12345);
    
                while(true) {
                    cmd = sc.nextLine();
                    client.send(cmd);
                }
            }
        }
    }



    풀 소스는 Github 저장소에 올려놓았다. 깃허브에 올린 소스는 완성본으로 위에 적어놓은 코드와는 다른 부분이 많을 것이다. 그거에 대해서는 다음 번에 설명할 예정이다.


    다음 글에서 계속

    1. 출처 https://ko.wikipedia.org/wiki/네트워크_소켓 [본문으로]
    저작자표시 동일조건 (새창열림)
    keyboard_arrow_up
    keyboard_arrow_down

    컴퓨터/개발 카테고리의 다른 글

    • 안드로이드 파일 공유 앱 개발: 설계

카테고리

  • 분류 전체보기 (17)
    • 자작 (4)
      • VR (4)
    • 컴퓨터 (11)
      • 개발 (2)
      • 웹 (1)
      • 정보 (1)
      • 자료 (1)
      • 팁 (6)
      • 일상 (1)
    • 편의기능 (2)

최근 포스트

최근 댓글

티스토리 로고 티스토리 로고

티스토리툴바