Echo 서버는 메아리처럼 client에서 메시지를 서버에 전송하여


서버가 받은 메시지를 그대로 client에 전송하는 프로그램이다.




서버


package chap12.exam03.echo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

	public static void main(String[] args) throws Exception {
		// 1. Server Socket 생성
		ServerSocket server = new ServerSocket();
		// 2. IP 개방 (localhost는 자기만 들어오고, 자신의 ip를 입력하면 외부에서 접속가능)
		server.bind(new InetSocketAddress("여기에 IP를 입력해야 정상적으로 동작해요", 5001));
		// 3. 요청 대기
		while(true) {
			System.out.println("Connect Ready");
			Socket socket = server.accept(); // block
			
			// 접속 요청이 있을 경우 요청 주소를 뽑아 낸다.(선택)
			InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress();
			System.out.println("접속 완료 : " + addr.getAddress());
			
			// 4. InputStream으로 보내온 메시지를 받는다.
			InputStream is = socket.getInputStream(); // 핵심
			BufferedInputStream bis = new BufferedInputStream(is);
			InputStreamReader reader = new InputStreamReader(bis);
			
			// 5. 출력
			char[]arr = new char[100];
			reader.read(arr);
			// ㅁㅁㅁ는 \0이고 배열의 공백을 의미한다, 38번째 줄의 replace를 생략한다면 이상한 문자를 볼 수 있다.
			String msg = new String(arr).replace('\0', ' ');
			System.out.println("[client] " + msg);
			
			// 6. OutputStream으로 보내온 메시지를 전송한다.
			OutputStream os = socket.getOutputStream();
			BufferedOutputStream bos = new BufferedOutputStream(os);
			OutputStreamWriter writer = new OutputStreamWriter(bos, "UTF-8");
			writer.write(msg);
			writer.flush();
			// 서버는 켜져있어야 하므로 자원반납을 생략
		}
	}

}


클라이언트


package chap12.exam03.echo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;

public class Client {

	public static void main(String[] args) throws Exception {
		// 1. 소켓 생성
		Socket socket = new Socket();
		// 2. 접속 요청
		try {
			socket.connect(new InetSocketAddress("여기에 IP를 입력해야 정상적으로 동작해요", 5001));
			System.out.println("접속 완료");
			
			OutputStream os = socket.getOutputStream();
			BufferedOutputStream bos = new BufferedOutputStream(os);
			OutputStreamWriter writer = new OutputStreamWriter(bos,  "UTF-8");
			writer.write("Hello, Server");
			writer.flush();
			// 3. 데이터 전송
			// 4. 받아온 데이터 출력
			InputStream is = socket.getInputStream();
			BufferedInputStream bis = new BufferedInputStream(is);
			InputStreamReader reader = new InputStreamReader(bis);
			
			char[] arr = new char[100];
			reader.read(arr);

			String msg = new String(arr);
			System.out.println("내가 받은 메세지 : " + msg);
			// 5. 자원반납
			os.close();
			bos.close();
			writer.close();
			is.close();
			bis.close();
			reader.close();
		} catch (Exception e) {
			// 
		}
		

	}

}


서버의 19번째 줄과 클라이언트의 20번째 줄에


접속할 대상의 IP를 입력해주어야 하고 1개의 컴퓨터로 테스트 할 경우


대상 Server가 자기 자신이므로 자신의 IP를 입력하면 된다.


그리고 서버는 이클립스에서 실행하고


클라이언트를 따로 실행해야 하는데


https://qdgbjsdnb.tistory.com/79 를 참조하여 cmd에서 클라이언트를 작동해보자.

TCP는 연결지향적 프로토콜로 데이터를 안전하고 정확하게 전달한다.


데이터가 잘 전송 됫는지 확인 작업이 따로 있으며


작업이 추가로 있기 때문에 UDP보다 느리다.





여기서는 Socket을 이용한 통신을 해볼것이다.


Socket은 안에 IP, 포트 등 다양한 정보를 담고


상대방에게 전송된다.


다음의 간단한 예제를 통해 확인해보자.



서버


package chap12.exam02.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

	public static void main(String[] args) {

		ServerSocket server = null;

		// 1. 서버 소켓 생성
		try {
			server = new ServerSocket();
			// 2. IP를 개방 (열어주길 원하는 IP, 오픈 호스트), localhost는 자기 자신의 IP를 의미
			server.bind(new InetSocketAddress("localhost", 5001));
			// 3. 요청 대기
			while(true) {
				System.out.println("요청 대기중");
				// 4. 접속 요청이 있을 경우 수락 (하나의 스레드, 조인, 블로킹하여 요청이 올 때 까지 대기)
				Socket socket = server.accept();
				// 4-1. 요청이 있을 경우 처리 (클라이언트 주소 알아내기)
				InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress();
				System.out.println("접속 완료 >" + addr.getAddress() + ":" + addr.getPort());
			}
			// 5. 자원 반납 (서버는 5번 아래로 잘 안씀)
			// 6. 종료
		} catch (IOException e) { // 서버에 문제가 있을 때
			// 5. 자원 반납 (서버는 5번 아래로 잘 안씀)
			try {
				server.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			// 6. 종료
			e.printStackTrace();
		}
	}

}


클라이언트


package chap12.exam02.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;

public class Client {

