즐겁게!! 자신있게!! 살아보세!!

재밌는 인생을 위하여! 영촤!

Language_Study/JAVA

[JAVA, App] 19.통신

Godwony 2020. 12. 27. 12:27
728x90
반응형

java.net.InetAddress

  • ip 정보를 저장하는 클래스
  • static 메소드인 getLocalHost(), getByName(String hostname), getAllByName(String hostname)
    • getLocalHost(): 자기 컴퓨터의 IP 정보를 리턴
    • getByName은 host의 ip 정보 1개를 리턴
    • getAllByName은 host의 모든 ip 정보를 리턴

소켓 통신

  • Socket: NIC(Network Interface Card - LAN Card)를 추상화한 클래스
  • 네트워크 프로그래밍이라는 용어 대신에 Socket Programming 이라고도 합니다.

1.통신 방법

1) TCP 프로토콜을 사용하는 스트림 소켓

2) UDP 프로토콜을 사용하는 데이터그램 소켓

2.TCP(연결형 통신)

  • 수신하는 쪽에서 송신하는 쪽으로 연결을 요청
  • 송신하는 쪽에서 연결을 하고 요청하는 데이터에 대한 메타 정보(데이터에 대한 정보 - 데이터 크기 등)를 송신
  • 수신하는 쪽에서 그 정보를 보고 다시 요청을 합니다.
  • 송신하는 쪽에서 데이터를 전송
  • 수신하는 쪽에서 데이터 수신 여부를 송신하는 쪽에 전송하고 통신이 종료
  • 신뢰성이 높지만 트래픽이 증가
  • HTTP, HTTPS 가 TCP 통신

3.UDP(비연결형 통신)

  • 송신하는 쪽에서 수신하는 쪽으로 일방적으로 데이터를 전송하고 통신이 종료
  • DHCP(IP 동적 할당), DNS(Domain -> IP)
  • 콜 센터, Apple의 APNS(Apple Push Notification Service - 애플 제품의 알림)
  • 수신 쪽에서 데이터를 제대로 받았는지 알 수 없음

4.Socket 클래스

1) 생성자

Socket()
Socket(InetAddress addr, int port): addr 의 port 번호에 해당하는 서비스에 접속
Socket(String addr, int port): addr 의 port 번호에 해당하는 서비스에 접속
Socket(InetAddress addr, int port, InetAddress localaddr, int localport): addr의 port 번호에 접속을 하는데 자신의 주소를 localaddr 그리고 port는 localport로 설정해서 접속
  • addr 이 잘못되면 NullPointerException 그리고 port 번호가 잘못되면 illegalArgumentException이 발생

2) 메소드

void close()
InetAddress getInetAddress(): 접속한 상대방 IP 정보
int getPort(): 상대방 포트 번호
InputStream getInputStream(): 상대방에게서 정보를 읽어오기 위한 스트림
OutputStream getOutputStream(): 상대방에게 정보를 전송하기 위한 스트림

5.스트림 소켓 - TCP 통신을 위한 소켓

1) 수신 받는 쪽의 소켓 생성과 요청

Socket 소켓변수 = new Socket(서버IP주소, 포트번호); //연결
//요청 전송
OutputStream 출력스트림변수 =  소켓변수.getOutputStream();

//바이트 단위 전송
출력스트림변수.write(byte [] b); 
//문자단위 전송
PrintWriter pw = new PrintWriter(출력스트림변수);
pw.println(String msg);
pw.flush()

//데이터 읽어오기
InputStream 입력스트림변수 =  소켓변수.getInputStream();
//바이트 단위로 읽어오기 - 파일 다운로드
입력스트림변수.read(byte [] b);

//문자열 단위로 읽어오기
BufferedReader br = new BufferedReader(new InputStreamReader(입력스트림변수));
String msg = br.readLine();//한 줄 읽어오기
//null을 리턴할 때 까지 읽으면 전송된 모든 내용을 읽을 수 있습니다.

6.www.daum.net 의 html 가져오기

  • www.daum.net 이 호스트 이름이고 http 서버는 기본 포트번호가 80
