포인터의 이해

언어로그/C/C++ 2011. 3. 11. 00:58





1. 포인터(pointer)
단어의 의미와 같이 특정 메모리를 가리킬 수 있는(참조) 변수를 의미한다. 
포인터는 변수의 주소를 저장할 수 있으며, 32비트 시스템에서 통상 4바이트를 갖는다.




2. 포인터를 사용하는 이유는?
포인터를 사용하면,  빠르고 간결한 프로그램을 작성할 수 있기 때문이다. 포인터를 제대로 사용하지
못하면 난해한 프로그램이 될 수 있지만, 잘 알고 사용하면, 유연하면서도 , 빠르고 간결한 프로그램을 작성할 수 있다.



3. 포인터의 특성
포인터를 사용하여 일반변수와 함수를 다룰 수 있다.  포인터로 변수에 접근하기 위해서는 포인터가 다룰 메모리의 크기를
알아야 의미있게 메모리를 해석할 수 있다. 그렇게 때문에 포인터 변수 선언시 어떻게 메모리를 해석할 것인지 알려주어야
한다.  (포인터 타입은 포인터가 가리키는 대상체 메모리의 단위크기이다.)

char    *cp;
int    *ip;
float    *fp;
double    *dp;

위와 같은 선언은  포인터가 가리키고 있는 메모리를 몇 바이트씩 읽되, 어떻게 데이터를 읽어내겠다는 것을 의미한다.

int a;
int *ip1, *ip2;
ip1 = &a;
ip2 = ip1;

포인터도 변수이기 때문에 포인터 변수에 다른 변수에 주소값을 갖도록 변경할 수 있으며, 또한 변수에 대해 동일한 주소를 
가리키는 여러 포인터를 유지 할 수도 있다.





4. 포인터 연산
포인터 간의 곱셉과 나눗셈은 허용되지 않는다. (컴파일 에러 발생)
포인터 간에 덧셈은 허용하지 않지만, 포인터와 상수의 덧셈은 허용된다..
포인터 간에 뺄셈과 포인터와 상수간에 뺄셈도 허용된다. 

ip1 * ip2// 컴파일 에러     
ip1 / ip2// 컴파일 에러   
ip1 + ip2// 컴파일 에러   

위와 같은 경우는 컴파일 에러가 발생한다.  포인터가 갖을 수 있는 일정 주소범위를 벗어날 수 있기때문에 에러로 보호된다.

ip1 + 1//  4바이트(int) 뒤로 이동 
ip1 - ip2          //  ip1이 1004, ip2가 1008이면, 1이 반환됨 ( (1008-1004)/4 )       
ip2 - 1    //  4바이트(int) 앞으로 이동

포인터 연산을 위해 사용된다. 포인터에 1을 더하는 것은 포인터가 메모리 블럭을 한칸 건너뛰는 작용을,  
1을 빼는 것은 포인터가 이전 메모리 블럭으로 건너뛰는 작용을 한다.  포인터에서 포인터를 빼는 것은 
메모리의 차를 구하는 연산으로,  주소값에 차를 포인터가 가리키는 타입의 바이트수로 나눗값이 계산된다. 
즉 메모리블럭의 차가 반환된다.

 ※ %I64d 변환코드 : double을 첫번째 비트를 부호비트, 나머지 63비트를 데이터 비트로 해서 정수로 해석한다.
                                      큰 단위의 정수를 다룰 때 사용한다.  




5. 증감연산자와 역참조 연산자의 결합 
*pa++;// *pa;pa = pa + 1;              
(*pa)++; // *pa;  *pa = *pa + 1;        
++*p;// *pa = *pa + 1;*pa;     
*++p;// pa = pa + 1; *pa;      

증감연산자와 역참조 연산자를 위와 같이 조합하여 다양하게 사용할 수 있지만, 실행되는 각 의미는 매우 다르다.
역참조 연산자 *와 증감연산자++는 그 우선순위가 같지만, 좌측결합성을  갖기때문에 오른쪽에서 왼쪽으로 평가된다.
(오른쪽에 있는 연산자를 먼저 적용한다.)





6. void 형 포인터
어떤 타입의 형도 가리킬 수 있는 포인터. 즉 대상체 크기가 정해져 있지 않은 포인터이다.
형변환 없이도 void형 포인터변수에 다른 타입의 포인터를 대입할 수 있다. 하지만 void형 포인터에서
값을 읽을 때는  몇바이트를 읽고, 어떻게 데이터를 해석해야 하는지를 알아야 하기 때문에 캐스트 연산자를
사용해야 한다.  void형 포인터를 사용하면, 각 타입을 위한 포인터 변수를 만들어야 하는 번거로움을 줄여 사용변수에 
개수를 줄일 수 있고, 함수의 인자로 사용하여 유연한 프로그램을 작성할 수 있다.  하지만 잘못 사용하면 가독성이 
떨어지고, 난해한 프로그램이 될 수 있다. 


'언어로그 > C/C++' 카테고리의 다른 글

전역변수와 지역변수  (0) 2011.03.27
함수의 이해  (0) 2011.03.27
포인터의 이해  (0) 2011.03.11
매크로, 연산자  (3) 2011.03.10
표준입출력함수 getchar() / putchar()  (0) 2011.02.22
비표준 입출력함수 getch(), getche()  (0) 2011.02.22

HMAC-SHA1

알고리즘.데이터구조 2011. 3. 11. 00:36


 

 

HMAC

송신자와 수신자가 비밀 키를 공유할 경우 서로 주고받은 메시지의 훼손여부를 확인하는데 사용하는 메시지 인증방식이다.

송신자가 원데이터의 해시값을 계산하여 원데이터와 해시값 모두 단일 메시지로 보내면, 수신자가 받은 메시지에 대해 

해시값을 다시 계산하여 계산된 HMAC과  전송된  HMAC의 일치여부를 확인한다. 


 

SHA-1(Secure Hash Algorithm)

미국 정부에서 공개한 암호화 해시 알고리즘으로, 임의 길이의 문자열에서 160비트 해시값을 생성을 생성한다. 

 SHS 및 Secure Hash Standard라고도 한다.



HMAC-SHA1

SHA1 해시함수를 사용하여,  HMAC 을 구현하는 키 지정 해시 알고리즘이다. 다음과 같은 HMAC 프로세스를 따른다.  

1) 비밀 키를 메시지 데이터와 혼합한다

2) 해시 함수를 통해 결과를 해시한다

3) 해시값을 비밀키와 다시 혼합한다 

4) 그런 다음 해시함수를 다시 적용한다 




'알고리즘.데이터구조' 카테고리의 다른 글

원 위에 나열된 수 제거하기  (0) 2011.03.23
합병정렬(Merge Sort)  (0) 2011.03.23
HMAC-SHA1  (0) 2011.03.11
문제5 그래픽편집기  (0) 2011.02.18
문제4 LCD디스플레이  (0) 2011.02.18
문제3 여행  (0) 2011.02.18

매크로, 연산자

언어로그/C/C++ 2011. 3. 10. 01:22




사칙연산자

C언어 설계의 특징 중 하나로 친숙성을 들 수 있는데, 그 예로 일상생활에서 사용하는 연산자를

그대로 언어에 채용하였다. 

