네트워크 프로그래밍

2022. 12. 10. 16:15자바

728x90

<IP 주소 얻기>

InetAddress

IP 주소와 도메인 이름을 캡슐화하는 데 사용

생성자가 아닌 정적 메서드인 팩토리 메서드 사용해서 오브젝트 생성

/*IP 주소 얻어오는 메서드*/
static InetAddress getLocalHost() //로컬 환경에서만 이용할 때
static InetAddress getByName(String hostName)//도메인주소를 인자로 넣으면 그에 해당하는 ip주소 반환
static InetAddress getAllByName(String hostName)
//여러개의 모든 ip주소 반환(서버다운을 대비하여 도메인 하나에 여러개의 ip주소를 만들어 놓아서 여러개임)
import java.net.*;

class InetAddressTest {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress Address = InetAddress.getLocalHost();//내 로컬 ip주소 반환
        System.out.println(Address);
        Address = InetAddress.getByName("www.google.com");//도메인 주소에 해당하는 ip주소 반환
        System.out.println(Address);
        InetAddress[] SW = InetAddress.getAllByName("www.naver.com");
        //dns에서 주는 주소가 조금씩 달라질 때가 있다. 
        for (int i = 0; i < SW.length; i++)
            System.out.println(SW[i]);
    }
}

<소켓을 이용한 통신>

ServerSocket: 서버를 위한 소켓 클래스

Socket: 클라이언트를 위한 소켓 클래스

socket 클래스 예제

import java.net.*;
import java.io.*;

class Whois {
    public static void main(String[] args) throws Exception {
        int c;
        Socket s = new Socket("whois.internic.net", 43);
        // 생성자 이용해 소켓 생성, 인자로 호스트이름과 포트번호를 받음
        // 생성하면 자동으로 저 호스트주소로 연결됨
        InputStream in = s.getInputStream(); // 소켓으로부터 입력스트림 얻음
        OutputStream out = s.getOutputStream(); // 소켓으로부터 출력스트림 얻음
        String str = (args.length == 0 ? "google.com" : args[0]) + "\n";
        // 아무것도 입력하지 않으면 google.com을, 입력하면 입력한 문자열을 연결된 주소에서 검색
        byte[] buf = str.getBytes();// 문자열을 바이트로 변환해 바이트배열에 저장
        out.write(buf);// 바이트배열을 출력스트림에 씀
        while ((c = in.read()) != -1) {// 검색 결과를 입력스트림으로부터 한바이트씩 읽어들임
            System.out.print((char) c);// 입력스트림으로부터 읽어들인 바이트를 문자로 변환해 한글자씩 출력
        }
        s.close(); // 소켓 닫음
    }
}

ServerSocket 클래스 예시

 

에코서버)

import java.io.*;
import java.net.*;

public class EchoServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(9999);
        //서버 소켓은 클라이언트의 연결을 기다리므로 서버가 사용할 포트 번호만 명시하면 된다.
        server.setReuseAddress(true);
        // 포트에 연결되어있던 클라이언트와의 연결이 끝나면 다른 클라이언트와의 연결도 가능하도록 함.
        Socket connection = server.accept();
        /* 클라이언트의 연결을 기다리다가 클라이언트가 들어와서 연결이 되면 소켓을 생성한다.
        ->이 만든 소켓으로 클라이언트와 통신한다.*/
        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        //클라이언트로부터 입력받은 걸 문자 스트림으로 변환->버퍼를 이용해 읽어 들임
        String msg = null;
        while ((msg = in.readLine()) != null) {//입력받은 데이터를 한줄씩 읽어서 msg에 저장한다.
            if (msg.contains("end"))//msg에 end가 포함되어있으면
                break;
            System.out.println("Received: " + msg);
        }
        in.close();//스트림을 닫는다.
        connection.close();//통신 소켓을 닫는다.
        server.close();//서버 소켓을 닫는다.
    }
}

에코클라이언트)

import java.io.*;
import java.net.*;
import java.util.Scanner;

