2018년 10월 30일 화요일

[Linux, Unix] File 입출력 예제

이번시간에는 file입출력 예제들을 살펴보도록 하겠습니다.


 1. test.txt파일의 내용을 test2.txt파일에 복사 (없으면 생성)  


 ▷ 실행코드
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<fcntl.h>
  5. #define FILE_SIZE       1024
  6. int main(){
  7.         int fd, fd2;
  8.         char * readData = (char *)malloc(sizeof(char) * FILE_SIZE);
  9.         memset(readData, 0, 1024);
  10.         if( ( fd = open("test.txt", O_RDONLY)) < 0){
  11.                 printf("open() error!\n");
  12.                 exit(1);
  13.         }
  14.         read(fd, readData, FILE_SIZE);
  15.         if( ( fd2 = open("test2.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0){
  16.                 printf("fd2 open() error\n");
  17.                 exit(1);
  18.         }
  19.         write(fd2, readData, strlen(readData));
  20.         close(fd);
  21.         close(fd2);
  22.         free(readData);
  23.         return 0;
  24. }


 ▶ 실행결과











: test2.txt파일을 생성하여 test.txt의 파일내용을 복사하여 붙여넣음.






2. cat명령어 구현하기 (명령어 인자로 '>'와 '>>'를 사용하기)


 ▷ 실행코드
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. #include<fcntl.h>
  5. #define FILE_SIZE       1024
  6. int main(int argc, char * argv[]){
  7.         int fd;
  8.         char * readData = (char *)malloc(sizeof(char) * FILE_SIZE);
  9.         char * writeData = (char *)malloc(sizeof(char) * FILE_SIZE);
  10.         if(argc == 2) // (simple) file print
  11.         {
  12.                 if( ( fd = open(argv[1], O_RDONLY)) < 0){ // not exists files
  13.                         printf("File not exists\n");
  14.                         exit(1);
  15.                 }
  16.                 read(fd, readData, FILE_SIZE);
  17.                 printf("%s print\n", argv[1]);
  18.                 printf("%s\n", readData);
  19.                 close(fd);
  20.                 free(readData);
  21.         }
  22.         else if(argc > 2) // parameteres exist(append OR overwrite)
  23.         {
  24.                 if(strcmp(argv[1], ">") == 0) // overwrite.
  25.                         fd = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);
  26.                 else if(strcmp(argv[1], ">>") == 0) // append.
  27.                         fd = open(argv[2], O_CREAT | O_WRONLY | O_APPEND, 0644);
  28.                 else
  29.                 {
  30.                         printf("%s don`t parameters\n", argv[1]);
  31.                         exit(1);
  32.                 }
  33.                         scanf("%s", writeData);
  34.                         write(fd, writeData, strlen(writeData));
  35.                         close(fd);
  36.                         free(writeData);
  37.         }
  38.         return 0;
  39. }


 ▶ 실행결과

1) cat명령어 (명령행 인자로 > 옵션 사용)




2) cat명렁어 (명령행 인자로 >> 옵션 사용)




: cat명령어와 동일한 옵션을 사용하였다.
1) 파일내용 출력 (기본 실행)
2) >옵션으로 파일내용 덮어쓰기(overwrite)
3) >>옵션으로 파일내용 덧붙이기(append)






3. dup사용 (파일 디스크립터 복사)

 ▷ 실행코드
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<fcntl.h>
  4. int main(){
  5.         int fd, copy_fd;
  6.         if( (fd = open("test.txt", O_RDONLY)) < 0){
  7.                 printf("open(0 error\n");
  8.                 exit(1);
  9.         }
  10.         copy_fd = dup(fd);
  11.         printf("fd : %d\n", fd);
  12.         printf("copy_fd : %d\n", copy_fd);
  13.         close(fd);
  14.         close(copy_fd);
  15.         return 0;
  16. }

 ▶ 실행결과







: fd는 3번, dup()로 복사된 파일 디스크립터 copy_fd는 4번을 가진다.
파일 디스크립터는 0부터 차례대로 채워서 생성이 되는데, 0~2는 기본 파일 디스크립터에 해당하므로 3번과 4번을 차례대로 부여하였다.






