반응형

netty, 작성 x

net

net #ex1

Overview // Receiver -> Server // Sender -> Client

Receiver // Socket socket = serverSocket.accept(); // Server가 실행되고, Client가 접속하기 전까지 기다린다.

Sender // Server와 연결하고, Server와 입출력을 주고 받을 in과 out을 준비한다. // Server도 마찬가지.

          // PrintStream out = new PrintStream(socket.getOutputStream()); // getOutputStream은 혼자 사용하기 힘들다.

          // PrintStream을 붙혀서 getOutputStream 사용을 도와주는데 이때 PrintStream을 데코레이터라고 한다.

          // Scanner in = new Scanner(socket.getInputStream()); // getInputStream도 혼자 사용하기 힘들다.

          // Scanner를 붙혀서 getInputStream 사용을 도와주는데 이때 Scanner는 Helper 라고 한다.

Receiver2 // 서버 + 키보드 입력 기능 추가

Sender2 // 클라이언트 + 키보드 입력 기능 추가

Receiver3 // 서버 + 키보드 입력 기능 추가 (무한정 턴키 방식)

Sender3 // 클라이언트 + 키보드 입력 기능 추가 (무한정 턴키 방식)

Receiver4 // 서버 + 파일 전송 // long filesize = in.readLong(); // 파일 사이즈를 먼저 받는다.

             // String filename = in.readUTF(); // 파일 이름을 받는다.

             // File file = new File("temp/ok_" + filename); // 파일을 전송 받을 빈 파일 객체를 만든다.

             // FileOutputStream fileOut = new FileOutputStream(file); // 파일에 데코레이터를 붙힌다.

             // for (long i = 0; i < filesize; i++) { fileOut.write(in.read()); }

             // in.read()는 1바이트씩 읽어오고 fileOut.write()은 1바이트씩 쓰기 때문에, 반복문을 돌린다.

Sender4 // 클라이언트 + 파일 전송

            // out.writeLong(file.length()); // 파일의 크기를 먼저 보낸다.

            // out.writeUTF(file.getName()); // 파일의 이름을 보낸다. 

            // int b; while ((b = fileIn.read()) != -1) { out.write(b); }

            // fileIn.read()은 파일에서 1바이트를 가져온다. 1바이트를 가져왔는지 알기 위해 int b를 미리 준비한다.

            // 만약 마지막 바이트까지 가져온다면, -1을 리턴한다. // -1일때 반복문 탈출.

Receiver5 // 서버 + 파일 전송 + 버퍼

Sender5 // 클라이언트 + 파일 전송 + 버퍼

 

net #ex2

Client0110 // 클라이언트(client) // 연결을 요청하는 쪽을 가리키는 용어다.

               // Socket socket = new Socket("IP 주소(ex: 234.3.4.56) 또는 도메인명(ex: www.daum.net), 포트번호)

               // 포트번호 // 서버를 구분하는 식별 번호. // IP 주소가 회사 대표 번호, 포트 번호는 내선 번호라고 생각

Server0110 // 서버(server) // 연결 요청을 받는 쪽을 가리키는 용어다.

               // ServerSocket ss = new ServerSocket(8888); // new ServerSocket(포트번호)

               // 컴퓨터에서 네트워크 연결을 기다리는 프로그램의 식별번호이다.

               // OS는 이 번호를 가지고 데이터를 받을 프로그램을 결정한다.

               // 포트번호 // well-known port   registered port   dynamic port

               // well-known port (0 ~ 1023) // 특정 프로그램이 관습적으로 사용하는 포트 번호 // 사용하지 말자

               // 7(echo), 20(FTP 데이터 포트), 21(FTP 제어포트), 23(telnet),

               // 25(SMTP), 53(DNS), 80(HTTP), 110(POP3), 143(IMAP)

               // registered port (1024 ~ 49151) // 통신 프로그램을 작성할 때 이 범위 포트 번호를 사용

               // 8080(proxy), 1521(Oracle), 3306(MySQL) 등 // 특정 프로그램이 널리 사용하는 번호 // 사용하지 말자

               // dynamic port (49152 ~ 65535) // 클라이언트가 OS로부터 자동 발급 받는 포트 번호

               // 통신을 하는 프로그램은 반드시 포트번호를 가져야 한다.

               // 프로그램에서 결정하는 것이 아니라, OS로부터 자동 발급 받는다.

Client0210 // Server 테스트

Server0210 // 대기열 // 클라이언트가 접속을 요청하면 대기열에 클라이언트 정보를 저장 // Queue 방식(FIFO)

               // 클라이언트는 설정된 시간(timeout)동안 응답을 받지 못하면 예외를 던지고 연결 요청을 취소한다

Server0220 // new ServerSocket(포트번호, 대기열크기); // ServerSocket ss = new ServerSocket(88882);

               // accept된 것 제외하고 대기열의 크기이다.

Client0310 // timeout // socket.connect(socketAddress, timeout); // timeout : milliseconds // 5000 // 5초

Server0310 // Client 테스트

Client0410 // Socket socket = new Socket();

               // SocketAddress socketAddress = new InetSocketAddress("localhost"8888);

               // socket.connect(socketAddress, 10000);

               // Socket에 ip와 포트번호까지 넣지만, timeout을 사용하려면 SocketAddress를 사용해야 한다.

               // SocketAddress는 추상클래스로 new로 객체를 생성할 수 없다.

               // SocketAddress의 하위 클래스인 InetSocketAddress의 생성자를 이용하여 객체를 생성한다.

               // connect에 socketAddress와 원하는 timeout 시간을 넣는다.

Server0410 // 클라이언트와 연결(대기열) // 큐(queue)에 대기중인 첫 번째 클라이언트의 연결을 승인한다.

                // 클라이언트가 서버에 연결을 요청하면, 서버는 대기열에 추가한다.

                // 클라이언트는 서버의 포트번호를 알아야 하지만, 서버는 클라이언트의 포트번호를 알 필요가 없다.

                // 서버소켓에서 연결을 승인하면 클라이언트와 통신할 수 있는 소켓을 리턴하기 때문이다.