	public static void main(String[] args) {
		Socket socket = null;
		
		try {
			// 1. 소켓 생성
			socket = new Socket();
			
			// 2. 접속 요청
			socket.connect(new InetSocketAddress("localhost", 5001));
			
			// 3. 시킬 일 있으면 요청
			System.out.println("접속 완료");
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 4. 종료
			try {
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}


먼저 서버를 작동한 후 클라이언트 코드도 작동을 해줘야 하는데


서버는 이클립스에서 돌리고


https://qdgbjsdnb.tistory.com/79 를 참조하여 cmd에서 클라이언트를 작동해보자.






Network는 다양한 컴퓨터들끼리 연결되어 있는 망을 의미한다.


컴퓨터 뿐만 아니라 다양한 사물(Things), 예를들어 냉장고 선풍기, 에어컨 등의 사물(Things)들이 Network, Internet에 연결이 되고


그것을 IOT(Internet Of Things) 사물 인터넷이라고 한다.


특히 Java는 컴퓨터를 벗어나 다양한 사물에 활용되기 좋아 널리 사용되고 있다.







Network의 깊은 지식이 목적이 아니기 때문에 전문적인 내용을 다루지 않고


Java에서 쓸만한 간단한 Server, Client 통신을 공부한다.







Network는 결국 목적지까지 어떻게 가느냐가 제일 중요한 과제이다.


그래서 컴퓨터에 고유 통신 번호인 IP가 지정되고 IP는 집주소 처럼 데이터를 전달할 목적지 주소로 쓰이게 된다.


또한 컴퓨터 장치 자체에 부여된 주소도 따로 있지만 IP만 사용한다.


IP와 함께 필요한게 하나 있는데 포트 번호가 꼭 필요하다.


일반적으로 사용하는 포트는 TCP와 UDP가 있고


TCP는 Three Way Handshake와 같은 방법으로 확실한 데이터를 전송하지만 속도가 조금 느리다.


UDP는 일방적인 전송을 하므로 불확실한 데이터를 전송하지만 속도가 빠르다는 장점이 있다.







IP와 포트의 예로는 127.0.0.1:8080과 같이


127.0.0.1은 IP가 되고


: 뒤에 8080은 포트 번호가 된다.


그리고 127.0.0.1은 자신을 지정하는 IP로 자신의 IP를 모를때 쓰는 IP이다.





Java에서 자신의 IP 주소를 얻는법과


특정 도메인의 IP를 얻는법을 확인해보자


InetAddress를 사용한다.





여기서 도메인은 네이버로 예를 든다면


naver.com이 도메인(IP의 닉네임으로 이해하면 된다)이다.


수많은 웹사이트들을 접속하는데 일일히 IP를 암기하고 사용 할 수 없으니 닉네임을 배정하는 혁신적인 기술이다.


도메인이 생기고 인터넷의 발전은 급격히 상승했다.



package chap12.exam01.ipaddr;

import java.net.InetAddress;

public class GetIp {

	public static void main(String[] args) throws Exception {
		// 내 컴퓨터의 IP 얻기
		// localhost => 127.0.0.1 => 자신의 주소를 지정하는 주소
		InetAddress addr = InetAddress.getLocalHost();
		System.out.println("my PC IP " + addr.getHostAddress());
		
		// 특정 도메인으로 IP 얻기
		String domain = "naver.com";
		addr = InetAddress.getByName(domain);
		System.out.println(addr.getHostAddress());
		
		// 특정 도메인으로 IP 전부 얻기
		domain = "www.youtube.com";
		InetAddress[] addrs = InetAddress.getAllByName(domain); // 반환값이 배열
		for(InetAddress ip : addrs) {
			System.out.println("ip : " + ip);
		}
	}

}



여기서 특정 도메인으로 IP 전부 얻기라는 말이 이해가 안 갈수도 있다.


왜 여러대가 나오는거지? 할 수 있는데 웹사이트들은 서버를 여러대를 두어 접속을 대비하여야 하기 때문에 여러개가 나온다.


예를들면 naver.com과 같이 대형 웹사이트는 1분안에 수만, 수십만명이 동시에 접속할 수 있기 때문에


서버를 여러대 준비하여 부하가 낮아지게끔 해야한다.


만약 여러대를 준비하지 않을 경우 사람이 몰리는 아침시간에 naver.com에 접속하려고 하면


과장해서 1시간, 2시간 걸릴 것이다...



'개념 및 코딩 > 10.네트워크' 카테고리의 다른 글

[JAVA]10-06.UDP, Datagram  (0) 2018.09.27
[JAVA]10-05.N:M MultiChat Server, Client  (2) 2018.09.27
[JAVA]10-04.1:1 Chat Server, Client  (0) 2018.09.27
[JAVA]10-03.Echo Server, Client  (0) 2018.09.27
[JAVA]10-02.TCP Server, Client  (0) 2018.09.27

Java NIO는 New Input Output의 줄임말로 기능 중 간단한 파일 제어만 설명


Java IO와는 다르게 Stream이 아니고 Channel 방식을 사용




1. Files, 파일 다루기


Files의 다양한 메소드들은 다음 예제를 통해 확인하자.



package chap11.exam13.files;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FilesExam {
	// 파일의 속성을 알아 볼 수 있다.
	public static void main(String[] args) throws Exception {
		
		Path path = Paths.get("C:/img/asd.txt");
		System.out.println("디렉토리 여부 : " + Files.isDirectory(path));
		System.out.println("파일 여부 : " + Files.isRegularFile(path));
		System.out.println("마지막 수정 시간 : " + Files.getLastModifiedTime(path));
		System.out.println("파일 크기 : " + Files.size(path));
		System.out.println("소유자 : " + Files.getOwner(path));
		System.out.println("숨김 파일 여부 : " + Files.isHidden(path));
		System.out.println("읽기 가능 여부 : " + Files.isReadable(path));
		System.out.println("쓰기 가능 여부 : " + Files.isWritable(path));
		System.out.println("실행 여부 : " + Files.isExecutable(path));
	}

}



2. FileSystem, 파일 정보


FileSystem은 현재 시스템의 다양한 정보를 불러올 수 있다.


Iterable에 FileStroe을 붙여서 사용



package chap11.exam13.files;

import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;

public class FileSystemExam {
	// 파일 시스템(파일/폴더/저장공간) 정보
	public static void main(String[] args) throws IOException {
		// 기본 파일 시스템 정보 추출하는 객체
		FileSystem fs = FileSystems.getDefault();
		Iterable<FileStore> infolist = fs.getFileStores();
		for(FileStore info : infolist) {
			System.out.println("드라이브 이름 : " + info.name());
			System.out.println("저장 타입 : " + info.type());
			System.out.println("용량 : " + info.getTotalSpace()/(1024*1024*1024) + "GB");
			System.out.println("사용 가능한 용량 : " + info.getUsableSpace()/(1024*1024*1024) + "GB");
		}
		
		System.out.println("====현재 사용중인 드라이버 루트 ====");
		for(Path path : fs.getRootDirectories()) {
			System.out.println(path.toString());
		}
	}

}



3. Path, 경로


Path는 경로에 관련된 다양한 메소드들이 있다.


폴더 생성 또는 파일 이름만 뽑는다던가 상위 폴더를 알아 낸다던가 등 여러가지 있다.



package chap11.exam13.files;

import java.nio.file.Path;
import java.nio.file.Paths;

public class PathExam {
	// 1. 파일 경로 확인 전문화
	public static void main(String[] args) {
		// 특정 경로의 파일 정보를 가져옴(Uri | String)
		Path path = Paths.get("C:/img/temp/10multi_copy");
		
		// 파일명
		System.out.println(path.getFileName());
		// 부모 확인
		System.out.println(path.getParent().getFileName());
		// 루트 확인
		System.out.println(path.getRoot());
		// 위에 쓴 경로들의 단계 수
		System.out.println(path.getNameCount());
		// 개별 단계 확인
		for(int i = 0; i < path.getNameCount(); i++) {
			System.out.println(i + " 단계 : " + path.getName(i));
		}
	}

}



package chap11.exam13.files;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class MKdirExam {
	// 디렉토리 및 파일 생성
	public static void main(String[] args) throws Exception {
		Path path = Paths.get("c:/img/test");
		// !File.exists()
		if(Files.notExists(path)) {
			System.out.println("폴더 생성");
			Files.createDirectories(path);
		}
		path = Paths.get("c:/img/test/textFile.txt");
		if(Files.notExists(path)) {
			System.out.println("파일 생성");
			Files.createFile(path);
		}
	}

}




4. WatchService, 파일 감시


WatchService는 지정한 폴더 내부의 파일이 생성되거나 수정되거나 삭제되는 경우를 감지하는 기능을 가지고 있다.


생성 시 감시할 이벤트들을 설정하고 사용한다.



package chap11.exam13.files;

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;

public class WatchServiceExam {
	// 특정 경로의 파일 변경을 감시
	public static void main(String[] args) throws Exception {
		
		// 기본 영역 지정
		WatchService watch = FileSystems.getDefault().newWatchService();
		
		// 감시 할 상세 주소 지정
		Path path = Paths.get("c:/img");
		
		// 어떤 와치 서비스가 어떤것을 감시하나?
		path.register(watch,  StandardWatchEventKinds.ENTRY_CREATE, 
				StandardWatchEventKinds.ENTRY_DELETE, 
				StandardWatchEventKinds.ENTRY_MODIFY);
		// 감시 실행
		while(true) {
			// 등록한 이벤트 발생 시 WatchKey로 반환
			WatchKey key = watch.take();
			// 이벤트 추출, ?는 와일드 카드 타입
			List<WatchEvent<?>> list = key.pollEvents();
			// 상세 내용을 추출
			for(WatchEvent e: list) {
				// 이벤트 상세 내용
				Kind evtKind = e.kind();
				Path evtPath = (Path) e.context();
				System.out.println(evtKind);
				System.out.println(evtPath);
			}
			// 다 쓰고난 이벤트 키는 리셋 해 주어야 한다.
			// reset을 하지 않으면 감시가 일회성으로 끝난다.
			// false : 초기화 실패, 해당 디렉토리가 사라진 경우
			boolean success = key.reset();
			System.out.println(success);
		}
	}

}



5. Path를 이용한 Write Read Copy


위에 사용한 Path를 이용하여 파일을 만들거나 읽거나 복사하는 기능


쓰기


package chap11.exam14.fileRW;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Scanner;

public class fileWriteEx {

	public static void main(String[] args) throws Exception{
		// 파일 위치 지정
		Path path = Paths.get("c:/img/write.txt");
		// 쓰기 할 값 입력 받기
		Scanner scan = new Scanner(System.in);
		System.out.println("아무거나 입력하세요!");
		String txt = scan.nextLine() + "\r\n";
		// 스트림 준비 + 쓰기
		Files.write(path, txt.getBytes(), StandardOpenOption.APPEND);
		System.out.println("저장 되었습니다.");
		// 자원반납(flush, close) - 생략
	}

}


읽기


package chap11.exam14.fileRW;

import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class FileReadEx {

	public static void main(String[] args) throws Exception {
		// 1. 경로
		Path path = Paths.get("C:/img/asd.txt");
		// 2. 스트림 생성		// 3. 읽기
		List<String> contents = Files.readAllLines(path, Charset.forName("UTF-8"));

		// 4. 출력
		for(String line : contents) {
			System.out.println(line);
		}
	}

}


복사


package chap11.exam14.fileRW;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class FileCopyEx {
	// 파일 복사
	public static void main(String[] args) throws Exception {
		// 읽어와서 -> 쓰기
		// 파일 지정
		Path from = Paths.get("c:/img/10multi.pdf");
		Path to = Paths.get("c:/img/temp/10multicopyFile");
		// 스트림 + 읽기 + 읽어온 값 쓰기
		Files.copy(from,  to, StandardCopyOption.REPLACE_EXISTING);
		System.out.println("파일 복사 완료");
		// REPLACE_EXISTING : 기존 파일이 있으면 덮어쓰기
		// COPY_ATTRIBUTES : 파일의 모든 속성 복사(권한, 읽기 전용)
		// 자원 반납
	}

}


Data Stream은 .dat 파일을 만들 때 사용하는 스트림으로 


특징은 dat 파일을 읽기 위해서는 어떤 타입이 어떤 순서로 들어있는지 알아야 읽을 수 있기 때문에


쉽게 읽어올 수 없다는 특징을 가진다.



package chap11.exam10.data;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class DataOutput {

	public static void main(String[] args) throws Exception {
		// 자료형을 유지해서 파일에 쓰기
		// 1. 파일 위치 설정
		String path = "C:/img/data.dat";
		// 2. 스트림 준비(보조)
		FileOutputStream fos = new FileOutputStream(path);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		DataOutputStream dos = new DataOutputStream(bos);
		
		// 3. 쓰기
		dos.writeUTF("aaa");
		dos.writeInt(1000);
		dos.writeBoolean(true);
		
		// 4. 비우기
		dos.flush();
		
		// 5. 닫기
		dos.close();
		bos.close();
		fos.close();
	}

}


package chap11.exam10.data;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;

public class DataInput {

	public static void main(String[] args) throws Exception {
		
		// 1. 파일 위치 설정
		String path = "C:/img/data.dat";
		// 2. 스트림 준비(보조)
		FileInputStream fis = new FileInputStream(path);
		BufferedInputStream bis = new BufferedInputStream(fis);
		DataInputStream dis = new DataInputStream(bis);
		
		// 3. 읽기 (파일에 있는 타입들을 알아야 하고
		// 입력한 순서대로 불러야 한다.)
		// 4. 출력
		System.out.println("이름 : " + dis.readUTF());
		System.out.println("급여 : " + dis.readInt());
		System.out.println("승진대상 : " + dis.readBoolean());
		
		
		
		// 5. 닫기
		dis.close();
		bis.close();
		fis.close();
	}

}



다양한 스트림들은 다양한 타입을 다루기에는 적절하지 않고 보통 하나에 특화 되어 있다.


그러나 Object Stream은 데이터 타입의 최상위 객체인 Object를 의미하는 보조 스트림으로


다양한 데이터 타입 뿐만 아니라 객체, 배열, 컬렉션 등 다양한 형태가 들어 갈 수 있다.


하지만 클래스의 경우는 직렬화를 하지 않으면 전달되지 않는다.


직렬화는 전달 할 데이터를 바이트로 바꿔서 나열하는 것이다.


다음 예제는 dat 파일을 만들어 읽는데 Sample이라는 클래스도 함께 전달하는 예제이다.



package chap11.exam11.obj;

import java.io.Serializable;
// 직렬화 인터페이스를 통해 모양을 잡아 준다.
public class Sample implements Serializable {
	/**
	 * 통신 시 사용하는 일련번호 serialVersionUID
	 * 객체 통신을 위한 일련번호
	 */
	private static final long serialVersionUID = 1L;
	int num = 11;
	String team = "edu";
	String job = "manager";
	
	public String method() {
		return "메소드 호출";
	}
}



package chap11.exam11.obj;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;

public class ObjOutput {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 설정
		String path = "C:/img/temp/obj.dat";
		// 2. 스트림 준비(보조)
		FileOutputStream fos = new FileOutputStream(path);
		DataOutputStream dos = new DataOutputStream(fos);
		ObjectOutputStream oos = new ObjectOutputStream(dos);
		// 3. 쓰기
		// - Map으로
		Map<String, String> map = new HashMap<>();
		map.put("name",  "kim");
		map.put("phone", "010-1234-5678");
		oos.writeObject(map);
		// - Array로
		oos.writeObject(new int[] {90, 95, 90, 100});
		// - String으로
		oos.writeUTF("기본형도 가능");
		// - Instance Object, 객체화하여 넣기
		oos.writeObject(new Sample());
		// 4. 비우기
		oos.flush();
		// 5. 자원 반납
		oos.close();
		dos.close();
		fos.close();
	}

}



package chap11.exam11.obj;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Map;
import java.util.Set;

public class ObjInput {

	public static void main(String[] args) throws Exception {
		// 1. 경로 설정
		String path = "C:/img/temp/obj.dat";
		
		// 2. 스트림 생성
		FileInputStream fis = new FileInputStream(path);
		BufferedInputStream bis = new BufferedInputStream(fis);
		ObjectInputStream ois = new ObjectInputStream(bis);
		
		// 3. 읽기 
		Map<String, String> map = (Map<String, String>)ois.readObject();
		int [] scores = (int[])ois.readObject();
		String msg = (String)ois.readUTF();
		Sample sample = (Sample)ois.readObject();
		
		// 4. 출력하기
		Set<String> set = map.keySet();
		for(String key : set) {
			System.out.println(key + " : " + map.get(key));
		}
		
		for(int i = 0; i < scores.length; i++) { System.out.println(scores[i]); }
		
		System.out.println(msg);
		
		System.out.println("sample : " + sample.num);
		System.out.println("sample : " + sample.job);
		System.out.println("sample : " + sample.team);
		System.out.println("sample : " + sample.method());
		
		// 4. 해제
		ois.close();
		bis.close();
		fis.close();
	}

}





Prop은 Properties의 줄임말로 보통 Java Project 내부에서 읽고 쓰기를 한다.


프로그램의 내부에서 사용하기 때문에 환경 설정, 저장한 데이터 등의 데이터를 저장한다.


Properties는 map 인터페이스를 상속받은 자료 구조이다.


다음 예제를 확인해보자. 예제를 실행 하면 클래스 파일 옆에 Properties 파일이 생성된다.



package chap11.exam12.prop;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.util.Properties;

public class PropWriter {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 지정
		String path = "src/chap11/exam12/prop/profile.properties";
		
		// 2. Properties 객체 준비
		Properties pro = new Properties();
		
		// 3. 객체에 값 추가 (무엇을 넣어도 문자열로 넣어버림
		pro.put("1", "1111");
		pro.put("2", "2222");
		pro.put("3", "3333");
		pro.put("4", "4444");
		pro.put("5", "5555");
		
		// 4. 스트림 준비
		FileOutputStream fos = new FileOutputStream(path);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		
		// 5. 저장(스트림, 주석)
		pro.store(bos, "simple Comment");
		System.out.println("저장 완료");
		
		// 6. 자원 반납
		bos.close();
		fos.close();
	
	}

}



package chap11.exam12.prop;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

public class PropReader {