public class EchoClient {
    public static void main(String[] args) throws IOException {
        try {
            Socket client = new Socket(); //빈 소켓 생성
            client.connect(new InetSocketAddress("localhost", 9999), 8888);
            // 서버로 연결 요청
            PrintWriter out = new PrintWriter(client.getOutputStream(), true);
            // 서버로 메시지 전송위한 출력 스트림 생성
            Scanner in = new Scanner(System.in);// 키보드 입력받기(내가 입력한 메시지)
            String msg;
            System.out.print("Type: ");
            while ((msg = in.nextLine()) != null) {//문자열 1줄씩 읽기
                if (msg.contains("end"))//end가 포함되어 있으면 종료
                    break;
                out.println(msg);//서버로 메시지 전송
            }
            out.close();//출력 스트림 닫기
            in.close();// 입력 스캐너 닫기
            client.close(); // 소켓 닫기
        } catch (Exception e) {
            System.out.println(e); // IO 예외 처리
        }
    }
}

* InetSocketAddress(String hostname, int port): 호스트명과 포트 번호로부터 소켓 주소 작성

* connect(endpoint, timeout): 이 소켓을 지정된 타임아웃 시간만큼 서버 주소로 연결을 시도

* timeout: 지정한 시간 내에 연결이 성공되지 않아서 진행이 자동으로 중단되는 것


<URL(Uniform Resource Locator)>

웹에서는 URL이라는 식별자로 웹 페이지의 주소를 지정

예) http://www.naver.com/   ://왼쪽에 응용 계층 프로토콜 지정

 https://cs.kw.ac.kr:501/main/main.php    https가 암호화가 잘됨

import java.net.*;

class URLDemo {
    public static void main(String[] args) throws MalformedURLException {
        URL hp = new URL("https://cs.kw.ac.kr:501/main/main.php");
        // URL을 받아서 오브젝트 생성
        System.out.println("Protocol: " + hp.getProtocol()); // 프로토콜: https
        System.out.println("Port: " + hp.getPort()); // 웹포트번호: 501
        System.out.println("Host: " + hp.getHost());// 호스트네임(도메인 주소): cs.kw.ac.kr
        System.out.println("File: " + hp.getFile()); // 파일경로: /main/main.php
        System.out.println("Ext:" + hp.toExternalForm());
        // 전체 URL의 문자열 객체를 리턴: https://cs.kw.ac.kr:501/main/main.php
    }
}

<HTTP>

HyperText Transfer Protocol

웹에서 정보를 주고받을 수 있게 하는 프로토콜

웹 문서의 각 요소 또는 자원을 요청/응답 할 때 사용

                   

<URLConnection 클래스> 

URL 오브젝트를 만든 후 openConnection() 호출 시 URLConnection 오브젝트가 생성됨

예) URLConnection urlc =url.openConnection();

상대 서버와 응용 계층 연결을 맺고 응답 메시지를 가져옴

 

<URLConnection 메서드>

Http Response의 헤더와 페이로드를 파싱한 값을 반환하는데 사용됨

헤더? 데이터 앞 부분에 파일에 대한 정보를 실어놓은 부분, 데이터의 종류에 따라 정리되기 쉽게 규격화해 놓은 데이터

예) 데이터 형식에 대한 정보나 시간 데이터, 주소 데이터 등

페이로드? 전송되는 데이터

데이터를 전송할 때, 헤더와 메타데이터, 에러 체크 비트 등과 같은 다양한 요소들을  함께 보내어,

데이터 전송의 효율과 안정성을 높히게 된다. 이 때, 보내고자 하는 데이터 자체를 의미하는 것이 바로 페이로드

우리가 택배 배송을 보내고 받을 때, 택배 물건이 페이로드이고, 송장이나 박스, 뾱뾱이와 같은 완충재 등등은 부가적인 것이기 때문에 페이로드가 아닙니다.

파싱? 문자열을 분석해서 정형화된 포맷으로 만드는 것

->HTTP는 텍스트 기반 프로토콜이기 때문

import java.net.*;
import java.io.*;
import java.util.Date;