4. dup사용 (복사된 디스크립터로 출력)

 - 그렇다면 복사된 파일 디스크립터로 출력이 가능한지 살펴보자.

 ▷ 실행코드
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<fcntl.h>
  5. int main(){
  6.         int fd, copy_fd;
  7.         char * readData = (char *)malloc(sizeof(char) * 1024);
  8.         memset(readData, 0, 1024);
  9.         if( ( fd = open("test.txt", O_RDONLY)) < 0){
  10.                 printf("open() error\n");
  11.                 exit(1);
  12.         }
  13.         copy_fd = dup(fd);
  14.         read(copy_fd, readData, 1024);
  15.         printf("fd : %d\n", fd);
  16.         printf("copy_fd : %d\n", copy_fd);
  17.         printf("%s\n", readData);
  18.         close(fd);
  19.         close(copy_fd);
  20.         free(readData);
  21.         return 0;
  22. }

 ▶ 실행결과










: 기존의 디스크립터를 O_RDONLY로 읽기전용으로 파일을 open한 뒤 복사한 copy_fd로 read()하여 파일 내용을 읽어온 뒤 출력하는 예제이다.
결과를 보니, 복사한 디스크립터도 기존 디스크립터와 같은 기능을 수행할 수 있다.







5. dup사용 (기존 파일 디스크립터 삭제 후 디스크립터 확인하기)

 - 기존 파일디스크립터를 삭제하고 복제된 파일 디스크립터는 그대로 사용이 가능한지 살펴보자.

 ▷ 실행코드
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<fcntl.h>
  5. int main(){
  6.         int fd, copy_fd;
  7.         char * writeData = "dup Test";
  8.         char * readData = (char *)malloc(sizeof(char) * 1024);
  9.         memset(readData, 0, 1024);
  10.         fd = open("anyFile.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
  11.         copy_fd = dup(fd);
  12.         printf("dup(fd)\n");
  13.         printf("fd : %d\n", fd);
  14.         printf("copy_fd : %d\n", copy_fd);
  15.         close(fd);
  16.         printf("fd close");
  17.         printf("fd : %d\n", fd);
  18.         printf("copy_fd : %d\n", copy_fd);
  19.         write(copy_fd, writeData, strlen(writeData));
  20.         lseek(copy_fd, 0, SEEK_SET);
  21.         read(copy_fd, readData, 1024);
  22.         printf("%s\n", readData);
  23.         close(copy_fd);
  24.         free(readData);
  25.         return 0;
  26. }

 ▶ 실행결과








: 파일 디스크립터 복사할때 기능도 복사되었고 기존 파일디스크립터에 의존성을 갖지 않는다. 중간에 lseek함수를 통해서 다시 파일을 읽을때 처음부터 읽을 수 있도록 만들었다.

write를 사용하면 파일이 write()함수가 실행된 만큼 이동하게 된다. 즉, 파일을 write한 부분부터 가리키게 된다. 그래서 read하게되면 아무값도 출력되지 않는다.
그러므로 lseek(파일디스크립터, 시작할위치, 앞이나 뒤 부분); 를 통해서 read할 파일 위치를 명시해야 한다.



6. dup2사용 (표준에러를 파일로 출력하기.)


 ▷ 실행코드
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<fcntl.h>
  4. int main(){
  5.         int fd, fd2;
  6.         fd = open("errorLog.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
  7.         dup2(fd, 2);
  8.         if( (fd2 = open("anyfile.txt", O_RDONLY)) < 0){
  9.                 perror("occurs error\n");
  10.         }
  11.         close(fd);
  12.         return 0;
  13. }

 ▶ 실행결과






: dup2를 사용해서 표준에러 출력(파일 디스크립터로 2번)을 파일로 출력하였다.
 (anyfile.txt라는 존재하지 않는 파일로 고의로 에러를 발생시킨것임.)



7. dup2의 사용 (파일 디스크립터 값을 확인해보자.)


 ▷ 실행코드
  1. #include<stdio.h>
  2. #include<fcntl.h>
  3. int main(){
  4.         int fd;
  5.         fd = open("test.txt", O_CREAT | O_RDONLY, 0644);
  6.         dup2(fd, 2);
  7.         printf("fd : %d\n", fd);
  8.         return 0;
  9. }


 ▶ 실행결과






: 역시 새로운 파일 디스크립터 번호를 할당받는다.




2018년 10월 29일 월요일

Network 중간정리,

지금까지 살펴본 네트워크에 대해서 중간정리를 해보자.


1. OSI7계층
: 서로다른 단말기간의 데이터를 네트워크 상에서 송/수신 하기위해 표준화된 통신 규약에 해당하는 각 영역별로 정리된 계층이다.

 ↓ 아래로 갈 수록 하위 계층
 7) Application Layer
 6) Presentation Layer
 5) Session Layer
 4) Transport Layer
 3) Network Layer
 2) DataLink Layer                                       
 1) Physical Layer           