사칙연산자 +, - , *, /  는 2개의 피연산자가 정수이면 결과는 정수로, 하나라도 실수라면 실수를 반환한다.

12 + 14 = 26 (정수)

12.0 + 14 = 26.0 (실수)

12 + 14.0 = 26.0 (실수)

12.0 + 14.0 = 26.0 (실수)



나머지 연산자

% 는 나머지(modular) 연산자로 2개의 피연산자가 반드시 정수이여야 하며, 실수 사용시 컴파일 에러가 발생한다.

 30.0 % 10 =>  컴파일 에러




증감연산자 (++, --)

증감연산자는 a++ , a--는 변수를 1씩 증가 또는 감소시키는 연산자로 각각 a = a+1 , a = a-1 과 같은 의미를 갖는다. 

++a  는 앞에 연산자가 붙어 전위 연산자라 부르고, a++ 는 뒤에 연산자가 붙어 후위 연산자라고 부른다.

전위 연산자는 해당하는 문장이 평가되기 전에 작용하고,  a++ 는 문장이 평가된 후 작용하는 차이가 있다.


while문과 if문의 비교문에서 후위 연산자가 사용되었을 경우, 조건문 괄호가 평가 된 후 바로 변수가 증가한다 것을 주의하자.

while (a++ > 0) { 
    printf("%d \n", a); 
}

if (a++ > 0) { 
    printf("%d \n, a"); 
}



타입캐스팅

데이터 타입은 (표현범위가) 작은 데이터 타입에서 큰 타입으로는 데이터 손실이 없기 때문에 자동 형변환이 이루어진다.

 하지만 그 반대의 경우는 큰 데이터를 작은 데이터타입에서는 표현할 수 없기 때문에 데이터 손실이 발생하기 때문에 

컴파일러 경고가 발생하는데 사용자가 수동으로 형변환을 하여 경고를 없앨 수 있다.

(프로그래머가 데이터 손실을 고려했다는 것을 컴파일러에게 알리는 것임)


char ch = 'a';
int i = 1;
float f = 1.0;
double d = 1.0;

d = ch * i * f + 3000;  // 자동 형변환이 일어남.



매크로 상수, 기호상수

# 기호는 전처리 지시어라고 하며, #이 붙은 키워드는 컴파일러가 번역을 수행하기 전에  전처리기가 소스코드를 변경하는 

작업을 수행하게 하는 명령이다.

#define BASE 700 // 매크로상수 선언 

이 문장은 매크로 상수 라 하며 소스코드에  사용된 모든 BASE 기호는 컴파일전에 모두 700으로 대치가 된다.
프로그램에서 추후 변경될 수도 있는 간단한 수치를  매크로 상수로 선언해두면 나중에 매크로 상수 하나의 변경만으로

해당 데이터가 사용된 소스코드를 모두 변경할 수 있게 하는 장점이 있다. 


매크로 상수 외에 기호상수를 사용할 수도 있다.  변수 앞에 const  키워드를 붙히면 해당 변수를 기호상수로 만들 수 있다.
기호 상수는 최초 초기화 시에만 값을 할당할 수 있으며,  이후 변경할 수 없는 변수이다.


const int BASE = 700;// 기호상수 선언



논리 연산자

AND 연산자 && 는 2개의 피연산자가 참일 경우에만 참을 반환하는 연산자이며,

OR 연산자 || 는 2개의 피연산자 중 적어도 하나가 참이면 참을 반환하는 연산자이다.

(변수가 0이 아닌 값은 참으로, 0이면 거짓으로 평가된다.)


논리연산자에서 다음과 같은 특성을 유추할 수 있다.

0 && a     //  a의 값에 상관없이 무조건 거짓(0)으로 평가되어, a값을 보지않음

1   ||  a     //  a의 값에 상관없이 무조건 참(1)으로 평가된어, a값을 보지않음



관계연산자

2개의 피연산자 비교하는 연산자로 다음과 같은 연산자가 있다.

<  : less than (lt)   

<= : less than or equal (le)   

>  : greater than (gt)   

>= : greater than or equal (ge)      

== : equal   (eq) 

!= : not equal (ne)




연산자 우선순위와 결합성

컴파일러는 모든 연산자에 부여된 우선순위에 따라 연산자를 순차적으로 처리한다.

연산자의 우선순위를 모두 암기할 필요는 없다. 연산자들이 평가되는 순서를 보장하기 위해서

가장 우선순위가 높은 () 연산자를 명시적으로 사용하면 좋다

동일한 우선순위의 연산자가 사용되었을 경우, 연산자가 갖는 결합성 특성에 따라 연산자가

처리된다. 오른쪽 결합성은 왼쪽에서 오른쪽 방향으로 처리되는 성질을, 왼쪽 결합성은 오른쪽에서

왼쪽으로 처리되는 특성을 갖으며,  = 할당연산자는 왼쪽 결합성을 갖는다.


int a = 10, b = 20, c = 30;
res = a += b -= c += 5;    
printf("a : %d, b : %d, c : %d\n",  a, b, c);// a : -5, b : -15, c : 35 가 출력됨

'언어로그 > C/C++' 카테고리의 다른 글

함수의 이해  (0) 2011.03.27
포인터의 이해  (0) 2011.03.11
매크로, 연산자  (3) 2011.03.10
표준입출력함수 getchar() / putchar()  (0) 2011.02.22
비표준 입출력함수 getch(), getche()  (0) 2011.02.22
표준입출력함수 printf() / scanf()  (0) 2011.02.22
  • 서정권 2011.03.10 08:52 ADDR 수정/삭제 답글

    성배 공부 진짜 열심히 한다ㅋ

  • 로그 @로그 2011.03.10 17:57 신고 ADDR 수정/삭제 답글

    열심히 하기는 ㅎㅎ. 예전에 작성한 것들 조금씩 이곳으로 옮기고 있는거야~ ㅎ

  • 서정권 2011.03.11 11:50 ADDR 수정/삭제 답글

    성배랑 놀고 싶구나

[자바] (1) 자바소개

언어로그/Java 2011. 3. 10. 00:49


나름 잘 보이게 한다고, 노란박스를 사용했는데...굉장히 눈을 자극하네요...otz...   


자바(Java)란?

자바언어의 개발사인 썬은 spac이란 서버제품군을 제작하는 회사였는데, 낮은 H/W 마진을 개선하고자, 자사의 H/W 위에 마진이 높은 SW(유닉스 등)를 얹혀 팔려했는데, H/W 마다 SW를 재작성해야하는 문제가 있었습니다.  이에 해결책으로 , 즉 한번 작성하면  다양한 머신 위에서  동일하게 작동할 수 있는 SW를 작성할 수 있는  java를 개발하게 된겁니다.


자바의 장정

객체지향적이고, 플래폼 독립적이고, 동적이고 배포하기 쉬우며, 견고하고 보안에 강하다고 합니다.

엄밀히 말하면 객체지향적이다는 특성은 전적으로 S/W를 작성하는 개발자에게 달려있습니다.

 

                                       

플랫폼 독립성