public class DaumMain {

    public static void main(String[] args) {
        try {
            //daum 의 주소를 생성
            InetAddress addr = InetAddress.getByName("www.daum.net");
            //TCP 소켓 생성
            Socket socket = new Socket(addr, 80);

            //요청 전송
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            pw.println("GET http://www.daum.net");
            //flush를 호출하지 않으면 전송이 안될 수도 있습니다.
            pw.flush();

            //데이터 읽기  - 문자 단위
            BufferedReader br = 
                new BufferedReader(
                    new InputStreamReader(
                            socket.getInputStream()));
            //읽을 데이터가 없을 때 까지 줄 단위로 읽어오기
            while(true) {
                String line = br.readLine();
                if(line == null) {
                    break;
                }
                System.out.println(line);
            }
            br.close();
            socket.close();

        }catch(Exception e) {
            //예외 메시지 출력
            System.out.println("예외:" + e.getMessage());
            //예외가 발생한 코드를 역추적
            e.printStackTrace();
        }

    }

}

7.TCP Server: 정보를 제공

  • ServerSocket을 사용

1) 생성자

ServerSocket()
ServerSocket(int port): port를 개방해서 클라이언트의 요청을 받을 수 있도록 서버가 생성
ServerSocket(int port, int backlog): port를 개방해서 클라이언트의 요청을 받을 수 있도록 서버가 생성되고 최대 접속 개수를 설정

2) 메소드
void close()
Socket accept(): 호출하면 클라이언트의 요청이 올 때 까지 대기 상태가 되고 클라이언트의 요청이 오면 클라이언트와 통신할 수 있는 Socket을 리턴하고 다음으로 넘어갑니다.

3) 통신과정

  • ServerSocket을 생성해서 클라이언트의 요청을 기다림
  • 클라이언트 쪽에서 Socket을 이용해서 서버에 접속
  • Socket의 스트림을 가지고 메시지를 전송
  • ServerSocket을 생성할 때 port는 사용 중이 아닌 번호로 설정 - 1024보다 큰 숫자로 설정하는 것을 권장
    • 서버 소켓을 생성하고 예외가 발생해서 프로그램이 중단되면 이전에 사용한 포트번호를 사용하지 못할 수 도 있습니다.
    • 자바로 실행 중인 프로세스를 찾아서 중단해야 이전 포트를 다시 사용 가능합니다.
  • 현재 사용 중인 포트를 확인: netstat -ano
    • 자신의 ip 확인: ipconfig(ifconfig - mac)
  • 다른 컴퓨터에서 자신의 컴퓨터에 접속하도록 할려면 방화벽 해제 되어 있어야 합니다.

4) TCP 통신

  • 서버 클래스
public class TCPServer {

    public static void main(String[] args) {
        try {
            //서버 소켓을 생성 - 9000번 포트를 이용해서 클라이언트와 접속
            ServerSocket ss = new ServerSocket(9000);
            while(true) {
                System.out.println("서버 대기 중....");
                //클라이언트의 접속을 기다림
                Socket socket = ss.accept();
                //접속한 클라이언트 정보 확인
                System.out.println("접속한 클라이언트:" + socket.getInetAddress());
                //클라이언트가 전송한 메시지 확인
                BufferedReader br = 
                    new BufferedReader(
                        new InputStreamReader(
                            socket.getInputStream()));
                String msg = br.readLine();
                System.out.println("메시지:" + msg);

                //클라이언트에게 메시지 전송
                PrintWriter pw = new PrintWriter(socket.getOutputStream());
                pw.println("서버가 보내는 메시지");
                pw.flush();

                br.close();
                pw.close();
                socket.close();
            }

        }catch(Exception e) {
            System.out.println("예외:" + e.getMessage());
            e.printStackTrace();
        }

    }

}
  • 클라이언트 클래스
public class TCPClient {