	public static void main(String[] args) throws Exception {
		// 1. 불러올 파일 위치 지정
		String path = "";
		// jar로 배포하고나면 사용자마다 경로가 상이하므로
		// 특정 클래스를 기준으로 리소스를 호출해야 한다.
		// 1-1. 경로를 가져오는 방법
//		path = PropReader.class.getResource("profile.properties").getPath();
//		System.out.println(path);
		// 1-2. 스트림으로 직접 불러오는 방
		InputStream is = PropReader.class.getResourceAsStream("profile.properties");
		
		// 2. 프로퍼티 객체 준비
		Properties pro = new Properties();
		
		// 3. 스트림 준비
		BufferedInputStream bis = new BufferedInputStream(is);
		
		// 4. 읽어오기
		pro.load(bis);
		
		// 5. 객체에서 값을 하나씩 추출
		System.out.println(pro.get("1"));
		System.out.println(pro.get("2"));
		System.out.println(pro.get("3"));
		System.out.println(pro.get("4"));
		System.out.println(pro.get("5"));
		
		// 6. 자원 반납
		bis.close();
		is.close();
		
		// 간단한 설정에 좋음, 보안성이 안좋음(전부 String)
		
	}

}



보조 스트림은 다른 스트림에게 스트림을 전달하는 기능이다.


일반적으로 스트림 자체가 기능이 하나만 특화되어 매우 한정적인데


다른 스트림에 전달 함으로써 다른 기능을 사용 할 수 있다.


하지만 Stream 선언이 늘어 난 만큼 close로 해제도 제때 해주어야 한다.


다음 예제는 File Stream을 Input, Output Stream에 전달한 예제이다.



package chap11.exam08.string;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class TextFileCopy {

	public static void main(String[] args) throws Exception {
		// 1. 경로 설정
		String ori = "C:/img/asd.txt";
		String target = "C:/img/temp/asd.txt";
				
		// 2. 스트림 준비
		// 보조스트림 - 바이너리 기반의 스트림에 문자열 특화 기능 추가
		FileInputStream fis = new FileInputStream(ori);
		InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
		
		FileOutputStream fos = new FileOutputStream(target);
		OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
		
		// 3. 읽어오기
		char[] arr = new char[100];
		while(reader.read(arr) != -1) {
			writer.write(arr);	// 4. 내보내기
		}

		// 5. 자원반납(flush, close)
		writer.flush();
		reader.close();
		writer.close();
		fis.close();
		fos.close();
		
	}

}






기존의 스트림이 하나씩 하나씩 데이터를 들고 왔다면


Buffer Stream은 데이터를 한꺼번에 여러개씩 들고, 한꺼번에 전송 할 수 있도록 해 주는 임시 저장소이다.


그만큼 속도도 확연하게 차이나게 된다.


다음 예제는 데이터를 하나씩 받았을 때와 


Buffer Stream을 이용해 Byte 배열 덩어리로 한꺼번에 받았을 때의 속도를 비교하는 코드이다.


TimeChecker chk = new TimeChecker는 클래스 파일을 새로 만들어 선언한 클래스이고, 시간을 체크하는 기능을 간단하게 만들었다.


버퍼를 사용하지 않을 때


package chap11.exam09.buffer;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class BufferNotUse {

	public static void main(String[] args) throws Exception {

		TimeChecker chk = new TimeChecker();
		// 1. 파일 위치 지정
		String ori = "C:/img/10multi.pdf";
		String target = "C:/img/temp/NotuseCopy.pdf";
		
		// 2. 스트림 준비
		FileInputStream fis = new FileInputStream(ori);
		FileOutputStream fos = new FileOutputStream(target);
		int data;
		chk.timeStart();
		
		// 3. 읽기
		while((data = fis.read()) != -1) {
			System.out.println("복사중...");
			fos.write(data); // 4. 쓰기(시간 체크)
		}
		
		// 5. 비우고 반납
		fos.flush();
		// 15667ms 걸림, 15.6초
		System.out.println("완료 : " + chk.timeStop() + " ms");
		fis.close();
		fos.close();
	}

}


버퍼를 사용 할 때


package chap11.exam09.buffer;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.Buffer;

public class BufferUse {
	