class UCDemo {
    public static void main(String[] args) throws Exception {
        int c;
        URL hp = new URL("http://www.google.com");
        URLConnection hpCon = hp.openConnection();
        /*구글로 request를 보내고, 응답을 받는다.-> hpCon에 저장
        URLConnection 클래스가 응답받은 데이터를 분석해서 파싱한다.*/
        System.out.println("Date: " + new Date(hpCon.getDate()));
        // getDate()는 서버에서 응답메시지를 보낸 시간을 반환
        System.out.println("Content-Type: " + hpCon.getContentType());
        // getContentType()은 서버에서 보낸 응답메시지의 페이로드 타입을 반환
        System.out.println("Expires: " + new Date(hpCon.getExpiration()));
        // getExpiration()은 서버에서 보낸 응답메시지의 유효기간을 반환
        System.out.println("Last-Modified: " + new Date(hpCon.getLastModified()));
        // getLastModified()는 서버에서 보낸 응답메시지의 마지막 수정 시간을 반환
        long len = hpCon.getContentLengthLong();
        /* getContentLengthLong()은 서버에서 보낸 응답메시지의 페이로드 길이를 반환
        (HTTP 헤더 중 Content-Length 필드를 참조)*/
        System.out.println("Content-Length: " + len);
        /* 웹으로부터 전달 받은 데이터를 출력하는 코드 */
        if (len != 0) {
            System.out.println("=== Content ===");
            InputStream input = hpCon.getInputStream();
            while (((c = input.read()) != -1)) {
                System.out.print((char) c);
            }
            input.close();
        } else {
            System.out.println("No content available.");
        }

    }
}

<HttpURLConnection 클래스>

URLCOnnection을 상속하는 자식 클래스

-openConeection()을 호출해서 연결 (단, 반환 값을 HttpURLConnection 타입으로 캐스팅해야함)

import java.net.*;
import java.io.*;
import java.util.*;

class HttpURLDemo {
    public static void main(String[] args) throws Exception {
        URL hp = new URL("http://www.google.com");
        HttpURLConnection hpCon = (HttpURLConnection) hp.openConnection();
        System.out.println("Request method is " + hpCon.getRequestMethod());
        // http 요청 메서드를 얻어온다. -> GET
        System.out.println("Response code is " + hpCon.getResponseCode());
        // http 응답 코드를 얻어온다. -> 200
        System.out.println("Response Message is " + hpCon.getResponseMessage());
        // http 응답 메시지를 얻어온다. -> OK
        Map<String, List<String>> hdrMap = hpCon.getHeaderFields();
        // 헤더 필드를 얻어와서 key:value 형태로 저장
        Set<String> hdrField = hdrMap.keySet();
        // key값만 저장
        System.out.println("\nHere is the header:");
        for (String k : hdrField) {
            System.out.println("Key: " + k + " Value: " + hdrMap.get(k));
            // key값을 통해 value값을 얻어온다.
        }
    }
}

*요청 메서드 종류

GET: 오직 데이터 받기만 

POST: 특정 리소스에 엔티티(데이터)를 제출할 때->종종 서버의 상태의 변화나 부작용 일으킴

*응답 코드 종류

200: 요청을 성공적으로 받았다

400: 요청을 이해할 수 없다. 잘못되었다.

*응답 메시지 종류

OK 성공

NOT FOUND 잘못됨


DatagramSocket:  UDP로 전송 계층 프로토콜을 사용하기 위해서 생성하는 소켓 클래스

DatagramPacket: UDP 클라이언트/서버 간 주고 받는 패킷을 담는 클래스

TCP/HTTP는 연결지향 :
핸드쉐이크 3번(서로 잘 연결되었는지 확인)
연결 끝낼때도 fin으로 신호보내 알려줌
리퀘스트를 보냈는데 일정시간동안 응답이 안오면 잘 살아있는지 확인메시지 보냄
UDP는 비연결지향 :
이런거 없음 안정성 떨어짐-> 대신 저런 과정없어서 되게 빠름
import java.net.*;

class WriteServer {
    public static int serverPort = 1099; // 서버 포트 지정
    public static int clientPort = 1098; // 클라이언트 포트 지정
    public static int buffer_size = 1024;
    public static DatagramSocket ds; // UDP 소켓
    public static byte[] buffer = new byte[buffer_size];
    // 바이트 배열형 버퍼에 표준입력을 받기위해 생성