Server0420 // 클라이언트와 연결(대기열) // ServerSocket ss = new ServerSocket(8888, 2);

               // 클라이언트가 c1 c2 c3 c4 c5 가 순차적으로 접속을 요청 했을 때 

               // Socket socket = ss.accept(); // c1은 연결 // c2 c3는 대기열 // c4 c5는 timeout에 들어감.

               // c2, c3에서 연결을 끊어도 c4 c5가 대기열에 들어갈 수 없다.

               // 맛집 예약리스트라고 생각. // 클라이언트가 취소해도 서버는 클라이언트를 부른다.

Server0430 // 클라이언트와 연결(대기열) // Socket socket = ss.accept();

               // accept()를 실행하면 c3 c4가 대기열에 들어간다. // c2는 접속

               // 대기열에서 꺼내는 순간 대기열은 한 칸씩 빈다.

 

net #ex3

Client0110 // OutputStream out = socket.getOutputStream(); // Server와 통신 할 출력 스트림 객체를 준비하기

              // InputStream in = socket.getInputStream(); // Server와 통신 할 입력 스트림 객체를 준비하기

              // byte stream 을 사용할 때는 바로 출력한다. // out.flush();를 출력하지 않아도 된다. // 버퍼 미사용시

              // byte stream은 1바이트씩 입출력 하기 때문에 바로바로 입출력하기 때문이다.

Server0110 // OutputStream out = socket.getOutputStream(); // Client와 통신 할 출력 스트림 객체를 준비하기

              // InputStream in = socket.getInputStream(); // Client와 통신 할 입력 스트림 객체를 준비하기

              // 클라이언트와 서버 간의 데이터를 주고 받는 통신 규칙을 "프로토콜(protocol)"이라 한다.

              // ex) 클라이언트가 문자열을 보내면, 서버는 읽고, 응답해야 한다.

Client0120 // byte stream // 바이트 배열 보내고 받기

               // int size = in.read(buf); // in.read(바이트배열)시 리턴 값은 바이트배열의 length이다.

               // out.flush(); // 바이트 배열을 보낼 때는 flush 하지 않아도 된다.

Server0120 // byte stream // 바이트 배열 보내고 받기

               // out.write(buf, 0, size); // out.write(바이트배열, 시작번호, 마지막번호) 생성자

               // 바이트 배열을 시작번호 번째부터 마지막번호 번째까지 출력한다.

               // Client가 바이트 배열을 보낸다면, Server도 배열을 준비해서 받아야 한다.

Client0130 // byte stream // int 값 보내고 받기 // int로 보낸다.

               // out.writeInt(1567891234); // out.writeInt(int값); 으로 보낸다.

Server0130 // byte stream // int 값 보내고 받기 // int로 받는다.

               // int value = in.readInt(); // int값으로 보낸건 int로 받는다.

Client0140 // byte stream // data 보내고 받기 // 각 값에 맞는 data로 보낸다.

               // out.writeByte(100); out.writeFloat(3.14f); out.writeUTF("ABC가각간");

               // 보내는 값에 맞는 메서드를 이용해 보낸다.

Server0140 // byte stream // data 보내고 받기 // 각 값에 맞는 data로 받는다.

               // byte value2 = in.readByte(); float value3 = in.readFloat(); String value4 = in.readUTF();

               // 보낸 값에 맞는 메서드를 이용해서 받는다.

Client0150 // byte stream // 문자열 보내고 받기 // String으로 보낸다.

               // out.println("ABC가각간"); // 문자열을 println으로도 보낼 수 있다.

               // toString을 이용해야 할 때, println으로 보내면 된다. // byteStream 일 때.

Server0150 // byte stream // 문자열 보내고 받기 // String으로 받는다.

               // String str = in.nextLine() // 문자열을 받을 때 nextLine()으로 받는다.

Client0160 // byte stream // buffer 사용 // 버퍼사용시 out.flush를 꼭 사용하여야 한다.

Server0160 // byte stream // buffer 사용

Client0210 // character stream // flush()를 꼭 수행하여야 한다. // 내부적으로 버퍼를 가지고 있어서이다.

               // PrintWriter out = new PrintWriter(socket.getOutputStream()); // PrintWriter를 데코레이터로 사용한다.

               // BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))

               // character를 주고 받기 때문에 Writer와 Reader를 사용해야 한다.

Server0210 // character stream // close 내부에서 flush 사용

               // close()가 사용되면 flush를 사용하지 않아도 남은 문자열을 보낸다.

 

net #ex4

Client0110 // Stateful 방식 // 서버와 연결한 후, 클라이언트에서 연결을 끊을 때까지 계속해서 연결을 유지한다.

        장점 // 요청할 때마다 연결 작업을 수행하지 않아 작업 속도가 빠르다. // 응답 속도가 빠르다

               // 작업처리시간 = 데이터 전송 시간 + 작업처리 시간 + 데이터 수신 시간.

               // ex) 게임 서버 연결, 화상 통신, 채팅 서버, 텔렛(원격 제어 프로그램), FTP(파일 전송 프로그램)

        단점 // 클라이언트가 작업을 하지 않더라도, 계속 유지를 하여 메모리를 많이 차지한다.

               // 많은 클라이언트의 요청을 처리하기 힘들다.

Server0110 // Stateful 방식 // Client0110과 단순한 1회 통신

Server0120 // Stateful 방식 + 여러 클라이언트 요청 처리 // while문 사용 // 다 회 통신 가능

Client0210 // Stateless 방식 // 요청/응답을 단 한번만 수행한다. // while문 사용

               // 서버에 작업을 요청할 때 연결하고, 서버로부터 응답을 받으면 연결을 끊는다.

        단점 // 요청할 때마다 매번 서버에 연결해야 하기 때문에 실행 시간이 오래 걸린다.

               // 사용자 인증이나 사용권한 확인 등의 경우 연결하는데 더더욱 많은 시간이 걸린다.

               // 사용자 인증(authentication; 아이디, 암호 유효 여부 검사)

               // 사용권한 확인(authorization; 사용자에게 허락된 작업 범위를 확인)

               // 작업처리시간 = 연결하는데 걸린 시간 + 데이터 전송 시간 + 작업처리 시간 + 데이터 수신 시간.

        장점 // 서버에 계속 연결된 상태가 아니기 때문에 서버 입장에서는 메모리를 많이 사용하지 않는다.

               // 클라이언트와 연결을 계속 유지하지 않기 때문에 작업 처리하는 동안만 연결 정보를 유지한다.

               // 따라서 같은 메모리라도 stateful보다 stateless가 더 많은 클라이언트의 요청을 처리할 수 있다.

               // ex) HTTP(웹), 메신저, 메일 전송, 