c언어와 같이 컴파일러가 머신에 의존적인 기계어 코드(실행파일)를  생성하면, 유닉스, 리눅스, 윈도우, 맥 등 각 머신에서 소스를  재컴파일 해야합니다. (각 머신에 맡는 바이너리를 생성해야함).  반면 자바는 OS  와 소스 사이에 Java Virtual Machine이라는 중간계층을 두어 이를 해결했습니다. 소스코드를 가상머신이 인식할 수 있는 중간언어인 바이트 코드로 컴파일해서 배포하는 겁니다. 그러면 각 OS에 설치되있는, JVM이 바이트코드를 머신 의존적인 기계어 코드로 번역하여 실행하는 겁니다. 

 (단,  OS마다 각 머신에 맞는 JVM을 설치해야하지만, 한번만 설치하면 이후에는 소스를 재컴파일 해야하는 번거로움이 사라집니다. 바이너리 레벨에서의 플랫폼 독립성을 확보하게 됩니다. )  



기업 인터넷 환경에서 자바

유연성을 위해  3 - tier ( 프레젠테이션 계층 - 비지니스 계층 - 데이터(DB) 계층 ) 의 형태로 구성된답니다.

현재는 더욱 확장된 N - tier로  됩니다.  

          


JVM(Java Virtual Machine)의 동작방식

클래스 로더는 먼저 소스코드를 보고 모든 필요한 클래스를 로드합니다. 필요한 클래스 위치는 CLASSPATH 환경변수를 참조하여 해당하는 위치에서 찾게됩니다. JVM Verifier는 오작동을  일으킬 수 있는 코드(무한루프)를 검사해 실행을 차단하고, 이어 바이트 코드를 실행합니다. 또한 Just-In-Time 컴파일러를 호출하여, 소스코드와 클래스 파일을 비교합니다. 클래스 파일이 생성되지 않았거나, 소스코드가 클래스파일보다 나중에 수정됐을 때만 재컴파일을  합니다.

(JIT 를 도입해서 자바의 느린성능을 어느 정도 극복했고, 버전업 될때마다, 개선을 통해 성능을 향상시키고 있습니다.)

가비지 컬레터라는 메모리 관리자는  더 이상 사용되지 않는 객체를 수시로 검사하여 자동으로 메모리를 반환해줍니다.



자바환경에서의 보안

언어차원 - 컴파일러 - 클래스 로더 - bytecode verifyer 등 계층적으로 위해한 코드를 차단해주어 안정성을 제공합니다.

자바애플리케이션은  - .zip 또는 .jar로  형태로 압축하여 배포합니다. .jar은 단순히 소스만 압축 할 수도 있고,  클래스파일

까지 포함하여 같이 압축해서 실행가능한 형태로 만들수 있습니다. 웹 기반 애플리케이션은  .WAR 형태로 압축해서 배포

합니다.



JDK(Java Development Kit)

보통 J2SE(Java2 Standard Edition)를 의미하며, 자바를 기반으로 SW를 개발할 수 있는 환경을 의미합니다.  

컴파일러(javac),  core class 라이브러리, 디버거(jdb), JVM, JAR(Java ARchive utility), 기타 등으로 구성됩니다.

Java1.2 버전 이후 부터 대폭 내부구조가 개선되었으며, 이런 변경을 표현하기 위해 1.2 버전 이후부터는 JAVA2라고 

부릅니다. 개발 아키텍처에 따라 J2SE, J2ME, J2EE 로 나뉘기도 합니다.  J2ME는 모발일 환경  SW 작성을 위해 꼭 필요한 

핵심 클래스만으로 이루어진 버전입니다. J2EE는 기업환경에 적합하게 병렬실행 등을 강화한 버전입니다. 


'언어로그 > Java' 카테고리의 다른 글

(2) 리플렉션(Reflection) 사용하기  (0) 2011.04.07
(1) 리플렉션(Reflection)  (3) 2011.04.07
예외처리 (Exception Handling)  (0) 2011.03.24
[Java] 어노테이션 사용하기  (1) 2011.03.19
[Java] 어노테이션(Annotation)  (2) 2011.03.15
[자바] (1) 자바소개  (0) 2011.03.10

리눅스 파일시스템 계층 표준(Filesystem Hierarchy Standard)

운영체제로그/Linux 2011. 3. 9. 02:27


unix_file_structure.gif

 

다음은 Filesystem Hierarchy Standard 2.3 내용을  간략히 요약한 내용이다. 

 

Bin 

시스템 관리자와 사용자가 동시에 사용하는 명령을 포함하고 있으며, 이 명령들은 다른 파일시스템이 마운팅 될 필요가 없는 명령이다. (싱글 유저 모드)

스크립트에서 간접적으로 사용되는 명령들도 포함하고 있다.  /bin 디렉토리에는 서브디렉토리가 없어여 한다. 

다음은 bin 디렉토리에서 볼 수 있는 몇 가지 명령들이다.  

cat, chmod, chirp, cp, date, echo, kill, mv, ls


/boot (부트로더의 정적파일)

이 디렉토리는 부팅시 필요한 초기화 파일을 포함하여 부팅과정에서 필요한 모든 것을 포함하고 있다. 

커널이 유저모드 프로그램 실행을 시작 시키기 전에 사용되는 데이터를 저장하고 있다. . 저장된 마스터 부트 섹터와 섹터 맵파일을 포함하고 있다 

운영체제 커널은 / (루트) 또는 /boot 디렉토리에 위치해야한다.


/dev (디바이스 파일)

디바이스 파일들이 있는 디렉토리이다.  /dev 에 디바이스들이 수동으로 생성될 필요가 있다면 , MAKEDEV 라는 이름의 

명령을 포함해야한다.  

로컬 디바이스를 위해 MAKEDEV.local 을 포함할수도 있다. 



/etc (호스트에 있는 특정시스템에 대한 구성파일)

configuration file을 포함한다. 구성파일이란 프로그램에 동작방식을 제어하는 로컬파일을 의미한다. 정적파일이어야 하며, 실행가능한 바이너리여서는 안된다

서브디렉토리 opt는 :/opt에 대한 설정을,  X11 는  X Window 시스템에 대한 설정을,  sgml 은  SGML 에 대한 설정을,  xml은  XML에 대한 설정파일을 포함한다.


/home(사용자 홈디레토리)

사이트에 대한 파일시스템으로,  호스트 마다 설정이 다를 수 있기 때문에 프로그램들은 이 위치에 의존해서는 안된다. 

사용자 개인의 애플리케이션 설정파일은 .(dot)으로 시작하는 이름으로 홈디렉토리에 저장된다. 애플리케이션이 dot 파일을 하나이상 생성해야한다면 dot 디렉토리를

생성하고, 거기에 .dot으로 시작하지 않는 이름으로 설정파일들을 저장해야한다.


/lib  (필수적인 공유 라이브러리와 커널 모듈)

시스템을 부팅시키거나 루트 파일시스템에서 명령을 실행하는데 필요한 공유라이브러리 이미지가 존재한다. 

예를 들어 /bin이나 /sbin에서…필요한…

다음과 같은 이름패턴을 가지고 있어야한다. 

libc.so.* 는  동적으로 링크된 c 라이브러리이며, ld* 는 실행타임 링커/로더를 나타낸다. 

 

/media (제거가능한 미디어에 다한 마운트 위치)
플로피 디스크나 cdrom, zip 디스크와 같은 제거 가능한 미디어를 위한 마운트 포인트로 사용되는 서브디렉토리를 포함한다 