위에 설명된 것들이 해당 영역의 계층들이며, 데이터가 송신될때, 해당 데이터 패킷이 각계층을 거쳐 캡슐화 된 후 네트워크를 통해 전달되어 분리되서 다시 해당 계층을 역순으로 데이터가 전송된다.

위의 OSI7계층을 다음과 같이 4계층으로 표현하기도한다.
 4) Application (OSI 7계층의 Application / Presentation / Session Layer가 해당됨.)
 : 사용자와 가장 인접한 계층이며, 네트워크를 응용프로그램(Telnet, FTP, SMTP)으로 이용한다.

 3) Transport (OSI 7계층의 Transport Layer가 해당됨.)
 : 데이터를 전송하는 역할을 담당한다. 각 시스템을 연결하여 TCP프로토콜을 사용해서 데이터를 전송한다.

 2) Internet (OSI 7계층의 Network Layer가 해당됨.)
 : 데이터를 올바른 목적지까지 전송하기 위해 패킷들을 경로를 지정한다.
IP프로토콜을 사용해서 라우팅을 역할을 한다.

 1) Network Interface (OSI 7계층의 DataLink / Physical Layer가 해당됨.)
 : 이더넷 카드 같은 하드웨어 장치를 뜻한다. 두 단말기간에 데이터를 어떤형태로 주고 받을지 데이터 전송방식을 정한다.  (아날로그신호 → 디지털신호로 / 디지털신호 → 아날로그)




2. ICMP
 Network Layer에 해당하는 프로토콜이며, ICMP헤더를 통해서 패킷들의 정보를 파악할 수 있다.

 - 고정된 4byte + 가변길이 4byte를 갖고있다.

 - 헤더 형태
  • Type (8bit)
  • Code (8bit)
  • CheckSum (16bit)
  • 가변 길이 (32bit)
  • Type : 메시지 유형을 정의한다. 보통 ping의 Echo사용시 - 메시지 유형 8번에 해당 됨.(패킷이 도달하지 못했을 경우 3으로 표시)
  • Code : Type에 대한 부가적인 설명을 한다. 이상이 없을 경우 보통 0번에 해당하는 값을 표시.
  • CheckSum : ICMP헤더의 패킷 손상여부를 체크. (네트워크를 통해 전송된 패킷이 변경여부(무결성)를 파악 할 수 있다.)
- CheckSum 방법
 1) ICMP헤더 패킷을 2바이트씩 16비트워드로 표현한다.
 2) 해당 값들을 전부 다 더한다. (기존 checksum에 해당하는 값들에 대해서는 0만들어서 계산하거나 무시하고 계산해야한다.) 이때, 캐리가 발생하였다면 더해주면된다.
 3) 해당 값들을 1의 보수로 표현하고 16진수로 바꿔준 뒤 기존의 CheckSum값과 비교하기. 기존 CheckSum값과 같을경우 패킷의 무결성을 인증한다.




3. RFC

 - Time Protocol
 : 각 독립된 사이트를 컴퓨터로 읽을 수 있는 날짜와 시간을 제공해준다.
   (1900년 1월 자정 이래로 초 단위로 시간을 보낸다.)

 - Daytime Protocol
 : 이 서비스는 입력된 값에 관계없이 현재 날짜와 시간을 문자열로 보내준다.





4. Encoding 

 - Encoding
(Encoding)인코딩은 문자를 컴퓨터로 저장하거나 불러오기 위해서 컴퓨터가 이해할 수 있도록 문자를 바이트로 표현하는 방법이다.
보통 컴퓨터에 사람이 사용하는 언어의 데이터로 저장하기 위해서 사용됨.

 - Encoding Type
  • ASCII : 영문/숫자/기호로 이루어진 영문키보드에 해당된 모든 값들을 1바이트로 표현한다. 0~127개 즉, 128개의 부호로 표현하며, 2byte이상의 값들은 표현할 수 없다.
  • Unicode : 전 세계의 문자를 2byte로 표현하며, 문자, 숫자가 각각 1:1매핑이 되어있다.
  • UTF-8 : ANSI(영어포함)문자를 1byte, 아시아 문자(한글 / 한문)는 3byte로 가변표기하는 방식이다.
  • MS-949 : 한글확장 완성형 방식이다. euc-kr이 표현할 수 없는 똠방각하를 표현할 수 있다.

