검색결과 리스트
로그의 노트에 해당되는 글 212건
- 2011.03.15 [Java] 어노테이션(Annotation) (2)
- 2011.03.11 포인터의 이해
- 2011.03.11 HMAC-SHA1
- 2011.03.10 매크로, 연산자 (3)
- 2011.03.10 [자바] (1) 자바소개
- 2011.03.09 리눅스 파일시스템 계층 표준(Filesystem Hierarchy Standard)
- 2011.03.09 [FTP] (2) 클라이언트 구현하기
- 2011.03.09 [FTP] (1) FTP(File Transmission Protocol) 프로토콜 이해
- 2011.03.08 2011.03.08.Tues 아키텍처 발전역사
- 2011.03.08 su, sudo : 특정 사용자 권한으로 쉘/명령 실행
글
[Java] 어노테이션(Annotation)
깊이 있게 생각해보지는 못했었다. 단순히 컴파일러에게 알려주기 위한 표식정도로 생각했었다. 그런데 Spring Roo 와
Spring3.0 과 같은 최신 프레임웍들의 변화 경향을 보면, 어노테이션을 적극활용하는 쪽으로 변화되고 있다. 어노테이션을
사용하여 코드의 작성량도 한결 줄어들었다고 한다. 어노테이션들의 어떤 특성을 활용한 것일까? 어노테이션이란 뭘까?
최신 프레임웍들에 변화경향을 보기에 앞서, 어노테이션에 대해서 먼저 알아보았다.
구성과 설정값들을 외부의 XML 설정파일에 명시하는 방식을 의미한다. 변경이 될 수 있는 데이터들을 최대한 코드가 아닌
외부설정파일에 분리하기 때문에 변경 요구사항이 들어왔을 때, 재컴파일 없이도 쉽게 변경사항을 적용할 수 가 있다.
유연성이란 장점을 얻었지만, 단점 또한 존재한다. 프로그램 작성을 위해 매번 많은 설정파일을 작성해야 한다는 것이다.
그 규모가 커질수록 설정의 양도 많아지게 되며 이를 잘 구조화 할 수 있는 방법도 필요하게 된다. 또 하나의 단점은 이것보다
좀 더 크다. 도메인 데이터 처리정보가 Model 클래스, 서비스 클래스, XML 설정파일에 분산되어 있어서, 이를 확인하기
위해서는 Model , Service 클래스와 XML 설정파일을 모두 뒤져야 한다는 것이다.
3. Annotation의 사용했을 때의 장점은?
Model 클래스에 직접 명시함으로써 해당 데이터들에 대한 유효조건을 쉽게 파악할수 있게되며, 코드의 양도 줄어든다.
(엄밀히 말하면, 코드의 양은 줄어들지 않는다. 하지만 코드가 깔끔해지고, 어노테이션의 재사용도 가능해진다. )
재컴파일 해야하는 단점이 있다. 애플리케이션 전체적인 설정이나 디플로이 환경에 따라 변경되는 사항들은 XML 설정을
사용하자. 각각의 장단점을 파악하고, 언제 무엇을 사용해야할지 아는 것이 중요하다.
그럼 이제부터 어노테이션을 사용해보자. 사용을 위해 먼저 선행지식들에 대해 잠깐 알아보자.
5. 일반적인 어노테이션의 용도
Single-value, Full 어노테이션으로 분류할 수 있다.
@Deprecated
마커 어노테이션으로 차후 버전에 지원되지 않을 수 있기 때문에 더 이상 사용되지 말아야할 메소드를 나타낸다.
특이하게 더이상 사용되지 말아야할 메소드와 같은 라인상에 놓여져야 한다. (이유모름)
(두 플래그 -deprecated 또는 Xlint:deprecated 중 하나와 javac 명령어를 사용하여 컴파일러 경고를 켜야만,
컴파일러 에러를 발생시켜준다)
@SupressWarning
의미데로 경고를 제거하는 어노테이션이다. Object형을 엘리먼트로 하는 컬렉션을 사용하면, 컴파일러 경고가 발생하는데
이 어노테이션을 사용하여 프로그래머의 의도적인 Object 형 사용임을 알려 경고를 제거할 수 있다.
7. 커스텀(Custom) 어노테이션
1) 커스텀 어노테이션 정의하기
public @interface InProgress { }
어노테이션 정의파일을 컴파일하고, 이 파일을 클래스패스에서 참조할 수 있으면 다른 소스코드상에서 어노테이션을
사용할 수 있다.
어노테이션 유형은 멤버변수를 가질 수 있으며, 이 변수들 컴파일시 또는 런타임에 메타-데이터로서 사용될 수 있다.
이렇게 정의를 하면 자동으로 accessor와 mutator를 제공해준다.
// 해당 태스크가 완료되야함 나타내는 어노테이션
public @interface TODO {
String value();
}
정의한 어노테이션을 다음과 같이 사용한다.
@TODO("Figure out the amount of interest per month")
public void calculateInterest(float amount, float rate) {
// ...
}
단일 멤버를 갖을 경우에만 위와 같이 사용할 수 있다.
3) 디폴트 값 설정하기
public @interface GroupTODO {
public enum Severity { CRITICAL, IMPORTANT, TRIVIAL, DOCUMENTATION };
Severity severity() default Severity.IMPORTANT;
String item();
String assignedTo();
String dateAssigned();
}
디폴트값을 사용한 예는 다음과 같다.
@GroupTODO(
item="Figure out the amount of interest per month",
assignedTo="Brett McLaughlin",
dateAssigned="08/04/2004"
)
public void calculateInterest(float amount, float rate) {
// ...
}
4) 메타-어노테이션
어노테이션에 사용되는 어노테이션으로 해당 어노테이션의 동작대상 및 보여타임을 결정한다.
① @Target 메타-어노테이션
어노테이션이 적용되는 동작 프로그래밍
엘리멘트를 지정하며, 다음과 같은 값들이 있다.
package java.lang.annotation;
public enum ElementType {
TYPE, // Class, Interface, enum
FIELD, // 프로퍼티 (enum 나열값들은 제외)
METHOD, // 메서드
PARAMETER, // 메서드 파라미터
CONSTRUCTOR, // 생성자
LOCAL_VARIABLE, // 로컬변수 또는 Catch문
ANNOTATION_TYPE, // 어노테이션(메타 어노테이션)_
PACKAGE // 자바 패키지
}
② @Retention 메타-어노테이션
자바컴파일러가 어노테이션을 다루는 방법과 관련이 있다. 소스파일, 클래스파일, 런타임 중 어느시점까지
어노테이션을 보유하고 있을 것인지를 결정한다.
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, // 어노테이션이 컴파일러에 의해 버려짐
CLASS, // 어노테이션이 클래스파일에 저장되지만, JVM에게 무시됨
RUNTIME // 어노테이션이 클래스파일에 저장되며, JVM에 의해 읽혀짐
}
위 세값 중 하나를 인자로 취해 다음과 같이 사용된다.
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
...
}
'언어로그 > 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 |
설정
트랙백
댓글
-
반가 2016.06.09 12:59
좋은 정보 잘보고 갑니다
글
포인터의 이해
'언어로그 > 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
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언어 설계의 특징 중 하나로 친숙성을 들 수 있는데, 그 예로 일상생활에서 사용하는 연산자를
그대로 언어에 채용하였다.
12 + 14 = 26 (정수)
12.0 + 14 = 26.0 (실수)
12 + 14.0 = 26.0 (실수)
12.0 + 14.0 = 26.0 (실수)
나머지 연산자
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 |
설정
트랙백
댓글
글
[자바] (1) 자바소개
나름 잘 보이게 한다고, 노란박스를 사용했는데...굉장히 눈을 자극하네요...otz...
자바(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)
다음은 Filesystem Hierarchy Standard 2.3 내용을 간략히 요약한 내용이다.
Bin
시스템 관리자와 사용자가 동시에 사용하는 명령을 포함하고 있으며, 이 명령들은 다른 파일시스템이 마운팅 될 필요가 없는 명령이다. (싱글 유저 모드)
스크립트에서 간접적으로 사용되는 명령들도 포함하고 있다. /bin 디렉토리에는 서브디렉토리가 없어여 한다.
다음은 bin 디렉토리에서 볼 수 있는 몇 가지 명령들이다.
cat, chmod, chirp, cp, date, echo, kill, mv, ls
/boot (부트로더의 정적파일)
이 디렉토리는 부팅시 필요한 초기화 파일을 포함하여 부팅과정에서 필요한 모든 것을 포함하고 있다.
커널이 유저모드 프로그램 실행을 시작 시키기 전에 사용되는 데이터를 저장하고 있다. . 저장된 마스터 부트 섹터와 섹터 맵파일을 포함하고 있다
운영체제 커널은 / (루트) 또는 /boot 디렉토리에 위치해야한다.
이 디렉토리는 부팅시 필요한 초기화 파일을 포함하여 부팅과정에서 필요한 모든 것을 포함하고 있다.
커널이 유저모드 프로그램 실행을 시작 시키기 전에 사용되는 데이터를 저장하고 있다. . 저장된 마스터 부트 섹터와 섹터 맵파일을 포함하고 있다
운영체제 커널은 / (루트) 또는 /boot 디렉토리에 위치해야한다.
/dev (디바이스 파일)
디바이스 파일들이 있는 디렉토리이다. /dev 에 디바이스들이 수동으로 생성될 필요가 있다면 , MAKEDEV 라는 이름의
명령을 포함해야한다.
로컬 디바이스를 위해 MAKEDEV.local 을 포함할수도 있다.
/etc (호스트에 있는 특정시스템에 대한 구성파일)
configuration file을 포함한다. 구성파일이란 프로그램에 동작방식을 제어하는 로컬파일을 의미한다. 정적파일이어야 하며, 실행가능한 바이너리여서는 안된다
서브디렉토리 opt는 :/opt에 대한 설정을, X11 는 X Window 시스템에 대한 설정을, sgml 은 SGML 에 대한 설정을, xml은 XML에 대한 설정파일을 포함한다.
configuration file을 포함한다. 구성파일이란 프로그램에 동작방식을 제어하는 로컬파일을 의미한다. 정적파일이어야 하며, 실행가능한 바이너리여서는 안된다
서브디렉토리 opt는 :/opt에 대한 설정을, X11 는 X Window 시스템에 대한 설정을, sgml 은 SGML 에 대한 설정을, xml은 XML에 대한 설정파일을 포함한다.
/home(사용자 홈디레토리)
사이트에 대한 파일시스템으로, 호스트 마다 설정이 다를 수 있기 때문에 프로그램들은 이 위치에 의존해서는 안된다.
사용자 개인의 애플리케이션 설정파일은 .(dot)으로 시작하는 이름으로 홈디렉토리에 저장된다. 애플리케이션이 dot 파일을 하나이상 생성해야한다면 dot 디렉토리를
생성하고, 거기에 .dot으로 시작하지 않는 이름으로 설정파일들을 저장해야한다.
사이트에 대한 파일시스템으로, 호스트 마다 설정이 다를 수 있기 때문에 프로그램들은 이 위치에 의존해서는 안된다.
사용자 개인의 애플리케이션 설정파일은 .(dot)으로 시작하는 이름으로 홈디렉토리에 저장된다. 애플리케이션이 dot 파일을 하나이상 생성해야한다면 dot 디렉토리를
생성하고, 거기에 .dot으로 시작하지 않는 이름으로 설정파일들을 저장해야한다.
/lib (필수적인 공유 라이브러리와 커널 모듈)
시스템을 부팅시키거나 루트 파일시스템에서 명령을 실행하는데 필요한 공유라이브러리 이미지가 존재한다.
예를 들어 /bin이나 /sbin에서…필요한…
다음과 같은 이름패턴을 가지고 있어야한다.
libc.so.* 는 동적으로 링크된 c 라이브러리이며, ld* 는 실행타임 링커/로더를 나타낸다.
시스템을 부팅시키거나 루트 파일시스템에서 명령을 실행하는데 필요한 공유라이브러리 이미지가 존재한다.
예를 들어 /bin이나 /sbin에서…필요한…
다음과 같은 이름패턴을 가지고 있어야한다.
libc.so.* 는 동적으로 링크된 c 라이브러리이며, ld* 는 실행타임 링커/로더를 나타낸다.
/media (제거가능한 미디어에 다한 마운트 위치)
플로피 디스크나 cdrom, zip 디스크와 같은 제거 가능한 미디어를 위한 마운트 포인트로 사용되는 서브디렉토리를 포함한다
/mnt (일시적으로 마운트되는 파일시스템을 위한 마운트포인트)
/opt (애드온 애플리케이션 소프트웨어 패키지)
애드온 애플리케이션 소프트웨어 패키지 설치를 위해 제공되는 디렉토리이다. . /opt에 설치된 패키지는 /opt/<package>, /opt/<provider> 에서 정적파일을 찾을수 있어야한다 <package> 는 소프트웨어 패키지를 기술하는 이름이며, <provider> 는
제공자의 LANANA 등록된 이름이다.
애드온 애플리케이션 소프트웨어 패키지 설치를 위해 제공되는 디렉토리이다. . /opt에 설치된 패키지는 /opt/<package>, /opt/<provider> 에서 정적파일을 찾을수 있어야한다 <package> 는 소프트웨어 패키지를 기술하는 이름이며, <provider> 는
제공자의 LANANA 등록된 이름이다.
/root (루트유저 홈데릭토리)
/sbin (시스템 바이너리)
/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) 클라이언트 구현하기
다음은 c언어로 구현한 open, get, put, ls, cd, pwd, quit/bye, hash 명령을 구현한 간단한 FTP 클라이언트 예제이다. 에러처리와 소켓의 recv, send 함수의 동작방식을 정확히 고려하지 않았기 때문에, 정확히 동작하지 않을 수 있다. 데이터 수신시 하나의 명령에 대한 메시지의 끝을 구분해 줄 수 있는 방법이 추가로 필요하다. (메시지 끝을 의미하는 \r\n 로 구분) : 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);
}
}
#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;
}
#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] (2) 클라이언트 구현하기 (0) | 2011.03.09 |
|---|---|
| [FTP] (1) FTP(File Transmission Protocol) 프로토콜 이해 (0) | 2011.03.09 |
| 바이트 오더링(byte ordering) (0) | 2011.03.08 |
| OAuth의 세부사항 (0) | 2011.01.01 |
| OAuth의 개념과 대략적인 흐름 (0) | 2010.12.31 |
설정
트랙백
댓글
글
[FTP] (1) FTP(File Transmission Protocol) 프로토콜 이해
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 명령을 통해 서버가 접속할 수 있도록 클라이언트가 대기할 주소와 포트번호를 전송한다.
앞에 2개의 수는 IP 를 구성하고 있는 수들이며, 4, 103은 2바이트 포트번호의 각 바이트를 십진수로 표현한 값(4x256 + 103)이다.
즉 위 프로토콜을 해석하면 202.13.180.230 주소, 1127 포트에서 대기할 테니 여기로 서버는 접속하시오 ~~ 라는 의미가
되겠다. 이렇게 서버가 클라이언트에게 접속하면, 클라이언트는 바로 데이터를 전송하면 된다.
passive 모드에서는 클라이언트는 PASV 프르토콜을 전송하여 passive 모드로 데이터를 전송받겠다는 것을 서버에게 알리면,
서버는 위와 통일한 포맷으로 서버가 대기할 주소와 포트번호를 알려준다. 클라이언트는 이 값을 읽어들여 해당 주소로 접속한다.
이후 LIST, RETR, STOR 을 전송하여 실제 데이터 전송을 시작해달라고 요청하게 된다.
다음은 이를 나타내고 있는 그림이다.
'네트워크로그' 카테고리의 다른 글
| [FTP] (2) 클라이언트 구현하기 (0) | 2011.03.09 |
|---|---|
| [FTP] (1) FTP(File Transmission Protocol) 프로토콜 이해 (0) | 2011.03.09 |
| 바이트 오더링(byte ordering) (0) | 2011.03.08 |
| OAuth의 세부사항 (0) | 2011.01.01 |
| OAuth의 개념과 대략적인 흐름 (0) | 2010.12.31 |
설정
트랙백
댓글
글
2011.03.08.Tues 아키텍처 발전역사
1. standalone
하나의 컴퓨터에 (프로그램의) 절차와 데이터가 모두 존재한다.
2. 2-Tier
다수의 공유가 가능하게 되었지만, 다수의 사람들이 동일한 데이터에 접근하여 수정하여 되면, 데이터가 망가지게 된다.
그래서 DataBase를 관리하는 시스템인 DBMS(DataBase Management System)을 두어, 동시 사용자들간에도 데이터를 이상없이
(무결성) 다룰 수 있도록 한다. (Data+Base는 Data를 저장하고 있는 기지라는 의미를 갖는다. )
유지보수의 관점에서는 엄청난 비용이 드는 문제점이다. 문제의 원인은 변경의 대상이 되는 절차가 모두 클라이언트 내에
존재하기 때문이다. (비지니스 로직을 포함하는 이런형태의 클라이언트를 Fat 클라이언트라 한다.)
이 문제를 어떻게 해결하였을까? 다음 세대의 아키텍처를 알아보자.
3. 2-Tier + Procedure
Procedure 라고 하며, 대부분의 현대 DBMS 에 지원하고 있다. 클라이언트는 처리로직을 가지고 있지 않으며, 단지
Database에 접근하기 위한 인터페이스로서만 동작하게 되며 이런 형태를 Thin 클라이언트라 한다.
(초창기 데이터베이스 시장은 Informix가 선점하고 있었으나, Oracle 이 Procedure를 통해 시장 주도권을 장악할 수 있었다.)
절차와 데이터가 함께 있는 형태는 장점이자 시스템의 속도를 너무 느리게 하는 단점으로 작용하였다. 이 문제를 어떻게
어떻게 해결하였을까? 다음세대의 아키텍처를 알아보자.
4. 3-Tier
이후에는 다수의 AS 노드와 DataBase 노드를 클러스터링하여 병렬시스템으로 구축한 소위 N-Tier 아키텍처가 유지된다.
5. 아키텍처의 발전역사
지금까지 알아본 아키텍처의 발전역사를 전체흐름은 아래 그림과 같다.
(정반합이란 헤겔의 변증법을 도식화한 논리전개 방식의 하나)
분리하여 해결하였다. 하지만 공유의 문제는 해결했지만, 유지보수성 문제가 발생하였다. 해결책으로 분리된 절차를
데이터베이스의 Procedure 형태로 결합하여 해결하였다. 하지만, 인터넷의 등장과 수많은 사용자들의 발생으로 데이터와
절차의 결합은 다시 성능의 문제를 가져왔고, 절차를 AS(Application Server)로 분리하여 이 문제를 해결하였다.
이상과 같이 아키텍처의 역사를 절차와 데이터의 관점에서 정반합의 연속으로 바라보면 이해하기 쉬워진다.
컴퓨터의 신호전달 과정
(이진수를 사용한 정보전달의 시초)
통해 모니터로 전달이 된다. (모니터에선 다시 디지털로 변경하여 최종출력을 할것이다.)
HAL과 Driver
제어할 수 있는 정보를 알고 있어야 한다. 하지만 수많은 하드웨어 정보를 운영체제가 포함하기에는 정보량이 너무 많아지게
된다. 그래서 HAL이라는 추상화 계층을 두어 운영체제는 특정 하드웨어에 종속적이지 않는 명령으로 하드웨어와 통신하려
한다. 각 하드웨어 제조사들이 제공하는 HW 제어정보를 담은 Driver는 이 HAL 계층에 결합 되어 실제적으로 하드웨어를
제어하는 나머지 퍼즐조각으로서 기능한다. kenerl 입장에서는 HAL로 인해 어떤 하드웨어를 사용하는지 모르고도, 미리
정해진 표준화된 규칙을 통해 하드웨어를 제어할 수 있게된다.
정리하느냐에 따라 문제해결을 빨리 할 수도, 느리게 할 수도 있다. 즉 자료구조란 문제를 빨리 해결하기 위해 데이터를
정리하는 방법이라 할 수 있다. 데이터란 절차 안에서만 그 의미를 갖기 때문에 절차로서의 알고리즘과 데이터로서의
자료구조는 뗄수야 뗄수 없는 관계에 있다.
'방법론로그 > OOAD' 카테고리의 다른 글
| 방법론(methodology) (0) | 2011.04.12 |
|---|---|
| 2011.03.08.Tues 아키텍처 발전역사 (0) | 2011.03.08 |
| [OOAD] 요구사항 수집부터 분석까지 (0) | 2010.12.13 |
| [OOAD] 아키텍처, IA , 화면기준안, 스토리보드 (1) | 2010.12.13 |
설정
트랙백
댓글
글
su, sudo : 특정 사용자 권한으로 쉘/명령 실행
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 실행을 허용하면, 위 명령은 성공하게 된다.
'운영체제로그 > Linux' 카테고리의 다른 글
| 리눅스 자격증 (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 |
| Bash 스크립트 (0) | 2011.01.20 |
FtpCommand.h