기본 콘텐츠로 건너뛰기

Part5 입출력 프로그래밍과 네트워킹(16장 ~ 18장)

java.io 패키지 : 데이터 읽고 쓰기

데이터를 읽는 대상(읽기용 빨대를 꽂는 대상, in = read) - 파일 데이터,  키보드, 네트워크, 이미지나 동영상 데이터

데이터를 쓰는 대상(쓰기용 빨대를 꽂는 대상, out = write) - 파일 데이터, 모니터의 화면, 네트워크를 통한 데이터 전송

InputStream(1byte), OutputStream(1byte), Reader(2byte(char)), Reader(2byte(char)) - 주로 Input, Output 씀 거의 대부분의 데이터는 byte 단위임

InputStream을 구하는 방법 두가지 : 어떤 클래스 자체로 설계되어서 객체를 생성하는 방식, 어떤 클래스의 메소드의 리턴 타입으로 InputStream 타입의 객체를 반환하는 방식(Socket)

        read() - 1byte의 데이터를 int타입으로 반환

        read(byte[] b) - 원하는 byte를 지정 가능, 4byte= 버퍼

InputStream in = new FileInputStream("C:\\newjava\\aaa.txt");
while(true) {
  //한 바이트를 읽어들인 결과
  int data = in.read();
  System.out.println(data);
  if(data == -1) {
    break;
  }
}

while 구문을 사용한 이유는 몇 번이나 루프를 돌아야 하는지 판단 할수 없을 때 사용함

-1은 더 읽어들일 데이터가 없는 경우

OutputStream을 구하는 방법 두가지 : 어떤 클래스 자체로 설계되어서 객체를 생성하는 방식, 어떤 클래스의 메소드의 리턴 타입으로 OutputStream 타입의 객체를 반환하는 방식

       write(int) - 1byte에 해당하는 데이터를 기록할 때 사용

       write(byte[] b, int off, int len) - byte[] 안에 있는 데이터를 원하는 위치(off)에서부터 원하는 숫자(len)만큼 데이터를 기록

        flush() - Stream에 기록된 데이터를 확실하게 보냄

OutputStream out = null;
try {
  out = new FileOutputStream("aaa.txt");
  String str = "이 문자열을 파일에 기록하고 싶습니다.";
  byte[] arr = str.getBytes();
  out.write(arr);
} catch (Exception e) {
  // TODO: handle exception
  e.printStackTrace();
} finally {
  if(out != null) {
    try {
      out.close();
    } catch (Exception e) {
	// TODO: handle exception
    }
  }
}

모든 연결은 반드시 finally를 통하여 close()처리를 해줘야함

1byte씩하면 속도의 이슈가 생김

InputStream in = null;
OutputStream out = null;

try {
  URL url = new URL("https://www.freelec.co.kr");
  in = url.openStream();
  out = new FileOutputStream("ccc.txt");
  //데이터를 한번에 읽기 위한 공간
  byte[] arr = new byte[5];

  while(true) {
    int count = in.read(arr);
    if(count == -1) {
      break;
    }
    out.write(arr, 0, count);
  }
	
} catch (Exception e) {
  // TODO: handle exception
  e.printStackTrace();
}finally {
  if(in != null) {
    try {
      in.close();
    } catch (Exception e) {
      // TODO: handle exception
    }
  }
  if(out != null) {
    try {
      out.close();
    } catch (Exception e) {
      // TODO: handle exception
    }
  }
}

숫자를 총 숫자를 파악해서 데이터를 한번에 읽기 때문에 속도 향상

DataOutputStream - 상속 구조로 보면 OutputStream의 하위클래스이지만 생성자를 보면 OutputStream을 객체 생성 시에 받아들임, 확장적인 성격

String str = "이 문자열을 기록해 볼까 합니다.";
OutputStream out = new FileOutputStream("ddd.txt");
DataOutputStream dos = new DataOutputStream(out);
dos.writeUTF(str);

//연결을 종료할 때는 연결된 순서의 역순으로 종료
dos.close();
out.close();

DataInputStream - 상속 구조로 보면 InputStream의 하위클래스이지만 생성자를 보면 InputStream을 객체 생성 시에 받아들임, 확장적인 성격