    public static void main(String[] args) {
        try {
            //서버에 접속하는 소켓을 생성
            Socket socket = new Socket(InetAddress.getByName("211.183.7.61"),9000);
            //메시지 전송
            Scanner sc = new Scanner(System.in);
            System.out.print("전송할 메시지:");
            String msg = sc.nextLine();

            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            pw.println(msg);
            pw.flush();

            //메시지 읽기
            BufferedReader br = 
                new BufferedReader(
                    new InputStreamReader(
                            socket.getInputStream()));
            String str = br.readLine();
            System.out.println(str);

            br.close();
            pw.close();
            socket.close();

        }catch(Exception e) {
            System.out.println("예외:" + e.getMessage());
            e.printStackTrace();
        }

    }

}

8.UDP

  • 비연결형 통신
  • 보내는 쪽에서 받는 쪽으로 메시지만 전송하고 통신이 종료

1) 통신방식

  • unicast: 1:1 로 통신하는 방식
  • multicast: 그룹에 속한 모든 클라이언트와 통신
  • broadcast: 자신의 IP 대역과 subnet mask를 이용해서 자신의 IP 대역과 같은 그룹에 속한 모든 클라이언트에게 전송

2) DatagramPacket 클래스와 DatagramSocket 클래스 이용

  • DatagramSocket 클래스
  • 생성자
DatagramSocket() : 전송을 하기 위한 소켓
DatagramSocket(int port) : 전송을 받기 위한 소켓
  • 메소드
void close()
void receive(DatagramPacket packet) : 데이터를 받는 메소드
void send(DatagramPacket packet) : 데이터를 보내는 메소드
  • DatagramPacket 클래스

생성자

DatagramPacket(byte [] buf, int length): 전송을 받기 위한 패킷으로 byte 배열에 데이터가 저장됩니다.
DatagramPacket(byte [] buf, int length, InetAddress addr, int port): 전송을 하기 위한 패킷으로 byte 배열의 내용을 length 만큼 addr 의 port에게 전송하기 위한 패킷

메소드

byte [] getData() : 데이터 리턴
int getLength() : 길이 리턴
  • String 과 byte 배열 변환
    • String -> byte 배열 : String.getBytes();
    • byte 배열 -> String : new String(byte 배열);

3) unicast

  • 1:1 통신

  • 받는 쪽

public class UDPReceive {

    public static void main(String[] args) {
        try {
            //받는 소켓을 생성
            DatagramSocket socket = new DatagramSocket(7777);

            //데이터를 전송받아서 읽기
            while(true) {
                //데이터를 저장할 패킷을 생성
                //이 두개의 문장을 반복문 바깥에 만들면 통신은 되는데 긴 메세지를 보내고 짧은 메시지를 보내면
                //짧은 메시지 뒤에 긴 메시지의 내용이 추가되는 형태가 됩니다.
                //반복문 안에서 계속 사용해야 하는 데이터는 반복문안에서 초기화를 해주어야 합니다.
                byte [] b = new byte[65536];
                DatagramPacket dp = new DatagramPacket(b, b.length);

                //대기하고 있다가 데이터를 전송받으면 동작
                socket.receive(dp);
                //보낸 곳 확인
                System.out.println("보낸 곳:" + dp.getAddress().getHostAddress());
                //데이터 확인
                String msg = new String(b);
                System.out.println(msg);

            }

        }catch(Exception e) {
            System.out.println("예외1:" + e.getMessage());
            e.printStackTrace();
        }

    }

}
  • 보내는 쪽
public class UDPSend {

    public static void main(String[] args) {
        try {
            //UDP 전송을 위한 소켓 생성
            DatagramSocket ds = new DatagramSocket();
            Scanner sc = new Scanner(System.in);
            while(true) {
                //메시지 입력
                System.out.print("전송할 메시지:");
                String msg = sc.nextLine();
                //전송할 패킷 생성
                DatagramPacket dp = new DatagramPacket(
                    msg.getBytes(), msg.getBytes().length,
                    InetAddress.getByName("211.183.7.61"), 7777);
                ds.send(dp);
            }

        }catch(Exception e) {
            System.out.println("예외1:" + e.getMessage());
            e.printStackTrace();
        }

    }

}