Server0210 // Stateless 방식 + 여러 클라이언트 요청 처리 // while문 사용 // 다 회 통신 가능

               // Server0120과 같지만 Client0120과 Client0210이 다르다

stateful // stateful 계산기 // 계산을 담당하는 process에 반복문을 사용, 한 번 접속으로 계산기를 계속 이용 가능

          // 계산 process 안에 result를 만들어서 1회의 계산 결과를 유지한다.

          // loop : // loop 라벨 // loop break; // 반복된 반복문 중 원하는 반복문까지 빠져나가기 위해 라벨을 붙힌다.

          // 특징 : 다 회의 계산기 사용(결과 미공유) // 1명씩 접속 가능 // 재접속 불필요

stateful2 // stateful 계산기 // 계산 process 밖에 result를 만들어서 quit하기 전까지 계산 결과를 유지한다.

            // 또한 값을 1개만 받아 계산을 적용한다. // 단점은 result가 0이라 처음에 * / 사용 불가.

          // 특징 :  회의 계산기 사용(결과 공유) // 1명씩 접속 가능 // 재접속 불필요

stateful3 // stateful에 thread 적용 // 쓰레드 적용으로 인해 여러명의 접속을 받을 수 있다.

            // InetSocketAddress remoteAddr = (InetSocketAddress) socket.getRemoteSocketAddress();

            // getRemoteSocketAddress를 이용해, 넘어온 소켓의 정보를 알 수 있고, 구분할 수 있다.

            // static class RequestHandler extends Thread { // Thread를 구현하는 별도 class를 만들어 오버라이딩 한다.

            // RequestHandler requestHandler = new RequestHandler(socket); // 반복문 안에 소켓을 넘겨주는 코드추가

            // 생성자를 통해 socket을 전달 받아, 각 접속한 Client를 구분할 수 있다.

            // requestHandler.start(); // Thread() 안의 start 기능을 실행한다. // start는 run()을 실행한다.

          // 특징 :  회의 계산기 사용(결과 공유) // 여러명 접속 가능 // 재접속 불필요

stateless // stateless 계산기 // 계산을 담당하는 process에 반복문을 사용하지 않는다. // 1회성 계산기 사용

            // 계산 process 안에 result를 만들어서 1회의 계산 결과를 유지한다.

            // 특징 : 1회의 계산기 사용(계산 결과 미공유) // 1명씩 접속 가능 // 재접속 필요

stateless2 // stateless 계산기 // clientId를 저장하는 Map을 이용, client를 구분한다.

              // Map을 사용해 key에 Client 고유의 값을 담고 value에 해당 Client의 작업 값을 넣는다.

              // 계산이 끝나면 clinetId를 리턴, Client에서 이 clientId 값을 가지고 있는다.

              // 재 접속 시, clientId를 가지고 기존의 value를 Map에서 꺼낼 수 있다.

              // 특징 : 1회의 계산기 사용(계산 결과 공유) // 1명씩 접속 가능 // 재접속 필요

stateless3 // stateless 계산기 // 쓰레드 적용으로 인해 여러명의 접속을 받을 수 있다.

              // Web은 모든 작업을 stateless로 실행하는데, 한번 처리하면 연결을 끊는다.

              // HTTP는 stateless 방식으로 작동한다.

 

net #ex5

Client0110 // Connection - Oriented // 연결 후 데이터 송수신

               // 알파넷 : 군사 목적으로 만든 통신 방법이다. // ex) 통신망을 노드로 구축하여 여러 방법으로 통신함.

               // 통신선이 하나라면, 그 선이 끊기면 연락이 불가능하다. // 따라서 여러 노드를 구축한다.

               // 알파넷이 민간으로 풀리면서 internet이 된다.

               // packet // 데이터 + 받는이 주소 + 포트번호

               // 원 데이터를 상대에게 쉽게 보내기 위해, 잘게 쪼개서 보낸다.

               // internet 망에서 router에서 packet을 보내는 것을 rout라고 한다.

               // router에서 packet을 보낼 때, 가장 한가한 망으로 보낸다. // 패킷마다 다른 rout로 보내질 수 있다.

               // 각기 다른 방법으로 보냈는데, 1 345는 정상적으로 오고 2번 패킷의 인터넷 망이 늦어 오지 않으면

               // 2번 패킷을 다시 요청하고, 요청 받은 쪽에서 2번 패킷을 다시 보낸다.

               // 2번 패킷이 만약 도착하지 않으면 계속 요청한다.

               // 2번 패킷이 도착하게 되면, 12345를 조립하여 원 데이터를 만든다.

               // 12345를 다 받고 못 받은건 다시 요청해서 받기 때문에 데이터를 신뢰할 수 있다.

               // 여러번 요청하여, 나중에 도착한 2번 패킷들은 버려버린다. // 이미 2번 패킷을 받은 상태라면.

Server0110 // Connection - Oriented // 연결 후 데이터 송수신

               // 연결 후에 데이터를 송수신 하기 때문에 데이터 송수신에 대한 신뢰를 보장

               // TCP 통신 방법이 전형적인 예이다. // FTP, Telnet, SMTP, POP3, HTTP 등

Client0210 // Connectionless // 연결없이 데이터 송신

               // Connectionless는 상대가 받던 말던 데이터를 보낸다. // 데이터를 신뢰할 수 없다.

               // 요청 받은 쪽에서 데이터가 오지 않으면 여러번 요청하기는 하나,

               // 여러번 요청해도 데이터가 오지 않으면 그냥 포기해 버린다.

               // DatagramSocket, DatagramPacket을 사용하여 처리한다.

               // java -cp bin/main step23.ex6.ConnectionlessClient [서버주소] [메시지] // 터미널에서 실행방법

               // DatagramSocket socket = new DatagramSocket(); // 소켓은 마지막 패킷을 보낼때 사용한다.

               // String receiver = "localhost"; int port = 8888; // 데이터를 받을 상대편 주소와 포트 번호

               // String receiver = args[0]; int port = 8888; // 이렇게도 가능하다.

               // args[0]은 실행방법에서 [서버주소]를 말한다.

               // byte[] bytes = "Hello".getBytes("UTF-8"); // "Hello"자리에 String 객체를 넣으면 된다.

               // String을 "UTF-8"형식에 맞는 Byte로 바꿔주고, bytes 배열에 담는다.

               // byte[] bytes = args[1].getBytes("UTF-8"); // args[1]은 실행방법에서 [메시지]를 말한다.

               // 실행방법에서 [메시지]는 String 객체가 가능해야 한다.

// DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(receiver), port);

               // DatagramPacket의 생성자를 보면, port를 받는 생성자와 받지 않는 생성자가 있다. // 그 외에도 있다.

               // (byte[] buf, int length, InetAddress address, int port) // (byte[] buf, int length, SocketAddress address)

               // port를 넘긴다면 InetAddress를 사용하여야 한다. // port를 넘기지 않는다면 SocketAddress를 사용한다.

               // InetAddress이다. InetSocketAddress가 아니다. // 두개의 클래스는 다르다.

               // InetAddress.getByName(receiver)를 넘긴 것은 getByName의 리턴 값은 InetAddress address이다.

               // (데이터가 저장된 바이트 배열, 전송할 데이터 갯수, 데이터를 받을 상대편 주소, 포트번호) 순서

               // socket.send(packet); // 소캣을 이용하여 패킷을 보낸다.

               // 쓰려는 package를 보면 이제 사용법이 제각기 다르다 보니, 사용법을 찾아가면서 사용해야 한다.

               // package의 사용을 확인해보려면, Java API에 들어간다. 해당 패키지를 찾아본다.

               // 해당 package가 interface인지, 추상클래스인지, 콘크리트 클래스인지를 가장 먼저 확인한다.

               // interface라면 해당 구현 클래스를 확인하고,

               // 추상클래스라면 해당 서브클래스를 확인하고, 콘크리트 클래스는 생성자를 확인한다.

               // 콘크리트 클래스의 생성자가 protected(아무것도 안붙어있는 상태)라면 factory method라 생각하자.

               // 리턴 값이 static 콘크리트 클래스인 것을 찾고, 파라미터로 필요한 생성자를 확인한다.

Server0210 // Connectionless // 연결없이 데이터 송신

               // 상대편이 네트워크에 연결되어 있지 않다면, 그 데이터는 버려진다.

               // 그래서 전송 여부를 신뢰할 수 없다 // ex) 편지, ping

               // DatagramSocket socket = new DatagramSocket(8888); // 포트로 들어온 데이터를 받을 소켓 준비

               // Client에서 보낼 때도 DatagramSocket이였고, 받을 때도 DatagramSocket 클래스를 이용한다.

               // byte[] buf = new byte[8196]; // 받은 데이터를 저장할 버퍼 준비 // 빈 바구니 준비

               // DatagramPacket emptyPacket = new DatagramPacket(buf, buf.length); // 빈 패킷(바구니)을 넘긴다.

               // socket.receive(emptyPacket); // socket을 통해 온 패킷의 데이터를 빈 패킷에 담는다.

               // String message = new String(emptyPacket.getData(), 0, emptyPacket.getLength(), "UTF-8");

               // 받아온 패킷에서 데이터를 읽고 0부터 배열의 크기만큼 "UTF-8"로 읽어온다. // message에 담는다.

               // System.out.println(message); // 출력

 

net #ex6

    Hyper Text : Text + 다른 문서의 연결 정보(Mata data = Markup = tag)

    HTML(Hyper-Text Markup Language) : Hyper Text를 일반 에디터로 작성하게 만든 문법

    HTTP (Hyper Text Transfer Protocol) : HTML을 요청/다운로드 관련 새 통신규칙 정함.

    HTTPS :  HTTP에 Security를 추가한 것이다.

    Web : HTML이 각 서로 HTML이 참조(Link)하는 모양이 거미줄 처럼 생겼다.

    Browser : HTML이 서로서로 링크하다보니, 둘러본다는 의미(Browse)에서 Browser라고 부른다.

    Web : Web Browser(Web Client = HTTP Clinet)와 WebServer(HTTPServer)로 나뉘어져 있다.

            // 일반인들은 웹 브라우저, 웹서버라고 한다. 개발자는 HTTP Clinet, HTTP Server라고 주로 한다.

HTTPClient //  HTTP 요청 프로토콜

               // request line => get /a/x.html   HTTP/1.1 // GET [자원주소] HTTP/1.1 (CRLF= \n = enter

               // header       => Host : www.naver.com // Host: [서버주소] (CRLF)

               // 빈줄          => 

               // 내용          => <HTM> ... <HTML>

               // Header는 request(요청) response(응답) general entity 4가지가 있다.

HTTP Server // HTTP 응답 프로토콜

               // status line   => HTTP/1.1   200   OK  // HTTP/1.1 200 OK(CRLF)

               // header       => text/html;  charset=UTF-8 // Content-Type: text/html; charset=UTF-8(CRLF)

               // 빈줄          => 

               // 내용          => <HTM> ... <HTML>

               // UTI(Uniform Resource Identifier) 통합 자원 식별자 // 인터넷에 있는 자원을 나타내는 유일한 주소.

               // URName방식과 URLocator방식이 있다. 

               // URN 통합 자원 이름 // ㅇ.ㅇㅇ.ㅇㅇㅇ. // '.'으로 이루어져 있다.

               // URL 통합 자원 이름 // ㅇ/ㅇㅇ/ㅇㅇㅇ. // '/'으로 이루어져 있다.

               // Status-Code // Client가 접속하면 서버에서 리턴하는 값이다.

               // 대표적인 Code // 200 OK // 301 주소가 바뀌었다. // 401 요청이 잘못 됐다. // 404 찾아보니 없다.

WebBrowser // tab을 켜고, 192.168.1.10:8888 // ip주소:포트번호 입력 // server는 당연히 실행중이여야 함.

                 // 꼭 Client가 HttpClient.java이라는 법이 없다. // 브라우저에서도 서버에 접속이 가능하다.

 

net #ex7