InputStream in = new FileInputStream("ddd.txt");
DataInputStream dis = new DataInputStream(in);
String str = dis.readUTF();
System.out.println(str);
dis.close();
in.close();

DataInputStream, DataOutputStream을 이용하면 데이터를 처리하는 방식에는 스트림을 연결만 해주면 되는 장점이 있지만, 읽고 쓰는 방식이 서로 통일 해야함, DataOutputStream으로 출력된 데이터는 반드시 DataInputStream을 통해서 읽어야만 함

문자(char) 기반의 스트림을 사용하는 방법 - InputStream이 read() 메소드를 통해서 하나의 바이트를 읽어들인다면 2byte(char)를 읽어냄, Writer 역시 한 바이트를 기록하는 것이 아니라 2byte의 char 자체를 기록함

String str = "이 문자열을 기록할 겁니다.\n";
Writer writer = new FileWriter("eee.txt");
writer.write(str);
writer.close();
Reader은 문자열로 읽어내는 기능이 없으므로 조금 복잡함 -> BufferedReader클래스 활용

Reader reader = new FileReader("eee.txt");
BufferedReader br = new BufferedReader(reader);

while(true) {
  String str = br.readLine();
  if(str == null) {
    break;
  }
  System.out.println(str);
}
br.close();
reader.close();

ObjectInputStream, ObjectOutputStream - 객체를 읽거나 쓸 수 있음, 하지만 Serializable 인터페이스로 구현이 되어야함, 단점은 저장할때와 복구할때 같은 클래스가 있어야함

public class PersonInfo implements Serializable{
  private static final long serialVersionUID = 1L;
  private String name;
  private int age;
  private String ssn;

객체 생성(직렬화)

PersonInfo person = new PersonInfo();
person.setAge(20);
person.setName("Hong Gil Dong");
person.setSsn("123456789");
OutputStream out = new FileOutputStream("person.dat");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(person);
oos.close();
out.close();

ObjectOutputStream 활용

InputStream in = new FileInputStream("person.dat");
ObjectInputStream oin = new ObjectInputStream(in);

Object obj = oin.readObject();
PersonInfo person = (PersonInfo)obj;

System.out.println(person.getName());
System.out.println(person.getAge());
System.out.println(person.getSsn());

oin.close();
in.close();

ObjectInputStream 활용

InputStreamReader, OutputStreamWriter - InputStream과 Reader의 1byte처리와 char 처리를 유연하게 사용

RandomAccessFile 객체 - 읽는 작업과 쓰는 작업이 객체 하나로 가능, 마음대로 읽고 쓰는 위치를 조정(맨 앞부터 작업할 필요가 없으므로 빈번하게 데이터의 수정이 일어날때 사용), 스트림과 같이 사용 못하고 파일만 사용가능