/mnt (일시적으로 마운트되는 파일시스템을 위한 마운트포인트)

시스템 관리자가 필요할 때마다 파일시스템을 일시적으로 마운트하기 위해 존재한다. 설치 프로그램에 의해 이 디렉토리가 사용되어서는 안된다. 


/opt (애드온 애플리케이션 소프트웨어 패키지)

애드온 애플리케이션 소프트웨어 패키지 설치를 위해 제공되는 디렉토리이다. . /opt에 설치된 패키지는  /opt/<package>, /opt/<provider> 에서 정적파일을 찾을수 있어야한다 <package> 는 소프트웨어 패키지를 기술하는 이름이며,  <provider> 는

제공자의 LANANA 등록된 이름이다.


 /root (루트유저 홈데릭토리)

루트 계정의 홈디렉토리는 개발자에 의해 결정될 수 있지만,  root 라는 디렉토리 명을 사용이 권장된다.  


/sbin (시스템 바이너리)

시스템 관리자에 의해 사용되는 유틸리티들이  /sbin,  /usr/sbin,   /usr/local/sbin 에 저장된다. /sbin은  시스템의 부팅,  복원, 복구, 수리를 위한 필수 바이너리를 포함한다.  


/tmp (임시파일)

임시파일을 요구하는 프로그램이 사용하기 제공되는 디렉토리이다 . /tmp에 있는 어떤 파일과 디렉토리도 보존되지 않는다는 것을 주의해야한다.  


'운영체제로그 > Linux' 카테고리의 다른 글

Wget  (0) 2011.04.25
리눅스 자격증  (0) 2011.04.09
리눅스 파일시스템 계층 표준(Filesystem Hierarchy Standard)  (0) 2011.03.09
su, sudo : 특정 사용자 권한으로 쉘/명령 실행  (0) 2011.03.08
cURL  (0) 2011.02.18
grub에서 디폴트 부팅 os 변경하기  (0) 2011.02.16

[FTP] (2) 클라이언트 구현하기

네트워크로그 2011. 3. 9. 02:22



다음은 c언어로 구현한  open, get, put, ls, cd, pwd, quit/bye, hash 명령을  구현한 간단한 FTP 클라이언트 예제이다. 

에러처리와 소켓의 recv, send 함수의 동작방식을 정확히 고려하지 않았기 때문에, 정확히 동작하지 않을 수 있다.   

데이터 수신시 하나의 명령에 대한 메시지의 끝을 구분해 줄 수 있는 방법이 추가로 필요하다. 

(메시지 끝을 의미하는 \r\n 로 구분)




FtpCommand.h

 : Ftp 커맨드 및 상수를  정의한 헤더파일 

#define CMD_OPEN "open"
#define CMD_LIST "ls"
#define CMD_GET "get"
#define CMD_PUT "put"
#define CMD_PWD "pwd"
#define CMD_CD "cd"
#define CMD_QUIT "quit"
#define CMD_BYE "bye"
#define CMD_HASH "hash"
#define CMD_SHELL "!"

#define MODE_DEBUG 1
#define MODE_NORMAL 0

#define FTP_PORT 21

extern int mode;

void debug(char *msg) {
        if (mode == MODE_DEBUG) {
                printf("[debug] : %s \n", msg);
        }
}



ClientSocket.h

: Ftp Client 구현 파일로 단일 프로세스만을 사용하였음 

#include <sys/types.h>
#include <sys/socket.h> 
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h>
#include <fcntl.h>

#define TEMP_BUFFER_SIZE 1024

int connectServer(char *serverIp, short port) {
        int sock;
        struct sockaddr_in servAddr;
        
        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
                perror("sock failed");
                exit(1);
        }
        
        memset(&servAddr, 0, sizeof(servAddr));
        servAddr.sin_family = AF_INET;
        servAddr.sin_addr.s_addr = inet_addr(serverIp);
        servAddr.sin_port = htons(port);
        
        if (connect(sock, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
                perror("connect failed");
                exit(1);
        }
        
        return sock;
}


void sendProtocol(int sock, char *protocol) {
        if (send(sock, protocol, strlen(protocol), 0) != strlen(protocol)) {
                perror("send failed");
                exit(1);
        }
	if (MODE_DEBUG == mode) {
		printf("send: %s", protocol);
	}
}

void recvProtocol(int sock, char *recvBuffer, int bufferSize) {
        int recvLen;
        
        if ((recvLen = recv(sock, recvBuffer, bufferSize-1, 0)) <= 0) {
                perror("recv failed");
                exit(1);
        }
        recvBuffer[recvLen] = '\0';

	if (MODE_DEBUG == mode) {
		printf("recv: %s", recvBuffer);
	}
}


unsigned int downloadFile(int sock, char *filePath, unsigned int fileSize, int hashFlag) {
        char readBuffer[TEMP_BUFFER_SIZE];
        unsigned int readBytes, totalBytes, numHash;
        
        int fd = open(filePath, O_WRONLY | O_CREAT, 0744);
        
        totalBytes = numHash = 0;
        while (totalBytes < fileSize) {
                if ((readBytes = read(sock, readBuffer, TEMP_BUFFER_SIZE)) <= 0) {
                        close(fd);
                        return totalBytes;
                }
                write(fd, readBuffer, readBytes);
                totalBytes += readBytes;
                
                if (hashFlag) {
                        if ((totalBytes/TEMP_BUFFER_SIZE) > numHash) {
                                numHash++;
                                printf("#");
                        }
                }
        }
        close(fd);
        printf("\n");
        
        return totalBytes;
}

unsigned int uploadFile(int sock, char *filePath, int hashFlag) {
        char readBuffer[TEMP_BUFFER_SIZE];
        unsigned int readBytes, totalBytes, numHash;
        
        int fd = open(filePath, O_RDONLY);
        
        totalBytes = numHash = 0;
        while ((readBytes = read(fd, readBuffer, TEMP_BUFFER_SIZE)) > 0) {
                write(sock, readBuffer, readBytes);
                totalBytes += readBytes;
                
                if (hashFlag) {
                        if ((totalBytes/TEMP_BUFFER_SIZE) > numHash) {
                                numHash++;
                                printf("#");
                        }
                }
        }
        close(fd);
        printf("\n");
        
        return totalBytes;
}



FtpClient.c

:  Ftp Client 구현 파일로 단일 프로세스만을 사용하였음

#include <stdio.h>
#include <string.h> 
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include "FtpCommand.h"
#include "ClientSocket.h"

#define COMMAND_MAX_SIZE 1024
#define BUFFER_SIZE 1024
#define FILENAME_SIZE 256
#define END_OF_PROTOCOL "\r\n"

void initializeFtpClient();
void startFtpClient(char *ip, char *port);
void commandHandle(char *cmd); 
void defaultHandler(char *cmd);
int modeCheck(const char *option);
void printMessage(char *msg);

void openCon(char *cmd);
void list(char *listCmd);
void get(char *getCmd);
void put(char *putCmd);
void cd(char *cdCmd);
void quit(char *quitCmd);
void pwd(char *pwdCmd);
void bye(char *byeCmd);
void hash(char *hashCmd);
void passiveMode(char *ip, int *port);
void shellEscape(char *shellCmd);

unsigned int downloadFile(int sock, char *filePath, unsigned int fileSize, int hashFlag);
unsigned int uploadFile(int sock, char *filePath, int hashFlag);