5. Thread

 - Thread
 하나의 프로세스내에서 프로그램을 실행하는 작업 단위이다. 해당 프로세스 내에서 동작을 독립적으로 수행할 수 있다.

예를들어, 하나의 프로그램에 2개 이상의 작업을 수행할때, 기존에는 하나의 작업이 완료된 직후 다음작업을 실행하였다면, 쓰레드를 사용한다면, 하나의 작업을 하는동안에 다른작업도 같이 독립적으로 수행할 수 있는것이다.

하지만, Thread는 꼭 독립적인 작업을 할 필요는 없다.

 - synchronized (동기화)
 예를들어 A(), B(), C()의 작업 3가지가 존재할경우, A()와 B()는 각각 동시에 수행해도 되는 작업이며, C()의 작업은 동시에 접근할 수 없도록 하기위해서 synchronized를 이용한다.
그러면 1번 이라는 쓰레드가 C()에 접근하고 있는 상태면, 다른 쓰레드들은 1번 쓰레드가 종료될 때 까지 접근할 수 없고 기다려야 한다.

 - wait()
 wait은 작업중인 쓰레드에 대해서 대기상태로 만들어버린다. 즉 누군가 깨워야만 작업이 진행된다. notify()를 통해 해당 쓰레드를 깨울 수 있다.

 - notify()
 notifty는 대기상태인 쓰레드에 대해서 runnable상태로 바꿔준다. 즉, 대기하는 상태에서 실행할 수 있도록 만들어 준다.

 - join() 
 wait()과 비슷한 작업을 하지만 조금 다르다. join()이 하나의 메소드안에 사용되었을경우, 다른Thread들에게는 wait()상태를 준다. 즉 해당된 쓰레드가 끝나야만 다른 쓰레드들이 실행할 수 있도록 하는것이다.
 예를들어 main에서 Thread를 생성 하면 Thread는 main()에 대해서 독립적인 작업을 할 것이다. 또한 main도 마찬가지다. 이때 main이 해당 Thread작업이 끝나고 종료되기를 원할때, Thread 객체.join()을 하면 마치 synchronized같은 작업이 되는 것이다. (Thread객체의 작업수행이 종료 될 때까지 main이 종료되지 않고 기다리기 때문이다.)

- wait(), notify() : synchronized 블록안에서 사용되어야 한다.


하지만 Thread를 사용하는 동안에 위험적인 상황이 발생한다.
바로, Deadlock이라는 것인데, 아래에서 살펴보자.

 - Deadlock
Deadlock은 임계구역이며, 두 가지 작업을 쓰레드들이 하는데 서로 작업이 끝날때까지만을 기다리면서 무한 대기하게 되는 상태를 말한다.

이러한 Deadlock을 방지하는 방법에 대해 살펴보자.

 - Deadlock 최소화
1) 같은 순서로 객체에 접근 할 수 있도록 한다.
객체에 접근하는 순서가 같을 경우 교착상태가 덜 발생한다.

2) 교착상태가 발생하면 서로 깨워주도록 한다.
if 조건문으로 교착상태가 발생하면, 해당 Thread의 wait()상태를 notify()로 깨워주거나 해당 코드를 벗어나도록 코드를 작성 해야한다.


2018년 10월 9일 화요일

Java DataInputStream, DataOutputStream사용

지금까지 Java의 FileInputStream과 FileOutputStream을 사용해서 파일의 내용을 읽고 쓰기 작업을 진행했다.

지금까지 배운것을(FileInputStream과 FileOutputStream내용) 간단히 요약하자면, 파일의 내용을 읽올때 == 스트림을 통해서 파일의 내용에 있는 문자들을 전부 바이트로 읽어들였다. 그래서 컴퓨터는 이를 해석하여, 우리가 읽을 수 있는 문자로 표현했다.
그리고 이 바이트를 읽어 들이는 방법은 여러가지가 있는데, 우리는 한 바이트씩읽기(read)와 텍스트의 한줄씩 읽기(BufferedInputStream 사용)를 통해서 속도의 차이도 비교했었다.

하지만 텍스트파일의 내용을 조금 더 간편하게 읽어오거나 쓰기가 가능하다.
바로 이번에 소개할 DataInputStream / DataOutputStream이다.

스트림에 바이트 단위로 파일의 내용을 읽어들었다면, 이번에는 자바의 기본 자료형에 맞게 파일의 내용을 집어넣어보자.