    public static void TheServer() throws Exception {
        int pos = 0;
        while (true) {
            int c = System.in.read(); // 표준입력을 받아서 c에 저장
            switch (c) {
                case 'q':// q를 입력하면 종료
                    System.out.println("Server Quits.");
                    ds.close();// 소켓 닫기
                    return;
                case '\r':// \r을 입력하면 무시
                    break;
                case '\n':// \n을 입력하면 버퍼에 저장된 내용을 클라이언트에게 전송
                    ds.send(new DatagramPacket(buffer, pos, InetAddress.getByName("127.0.0.1"), clientPort));
                    // DatagramPacket 생성자에는 전송할 데이터, 데이터의 길이, 전송할 주소(내 로컬 주소), 전송할 포트를 지정
                    //send() 메소드를 이용하여 전송
                    pos = 0;
                    break;
                default:
                    buffer[pos++] = (byte) c;// 버퍼에 입력받은 내용을 저장
            }
        }
    }

    public static void TheClient() throws Exception {
        while (true) {
            DatagramPacket p = new DatagramPacket(buffer, buffer.length);
            // DatagramPacket 생성자에는 받을 데이터, 데이터의 길이를 지정
            //버퍼에 입력받은 거 패킷에 저장
            ds.receive(p);// 서버에서 보낸 패킷을 받음
            System.out.println(new String(p.getData(), 0, p.getLength()));
            // 받은 데이터를 String으로 변환하여 출력
            // p.getData()는 받은 데이터를 byte 배열로 반환
            //바이트 배열을 시작위치 0부터 기록된 길이만큼 String으로 변환
    
        }
    }

    public static void main(String[] args) throws Exception {
        if (args.length == 1) {//내가 입력하면 서버 실행
            ds = new DatagramSocket(serverPort); // 서버 포트로 보낼 소켓 생성
            ds.setReuseAddress(true); // 소켓 재사용하도록 하기위한
            TheServer();// 서버 실행
        } else {
            ds = new DatagramSocket(clientPort); // 클라이언트 포트로 보낼 소켓 생성
            ds.setReuseAddress(true);
            TheClient();// 클라이언트 실행
        }
    }
}

java.net.http 패키지

-기존 java.net의 HttpURLConnection보다 사용하기 편리함

-핵심요소

1. HttpClient : HTTP 요청/응답 메시지 전송 메커니즘을 캡슐화한 클래스

2. HttpRequest: HTTP 요청 메시지를 캡슐화한 클래스

3. HttpResponse: HTTP 응답 메시지를 캡슐화한 클래스

import java.net.*;
import java.net.http.*;
import java.io.*;
import java.util.*;

class HttpClientDemo {
    public static void main(String[] args) throws Exception {
        HttpClient myHC = HttpClient.newHttpClient();
        //오브젝트 생성
        HttpRequest myReq = HttpRequest.newBuilder(new URI("http://www.google.com/")).build();
        //해당 URL로 보낼 요청을 생성
        HttpResponse<InputStream> myResp = myHC.send(myReq,
                HttpResponse.BodyHandlers.ofInputStream());
                //send() 메소드로 요청을 보낸 후 받은 응답을 저장
        System.out.println("Response code is " +
                myResp.statusCode());
                //응답 코드 출력
        System.out.println("Request method is " +
                myReq.method());
                //요청 메소드 출력
                HttpHeaders hdrs = myResp.headers();
                //응답 헤더를 가져옴
         }
}

*HttpResponse.BodyHandler:

HTTP 응답 메시지를 받을 때 호출 되는 핸들러

HTTP 패킷에서 body 부분에 접근하는 방법 설정 가능 예) 파일, InputStream, String 등

위의 예제에선 InputStream으로 접근방법 설정

*body 부분엔 html 코드들이 있다

 

728x90

'자바' 카테고리의 다른 글

정적 배열 정리  (0) 2023.03.25
컬렉션 프레임워크  (0) 2022.12.11
네트워크 기본 용어  (0) 2022.12.10
람다 표현식  (2) 2022.12.10
static 키워드  (1) 2022.12.09