typedef struct _FtpCmdHandler {
        char cmd[5];
        void (*handler)(char* arg);
        
} FtpCmdHandler;

// Map Ftp Command to Handler
FtpCmdHandler ftpCmdHandler[] = {
        { CMD_OPEN, openCon },
        { CMD_LIST, list },
        { CMD_PUT, put },
        { CMD_GET, get },
        { CMD_CD, cd },
        { CMD_PWD, pwd },
        { CMD_HASH, hash },
        { CMD_QUIT, quit },
        { CMD_BYE, bye },
        { CMD_SHELL, shellEscape },
        
};

// sock - PI socket ,  dtpSock - DTP socket
int sock, dtpSock;
int state;      
int mode;
int hashFlag;


int main (int argc, char const *argv[])
{
 	if (argc == 3) {
	// argv[1] == ip, argv[2] == port
		startFtpClient(argv[1], argv[2]);
	} else if (argc == 4 && modeCheck(argv[1]) == MODE_DEBUG) {
	// argv[1] == -d, argv[2] = ip, argv[3] == port
		startFtpClient(argv[2], argv[3]);
	}  else {
		fprintf(stderr, "Usage: %s [-d]  \n", argv[0]);
	}
                
        return 0;
}

int modeCheck(const char *option) {
        if (!strcmp(option, "-d")) {
                // debug mode
                mode = MODE_DEBUG;
        } else {
                mode = MODE_NORMAL;
        }
        return mode;
}

// initialize ftp client
void initializeFtpClient() {
        hashFlag = 1;
        state = INITIAL_STATE;  
        debug("initialized");
}



// ftp client start 
void startFtpClient(char *ip, char *port) {   
        char cmd[COMMAND_MAX_SIZE];

        initializeFtpClient();
        
        while (1) { 
                // input user command
                if (ip == 0 && port == 0) {
                        printMessage("ftp>");
                        fgets(cmd, COMMAND_MAX_SIZE, stdin);
                } else {
                        sprintf(cmd, "open %s %s", ip, port);
                        startCmd = 0;
                }       
                // call handler 
                commandHandle(cmd);
        }

}

// map command to handler 
void commandHandle(char *cmd) {
        int i;
        int numCmd = sizeof(ftpCmdHandler)/sizeof(FtpCmdHandler);
        
        for (i = 0; i < numCmd; i++) {
                if (!strncmp(cmd, ftpCmdHandler[i].cmd, strlen(ftpCmdHandler[i].cmd))) {
                        (*(ftpCmdHandler[i].handler))(cmd);
                        break;
                }
        }       
}


void defaultHandler(char *cmd) {
        printf("default handler: %s\n", cmd);
}

// ftp server connect 
void openCon(char *openCmd) {
        char serverIp[16], serverPort[16];
        char cmd[BUFFER_SIZE];
        char sendBuffer[BUFFER_SIZE];
        char recvBuffer[BUFFER_SIZE];
        
        sscanf(openCmd,"%*s %s %s%*c", serverIp, serverPort);
        debug(serverIp);
        
        // connect to server
        sock = connectServer(serverIp, atoi(serverPort));
        recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
        
        // send user name
        printf("Name: ");
        fgets(cmd, COMMAND_MAX_SIZE, stdin);
        sprintf(sendBuffer, "User %s", cmd);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
        printMessage(recvBuffer);

        // send password
        printf("Password: ");
        fgets(cmd, COMMAND_MAX_SIZE, stdin);
        sprintf(sendBuffer, "PASS %s", cmd);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
        printMessage(recvBuffer);
                
        // get server os information    
        sprintf(sendBuffer, "SYST%s", END_OF_PROTOCOL);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
        printMessage(recvBuffer);

}


// send EPSV or PASS to Server
void passiveMode(char *ip, int *port) {
        char sendBuffer[BUFFER_SIZE];
        char recvBuffer[BUFFER_SIZE];
        int host0, host1, host2, host3;
        int port0, port1;
        
        sprintf(sendBuffer, "PASV%s", END_OF_PROTOCOL);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
        printMessage(recvBuffer);
        
        sscanf(strchr(recvBuffer, '(')+1, "%d,%d,%d,%d,%d,%d", &host0, &host1, &host2, &host3, &port0, &port1);
        sprintf(ip, "%d.%d.%d.%d", host0, host1, host2, host3);

        *port = port0*256 + port1;
        
        debug(ip);
        printf("dtp port : %d\n", *port);
}


// get remote working directory file list
void list(char *listCmd) {
        int port;
        char ip[16];
        char sendBuffer[BUFFER_SIZE];
        char recvBuffer[BUFFER_SIZE*8];
        
        debug("list");

        // recv server response and parsing 
        passiveMode(ip, &port);
        
        // connect to DTP  
        dtpSock = connectServer(ip, port);
        
        // send LIST command to PI server
        sprintf(sendBuffer, "LIST%s", END_OF_PROTOCOL);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
        
        // recv file list from DTP
        recvProtocol(dtpSock, recvBuffer, BUFFER_SIZE*8);
        printMessage(recvBuffer);
        
        // recv complete message from PI server
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
        
        close(dtpSock);
}


// file download
void get(char *getCmd) {
        int port;
        unsigned int fileSize;
        char ip[16], filePath[FILENAME_SIZE], fileName[50];
        char sendBuffer[BUFFER_SIZE];
        char recvBuffer[BUFFER_SIZE];
        
        // get local current working directory 
        getcwd(filePath, FILENAME_SIZE);
        sscanf(getCmd, "%*s %s%*c", fileName);
        sprintf(filePath, "%s/%s", filePath, fileName);

        debug("get");
        printf("fileName: %s\n", fileName);
        printf("filePath: %s\n", filePath);

        passiveMode(ip, &port);
        
        // connect to DTP
        dtpSock = connectServer(ip, port);
        
        // request server for transfer start - RETR fileName
        sprintf(sendBuffer, "RETR %s%s", fileName, END_OF_PROTOCOL);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
        
        // extract fileSize 
        sscanf(strchr(recvBuffer, '(')+1, "%u", &fileSize);
        printf("fileSize: %u\n", fileSize);
        
        // download file from DTP 
        downloadFile(dtpSock, filePath, fileSize, hashFlag);
        
        // recv complete message from PI server
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
        
        close(dtpSock);
}




// file upload
void put(char *putCmd) {
        int port;
        unsigned int fileSize;
        char ip[16], filePath[FILENAME_SIZE], fileName[50];
        char sendBuffer[BUFFER_SIZE];
        char recvBuffer[BUFFER_SIZE];
        
        sscanf(putCmd, "%*s %s%*c", fileName);
        
        // get local current working directory
        getcwd(filePath, FILENAME_SIZE);
        sscanf(putCmd, "%*s %s%*c", fileName);
        sprintf(filePath, "%s/%s", filePath, fileName);

        debug("put");
        debug(filePath);
        
        passiveMode(ip, &port);
        
        // connect to DTP
        dtpSock = connectServer(ip, port);
        
        // request server for transfer start - STOR fileName
        sprintf(sendBuffer, "STOR %s%s", fileName, END_OF_PROTOCOL);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
        
        // file upload to DTP 
        fileSize = uploadFile(dtpSock, filePath, hashFlag);
        
        close(dtpSock);
        
        /// recv complete message from PI server
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
}