4) multicast

  • 그룹에 속한 모든 단말에게 데이터를 전송하는 방식

  • 224.0.0.0 ~ 239.255.255.255 사이의 주소를 이용

    • 이 주소 대역은 D Class 대역으로 Multicast 용으로 예약된 IP 주소 대역
  • MulticastSocket 을 이용해서 구현

  • 생성자

MulticastSocket()
MulticastSocket(int port)
  • 메소드
joinGroup(InetAddress addr): 그룹에 참여
leaveGroup(InetAddress addr): 그룹에서 빠져나오는 메소드
  • 데이터 전송방식은 DatagramSocket 과 동일

  • multicast 받는 쪽

public class MultiReceive {

    public static void main(String[] args) {
        try {
            MulticastSocket ms = new MulticastSocket(9999);
            //멀티캐스트에 참여
            ms.joinGroup(InetAddress.getByName("230.100.100.100"));
            System.out.println("멀티 캐스트 시작");
            while(true) {
                //전송받은 데이터를 저장할 바이트 배열 - 크기는 8의 배수로 설정하는 경우가 많음
                byte [] b = new byte[65536];
                //패킷을 생성
                DatagramPacket dp = new DatagramPacket(b, b.length);
                //데이터를 받을 수 있도록 대기
                ms.receive(dp);

                //데이터 읽기
                String msg = new String(dp.getData());
                System.out.println(msg.trim());
            }

        }catch(Exception e) {
            System.out.println("예외1:" + e.getMessage());
            e.printStackTrace();
        }

    }

}
  • multicast 보내는 쪽
public class MultiSend {

    public static void main(String[] args) {
        try {
            MulticastSocket ms = new MulticastSocket();
            Scanner sc = new Scanner(System.in);
            System.out.print("닉네임:");
            String nickname = sc.nextLine();

            while(true) {
                System.out.print("전송할 메시지(종료는 end):");
                String msg = sc.nextLine();
                //문자열은 ==로 비교하면 참조를 비교
                //equals 로 비교해야 값을 비교
                if(msg.equals("end")) {
                    System.out.println("종료");
                    break;
                }
                msg = nickname + ":" + msg;
                DatagramPacket dp = 
                    new DatagramPacket(msg.getBytes(), msg.getBytes().length,
                        InetAddress.getByName("230.100.100.100"), 9999);
                ms.send(dp);
            }

        }catch(Exception e) {
            System.out.println("예외1:" + e.getMessage());
            e.printStackTrace();
        }
    }
}
  • 채팅 처럼 동시에 주고받는 것이 가능하도록 할려면 보내고 받는 로직을 스레드를 이용해서 작업
    콘솔에서는 쉽지 않습니다.
    • 입력창과 출력창이 같아서 콘솔용 채팅은 동시에 입출력 한계가 있습니다.

URL 통신

  • 소켓 통신을 저수준의 통신방식이라고 하고 그 이외의 통신 방식들은 고수준이라고 부릅니다.

    • 성능은 소켓 통신이 우수한데 소켓 통신은 프로그램을 별도로 설치해야만 통신이 가능합니다.
    • URL 통신은 브라우저를 통해서 사용이 가능하고 현재는 거의 모든 운영체제가 웹 브라우저를 하나씩 가지고 있습니다.
    • 최근에는 웹에서도 소켓 방식의 통신이 가능합니다.
    • WebSocket API를 HTML5에서 제공
  • URL 구성

    • 프로토콜://도메인이나IP:포트번호/파일경로?이름=값&이름&값...
    • 프로토콜과 도메인은 생략 못함
  • 포트번호는 서비스의 기본포트를 사용하는 경우에는 생략이 가능

    • http:80, https:443
  • 파일경로를 생략하는 경우가 있는데 이 경우는 서버의 설정을 이용해서 파일을 찾아옵니다.

  • 파일 경로 뒤에 ?는 parameter라고 하는데 클라이언트가 서버에게 넘겨주는 데이터로 key-value 형식으로 대입

  • parameter 전송 방식을 가지고 GET 방식과 POST 방식을 구분

  • 파일 경로 뒤에 #이 붙는 경우는 책갈피입니다.

  • 페이지 내에서 이동