이를 통해서 우리는 간편하게 스트림에 바이트로 작성하거나 읽어들일 필요가 없이
자바에서 지원하는 기본 자료형의 크기에 맞게 텍스트들을 넣을 수 있게 된 것이다.

역시 우리는 실습을 통해 살펴보는게 빠르다.

1. DataOutputStream을 통해서 새로운파일 생성 → DataInputStream을 통해서 생성한 파일 읽기

  1. package org.java.project1.subproject;
  2.  
  3. import java.io.BufferedInputStream;
  4. import java.io.DataInputStream;
  5.  
  6. import java.io.DataOutputStream;
  7. import java.io.FileInputStream;
  8. import java.io.FileOutputStream;
  9. import java.io.IOException;
  10.  
  11. interface DataStream{
  12.     public void dataWrite(DataOutputStream out) throws IOException;
  13.     public void dataRead(DataInputStream in) throws IOException;
  14. }
  15. class DataInputStreamExam implements DataStream{
  16.     public static String FileName;
  17.     public int[] arrInt = {1, 2, 3};
  18.     public String[] arrStr = {"one하나", "two둘", "three셋"};
  19.     public Double[] arrDouble = {1.0, 2.0, 3.0};
  20.    
  21.     @Override
  22.     public void dataWrite(DataOutputStream out) throws IOException{
  23.         for(int i = 0; i< arrInt.length; i++) {
  24.             out.writeInt(this.arrInt[i]);
  25.             out.writeDouble(this.arrDouble[i]);
  26.             out.writeUTF(this.arrStr[i]);
  27.         }
  28.     }
  29.    
  30.     @Override
  31.     public void dataRead(DataInputStream in) throws IOException{
  32.         String fileContent = "";
  33.         for(int i = 0; i < arrInt.length; i++) {
  34.             fileContent += in.readInt();
  35.             fileContent += in.readDouble();
  36.             fileContent += in.readUTF();
  37.         }
  38.         System.out.println(DataInputStreamExam.FileName + "의 내용 : " + fileContent);
  39.     }
  40.     public void fileRead(DataInputStreamExam obj) throws IOException{
  41.        
  42.     }
  43.     public void fileWrite(DataInputStreamExam obj) throws IOException {
  44.        
  45.     }
  46.    
  47. }
  48. class DataInputStreamExamInfo extends DataInputStreamExam{
  49.    
  50.     public DataInputStreamExamInfo(String FileName) {
  51.         DataInputStreamExam.FileName = FileName;
  52.        
  53.     }
  54.     @Override
  55.     public void fileRead(DataInputStreamExam obj) throws IOException{
  56.         obj.dataRead(new DataInputStream(new BufferedInputStream(new FileInputStream(DataInputStreamExam.FileName))));
  57.     }
  58.     @Override
  59.     public void fileWrite(DataInputStreamExam obj) throws IOException{
  60.         obj.dataWrite(new DataOutputStream(new FileOutputStream(DataInputStreamExam.FileName)));
  61.     }
  62.    
  63. }
  64.  
  65. public class Exam{
  66.     public static void main(String[] args) throws IOException {
  67.         DataInputStreamExam in = new DataInputStreamExamInfo("dataStream.txt");
  68.         in.fileWrite(in);
  69.         in.fileRead(in);
  70.     }
  71. }

▶실행결과
dataStream.txt의 내용 : 11.0one하나22.0two둘33.0three셋



이로써 자바의 기본형으로 바이트로 읽기가 쓰기작업 없이 자동으로 데이터형을 맞춰주어서 편리하게 사용가능했다. 파일의 내용을 한번 살펴볼까?


▶실행파일결과











왜 파일이깨지는가.. 잘못된거같다.. 라고 생각할 수 있으나 당연한것이다.
텍스트의 내용을 바이트단위로 처리해줄때 어떠한 인코딩 형태로 해주었는지에 따라 값이 다르게 나오고 또한 텍스트 파일의 크기도 달라지게 되있다.

그래서 다음시간에는 인코딩형태에따라 파일크기 및 데이터들이 어떻게 저장되는지 살펴보겠다. OutputWriter / InputReader에 대해서 살펴보도록하자.




[Java] N-I/O(Non-Blocking) 파일 읽기 쓰기 - GatheringByteChannel, ScatteringByteChannel, ByteBuffer 사용.

우리는 지금까지 다음과 같이 살펴보았다. 1.  InputStream / OutputStream : 입, 출력 스트림을 바이트로 처리하여 읽기, 쓰기. 2.  FileInputStream / FileOutputStream : 입, 출력 스트림을 ...