	// try-with-resource == 트라이문이 끝나면 아래에 소켓 버퍼 등 close를 할 필요가 없음.
	public static void main(String[] args) throws Exception {
		TimeChecker chk = new TimeChecker();
		// 1. 파일 위치 지정
		String ori = "C:/img/10multi.pdf";
		String target = "C:/img/temp/useCopy.pdf";
		
		// 2. 스트림 준비(보조 스트림)
		try(  // 트라이가 끝나면 자동으로 close가 된다.
				FileInputStream fis = new FileInputStream(ori);
				BufferedInputStream bis = new BufferedInputStream(fis);
				
				FileOutputStream fos = new FileOutputStream(target);
				BufferedOutputStream bos = new BufferedOutputStream(fos);
			){
			byte[] arr = new byte[1024];
			chk.timeStart();
			while(bis.read(arr) != -1 ) {
				bos.write(arr);
			}
			// 5. 비우고 반납
			bos.flush();
			// 15667ms 걸림, 15.6초
			// 6044ms 걸림, 6초 (data를 배열로 받지 않았을 때)
			// 4ms 걸림, 0.004초 (byte[]배열로 받았을 때)
			System.out.println("완료 : " + chk.timeStop() + " ms");
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	
//	public static void main(String[] args) throws Exception {
//
//		TimeChecker chk = new TimeChecker();
//		// 1. 파일 위치 지정
//		String ori = "C:/img/10multi.pdf";
//		String target = "C:/img/temp/useCopy.pdf";
//		
//		// 2. 스트림 준비(보조 스트림)
//		FileInputStream fis = new FileInputStream(ori);
//		BufferedInputStream bis = new BufferedInputStream(fis);
//		
//		FileOutputStream fos = new FileOutputStream(target);
//		BufferedOutputStream bos = new BufferedOutputStream(fos);
//		
//		byte[] arr = new byte[1024];
//		chk.timeStart();
//		while(bis.read(arr) != -1 ) {
//			bos.write(arr);
//		}
//		
//		
//		
//		
//		
//		
//		
//		
////		int data;
////		chk.timeStart();
////		
////		// 3. 읽기
////		while((data = bis.read()) != -1) {
////			System.out.println("복사중...");
////			bos.write(data); // 4. 쓰기(시간 체크)
////		}
//		
//		// 5. 비우고 반납
//		bos.flush();
//		// 15667ms 걸림, 15.6초
//		// 6044ms 걸림, 6초 (data를 배열로 받지 않았을 때)
//		// 4ms 걸림, 0.004초 (byte[]배열로 받았을 때)
//		System.out.println("완료 : " + chk.timeStop() + " ms");
//		bos.close();
//		bis.close();
//		fis.close();
//		fos.close();
//	}

}


시간을 체크 할 타이머


package chap11.exam09.buffer;

public class TimeChecker {
	long start = 0;
	long end = 0;
	
	public void timeStart() {
		start = System.currentTimeMillis();
	}
	
	public long timeStop() {
		end = System.currentTimeMillis();
		return end-start;
	}
}



2번째 코드에서 try catch 문의 try 앞에 괄호가 추가 되었는데


괄호 안에 Stream을 선언하였을 경우 


try catch문이 끝났을 때 자동으로 close, 해제가 된다. (아래 복사)



		// 2. 스트림 준비(보조 스트림)
		try(  // 트라이가 끝나면 자동으로 close가 된다.
				FileInputStream fis = new FileInputStream(ori);
				BufferedInputStream bis = new BufferedInputStream(fis);
				
				FileOutputStream fos = new FileOutputStream(target);
				BufferedOutputStream bos = new BufferedOutputStream(fos);
			){
			byte[] arr = new byte[1024];
			chk.timeStart();
			while(bis.read(arr) != -1 ) {
				bos.write(arr);
			}
			// 5. 비우고 반납
			bos.flush();
			// 15667ms 걸림, 15.6초
			// 6044ms 걸림, 6초 (data를 배열로 받지 않았을 때)
			// 4ms 걸림, 0.004초 (byte[]배열로 받았을 때)
			System.out.println("완료 : " + chk.timeStop() + " ms");
			
		} catch (Exception e) {
			e.printStackTrace();
		}





File class는 I/O에 해당하는 클래스가 아니다.


하지만 File을 읽어오거나 쓸 때 사용하며 이 기능을 사용 할 때 Stream과 함께 사용된다.


일반적으로


1. 파일 객체를 만들어 읽어올 경로를 설정하고


2. 읽어올 파일을 Input Stream에 집어 넣고


3. 프로그램에서 Stream을 이용해 작업 한 후


4. 작업한 데이터를 Output Stream에 집어 넣고


5. 파일 객체 만들어 파일을 만들 경로를 설정하여 내보냄


내보낼 때에는 File Input Stream, File Output Stream을 사용한다.



다음 예제는 File 객체의 메소드를 활용한 예제이다.



package chap11.exam04.file;

import java.io.File;

public class FileMain {

	public static void main(String[] args) throws Exception {
		// 폴더 만들기 - File 객체 사용
		File dir = new File("c:/img/temp");
		if(!dir.exists()) { // 폴더가 존재 하지 않을 경우
			System.out.println("폴더 생성");
			dir.mkdirs();
		}		
		
		// 파일 만들기 - File 객체 사용
		 File file = new File("C:/img/temp/test.txt");
		 if(!file.exists()) {
			 System.out.println("파일 생성");;
			 file.createNewFile();
		 }

		 // 폴더 또는 파일에 대한 정보 - File 객체 사용
		 dir = new File("c:/img");
		 // 폴더에 해당하는 파일명
		 String[] fileNames = dir.list();
		 for(String name : fileNames) {
			 System.out.println(name);
		 }
		 
		 // 파일 객체를 뽑아내기
		 File[] files = dir.listFiles();
		 String gubun;
		 for(File f : files) {
			 if(f.isDirectory()) {
				 System.out.println("true");
				 gubun = "[디렉토리]";
			 } else {
				 gubun = "[파일]";
			 }
			 System.out.println(gubun + " " + f.getName() + " / " + f.length() + "byte");
		 }
	}

}


package chap11.exam05.fileinput;

import java.io.File;
import java.io.FileInputStream;

public class FileInputMain {

	public static void main(String[] args) throws Exception {
		// 1. 읽어올 파일 위치 지정
		String path = "C:/img/temp/test.txt";
		
		// 2. 파일 객체 생성
		File file = new File(path);
		
		// 3. 스트림 준비(빨대)
		// FileInputStream은 파일 특화이고 생성자 경로를 문자열로 넣어도 됨
		FileInputStream fis = new FileInputStream(file);
		// fis = new FileInputStream(path);
		
		// 4. 빨아 들인다.
		int data;
		while((data = fis.read())!=-1) { // -1 == EOF
			// 5. 출력(시스템안에서 이루어짐)
			System.out.print((char)data);
		}

		
		// 6. 닫아준다.(빨대 치우기)
		fis.close();
	}

}



package chap11.exam05.fileinput;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class FileOutputMain {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 선정(읽어올 곳, 내보낼 곳)
		String pathInput = "C:/img/001.jpg";
		String pathInput2 = "C:/img/10multi.pdf";
		String pathOutput = "C:/img/temp/food.jpg";
		String pathOutput2 = "C:/img/temp/10multi_copy.pdf";
		// 2. 스트림 준비(input, output)
		FileInputStream fis = new FileInputStream(pathInput);
		FileOutputStream fos = new FileOutputStream(pathOutput);
		FileInputStream fis2 = new FileInputStream(pathInput2);
		FileOutputStream fos2 = new FileOutputStream(pathOutput2);
		// 3. 읽어오기
		int data;
		int i = 0;
		
//		while((data = fis.read())!=-1) { // -1 == EOF
//			// 4. 내보내기
//			fos.write(data);
//			i++;
//			System.out.println("파일 복사중... " + i);
//		}
		
		// 속도 향상을 위해 배열 사용
		byte[] arr = new byte[1024];
		while(fis2.read(arr)!=-1) { // -1 == EOF
			// 4. 내보내기
			fos2.write(arr);
			i++;
			System.out.println("파일 복사중... " + i);
		}
		
		System.out.println("복사 완료");
		// 5. flush, close
		fis.close();
		fos.flush();
		fos.close();
		fis2.close();
		fos2.flush();
		fos2.close();
	}

}



다음 예제는 File Reader와 File Writer을 이용한 예제이다.


Writer와 Reader는 문자형 특화로 txt 파일을 만들 때 자주 쓰인다.





package chap11.exam06.charIO;

import java.io.FileReader;

public class TextInput {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 지정
		String path = "C:/img/asd.txt";
		// 2. 스트림 준비
		FileReader fr = new FileReader(path);
		// 3. 읽어오기
		char[] arr = new char[100];
		