// change remote working directory
void cd(char *cdCmd) {
        char sendBuffer[BUFFER_SIZE];
        char recvBuffer[BUFFER_SIZE];
        debug("cd");

        sscanf(cdCmd, "%*s %s%*c", recvBuffer);
        debug(recvBuffer);

        sprintf(sendBuffer, "CWD %s%s", recvBuffer, END_OF_PROTOCOL);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
}

// ftp client exit 
void quit(char *quitCmd) {
        char sendBuffer[BUFFER_SIZE];
        char recvBuffer[BUFFER_SIZE];
        debug("quit");
        
        sprintf(sendBuffer, "QUIT%s", END_OF_PROTOCOL);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
        
        close(sock);
        exit(0);
}

// same quit  
void bye(char *byeCmd) {
        quit(0);
}

// get remote working directory
void pwd(char *pwdCmd) {
        char sendBuffer[BUFFER_SIZE];
        char recvBuffer[BUFFER_SIZE];
        debug("pwd");
        
        sprintf(sendBuffer, "PWD%s", END_OF_PROTOCOL);
        sendProtocol(sock, sendBuffer);
        recvProtocol(sock, recvBuffer, BUFFER_SIZE);
        printMessage(recvBuffer);
}


// hash option on/off
void hash(char *hashCmd) {
        debug("hash");
        hashFlag = !hashFlag;
        
        if (hashFlag == 0) {
                printMessage("hash off");
        } else {
                printMessage("hash on");
        }
}

// shell command - not implemented
void shellEscape(char *shellCmd) {
        printMessage("not implemented");
}



void printMessage(char *msg) {
        printf("%s", msg);
}





[FTP] (1) FTP(File Transmission Protocol) 프로토콜 이해

네트워크로그 2011. 3. 9. 01:44



응용계층 프로토콜에 한 종류인 FTP에 대해 알아보고,  C언어를 사용하여 간단한 FTP 프로그램을 만들어보자.  

 


1. FTP 란? 

FTP 는 2대의 컴퓨터 간에 파일 전송을 위한 애플리케이션 프로토콜로 RFC 959에 정의되어 있다. 

Transport  계층 프로토콜로 TCP를 사용하며,  클라이언트/서버 모델 구성을 가지고 있으며,  FTP 서버는

21번 포트에서 FTP 클라이언트의 접속을 기다린다. FTP는 텔넷과는 달리,  클라이언트와 서버간에 2개의 

커넥션을 맺어 통신을 한다. 제어 명령을 송수신하는 PI(Protocol Interpreter) 프로세스와, 데이터를 송수신하는

DTP(Data Transmission Process) 로 구성된다. 





2. Active 모드 vs Passive 모드 

데이터를 전송하는 방식에 따라 Active 모드와 Passive 모드가 존재한다. 서버를 기준으로 했을 때,  서버가 

클라이언트에 접속하여 데이터를 송수신하는 것을  Active 모드라 하며,  클라이언트가 서버에 접속하여 

데이터를 송수신하는 것이 Passive 모드이다. 

 




3.  FTP  애플리케이션의 구조 

FTP 애플리케이션의 구조는 다음과 같다. 



 

 알툴즈의  알 FTP 와 같이 그래픽 유저 인터페이스를 제공하는 FTP 클라이언트도 있는가 하면, 유닉스/리눅스에서와 같이   

터미널에서 동작하는  텍스트 인터페이스의 FTP 클라이트도 존재한다. 하지만 어떤 클라이언트 형태는 RFC959에 정의된 

FTP 프로토콜에 따라 내부적으로 FTP 서버와 통신하게 된다. 

 

FTP 클라이언트 인테페이스를 통해, FTP 명령을 치면 클라이언 PI와 서버 PI간에 통신이 이루어진다. 작업디렉토리에 파일목록

출력과 데이터 업로드, 다운로드와 같은 작업들은 PI 를 통해 초기화가 이루어지면, 실제 데이터 전송은 서버와 클라이언트의 DTP 

간에 별도의 커넥션을 통해 이루어진다.

 PI와 DTP는 구조상에  개념이며, 반드시 FTP 클라이어트가 2개의 프로세스로 구성되어야  하는 것은 아니다. 

한 개의 프로세스여도, 프로토콜 전송 주소와 포트, 절차만을 준수하면 이상없이 서버와 통신할 수 있다. 

 




4.  FTP 명령 / 명령을 완료하기 위해 전송되는 프로토콜


FTP 클라이언트에서 사용할 수 있는 명령들은 다음과 같다. 더 많지만 자주 사용되는 명령들만을 간추려 보았다. 
 

커맨드 기능 프로토콜 
 open
<아이피 주소 또는 호스트명>
 해당 주소의 FTP 서버로 접속한다. 접속이 성공하면 Name과 Password를
입력하여  로그인할 수 있다.

 커넥트

 USER

 PASS

 SYST

 pwd  현재 원격(서버) 작업 디렉토리를 얻어온다.  PWD
 cd <디렉토리>  원격 작업 디렉토리를 변경한다   CWD
 ls  현재 원격 작업디렉토리의 파일목록을 얻어온다.

 PASV

 LIST

 hash  파일 업로드/다운로드시 1024바이트 단위로 해시문자 출력 옵셥을 on/off  한다.    
 get <파일명>   서버에 파일을 로컬로 다운로드한다. 

 PASV

 GET

 put <파일명>  로컬 파일으로 서버로 업로드한다. 

 PASV

 PUT

 quit/bye  FTP를 종료한다.   QUIT
 !<쉘명령>  셀 이스케이프. 쉘 명령을 수행하고, FTP 로 되돌아온다.   
ascii / binary  파일을 아스키 또는 바이너리로 전송한다.   TYPE I./A
 ?  FTP 명령 사용법을 출력한다.   

 

  커맨드는 텍스트 기반 FTP 클라이언트에서 사용자게 터미널을 통해 입력받는 명령이며, 이러한 명령의 기능을 수행하기 위해

 클라이언트는 서버에게  한개 이상의 프로토콜을  전송하게 된다. 클라이언트가 서버에게 전송하는 프로토콜은 텍스트이며, 

프토토콜의 종결문자로 \r\n을 사용한다.  다음은 몇가지 FTP 프로토콜이다. 

 


4.1 FTP 프로토콜

커맨드 내용 비고
 USER 사용자명  FTP 계정에 사용자 명을 전송한다   
 PASS 비밀번호  FTP 계정에 사용자 비밀번호를 전송한다  
 SYST  FTP 서버에 운영체제 종류를 문의한다.   
 PWD  현재 작업디렉토리 경로를 얻어온다.   
 CWD 디렉토리  인자로 주어진 디렉토리로 현재 작업디렉토리를 변경한다    
 PASV  passive 모드로 전환한다. 서버는 클라이언트의 접속을 기다릴 주소와 포트를 쉼표를 구분자로 하여 (127,0,0,1,234,13) 의 포맷으로 응답해준다.   
 LIST  현재 작업 디렉토리의 파일목록을 얻어온다.   
 GET 파일명  원격 작업디렉토리 상에 파일을 로컬로 다운로드 한다.   
 PUT 파일명   로컬의 파일을 원격 작업디렉토리로 업로드 한다.   
 RETR  로컬로 파일 다운로드시 클라이언트가 서버에게 데이터 전송시작을 요청한다.   
 STOR  파일 업로드시 클라이언트가 서버에게 데이터 전송시작을 요청한다.   
 PORT h0,h1,h2,h3,p0,p1   Active 모드로 데이터 전송시  서버에게 클라이언트가 대기할 주소와 포트를 쉼표를 구분자로 전송한다. p0, p1은 2바이트 포트의 각 바이트의 십진수값이다  
 QUIT  연결을 종료한다.   


 


