코드가 코드를 호출하는 형태
→ 업무가 복잡 ⇒ 동시 다중 사용자 자원이 필요하다.
→ 업무 규모 증가 → 동시 사용자 접속 증가 → 시스템(H/W, S/W, CPU, RAM)규모가 증가한다.
→ 한대의 컴퓨터에 (CPU 개수를 늘리는 것, RAM 용량 증가시키는 것)이 한계에 봉착하게 된다.
⇒ 하나의 서버로 감당 X
→ 효율적 관리를 위하여 시스템을 기능별 분리하게된다. (서버 분리)
서버를 분리했지만 각 시스템 끼리 서로 사용할 수가 없다.
즉, 시스템 간의 기능 호출이 필요하게 된다.
⇒ 원격의 메서드 호출 기술이 필요하다.
↓ 객체지향 프로그래밍 언어 지원
rmic
연결을 끊을 때까지 반복한다. → 한 클라이언트가 서버를 독점 ⇒ 여러 클라이언트가 동시에 작업할 수 없다!
요청할 때마다 연결한다.
→ 한 클라이언트의 독점을 완화(완전 독점은 막을 수 없음 → 한 번에 하나의 클라이언트만 연결할 수 있기 때문이다.)
⇒ 짧은 연결을 통해서 여러 클라이언트의 동시 요청 어느 정도 지원
원리: 실행 흐름을 분리
main(){-}
run(){-}
run(){-}
run(){-}
while(ture)
로 무한 반복)package com.bitcamp.board;
// 클라이언트 요청을 main 실행 흐름과 분리하여 별도의 실행으로 다루는 클래스
public class RequestThread extends Thread{
private Socket socket;
private Map<String, Servlet> servletMap;
public RequestThread(Socket socket, Map<String, Servlet> servletMap) {
this.socket = socket;
this.servletMap = servletMap;
}
// 별도의 실행흐름에서 수행할 작업 정의
@Override
public void run() {
try (Socket socket = this.socket; // 위에 있는데 또 선언하는 이유: () 하는 곳 안에서만 자동으로 Close 되기 때문.
// socekt을 가지고 입출력 stream 얻기
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());) {
System.out.println("클라이언트와 연결 되었음!");
// 클라이언트와 서버 사이에 정해진 규칙(protocol)에 따라 데이터를 주고 받는다.
String dataName = in.readUTF();
Servlet servlet = servletMap.get(dataName);
if (servlet != null) {
servlet.service(in, out);
} else {
out.writeUTF("fail");
}
System.out.println("클라이언트와 연결을 끊었음!");
} // 안쪽 try
catch(Exception e) {
System.out.println("클라이언트 여청 중 오류 발생!");
e.printStackTrace();
}
super.run();
}//run()
}
package com.bitcamp.board;
public class ServerApp {
public static void main(String[] args) {
System.out.println("[게시글 데이터 관리 서버]");
try (ServerSocket serverSocket = new ServerSocket(8888);) {
...
while (true) {
// 클라이언트가 연결되면,
Socket socket = serverSocket.accept();
// 클라이언트 요청을 처리할 스레드를 만든다.
RequestThread t =new RequestThread(socket, servletMap); //servletMap은 RequestThread로 넘겨서 거기서 처리하도록 맡긴다.
// main 실행 흐름에서 분리하여 별도의 실행 흐름으로 작업을 수행시킨다.
t.start(); // 여러개 실행
}
} catch (Exception e) {
e.printStackTrace();
} // 바깥 쪽 try
System.out.println("서버 종료!");
}
}
각각의 컴퓨터에 부여된 고유 번호
하지만 사람이 식별하기 어렵기 때문에 1byte씩 잘라서 10진수로 표기해서 사용한다.
IP 주소를 직접 사용하는 대신에, IP주소에 부여된 알파벳과 숫자로된 텍스트 이름을 사용하는 것이 편하다.
→ DNS서버: IP 주소에 대한 텍스트 이름을 알려주는 서버
(DNS: Domain Name System)
서버 | 이름 | IP 주소 |
---|---|---|
네이버 서버 | www.naver.com | 223.10.200.104 |
카카오 서버 | www.daum.net | 211.249.220.24 |
나:
www.naver.com 검색 → KT DNS → DNS naver.com 검색 서버 알려줌
→ Naver DNS: IP 주소를 가지고 있다가 알려준다, www 서버 IP 를 나에게 돌려준다.
→ 나 → www 요청 → www 응답 → 나
DNS → Naver DNS 서버 → www, shop, news, ... ip 주소를 가지고 있음
즉, DNS는 자세한 건 모르고 굵직한 DNS 서버를 알고 있다.
ServerSocket ss = new ServerSocket(포트번호);
연결을 기다리고 있는 클라이언트가 있다면 맨 먼저 접속한 클라이언트의 연결을 승인한다.
소켓 객체를 통해 읽고 쓸 수 있도록 입출력 스트림을 얻는다.
상대편이 보낸 문자열을 한 줄 읽는다.
상대편으로 문자열을 한 줄 보낸다.
항상 입출력 도구는 사용 후 닫아야 한다.
네트워크 연결도 닫는다.
Socket s = new Socket(원격 호스트의 IP 주소/도메인이름, 원격 호스트 프로그램의 포트번호)
public class Receiver3 {
public static void main(String[] args) throws Exception {
System.out.println("미니쥐 서버 실행!");
Scanner keyScan = new Scanner(System.in);
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
while (true) {
// 클라이언트가 보낸 문자열을 수신한다.
String str = in.nextLine();
System.out.println(str);
// 키보드 입력을 받아서 클라이언트로 송신한다.
System.out.print("입력> ");
String input = keyScan.nextLine();
out.println(input);
if (str.equals("quit"))
break;
}
in.close();
out.close();
socket.close();
serverSocket.close();
keyScan.close();
}
}
public class Sender3 {
public static void main(String[] args) throws Exception {
System.out.println("클라이언트 실행!");
Scanner keyScan = new Scanner(System.in);
Socket socket = new Socket("localhost", 8888);
PrintStream out = new PrintStream(socket.getOutputStream());
Scanner in = new Scanner(socket.getInputStream());
while (true) {
// 키보드 입력을 받아서 서버에게 전송한다.
System.out.print("입력> ");
String input = keyScan.nextLine();
out.println(input);
// 서버가 보낸 데이터를 수신한다.
String str = in.nextLine();
System.out.println(str);
if (input.equals("quit"))
break;
}
in.close();
out.close();
socket.close();
keyScan.close();
}
}
상대 서버에 내 파일을 업로드하는 로직이다.
Buffer(BufferedInputStream, BufferedOutputStream)를 이용해야 확실히 전송 및 수신 속도가 빨라진다.
public class Receiver5 {
public static void main(String[] args) throws Exception {
System.out.println("미니쥐 서버 실행 중...");
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
System.out.println("클라이언트가 연결됨.");
PrintStream out = new PrintStream(
new BufferedOutputStream(socket.getOutputStream()));
DataInputStream in = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
System.out.println("클라이언트로부터 데이터 수신 중...");
//1) 파일 크기 읽기
long filesize = in.readLong();
//2) 파일 이름 읽기
String filename = in.readUTF();
//3) 파일 데이터 읽기
File file = new File("temp/ok_라" + filename);
BufferedOutputStream fileOut = new BufferedOutputStream(
new FileOutputStream(file));
for (long i = 0; i < filesize; i++) {
fileOut.write(in.read());
}
System.out.println("클라이언트로부터 데이터 수신 완료!");
//4) 클라이언트에게 응답하기
out.println("OK!");
out.flush(); // 버퍼의 남아있는 데이터 방출하기
in.close();
out.close();
socket.close();
serverSocket.close();
fileOut.close();
}
}
public class Sender5 {
public static void main(String[] args) throws Exception {
File file = new File("temp/cuties.jpeg");
BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream(file));
System.out.println("미니쥐가 서버에 연결 중...");
Socket socket = new Socket("localhost", 8888);
System.out.println("서버에 연결 완료!");
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
Scanner in = new Scanner(new BufferedInputStream(socket.getInputStream()));
System.out.println("서버에 데이터 송신 중...");
long startTime = System.currentTimeMillis();
// 1) 파일 크기 보내기
out.writeLong(file.length());
// 2) 파일 이름 보내기
out.writeUTF(file.getName());
// 3) 파일 데이터 보내기
int b;
while ((b = fileIn.read()) != -1) {
out.write(b);
}
out.flush(); // 버퍼에 남아있는 데이터를 방출하기
long endTime = System.currentTimeMillis();
System.out.printf("서버에 데이터 송신 완료!(%d밀리초)\n", endTime - startTime);
// 4) 서버의 응답받기
String response = in.nextLine();
System.out.println(response);
in.close();
out.close();
socket.close();
fileIn.close();
}
}