Exam01 -> URL(Uniform Resource Locator) // URL을 다루는 클래스

             // URL url = new URL("https://bitcamp.co.kr:80/index.php?main_page=home");

             // [프로토콜]://서버주소:포트번호/자원의경로 // 웹 상에서 자원의 위치를 표현하는 방법

             // 프로토콜 : http, https, ftp 등 // 서버주소 : IP주소(192.168.0.1), 도메인명(www.bitcamp.co.kr)

             // 포트번호 : 80(생략 가능), 8080(프록시서버) // 자원경로 : /index.html, /board/list.jsp 등)

Exam02 -> URL 분석

             // URL url = new URL("https://bitcamp.co.kr:80/index.php?main_page=home");

             // System.out.printf("프로토콜: %s\n", url.getProtocol()); // https

             // System.out.printf("서버주소: %s\n", url.getHost()); // bitcamp.co.kr:

             // System.out.printf("포트번호: %d\n", url.getPort()); // 8

             // WebBrowser에서 포트번호를 생략하면 80번으로 간주한다. 

             // 단, getPort()의 리턴 값은 포트번호를 지정하지 않으면 -1 리턴

             // System.out.printf("자원경로: %s\n", url.getPath()); // index.php

             // System.out.printf("QueryString: %s\n", url.getQuery()); // main_page=home

Exam03 -> URL 분석 // 참조경로(내부위치)

             // URL url = new URL("http://www.bitcamp.co.kr/a/b/hello.html#footer");

             // System.out.printf("참조경로(내부위치): %s\n", url.getRef()); // #footer

             // 자원경로 다음에 문서의 내부 위치를 지정하면 웹브라우저는 해당 위치로 자동 스크롤 한다.

Exam04 -> URL 분석 // 자원의 위치 

             // URL url = new URL("http://www.bitcamp.co.kr/a/b/hello.html?name=hong&age=20&tel=111-1111");

             // Query String // 파라미터명=값&파라미터명=값&파라미터명=값 // 없으면 null 리턴

             // 자원의 경로(/index.php)다음 물음표 다음에 오는 파라미터 // name=hong&age=20&tel=111-1111

Exam05 -> URL분석 // Windows OS의 로컬 파일 경로

             // URL url = new URL("file:///c:/Users/user/git/bitcamp-study/Hello.java"); // 실제 파일의 위치

             // WebBrowser tab을 켜고 file:///c:/Users/user/git/bitcamp-study/Hello.java 입력

             // 파일의 정보를 가져온다. // 

Exam06 -> URL분석 // macOS, Linux, Unix의 로컬 파일 경로

             // URL url = new URL("file:///Users/user/git/bitcamp-study/Hello.java"); 

             // macOS, Linux, Unix에서는 굳이 C위치를 적지 않아도 된다.

 

net #ex8

Exam0110 -> URL 클래스 // java.net.URL;

               // URL 클래스를 이용하면 HTTP 프로토콜을 신경쓰지 않고 HTTP 요청을 할 수 있다.

               // URL url = new URL("http://www.daum.net"); // URL 주소를 검증하고 준비한다.

               // InputStream in = url.openStream();

               // 서버와 연결하고 HTTP 요청을 수행하고 웹서버의 응답 데이터를 읽어들일 도구를 리턴한다.

               // BufferedReader in2 = new BufferedReader(new InputStreamReader(in));

               // 서버가 보낸 데이터를 한 줄씩 읽기 위해 데코레이터를 붙인다.

               // while (true) { String str = in2.readLine(); if (str == null) break; System.out.println(str); }

               // 반복문으로 읽어온다.

               // URL url = new URL("http://www.daum.net2"); // 없는 URL을 주면 에러가 발생한다.

Exam0210 -> URLConnection 사용 // java.net.URLConnection;

                // URL url = new URL("http://www.daum.net");

                // URLConnection con = url.openConnection(); // URL 정보를 가지고 HTTP 요청을 수행할 객체를 얻는다.

                // con.connect(); // 웹서버와 연결한 후 HTTP 요청한다.

                // InputStream in = con.getInputStream();

                // BufferedReader in2 = new BufferedReader(new InputStreamReader(in));

                // while (true) { String str = in2.readLine(); if (str == null) break; System.out.println(str); }

Exam0310 -> Apache의 HttpClient 라이브러리 사용 // 실무에서 많이 사용함.

                 // HttpClient, HttpGet 등 클래스

                 // www.apache.org 사이트에서 제공하는 HttpComponent 라이브러리에 들어 있는 클래스 

                 // 사용하려면 먼저 외부 라이브러리를 프로젝트로 가져와야 한다.

                 // 1. mvnrepository.com 에서 apache httpclient 키워드로 검색한다.

                 // 2. 최신 라이브러리 정보를 build.gradle의 dependencies {} 블록에 추가한다.

                 // 3. 콘솔에서 "gradle eclipse"를 실행하여 .classpath 파일을 갱신한다. // 외부 라이브러리 자동 다운로드

                 // 4. 이클립스 에디터에서 해당 프로젝트를 리프래시 한다.

                 // 5. 이클립스의 프로젝트에서 해당 라이브러리가 추가되었는지 확인

                 // Http 프로토콜을 좀 더 정밀하게 제어할 수 있다는 장점이 있다. // 상세히 나눠놨다고 생각

                 // CloseableHttpClient httpClient = HttpClients.createDefault(); // 1. HTTP 요청을 수행할 객체 준비

                 // Http에 맞는 객체를 생성하기 위해 빈방을 만들었다고 생각.

                 // HttpGet get = new HttpGet("https://www.daum.net"); // 2. HTTP GET 요청 정보를 준비한다.

                 // Http가 올바른 값이 맞는지 확인하는 절차라고 생각하면 된다.

                 // CloseableHttpResponse response = httpClient.execute(get);

                 // 3. HttpClient를 사용하여 GET 요청을 실행한다. // 리턴 값은 웹 서버의 응답 데이터를 다루는 도구이다.

                 // 2번에서 올바른 URL인지 확인을 했고, 데이터를 처리하기 쉽게, Client에 get을 넘겨 response를 만든다.

                 // HttpEntity entity = response.getEntity(); // 4. 응답 도구를 이용하여 서버가 보낸 데이터를 꺼낸다.

                 // Entity는 업무에 필요하고 유용한 정보를 저장하고 관리하기 위한 집합이라는 뜻을 가지고 있다. // 의역

                 // 따라서 response에서 보다 관리하기 쉬운 데이터들을 뽑는다고 생각한다. // HttpEntity

                 // if (entity != null) {

                 // 5. HttpEntity에 들어 있는 값을 문자열로 변환하여 출력 // 관리하기 쉽게 쪼개놨으니 쉽게 꺼낸다.

                 // System.out.printf("응답 데이터 크기 => %d\n", entity.getContentLength());

                 // System.out.printf("응답 데이터의 타입 => %s\n", entity.getContentType());

                 // System.out.println("---------------------------------");

                 // HttpEntity에 들어 있는 서버 응답 데이터를 꺼내려면 getContent()를 사용해야 한다.

                 // getContent()의 리턴 값은 InputStream 객체이다.

                 // InputStream을 가지고 데이터를 읽으려면 입출력 코딩을 작성해야 한다.

                 // String content = EntityUtils.toString(entity); // 이 부분을 대신 해주는 도우미 클래스가 있다.

                 // EntityUtils 패키지는 entity를 도와주는 클래스.

                 // System.out.println(content);

                 // Http를 이렇게 자세하게 쪼개놨기 때문에, Apache를 사용하는 것이 귀찮지만, 자세하게 다룰 수 있다.

 