4.2  데이터 전송모드

데이터 전송은 passive 모드와  active 모드로 전송할 수 있으며, 그 개념상에 차이는 이전에 설명했다. active 모드로 데이터 

전송시  클라이언트는   PORT 명령을 통해 서버가 접속할 수 있도록 클라이언트가 대기할 주소와 포트번호를 전송한다. 


 

PORT 202,13,180,230,4,103 


 앞에 2개의 수는 IP 를 구성하고 있는 수들이며, 4, 103은 2바이트 포트번호의 각 바이트를 십진수로 표현한 값(4x256 + 103)이다.

 즉 위 프로토콜을 해석하면  202.13.180.230 주소, 1127 포트에서 대기할 테니 여기로 서버는 접속하시오 ~~ 라는 의미가 

되겠다.  이렇게  서버가 클라이언트에게 접속하면, 클라이언트는 바로 데이터를 전송하면 된다. 

 passive 모드에서는 클라이언트는 PASV 프르토콜을 전송하여 passive 모드로 데이터를 전송받겠다는 것을 서버에게 알리면, 

서버는 위와 통일한 포맷으로 서버가 대기할 주소와 포트번호를 알려준다. 클라이언트는 이 값을 읽어들여 해당 주소로 접속한다. 

이후 LIST, RETR, STOR 을 전송하여 실제 데이터 전송을 시작해달라고 요청하게 된다. 

 

다음은 이를 나타내고 있는 그림이다. 



 




2011.03.08.Tues 아키텍처 발전역사

방법론로그/OOAD 2011. 3. 8. 23:30




소프트웨어 아키텍처는 연결되는 노드(컴퓨터)의 구성,  그로 인한 데이터 보안성과 공유성, 그리고 
데이터와 (처리)절차의 관점에서 바라보면 그 흐름을 쉽게 이해할 수 있다. 이 점에 초점을 맞추어 아키텍처의
발전역사를 알아보자. 


1. standalone
가장 초기의 SW 아키텍처로  독립적인 머신 단일머신에서 동작하는 형태이다.  
하나의 컴퓨터에 (프로그램의) 절차와 데이터가 모두 존재한다.
 


단일머신에 데이터가 존재하기 때문에 보안적으로 안전하지만, 해당 PC 사용자만이 데이터를 사용할 수 있다. 
즉 데이터의 공유성이 좋지 않다.  공유성 문제를 해결하기 위해 2-Tier 아키텍처가 등장하게 된다. 



2.  2-Tier
절차를 가지는 노드와 데이터를 가지는 노드가 분리되어 네트웍으로 연결된 아키텍처이다.
다수의 공유가 가능하게 되었지만,  다수의 사람들이 동일한 데이터에 접근하여 수정하여 되면, 데이터가 망가지게 된다. 
그래서 DataBase를 관리하는 시스템인 DBMS(DataBase Management System)을 두어,  동시 사용자들간에도 데이터를 이상없이
(무결성) 다룰 수 있도록 한다.  (Data+Base는 Data를 저장하고 있는 기지라는 의미를 갖는다. )
 



공유성 문제는 해결이 되었지만,  처리절차의  변경 요구사항이 생겼을 때,  모든 클라이언트 모두를 업데이트 해주어야 한다. 
유지보수의 관점에서는 엄청난 비용이 드는 문제점이다. 문제의 원인은  변경의 대상이 되는  절차가 모두 클라이언트 내에 
존재하기 때문이다. (비지니스 로직을 포함하는 이런형태의 클라이언트를 Fat 클라이언트라 한다.)
이 문제를 어떻게 해결하였을까? 다음 세대의 아키텍처를 알아보자.  



3. 2-Tier + Procedure
처리절차(비지니스 로직)를 Database에  포함시키도록 하여 유지보수 문제를 해결하였다.  처리절차를 다루는 요소를 
Procedure 라고 하며,  대부분의 현대 DBMS 에 지원하고 있다.  클라이언트는 처리로직을 가지고 있지 않으며, 단지 
Database에 접근하기 위한 인터페이스로서만 동작하게 되며 이런 형태를 Thin 클라이언트라 한다. 
(초창기 데이터베이스 시장은 Informix가 선점하고 있었으나,  Oracle 이 Procedure를 통해 시장 주도권을 장악할 수 있었다.)


유지보수의 문제를 멋지게 해결해주었지만, 그에 따른 문제가 발생하였다. 인터넷의 등장 그리고  클라이언트 PC 의 급증으로
절차와 데이터가 함께 있는  형태는 장점이자 시스템의 속도를  너무 느리게 하는 단점으로 작용하였다.  이 문제를  어떻게 
어떻게 해결하였을까? 다음세대의 아키텍처를 알아보자.  




4. 3-Tier
결합되어 있는 처리절차를 다시 분리하여, 처리절차를 전담하는 AS(Application Server)에 할당하여 구성한 아키텍처 형태이다. 
이후에는 다수의 AS 노드와  DataBase 노드를  클러스터링하여 병렬시스템으로 구축한 소위 N-Tier 아키텍처가 유지된다. 





5. 아키텍처의 발전역사
지금까지 알아본 아키텍처의 발전역사를 전체흐름은 아래 그림과 같다. 



(처리)절차와 데이터의 관점으로 보았을 때,   아키텍처 발전의 역사는 정반합의 연속임을 알 수 있다.  
(정반합이란 헤겔의 변증법을 도식화한 논리전개 방식의 하나) 

절차와 데이터가 한데 모여 있었던 standalone에서는 데이터 공유문제를 해결하기 위해 데이터와 절차를 2-tier구조로 
분리하여 해결하였다. 하지만 공유의 문제는 해결했지만,  유지보수성 문제가 발생하였다.  해결책으로 분리된 절차를
데이터베이스의 Procedure 형태로 결합하여 해결하였다. 하지만, 인터넷의 등장과 수많은 사용자들의 발생으로  데이터와 
절차의 결합은 다시 성능의 문제를 가져왔고, 절차를 AS(Application Server)로 분리하여 이 문제를 해결하였다. 
이상과 같이 아키텍처의 역사를 절차와 데이터의 관점에서 정반합의 연속으로 바라보면 이해하기 쉬워진다. 













컴퓨터의 신호전달 과정 
그 옛날 정보전달을 위해 사용되었던 봉화 => 불꽃 연기의 피어오름의 유무를 통해 정보전달 하였다. 
이때 불꽃이 어디서 피어오르는지(불꽃의 위치), 불꽃의 개수, 불꽃의 순서가 중요한 의미를 가지게 된다.  
(이진수를 사용한 정보전달의 시초) 