1.java.net.URL 클래스

  • URL을 만들기 위한 클래스

1) 생성
URL(String url)

  • 없는 url을 대입하면 MalformedURLException이 발생

2) 메소드

  • URLConnection openConnection(): URL과 양방향 통신이 가능한 Connection을 리턴
  • 여기서 리턴한 Connection 은 HttpURLConnection 이나 JarURLConnection으로 형변환해서 사용해야 합니다.
  • URLConnection은 추상 클래스라서 메소드가 구현되어 있지 않습니다.

2.HttpURLConnection

  • URL 통신을 하기 위한 클래스

  • URL 클래스의 openConnection 메소드를 이용해서 생성

  • 메소드

    • setConnectTimeout(int timeout): 밀리초 단위로 타임아웃을 설정하는 메소드로 타임아웃 동안 접속이 안되면 접속 실패
  • setUseCaches(boolean isCache): 이전에 접속했던 URL에 다시 요청할 때 이전 데이터를 가져올 것인지 설정

  • 자주 변경되는 URL의 데이터는 반드시 false로 설정을 해주어야 합니다.

setRequestProperty(String field, String value)
addRequestProperty(String field, String value)

  • 헤더에 값을 추가하는 메소드

setRequestMethod(String method): 전송 방식을 설정

int getResponseCode(): 서버로부터의 상태를 리턴

200번대 정상응답
300번대 리다이렉트 중
400번대 클라이언트 오류(404 - 잘못된 URL)
500번대 서버 오류

InputStream getInputStream() : 데이터를 읽기 위한 스트림 리턴

3.웹의 데이터 포맷

  • XML: 태그 형식으로 표현하는 포맷, 별도의 라이브러리 없이 파싱 가능
  • JSON: 자바스크립트 객체 표현 방법으로 데이터를 표현, 별도의 라이브러리를 추가해야 파싱 가능
  • CSV: 구분자가 있는 문자열
  • HTML: 뷰의 용도로 사용되는 포맷인데 사이트에서 XML이나 JSON 형태로 데이터를 제공해주지 않아서 사용
    • 파싱을 할려면 별도의 라이브러리를 추가해서 가능

4.Open API

  • 데이터를 가진 곳에서 일반 사용자들에게 데이터를 사용할 수 있도록 XML 이나 JSON 형식으로 제공하는 것

  • 데이터 뿐 아니라 라이브러리나 프레임워크 등을 제공하기도 함

  • 데이터는 대부분의 경우 가입을 해서 키를 받는 형태로 제공

  • 키값을 주소에 넣기도 하고 헤더에 넣기도 합니다.

5.웹 사이트에서 문자열 가져오기

public class StringDownload {

    public static void main(String[] args) {
        try {
            //다운로드 받을 URL을 생성
            URL url = new URL("https://www.naver.com");
            //URL 연결 객체 생성
            HttpURLConnection con = (HttpURLConnection)url.openConnection();
            //연결 옵션 설정
            con.setConnectTimeout(30000); //30초 동안 연결이 안되면 연결 시도 종료
            //캐시 사용을 하지 않음
            con.setUseCaches(false);

            //데이터를 읽어올 스트림을 생성
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(con.getInputStream()));

            //많은 양의 문자열을 읽어야 하는 경우
            StringBuilder sb = new StringBuilder();
            while(true) {
                //한 줄 읽기
                String line = br.readLine();
                //읽은 데이터가 없으면 반복문 중단
                if(line == null) {
                    break;
                }
                //데이터가 있으면 sb에 추가
                sb.append(line + "\n");
            }

            //StringBuilder 의 데이터를 String 으로 변환
            String html = sb.toString();
            System.out.println(html);

        }catch(Exception e) {
            System.out.println("다운로드 예외:" + e.getMessage());
            e.printStackTrace();
        }

    }

}