net #ex9

Exam0110 -> 

 

 

 

 

reflect

reflect #ex01

Exam01 -> 클래스 로딩 조건 확인 // 

A obj = null;
A[] arr;
arr = new A[100];

        // 래퍼런스 선언과, 래퍼런스 배열 선언, 레퍼런스 배열 선언도 클래스 로딩과는 관계 없다. // 로딩되지 않는다.

A.i = 100;
A.m();
new A();
Class.forName("com.eomcs.reflect.ex1.A");

        // 변수에 값을 넣을 때, 메서드를 호출할 때, new A()로 클래스를 로딩할 때,

        // Class.forName으로 명시적으로 호출할 때 // 클래스가 로딩 된다.

        // Class.forName(패키지명을 포함한 전체 클래스 이름) // fully qualified class name = FQName = QName

        // 클래스가 로딩 되면, 스태틱 변수를 준비한다. 스태틱 블록을 실행한다. // 단 클래스는 한번만 로딩된다.

        // 즉 스태틱 변수, 스태틱 블록도 1번만 생성이 가능하다.

Exam02 -> 중첩 클래스 로딩 확인 //

  static {
    System.out.println("Exam02 로딩 됨!");
  }

  static class A {
    static int s_var = 100;
    int i_var = 200;
    static void s_m() {
    }
    void i_m() {
    }
    static {
      System.out.println("Exam02의 중첩클래스 A 로딩됨!");
    }
  }

  public static void main(String[] args) throws Exception {
    Class clazz = Class.forName("com.eomcs.reflect.ex1.Exam02");
  }

        // Exam02를 호출 했을 때, A는 로딩 되지 않는다.

        // Class clazz = Class.forName("com.eomcs.reflect.ex1.Exam02$A"); // 중첩클래스를 직접 로딩 해야 한다.

        // 중첩 클래스는 "클래스명$중첩클래스명" 형식의 이름을 갖는다.

        // 이 경우에는 중첩클래스가 로딩 되려면 바깥클래스도 알아야 하기 때문에, 바깥클래스도 같이 로딩된다.

        // 바깥클래스가 로딩 될 때, 중첩클래스는 로딩 되지 않는다.

        // 중첩클래스를 로딩 할 때, 바깥클래스는 로딩된다. // 바깥클래스가 이미 로딩되어있다면, 다시 로딩하지 않는다

Exam03 -> 클래스 로딩과 class라는 스태틱 변수 //

        // 자바의 모든 클래스는 "class"라는 특별한 스태틱 변수를 갖고 있다.

        // "class" 변수에는 해당 클래스의 정보를 담은 Class 객체의 주소가 저장되어 있다.

        // Class clazz = A.class; // Class.forName("com.eomcs.reflect.ex1.Exam02$A"); // 다음 코드와 같은 값을 리턴

        // 이 방식으로 클래스를 로딩하면 static {} 블록을 실행하지 않는다.

        // 스태틱 멤버를 사용하는 최초의 순간에는 static 블록이 실행된다.

        // A.class로 호출하는 것 보다 forName으로 호출해야 되는 이유 ?

        // forName()의 파라미터는 문자열이다. // 외부에서 문자열을 입력 받아 해당 클래스를 임의로 로딩할 수 있다.

        // "class"라는 스태틱 변수를 사용하는 것은 자바 소스 안에 명확히 해당 클래스를 지정해야한다.

        // 임의로 다른 클래스를 로딩하는 코드를 작성할 수 없다. // Exam04에서 확인

Exam04 -> forName()의 장점과 class직접 호출의 단점

        // forName()은 프로그램 아규먼트나 키보드 입력을 통해 클래스 이름을 입력 받아서 로딩할 수 있다.

        // Class clazz = A.class; // "class" 변수를 사용하면 이름을 고정하기 때문에 임의의 클래스를 로딩할 수 없다.

Exam05 -> 클래스 로딩과 인스턴스 생성

    Class clazz = Class.forName("com.eomcs.reflect.ex1.Exam05$A");
    A obj = (A) clazz.newInstance();
    obj.m();

        // 타입(클래스) 정보만 있다면 인스턴스 생성할 수 있다. // 타입을 가지고 객체를 생성할 수 있다.

 

reflect #ex02

Exam01 -> 클래스 이름 정보 알아내기

        // Class clazz = Class.forName("java.lang.String");

        // clazz.getSimpleName() // 클래스명만 알아내기 // clazz.getName() // 클래스의 경로를 포함한 이름 알아내기

        // clazz.getCanonicalName() clazz.getTypeName() // clazz.getName()과 리턴값이 같다 // 차이는 안짚고 넘어감

Exam02 -> 클래스의 수퍼 클래스 정보 알아내기

        // Class clazz = Class.forName("com.eomcs.reflect.ex02.Exam02$C");

        // superClazz = clazz.getSuperclass(); // 클래스의 수퍼 클래스 알아내기