		while(fr.read(arr)!=-1) { // -1 == EOF
			String data = new String(arr);
			// 4. 출력하기
			System.out.println(data);
			// System.out.print(arr);
		}

		
		// 5. 자원닫기
		
	}

}


package chap11.exam06.charIO;

import java.io.FileWriter;

public class TextOutput {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 설정
		String fileName = "C:/img/temp/write.txt";
		// 2. 스트림 준비
		// append 인자 - 이어쓰기 여부
		FileWriter fw = new FileWriter(fileName, true);
		// 3. 쓰기
		System.out.println("저장 시작");
		fw.write("for 문 시작 \r\n");
		for(int i = 1; i <= 10; i++) {
			fw.write("for 문 증가 : " + i + "\r\n");
		}
		fw.write("for 문 종료\r\n");
		// 4. flush, close
		System.out.println("저장 종료");
		fw.flush();
		fw.close();
		
		// 실행을 두 번 해보면 데이터가 새로 쓰이지 않고 축적된다.
		// append를 true값으로 해서
	}

}


package chap11.exam07.test;

import java.io.FileWriter;
import java.util.Scanner;

public class WriteTest {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 설정
		String fileName = "C:/img/temp/testkey.txt";
		
		// 2. 스캐너 준비
		Scanner scan = new Scanner(System.in);
		