6.비동기 다운로드

  • 위처럼 스레드를 사용하지 않고 다운로드 받는 방식을 동기식 이라고 합니다.
    • 동기식은 데이터를 다운로드 받는 동안 다른 작업을 할 수 없습니다.
    • 데이터 다운로드와 관련없는 작업도 데이터를 다운로드 동안은 수행할 수 없습니다.
    • 네트워크 작업은 스레드를 이용해서 비동기식으로 동작하도록 만드는 것을 권장(안드로이드는 필수)
    • 다운로드와 관련없는 작업은 다운로드 받는 동안 수행되도록 작성하는 것이 좋습니다.
  • 이미지 파일을 다운로드 받아서 현재 디렉토리에 저장하기
public class ImageDownload {

    public static void main(String[] args) {
        Thread th = new Thread() {
            public void run() {
                try {
                    String addr = 
                        "https://img0.yna.co.kr/photo/yna/YH/2019/04/07/PYH2019040703010000700_P4.jpg";
                    //파일명을 만들기 위해서 마지막 / 다음의 문자열 가져오기
                    int len = addr.lastIndexOf('/');
                    String filename = addr.substring(len+1);
                    //System.out.println(filename);

                    //현재 디렉토리에 위 파일이 있으면 있다고 출력하고 없다면 다운로드 받아서 저장
                    File f = new File("./" + filename);
                    if(f.exists() == true) {
                        System.out.println("파일이 이미 존재합니다.");
                        return;
                    }else {
                        //주소 객체 생성
                        URL url = new URL(addr);
                        HttpURLConnection con = (HttpURLConnection)url.openConnection();
                        con.setConnectTimeout(30000);
                        con.setUseCaches(false);

                        /*
                        //다운로드 받을 파일의 크기를 가져오기
                        int length = con.getContentLength();
                        //데이터를 저장할 바이트 배열 생성
                        byte [] b = new byte[length];
                        //바이트 단위로 데이터를 읽어올 스트림 생성
                        BufferedInputStream bis = 
                            new BufferedInputStream(con.getInputStream());
                        //데이터를 읽어서 b에 저장
                        bis.read(b);
                        //읽어온 내용을 파일에 저장
                        PrintStream ps = new PrintStream(new FileOutputStream("./" + filename));
                        ps.write(b);
                        */

                        //나누어서 읽어서 기록
                        //바이트 단위로 데이터를 읽어올 스트림 생성
                        BufferedInputStream bis = 
                            new BufferedInputStream(con.getInputStream());
                        PrintStream ps = new PrintStream(new FileOutputStream("./" + filename));

                        while(true) {
                            //512 바이트 배열
                            byte [] b = new byte[512];
                            //내용을 읽어서 b에 저장
                            //읽은 개수를 r에 저장
                            int r  = bis.read(b);
                            //읽은게 없으면 중단
                            if(r <= 0) {
                                break;
                            }
                            //읽은 데이터가 있으면 기록
                            ps.write(b, 0, r);
                            //버퍼에 내용이 남아있는 것을 방지하기 위해서 마지막에 flush를 호출
                            ps.flush();
                        }

                        //사용한 스트림 닫기
                        ps.close();
                        bis.close();
                        //연결 끊기
                        con.disconnect();
                    }

                }catch(Exception e) {
                    System.out.println("다운로드 예외:" + e.getMessage());
                    e.printStackTrace();
                }
            }
        };
        th.start();

        //스레드 동작 중 쉬는 시간이 생기면 동작
        System.out.println("스레드와 상관없는 코드");
    }

}
728x90
반응형

'Language_Study > JAVA' 카테고리의 다른 글

[JAVA, App] 21.Lambda  (0) 2020.12.28
[JAVA, App] 20.Parsing  (0) 2020.12.28
[JAVA, App] 18.Stream  (0) 2020.12.27
[JAVA, App] 17.자바GUI  (0) 2020.12.27
[JAVA, App] 16.이벤트처리  (0) 2020.12.27