Exam03 -> 클래스의 중첩 클래스 정보 알아내기

  static class A {
    static class B {
    } // static nested class

    class C {
    } // non-static nested class == inner class

    public void m() {
      class D {
      } // local class
    }

    public void m2() {
      Object obj = new Object() {
      }; // anonymous class
    }

    public static class E {
    }

    public class F {
    }

    private class G {
    }

    protected class H {
    }

    public interface X {
    }
  }

        // getClasses() // public 으로 공개된 중첩 클래스 및 인터페이스 정보를 가져온다.

        // Class clazz = Class.forName("com.eomcs.reflect.ex02.Exam03$A");

        // Class<?>[] nestedList = clazz.getClasses();

        // com.eomcs.reflect.ex02.Exam03$A$E $A$F $A$X // 길어서 F,X는 짜름 //  실행하면 E, F, X만 나옴

Exam04 -> 클래스의 중첩 클래스 정보 알아내기

        // clazz.getDeclaredClasses() // 접근 범위에 상관 없이 모든 중첩 클래스 및 인터페이스 정보를 가져온다.

        // com.eomcs.reflect.ex02.Exam03$A$B $A$C $A$E $A$F $A$G $A$H $A$X // 실행하면 D빼고 다 나옴

Exam05 -> 인터페이스 정보 알아내기

  static interface A {
  }

  static interface B {
  }

  static interface C {
  }

  static class D implements A, B, C {
  }

        // Class clazz = Class.forName("com.eomcs.reflect.ex02.Exam05$D");

        // Class[] list = clazz.getInterfaces(); // 해당 클래스가 구현한 인터페이스 정보를 가져온다.

        // com.eomcs.reflect.ex02.Exam05$A $B $C // 실행하면 A, B, C가 나온다.

Exam06 -> 패키지 정보 알아내기

        // Class clazz = Class.forName("com.eomcs.reflect.ex02.Exam06$D");

        // Package p = clazz.getPackage(); 정보 알아내기 // 넘겨준 경로의 패키지 정보를 알려준다.

        // com.eomcs.reflect.ex02 // 실행하면 패키지 정보를 알려줌.

 

reflect #ex03

Exam01 -> 메서드 정보 추출 // 상속받은 클래스의 메서드 추가

  public static void m1() {}
  public void m2() {}
  protected void m3() {} 
  void m4() {}
  private void m5() {}

        // Class clazz = Exam01.class; // Method[] list = clazz.getMethods(); 

        // 해당 클래스에 선언된 public 메서드 + 상속 받은 public 메서드 정보 추출

        // 실행 결과 // main m2 m1 wait wait wait equals toString hashCode getClass notify notifyAll

        // main m2 m1은 Exam01 내부 메서드 정보이다.

        // 그 외는 Object의 메서드 이다. // 모든 클래스는 Object를 상속함.

Exam02 -> 메서드 정보 추출 // 현재 클래스에 선언된

        // Class clazz = Exam02.class; // Method[] list = clazz.getDeclaredMethods(); // 클래스에 선언된 모든 메서드

        // main m2 m1 m3 m4 m5 // 전부 나온다. public 등 접근제어 신경쓰지 않는다.

Exam03 -> 특정 메서드 정보 추출

        // clazz.getMethod("m1") // getMethod("메서드명") // 메서드 명을 가진 메서드를 찾는다 // public만 찾음

        // clazz.getDeclaredMethod("m3") // getetDeclaredMethod("메서드명")

        // 메서드 명을 가진 메서드를 찾는다 // public을 포함한 전부 다 찾음.

Exam04 -> 특정 메서드 정보 추출 // 파라미터 2개

    Class parameterType = String.class;
    Method m = clazz.getMethod("m2", parameterType);
    System.out.println(m.getName());

    parameterType = Class.forName("java.lang.String");
    m = clazz.getMethod("m2", parameterType);
    System.out.println(m.getName());

    Class intType = int.class;
    Class stringType = String.class;
    m = clazz.getMethod("m3", stringType, intType);
    System.out.println(m.getName());

        // 파라미터가 없는 메서드를 찾을 때는 파라미터의 타입 정보를 넘기지 않는다. 

        // 파라미터가 있는 메서드를 찾을 때 그 파라미터의 타입 정보를 넘겨야 한다.

        // 타입정보 = 클래스 정보 = Class 객체

Exam05 -> 메서드 호출

  public static void plus(int a, int b) {
    System.out.printf("합계: %d\n", a + b);
  }

  public void minus(int a, int b) {
    System.out.printf("빼기: %d\n", a - b);
  }

        // Class clazz = Exam05.class; // Method m = clazz.getMethod("plus", int.class, int.class);

        // m.invoke(null, 10, 20); // 스태틱 메서드라, plus는 로딩 되어있어 사용이 가능하다.

        // Class clazz = Exam05.class; // Method m = clazz.getMethod("minus", int.class, int.class);

        // m.invoke(null, 10, 20); // 논스태틱 메서드라, minus는 로딩 되어있지 않아 사용이 불가능하다.

        // Exam05 obj = new Exam05(); // 클래스를 로딩하여 논 스태틱 메서드도 로딩 시킨 후 사용 가능하다.

 

reflect #ex04

Exam01 -> 생성자 정보 추출 // 모든 생성자 정보 추출 // 반복문 사용

    Class clazz = Exam01.class;
    Constructor[] list = clazz.getConstructors();

        // clazz.getConstructors(); // 생성자의 정보를 가져온다. // c.getParameterCount() // 해당 값의 순서를 가져온다.

Exam02 -> 특정 생성자 정보 추출

  public Exam02() {}
  public Exam02(int i) {}
  public Exam02(String s, int i) {} 

        // Class clazz = Exam02.class; // Constructor c = clazz.getConstructor(int.class); // Exam02 생성자만 호출 된다.

Exam03 -> 특정 생성자 호출

public class Exam03 {
  int value;

  public Exam03(int i) {
    this.value = i;
  }

  public void print() {
    System.out.printf("value=%d\n", this.value);
  }

        // Exam03 obj = (Exam03) clazz.newInstance(); // 실행 오류

        // newInstance()는 객체를 생성하고 기본 생성자를 호출한다.

        // Exam03에는 기본 생성자가 없어서 실행 오류가 발생한다.

        // Constructor c = clazz.getConstructor(int.class); // 생성자를 준비한다.

        // Exam03 obj = (Exam03) c.newInstance(200); // 생성자와 값을 넘겨주어야 한다.

        // Class의 newInstance메서드는 무조건 기본 생성자만 호출이 가능하다.