		// 3. Writer 준비
		// append 인자 - 이어쓰기 여부
		FileWriter fw = new FileWriter(fileName, true);
		
		// 4. 읽어오기
		String temp = "";
		System.out.println("저장 시작");
		while(true) {
			System.out.println("추가할 문장을 입력하세요.(end test 입력 할 경우 끝남)");
			temp = scan.nextLine();
			if (temp.equals("end test")) { break; }
			else {
				// 5. 내보내기
				fw.write(temp);
				fw.write("\r\n");
				fw.flush();
			}
		}
		
		// 6. 자원 종료
		System.out.println("저장 종료");
		fw.close();
	}

}


InputStream과 OutputStream을 이용하여 콘솔에 간단한 입력과 출력을 해보자


package chap11.exam01.sysio;

import java.io.IOException;
import java.io.InputStream;

public class SysInput {

	public static void main(String[] args) {
		System.out.println("아무거나 입력 하세요!");

		InputStream in = System.in;

		try {
			char a = (char)in.read();
			System.out.println(a);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}



package chap11.exam01.sysio;

import java.io.OutputStream;

public class SysOutput {

	public static void main(String[] args) throws Exception {
		OutputStream os = System.out;
		
		for(byte b = 97; b <= 122; b++) {
			os.write(b); // 바이너리를 내부적으로 처리 (파일 처리용)
//			System.out.println((char)b); // 외부로 출력
		}
		os.flush(); // flush가 없으면 출력이 안 될 수도 있다.
//		os.close(); // 여기서 close를 하면 밑에서 다시 객체화하여도 사용할 수 없음
		
		System.out.println();
		
		
		// 문자열을 배열로 만들어서 출력
		String hangul = "가나다라마바사아자차카타파하";
		
		byte[] aa = hangul.getBytes();
//		for(int i = 0; i < aa.length; i++) {
//			System.out.println((char)aa[i]);
//		}
		os.write(aa);
		// 단일 스레드에서 여러 개의 스트림을 다룰 수 없다.
		os.flush(); // flush가 없으면 출력이 안될 수도 있다.
		os.close();
	}

}


flush는 (변기의)물을 내리다 라는 의미를 가지고 있는데 


Stream을 flush하다 라고 이해하면 쉽다.


현재 메모리에 Stream이 그대로 흘러와 담겨있는데


flush하여 데이터를 뿌려준다고 할 수 있다.


그리고 Stream을 close 한다면 메모리를 반환하게 된다.


close를 한번 한 스트림은 다시 객체화 하여도 사용 할 수 없게 된다.










Console 클래스는 여태껏 사용해온 콘솔창의 입력 받은 문자를 쉽게 읽을 수 있는 클래스이고 cmd(Windows 커맨드 창)에서 실행된다.


Console은 정수, 실수 값을 바로 읽어 들이지 못하기 때문에 Scanner 클래스와 함께 이용하면 쉽게 사용가능하다.


여태껏 사용해온 Scanner scan = new Scanner(System.in);과 같다.




package chap11.exam02.console;

import java.io.Console;

public class Main {

	public static void main(String[] args) {
		// cmd에서만 실행 가능
		// 프로젝트 내 bin 폴더 > java [패키지이름.실행클래스] (경로)
		// 입력 순서 ex
		// d:
		// D:\JAVA\Chapter11\bin
		// java chap11.exam02.console.Main
		
		Console console = System.console();
		
		System.out.print("아이디 : ");
		String id = console.readLine();
		System.out.print("비번 : ");
		char[] pw = console.readPassword();
		String pass = new String(pw);
		System.out.println("---------------------");
		System.out.println("ID : " + id);
		System.out.println("PW : " + pass);
		
	}

}



콘솔 창으로 실행하는 방법은 https://qdgbjsdnb.tistory.com/79?category=715136 에서 확인하자





Scanner은 딱히 설명 할 것이 없으므로 예제만 올려보겠습니다.



package chap11.exam03.scanner;

import java.util.Scanner;

public class ScanMain {

	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		
		System.out.print("문자열 입력>");
		String inputStr = scan.nextLine();
		System.out.println(inputStr);
		
		System.out.print("정수 입력>");
		int inputInt = scan.nextInt();
		System.out.println(inputInt);
		
		System.out.print("실수 입력>");
		double inputDouble = scan.nextDouble();
		System.out.println(inputDouble);
		
	}

}




윈도우키(Ctrl, Alt 사이 윈도우 아이콘)를 누르고 cmd 입력


or


윈도우키 + R을 누르면 실행창이 나오는데 cmd 입력해서 실행



두가지 방법 중 마음에 드는 방법으로 실행 후 다음 사진과 같이 입력한다.







폴더 구조와 함께 이해해 보자.


콘솔창에 cd [Workspace 경로][프로젝트 이름 + \bin]을 입력해서 경로를 수정하고


java [패키지 명].[실행할 java 파일 이름(대소문자 구분)] 하고 입력하면 코드가 실행된다.






Thread 그룹은 프로젝트가 커질수록 그에 맞춰 쓰레드도 많아지게 된다.


몰론 스레드마다 이름을 지정해 관리 할 수 있지만 숫자가 많아지면 한계가 있다.


그러한 이유로 그룹으로 묶어 공통으로 설정하고 제어 할 수 있는 기능이 바로 Thread Group이다


기본적으로 모든 스레드는 Main Group으로 자동적으로 설정이 되고





- 스레드 그룹 이름 얻기 -


Threadgroup group = Thread.currentThread.getThreadGroup();


String groupName = group.getName();



스레드 그룹을 생성 시 부모 그룹과 묶을 수 있는데 만약 묶지 않는다면 현재 스레드의 하위 그룹과 묶인다.


- 스레드 그룹 생성 -


Threadgroup group = new ThreadGroup(String name);


Threadgroup group = new ThreadGroup(ThreadGroup parent, String name);




- 스레드에 생성 시 그룹 부여 방법 4가지 -


Thread th = new Thread(Thread group, Runnable target);


Thread th = new Thread(Thread group, String name);


Thread th = new Thread(Thread group, Runnable target, String name);


Thread th = new Thread(Thread group, Runnable target, String name, long stackSize);




그룹을 통해 묶은 스레드는 메소드로 한번에 통제 할 수 있다.


예를들어 interrupt()로 한꺼번에 정지 시키거나


setDaemon(boolean)으로 데몬 그룹으로 설정 할 수 있다.


다양한 메소드는 직접 Eclipse에서 ThreadGroup이름에서 '.'을 눌러 나오는 자동완성 메소드들을 통해서 확인해보면


익숙한 메소드가 많을 것이다.














'개념 및 코딩 > 08.Thread 스레드' 카테고리의 다른 글

[JAVA]08-07.Thread Pool, Pool Block  (0) 2018.09.06
[JAVA]08-05.Demon Thread  (0) 2018.09.06
[JAVA]08-04.Thread State, Control  (0) 2018.09.06
[JAVA]08-03.Priority, Synchronized  (0) 2018.09.06
[JAVA]08-02.Runnable, Thread  (0) 2018.09.04

+ Recent posts