컴퓨터 내외부에서 일어나는 모든 정보전달은 전기를 통해서 일어난다.  키보드 자판을 두드려서  일어난 신호는 
전선을 통해 컴퓨터에 전달이 되며, ADC(Analog Digital Converter)에 의해 디지털 신호로 변경된다.  이 데이터는 
메모리 공간에 저장이 되며, 두뇌가 되는 CPU에 의해 해석이 된다. 
출력은 CPU에 의해 출력정보가 메모리에 저장이 되며, 이 정보가 Driver를 통해 아날로그 신호로 변환되고,  다시 전선을
통해 모니터로 전달이 된다. (모니터에선 다시 디지털로 변경하여 최종출력을 할것이다.)

메모리에 대한 접근은 반드시 OS를 통해서 이루어진다. 사용자는 직접적으로 메모리에 접근할 수 없다. 



HAL과 Driver
Driver는 HAL(Hardware Abstract Layer)의 구현체이다. 동일한 운영체제가 다양한 머신에서 동작하기 위해서는 각각의 머신을
제어할 수 있는 정보를 알고 있어야 한다. 하지만 수많은 하드웨어 정보를 운영체제가 포함하기에는 정보량이 너무 많아지게
된다.  그래서 HAL이라는 추상화 계층을 두어 운영체제는 특정 하드웨어에 종속적이지 않는 명령으로 하드웨어와 통신하려
한다.  각 하드웨어 제조사들이 제공하는  HW 제어정보를 담은 Driver는 이  HAL 계층에 결합 되어 실제적으로 하드웨어를
제어하는 나머지 퍼즐조각으로서 기능한다.   kenerl 입장에서는 HAL로 인해  어떤 하드웨어를 사용하는지 모르고도, 미리
정해진 표준화된 규칙을 통해 하드웨어를 제어할 수 있게된다. 
ex) 누가 일을 하는지에 상관없이 일을 할수 있게된다. 




API(Application Programming Interface) 
무엇인가 컴퓨터의 기능을 사용하기 위해서는 반드시 OS를 거쳐야 한다. 그리고 기능들은 각각 일련의 절차들로
구성되어 있다.  OS의 기능을 사용할 수 있도록, 절차들을 묶어내어 제공하는 것이 API이다. 
ex) 일이 어떤  절차로 구성되어 있는지 상관없이 그 일을 할 수 있게된다. 

※ 추상과 인터페이스는 프로그래밍에서 중요한 의미를 갖는다. 추상과 인터페이스가 무엇이며, 이들이 어떻게 
다르며 어떤경우에 사용되는지를 잘 알아두자. 



알고리즘 vs  자료구조
알고리즘과 자료구조는 어떻게 다른가? 
알고리즘이란 문제를 해결하는 절차이다. 문제를 다루다 보면 데이터를 다루게 다루게 되며, 이 데이터를 어떻게 
정리하느냐에 따라 문제해결을 빨리 할 수도, 느리게 할 수도 있다. 즉 자료구조란 문제를 빨리 해결하기 위해 데이터를
정리하는 방법이라 할 수 있다. 데이터란 절차 안에서만 그 의미를 갖기 때문에 절차로서의 알고리즘과 데이터로서의
자료구조는 뗄수야 뗄수 없는 관계에 있다. 






su, sudo : 특정 사용자 권한으로 쉘/명령 실행

운영체제로그/Linux 2011. 3. 8. 18:10

 

su

su 는 substitue(교체하다)의 약자로  다른 사용자의 계정으로 전환해 주는 유틸리티이다. 

-,  -l, -login 모두 동일한 옵션으로, 이 옵션을 주고, 실행을 하면, 해당 사용자의 권한으로 쉘을 실행하고, 환경변수까지 변경해준다.  

su - hiddenviewer=> hiddenviewer 계정으로 쉘을 실행하고, 환경변수까지 변경함

 - 옵션을 사용하지 않고, su를 실행하면 해당 사용자의 계정으로 쉘을 실행하지만 환경변수는 변경하지 않는다. 

 

 

 

sudo

시스템 관리자나 다른 사용자의 권한으로 해당 명령을 실행할 수 있게 하는 명령으로,  이런 허용여부는 /etc/sudoers 파일에 설정되어 있다.  

일반 사용자 계정으로 로그인 했을 때, 잠시 관리자 권한으로 특정자원에 접근하고 싶을 때 sudo 명령을 사용하여 관리자 권한으로 

명령을 실행할 수 있다.  다른 사용자의 권한을 획득하려 할 때, 비밀번호를 묻게 되는데 이 비밀번호를 올바로 입력했다 하더라도, 

/etc/sudoers 파일에 해당 사용자가 지정되어 있지 않으면, sudo 명령은 실패하게 된다. 

 

sudo cat /etc/sudoers<= -u옵션으로 사용자를 지정하지 않으면, 기본으로 root로 실행함
Password:   <= 여기서 올바로 비밀번호를 입력했다 하더라도, /etc/sudoers에 hiddenviewer가 
명시되어 있지 않으면 실패한다.

 

 

/etc/sudoers 파일에

 hiddenviewer ALL=(ALL) ALL 
이라는 문장을 추가하여 sudo 실행을 허용하면, 위 명령은 성공하게 된다. 

바이트 오더링(byte ordering)

네트워크로그 2011. 3. 8. 15:04



바이트 오더는 데이터가 바이트 단위로 메모리에 저장되는 순서를 의미하며 각 CPU 벤더 의존적인 특징을 가지고 있다.
크게 Big endian과 Little   endian 방식이 존재하며,  Little Endian을 사용하는 대표적인 벤더가 인텔이며, Big endian을 사용하는
벤더가 AMD 이다.  이기종 간에 통신을 하는 네트워크 프로그래밍에서는  두 종단 간에 올바로 통신하기 위해서는 통일된
방식이 필요하게 되는데, 네트워크 바이트 오더 표준은 Big Endian 방식이다. 네트웍을 통한 데이터 전송은 Big Endian을 통해
전송하며, 데이터를 송수신 하는 곳에서는 각자의 플랫폼에 맞는 바이트 오더의 변환이 필요하게 된다.  물론 바이트 오더는
2바이트 이상의 프리미티브 타입에 적용되는 내용이다. 1바이트 단위의 문자열 데이터는 바이트 오더의 변경이 불필요하기
때문이다.



Big endian과 Little endian 간의 차이를 다음 그림을 통해 알아보자 

Big-endian은 가장 최상위 바이트(0A)가 가장 낮은 메모리 주소에 저장되는 방식이다.




Little-endian은 가장 최상위 바이트(0A)가 가장 높은 메모리 주소에 저장되는 방식이다. 




다음은 위의 개념을 이용하여 공용체로 바이트 오더를 알아보는 간단한 프로그램이다.  
 

#include <stdio.h>

int main(int argc, char **argv){    
    union {      
        short s;        
        char c[sizeof(short)];    
    } un;  

    un.s = 0x0102;      

    if (sizeof(short) == 2) {        
        if (un.c[0] == 1 && un.c[1] == 2)           
            printf("big-endian\n");        
        else if (un.c[0] == 2 && un.c[1] == 1)            
            printf("little-endian\n");       
        else            
            printf("unknown\n");   
    } else{      
            printf("sizeof(short) = %d\n", sizeof(short));   
    }   
    return 0;
}