        // Constructor의 newInstance메서드는 파라미터를 넘길 수 있다. // 따라서 생성자 생성후, 메서드 호출하는 것.

 

reflect #ex05

Exam01 -> 파라미터 정보 추출

  public void m1(String name, int age) {}
  public void m2() {}
  public void m3(File file, String name) {}

  public static void main(String[] ok) {
    Class clazz = Exam01.class;
    Method[] methods = clazz.getDeclaredMethods();
    for (Method m : methods) {
      System.out.printf("%s:\n", m.getName());

      Parameter[] parameters = m.getParameters();
      for (Parameter p : parameters) {
        System.out.printf("    %s : %s\n", 
            p.getName(), p.getType().getName());
      }
    }
  }

        // getDeclaredMethods()로 모든 메서드를 배열로 받아온다.

        // Parameter[] parameters = m.getParameters(); // 메서드의 파라미터 정보를 가져온다.

        // p.getName()을 호출하면 파라미터의 이름을 arg0, arg1, arg2 와 같이 가져온다.

        // .class 파일에는 분명 파라미터의 이름이 보관되어 있지만, Reflection API에서는 보관된 값을 꺼낼 수 없다.

        // m1을 예로들면, name과 age 처럼 파라미터의 넘겨준 이름을 가져오려면 다른 방법을 사용하여 가져와야한다.

        // 방법 1. // 잘 안쓴다. // compile할 때 -parameters 옵션을 준다.

        // javac -d bin/main -encoding UTF-8 -parameters src/com/eomcs/reflect/ex05/Exam01.java

        // 방법 2. // Spring Framework에서 지원하는 기능을 사용한다. // 주로 쓴다.

Exam02 -> 파라미터 정보 추출

  public String m1(String name, int age) {return null;}
  public char[] m2() {return null;}
  public ArrayList<String> m3(File file, String name) {return null;}
  public void m4() {}

  public static void main(String[] ok) {
    Class clazz = Exam02.class;

    Method[] methods = clazz.getDeclaredMethods();
    for (Method m : methods) {
      System.out.printf("%s:\n", m.getName());
      Class returnType = m.getReturnType();
      System.out.printf("    리턴: %s\n", 
          returnType.getName());
    }
  }

        // Class returnType = m.getReturnType(); // 메서드의 리턴 타입을 가져온다.

        // m1리턴타입 : java.lang.String // m2리턴타입 : [C // m3리턴타입 : java.util.ArrayList // void 리턴타입 : void

 

reflect #ex06

Exam0110 -> 인터페이스 구현체 자동으로 생성하기

        // java.lang.reflect.Proxy // 인터페이스 구현체를 만드는 역할을 한다.

        // newProxyInstance(구현체를 만들기 위해 사용하는 인터페이스의 클래스로더,

        // 구현할 인터페이스 정보 목록, 실제 작업을 수행하는 객체)

        // 파라미터로 넘겨 받은 인터페이스를 모두 구현한 클래스를 만들어 리턴한다.

        // 클래스 로더 // 클래스 정보를 로딩하는 역할을 수행한다.

        // 클래스 로더를 얻는 방법 // 클래스정보.getClassLoader()

        // 클래스 정보 // 인스턴스.getClass() or 클래스명.class or Class.forName("패키지명을 포함한 클래스명")

        // InvocationHandler 구현체 // java.lang.reflect.InvocationHandler 인터페이스에 따라 동작하는 객체

    class MyHandler implements InvocationHandler {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        int a = (int) args[0]; // auto-unboxing => ((Integer)args[0]).intValue();
        int b = (int) args[1]; // auto-unboxing => ((Integer)args[1]).intValue();

        switch (method.getName()) {
          case "plus":
            return a + b;
          case "minus":
            return a - b;
        }
        return 0;
      }
    }

    Calculator c1 = (Calculator) Proxy.newProxyInstance(
        Calculator.class.getClassLoader(), 
        new Class[] {Calculator.class}, 
        new MyHandler());

    System.out.println("++++");
    System.out.println(c1.plus(10, 20));
    System.out.println("----");
    System.out.println(c1.minus(10, 20));
  }

Exam0120 -> 여러 개의 인터페이스를 구현한 객체를 자동 생성

Object proxy = Proxy.newProxyInstance(
        Exam0120.class.getClassLoader(), 
        new Class[] {
            Calculator.class, 
            Calculator2.class, 
            Calculator3.class}, 
        new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           switch (method.getName()) {
              case "plus":
                return a + b;
              case "minus":
                return a - b;
              case "multiple":
                return a * b;
              case "divide":
                return a / b;
              case "mod":
                return a % b;
            }
            return 0;
          }
        });

    Calculator c1 = (Calculator) proxy;
    Calculator2 c2 = (Calculator2) proxy;
    Calculator3 c3 = (Calculator3) proxy;
    
    System.out.println(c1.plus(10, 20));
    System.out.println(c1.minus(10, 20));
    System.out.println(c2.multiple(10, 20));
    System.out.println(c2.divide(10, 20));
    System.out.println(c3.mod(10, 20));
  }

Exam0130 -> 여러 개의 인터페이스를 구현한 객체를 자동 생성 // 람다

Object proxy = Proxy.newProxyInstance(
        Exam0130.class.getClassLoader(), 
        new Class[] {
            Calculator.class, 
            Calculator2.class, 
            Calculator3.class}, 
        (Object proxyObj, Method method, Object[] params) -> {
            int a = (int) params[0]; // auto-unboxing => ((Integer)args[0]).intValue();
            int b = (int) params[1]; // auto-unboxing => ((Integer)args[1]).intValue();

            switch (method.getName()) {
              case "plus":
                return a + b;
              case "minus":
                return a - b;
              case "multiple":
                return a * b;
              case "divide":
                return a / b;
              case "mod":
                return a % b;
            }
            return 0;
          });

    Calculator c1 = (Calculator) proxy;
    Calculator2 c2 = (Calculator2) proxy;
    Calculator3 c3 = (Calculator3) proxy;
    
    System.out.println(c1.plus(10, 20));
    System.out.println(c1.minus(10, 20));
    System.out.println(c2.multiple(10, 20));
    System.out.println(c2.divide(10, 20));
    System.out.println(c3.mod(10, 20));
  }
반응형

+ Recent posts