        see() : 마음대로 위치를 이동할 수 있음

운영체제에 따라서 파일 경로 구분자가 다르므로 File객체에서는 File.separator와 같은 상수를 사용

File file = new File("aaa.txt");
String absolutePath = file.getAbsolutePath();
System.out.println("절대 경로 :" +absolutePath);
boolean isFile = file.isFile();
System.out.println("파일 여부  :" + isFile);
boolean isDirectory = file.isDirectory();
System.out.println("디렉터리 여부 :" + isDirectory);
System.out.println("경로 구분자 : " + File.separator);

Java 엑셀  라이브러리 - JXL과 POI를 주로 씀

JXL라이브러리 활용

엑셀 파일 생성

import java.io.File;
import jxl.Workbook;
import jxl.write.Label;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;

public class ExcelMaker {
  public static void main(String[] args) throws Exception{
    WritableWorkbook workbook = Workbook.createWorkbook(new File("data.xls"));
     //sheet만들기
    WritableSheet s1 = workbook.createSheet("Sheet 0", 0);
    WritableSheet s2 = workbook.createSheet("Sheet 1", 1);
    WritableSheet s3 = workbook.createSheet("Sheet 2", 2);
    //cell 만들기
    for (int i = 0; i < 100; i++) {
      Label label = new Label(0, i, "데이터...." + i);
      s1.addCell(label);
    }
    workbook.write();
    workbook.close();
  }
}

파일 읽기

import java.io.File;
import java.io.IOException;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;

public class ExcelReader {
  public static void main(String[] args) throws BiffException, IOException {
    Workbook wb = Workbook.getWorkbook(new File("data.xls"));
    Sheet sheet = wb.getSheet(0);

    int i = 0;
    while (true) {
      try {
        Cell cell = sheet.getCell(0, i);
        i++;			 
       System.out.println(cell.getContents());
      } catch (Exception e) {
        break;
      }
    }
  }
}

데이터를 주고받는 네트워킹

TCP : 패킷 데이터를 목적지에 전달하고, 이 패킷이 제대로 전달되었는지를 확인하고 다음 데이터를 보내므로 안정성은 좋으나 속도가 중요할 경우에는 문제가 될 수 있음

UDP : TCP와 달리 확인 절차를 가지지 않으므로 데이터의 전달은 빠르지만 안정성은 그만큼 손해를 봄, 주로 동영상이나 멀티미디어의 데이터를 주고 받을 때 선호 함

Socket : 메세지를 주고 받는 장치이므로 socket클래스의 I/O를 주로 사용함

Protocol : 어떤 식으로 메시지를 주고받거나 어떤 구조에 맞춰서 데이터를 보내고 받는 것을 의미

Port :  0 ~ 65536까지 존재 함, 0 ~ 1024는 주요 서비스에서 예약한 상태임, 인터넷 기본포트는 80이고, ftp는 21번 포트를 기본으로 사용

ServerSocket(Listener) : 서버 역할을 하는 곳에서 쓰이고, 외부의 소켓을 accept()하는 역할

소켓 통신의 예시(입력데이터 돌려받는 echo)

클라이언트

Socket socket = new Socket("localhost", 8111);
System.out.println("서버 연결 완료");

//데이터를 보낼 준비
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);

//데이터를 받을 준비
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);

Scanner scanner = new Scanner(System.in);

while(true) {
	System.out.println("서버로 전송할 메시지를 입력해주세요.");
	String msg = scanner.nextLine();
	dos.writeUTF(msg);
	dos.flush();
	
	String readMsg = din.readUTF();
	System.out.println("받은메시지:" + readMsg);
	
	if(msg.equals("EXIT")) {
		break;
	}
	
}
//읽는 스트림 종료
din.close();
in.close();
//쓰는 스트림 종료
dos.close();
out.close();
socket.close();

서버

ServerSocket serverSocket = new ServerSocket(8111);
System.out.println("서버 준비 완료");

Socket socket = serverSocket.accept();
System.out.println("클라이언트 연결 완료");

//읽는 스트림
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);

//쓰는 스트림
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);

while(true) {
  String userMsg = dis.readUTF();
  System.out.println("사용자 메시지:" + userMsg);
  if(userMsg.equals("EXIT")) {
    break;
  }
  //받은 메시지를 다시 전송
  dos.writeUTF(userMsg);
  dos.flush();
}
dis.close();
in.close();
socket.close();

웹을 위한 스레드와 네트워킹

스레드 만드는 방법

    1. 동시에 진행되었으면 하는 작업을 선정

    2. 클래스 위에 extends Thread로 상속 혹은 implements Runnable 인터페이스 추가

    3. public void run() 메소드를 오버라이드

    4. 원하는 만큼의 스레드를 만들어서 start()

public class SumEx implements Runnable{
  public static void main(String[] args) throws Exception{
    SumEx ex = new SumEx();
    //ex.doJob();
    Thread t0 = new Thread(ex);
    Thread t1 = new Thread(ex);
    //t0.start();
    //t1.start();
    System.out.println("---------------------------");
  }
  public void doJob() throws Exception{
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
      sum = sum + i;
      Thread.sleep(100);
      System.out.println(sum + ":" + this + ":" + Thread.currentThread().getName());
    }
  }
  @Override
  public void run() {
    try {
      doJob();
    }catch (Exception e){
      e.printStackTrace();
    }
  }
}

extends Thread - 객체 하나당 스레드 하나: 동시에 여러 개가 조금씩 움직이는 상황(경마게임)

