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(8888, 2);
// 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));
}
'IT Developer > Bitcamp' 카테고리의 다른 글
비트캠프 프론트엔드 및 벡엔드 개발자 Webapp el, jsp, jstl (0) | 2020.04.13 |
---|---|
비트캠프 프론트엔드 및 벡엔드 개발자 Web (0) | 2020.04.03 |
비트캠프 프론트엔드 및 백엔드 개발자 #Project v55~60 (0) | 2020.03.31 |
비트캠프 프론트엔드 및 벡엔드 개발자 DB (0) | 2020.03.26 |
비트캠프 프론트엔드 및 벡엔드 개발자 ioc, jdbc, mybatis, (0) | 2020.03.11 |
비트캠프 프론트엔드 및 백엔드 개발자 #Project v43 ~ v54 (0) | 2020.03.10 |
비트캠프 프론트엔드 및 백엔드 개발자 SQL (0) | 2020.02.18 |
비트캠프 프론트엔드 및 백엔드 개발자 #Project v26 ~ v42 (0) | 2020.01.21 |