implements Runnable - 객체와 별개의 스레드를 생성할 때 : 객체는 Runnable 인터페이스를 구현한 상태로만 작업하고, 필요한 만큼 객체를 생성하고 싶을 때 implements Runnable을 추가

synchronized : 지정된 메소드나 블록 단위를 실행할 때 하나의 스레드가 객체를 차지함(한개에 여러개의 스레드를 사용할 때 객체의 영향을 피할때 유용), Lock을 획득한 스레드가 무한 루프에 빠지는 문제 주의

    특정 메소드에 처리 방식

    코드의 특정 부분만을 synchronized block으로 처리

public synchronized void doA(){

public void doA(){
  synchronized(buffer){
    for(int i=0; i<100; i++){
    ...
    }
  }
























댓글

이 블로그의 인기 게시물

mac 맥 맥북 Brew 완전 삭제

맥북에서 Brew 초기화 Brew를 써서 h2를 쓰려고 하는데 brew install h2가 안되서 이리 저리 알아보다가 완전 삭제 후 다시 설치 하니까 되서 그 방법을 남겨놈 1. 터미널에 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)" 입력후 y랑 뭐 비번.. 2. /usr/local 폴더에서 Homebrew 폴더 삭제 rm -rf Homebrew/ 권한설정으로 잘.....삭제하고 3. 다시 설치 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" 좀 오래걸리니까 기다려야한다는걸 배움... 출처.... https://discourse.brew.sh/t/error-no-formulae-found-in-taps/8331/9

메이븐으로 라이브러리 인식

 간혹 퍼블릭 jar가 아닌 파일이 있는데 그럴때 쓰면 될듯 <dependency> <groupId> SimpleCryptLib </groupId> <artifactId> SimpleCryptLib </artifactId> <version> 1.1.0 </version> <scope> system </scope> <systemPath> ${basedir}/src/main/webapp/WEB-INF/lib/SimpleCryptLib-1.1.0.jar </systemPath> </dependency> version, scope, systemPath는 꼭 작성해야 한다 groupId, artifactId, version은 암거나 해도 되는거 같음 최근(2021.05.04)스프링 부트    < dependency > < groupId > NiceID </ groupId > < artifactId > NiceID </ artifactId > < version > 1.0 </ version > < scope > system </ scope > < systemPath > ${basedir}/src/main/resources/lib/NiceID.jar </ systemPath > </ dependency > 이걸 추가해주는것도 필요할지도..?? < build > < plugins > < plugin > < groupId > org.springframework.boot </ groupId > < artifactId > spring-bo...

ORA-28000 계정이 잠금되었습니다 계정 잠길때

오라클 계정이 잠길때 해제방법 증상 t he account is locked 오류 발생 원인 Oracle 에서 t he account is locked  에러가 나는 원인은 ● 잘못된 패스워드로 설정횟수만큼 접속 시도시 Lock. ●  30일동안(Default) 해당 계정으로 로그인을 하지 않았을 경우 Lock. 등이 있다. 해결방법 command창에서 * 로컬일경우, sqlplus "/as sysdba"  또는  sqlplus /nolog  conn /as sysdba  * 로컬이 아닐 경우, sqlplus /nolog conn sys/password@<sid> 이름/패스워드@sid로 입력 로 접속 후 SELECT username, account_status, lock_date FROM dba_users; 으로 Lock이 된 사용자를 확인한 후 LOCKED<TIMED> 라고 되있으면, 패스워드 설정횟수 입력 오류로, 아래의 Unlock 명령만, EXPIRED & LOCKED 라고 되있으면, 패스워드 기간만료로, Unlock 후 비밀번호를 지정해줘야 한다. ALTER USER 사용자명 ACCOUNT UNLOCK; 로 Lock된 사용자를 Unl ock 시킨다 방금 말했다시피, 다시 Lock된 사용자 확인했는데,  Open되지 않고 EXPIRED되어 있다면, alter user 사용자명 identified by 바꿀패스워드;  로 패스워드를 변경하거나 또는 SQL*PLUS 를 재시작하여 Lock를 해제한 계정(사용자명/패스워드)로 로그인 하면 패스워드 변경 창이 뜬다. 추가로 패스워드 Lock 횟수 확인하는 방법은 SELECT U....