MISTERY


위 표를 보다시피 strcmp() 의 원형은

int strcmp(const char * str1, const char * str2) 가 되겠다


strcmp() 는 1번인자와 2번인자의 문자열 크기를

대소비교 하여 int형으로 값을 반환해 주는 함수다


두 인자의 문자열이 같으면 '0'을 반환해주며

같지 않을때에는 '0'이 아닌 수를 반환한다.

그러면, '0'이 아닌수가 의미하는 바는 무엇일까??


문자열이 같을 때

#include <stdio.h>
#include <string.h>

int main()
{
  char str1[100= "banana";
  char str2[100= "banana";

  printf("%d\n",strcmp(str1,str2));
  
  return 0;
}


접기

0

접기




문자열이 다를 때

#include <stdio.h>

int main()
{
  char str1[100= "apple";
  char str2[100= "banana";

  
  printf("%d\n",strcmp(str1,str2));
  return 0;
}



접기

-1

접기


일반적으로 알고있는 이론은


왼쪽인자의 문자열 길이가 작으면 -1 반환되고

오른쪽의 문자열 길이가 작으면 1 가 반환된다

둘다 같다면 0 이 반환된다.


일 것이다.


하지만 알아두어야 할 점은

strcmp()는 ASCII code를 비교하는 함수다.



#include <stdio.h>
#include <string.h>

int main()
{
  char str1[100= "AAA";
  char str2[100= "AAA";

  char str3[100= "AAB";
  char str4[100= "AAA";

  printf("str1= AAA, str2 = AAA : %d\n",strcmp(str1,str2));
  printf("str3= AAA, str4 = AAA : %d\n",strcmp(str3,str4));
  printf("str4= AAA, str3 = AAA : %d\n",strcmp(str4,str3));
  
  return 0;
}


접기

str1= AAA, str2 = AAA : 0

str3= AAA, str4 = AAA : 1

str4= AAA, str3 = AAA : -1

접기



str1과 str2를 비교했을때는 같으니까 '0'이 나왔다.

그다음

str3과 str4를 비교했을때

str3은 'AAB' 이므로 아스키코드로 '65 65 66'이다.

str4는 'AAA' 이므로 아스키코드로 '65 65 65'이다.


str3이 1번인자에 있으므로 '1'이 출력됬다.


그다음 str4와 str3을 비교했을때에는

'-1'이 출력되었다.


이로써 알 수 있는점은

1번인자의 아스키코드의 합이 2번인자보다 크면 양수(1)가 반환되고

2번인자의 아스키코드의 합이 1번인자보다 크면 음수(-1)가 반환되는 것을 알 수 있다.

 

 

 

 

 

 

 

 

 

 

 

출처 : http://euro87.tistory.com/117

 

 

 

 

 

 

신고

'Programming > C+C++' 카테고리의 다른 글

[C언어]strcmp함수  (0) 2014.07.18
다시쓰는 C언어 강좌] 079 - 열거형 - enum  (0) 2014.07.18
비동기 입출력 프로그래밍  (0) 2014.04.16
Chapter 1. C++ 시작하기 pdf 파일  (0) 2014.03.31
[C#] Queue, Thread, AutoReset  (0) 2014.03.31

Comment +0

이번엔 구조체나 공용체와 완전히 다른 형식이지만
역시 뭔가 모아 놓고 쓸 수 있는
열거형(enumeration)입니다.
열거형은 여러 문자열을 하나의 그룹으로 만들어 놓고
각각 번호를 붙여서, 그 키워드가 곧 값이 되는 상수와도 비슷한 존재인데

#include <stdio.h>

enum week {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

int main() {

enum week Day = Sun;
printf("Sun: %d\n", Day);
Day = Tue;
printf("Tue: %d\n", Day);
Day = Fri;
printf("Fri: %d\n", Day);

return 0;
}

단, 열거형으로 선언한 상수 집단을 쓰고 싶다면
그 열거형으로 변수를 선언해야 합니다. 저렇게요.
그 뒤 키워드를 넣으면 해당하는 정수값이 들어가며,
기본적으로 맨 처음 키워드가 0,
그 다음부터 1씩 증가합니다.
그래서 Mon은 1, Tue는 2, ... Sat는 6 식입니다.

Sun: 0
Tue: 2
Fri: 5
[커서]

이렇게 나옵니다.
물론 무조건 값이 이렇게 정해지는 건 아니고,

#include <stdio.h>

enum week {Sun=3, Mon, Tue, Wed=9, Thu, Fri, Sat};

int main() {

enum week Day = Sun;
printf("Sun: %d\n", Day);
Day = Tue;
printf("Tue: %d\n", Day);
Day = Fri;
printf("Fri: %d\n", Day);

return 0;
}

열거형 선언에서 필요에 따라 키워드에 값을 대입하면
그 값을 가지게 되며, 그 뒤 키워드들은 그 값으로부터 1씩 증가되는 값을 가집니다.
이때는 Sun은 3, Mon은 4, Tue는 5가 되고
다시 Wed는 9, Thu는 10, Fri는 11, Sat는 12가 되죠.

Sun: 3
Tue: 5
Fri: 11
[커서]

굳굳. 이렇게 됩니다.
그리고... 열거형 변수에는 정수형을 바로 대입할 수가 없고
저렇게 키워드로 대입해야 합니다. 안 그러면 컴파일 에러.
그러나, 열거형으로 형변환을 해 주면 대입 가능합니다.

#include <stdio.h>

enum week {Sun=3, Mon, Tue, Wed=9, Thu, Fri, Sat};

int main() {

enum week Day = (enum week)4;
printf("%d\n", Day);

return 0;
}

이렇게 해 주면 됩니다.

그 반대로, 정수형을 대입하지 못하고 키워드로만 대입하니
한 열거형 그룹 안에 중복되는 값이 있어도 괜찮습니다.

enum week {Sun=3, Mon, Tue, Wed=4, Thu, Fri, Sat};

이래도 문제 없습니다.
이대로라면 Mon, Wed가 중복되고 Tue, Thu가 중복되죠.
단, 같은 키워드는 코드 전체에서 중복되면 안 됩니다.
서로 다른 열거형 그룹이어도 같은 키워드는 존재하면 안 됩니다.



이제 예제입니다.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

enum dice {One=1, Two, Three, Four, Five, Six};

int main() {

srand(time(0));
enum dice n;
int score = 0;

do{
n = (enum dice)(rand()%6 + 1);
printf("주사위의 눈은 %d입니다.\n", n);
switch(n){
case One
printf("게임이 종료됩니다.\n");
break;
case Two
printf("점수 +1점, 현재 점수: %d점\n", score+=1);
break;
case Three
printf("점수 +1점, 현재 점수: %d점\n", score+=1);
break;
case Four
printf("점수 +2점, 현재 점수: %d점\n", score+=2);
break;
case Five
printf("점수 +5점, 현재 점수: %d점\n", score+=5);
break;
case Six
printf("점수 *2,   현재 점수: %d점\n", score*=2);
break;
}
}while(n!=One);

printf("---------------------------\n");
printf("최종 점수: %d점\n", score);

return 0;
}

무려 switch 문과 중복했습니다.
이때 switch 문에 들어간 변수가 열거형 변수입니다.
컴퓨터가 알아서 주사위를 계속 던집니다. 1이 나올 때까지.
그 전 까지는 점수를 계속 더하거나 곱해가다가
주사위 눈이 1이 나오면 최종 점수를 발표합니다.

주사위의 눈은 6입니다.
점수 *2,   현재 점수: 0점
주사위의 눈은 4입니다.
점수 +2점, 현재 점수: 2점
주사위의 눈은 4입니다.
점수 +2점, 현재 점수: 4점
주사위의 눈은 6입니다.
점수 *2,   현재 점수: 8점
주사위의 눈은 6입니다.
점수 *2,   현재 점수: 16점
주사위의 눈은 4입니다.
점수 +2점, 현재 점수: 18점
주사위의 눈은 5입니다.
점수 +5점, 현재 점수: 23점
주사위의 눈은 3입니다.
점수 +1점, 현재 점수: 24점
주사위의 눈은 2입니다.
점수 +1점, 현재 점수: 25점
주사위의 눈은 5입니다.
점수 +5점, 현재 점수: 30점
주사위의 눈은 6입니다.
점수 *2,   현재 점수: 60점
주사위의 눈은 3입니다.
점수 +1점, 현재 점수: 61점
주사위의 눈은 2입니다.
점수 +1점, 현재 점수: 62점
주사위의 눈은 6입니다.
점수 *2,   현재 점수: 124점
주사위의 눈은 5입니다.
점수 +5점, 현재 점수: 129점
주사위의 눈은 1입니다.
게임이 종료됩니다.
---------------------------
최종 점수: 129점
[커서]

이건 아름다운 결과 중 하나.(...)



< 보너스 >

#include <stdio.h>

typedef enum week {Sun=3, Mon, Tue, Wed=9, Thu, Fri, Sat} Week;

int main() {

Week Day = (Week)4;
printf("%d\n", Day);

return 0;
}

열거형도 typedef 가능합니다!!

 

 

 

 

 

 

 

 

출처 : http://kks227.blog.me/60198053240

         좋은 강의 감사합니다.

 

 

 

 

 

 

 

 

신고

'Programming > C+C++' 카테고리의 다른 글

[C언어]strcmp함수  (0) 2014.07.18
다시쓰는 C언어 강좌] 079 - 열거형 - enum  (0) 2014.07.18
비동기 입출력 프로그래밍  (0) 2014.04.16
Chapter 1. C++ 시작하기 pdf 파일  (0) 2014.03.31
[C#] Queue, Thread, AutoReset  (0) 2014.03.31

Comment +0

1 비 동기 입출력 프로그래밍

1.1 입출력 모델

소켓 응용 프로그램을 개발하다보면 종종 봉쇄(blocking) 소켓, 비 봉쇄(non-blocking) 소켓이란 말을 듣는다. 봉쇄 소켓 보다 비 봉쇄 소켓이 성능이 좋다느니, 이런 경우에는 비 봉쇄 소켓을 사용해야 한다느니 하는 것들이 그것이다. 특히 요즘에는 단일 프로세스 (단일 쓰레드)처리 방식이 선호되면서, 비 봉쇄 소켓에 대한 관심이 많아지고 있다.

봉쇄 모델과와 비 봉쇄모델은는 프로그램이 어떤 상태로 작동하는 지를 묘사한다. 함수호출을 한 영역에서 프로그램이 (반환 될 때까지)대기 하면, 봉쇄 모델 그렇지 않으면 비 봉쇄 모델라고 한다.

동기 / 비 동기는 데이터 상태와 관련된다. 데이터의 입출력 상태를 서로가 알면 동기, 그렇지 않으면 비 동기다. 데이터 상태와 관련되어 있다. 동기 입출력 모델에서는 A에서 데이터를 지금 쓰면, B에서도 지금 데이터를 읽을 것을 안다. B는 A가 데이터를 보내면, 이에 맞추어 데이터를 읽는다.

비 동기 입출력 모델은 입력과 출력의 시점을 알지 못하는 상태를 의미한다. A가 B로 데이터를 보낸다고 가정해 보자. 이 상태에서 B는 A가 언제 데이터를 보낼지 알 수 없으며, 데이터를 기다리지 않는다. 대신 "이벤트 통지"를 기다린다. 즉 다른 일을 하고 있다가 "A가 데이터를 전송했다"라는 비 동기적 신호를 받으면 그때, 데이터 읽기를 시작한다. 쓰는 시점과 읽는 시점이 일치 하지 않는다.

일반적으로 소켓은 봉쇄 소켓 으로 만들어진다. 데이터 입출력 상태로 보자면 동기 입출력 상태인 "동기 & 봉쇄 모델"이다. 봉쇄형 소켓이란 읽기와 쓰기의 과정이 완전히 끝날 때 까지, 머물러 있음을 의미한다. read함수를 예로 들어보자. 봉쇄형 소켓을 read할 경우, read함수는 데이터를 모두 읽을 때까지 대기한다. 봉쇄 소켓을 사용하면, 해당 영역에서 머무르기 때문에 다른 입출력 관련 작업을 할 수 없다는 문제가 생긴다. 예컨데, 두 개 이상의 입출력을 처리할 수 없다. 파일 지정 번호 4와 5를 가진 소켓이 있다. 현재 프로그램은 4번 소켓에서 읽기 위해서 기다리고 있다면, 4번 소켓에서의 읽기 작업이 끝나기 전에는 5번 소켓을 처리할 수 없다.

비 봉쇄인 경우 소켓 함수는 바로 반환한다. 반환 값을 에러를 나타내는 -1 을 가진다. 실제 에러가 아님에 주의해야 한다. 비 봉쇄 소켓을 사용할 경우에는 함수 반환 값이 아닌, errno값을 이용해서 함수 상태를 검사한다.

비 봉쇄는 두 가지 방법으로 구현한다.
  1. 입력이나 출력 함수를 호출하기 전에, 파일들로 부터 입력과 출력이 있는지를 검사한다.
    입출력 다중화epoll에서 사용하는 방법이다. 입출력 함수들은 여전히 봉쇄이지만, 이들을 호출하기전에 ( selectpoll함수로 ) 입출력을 검사해서 데이터가 있을 때만, 입출력 함수들을 검사하기 때문에 "비 봉쇄 인것처럼" 작동한다. 입출력 함수를 비 봉쇄로 해서 입출력 다중화를 구현하는 방법도 있다.
  2. 파일을 비 봉쇄로 한다. 그리고 비동기 통지 (asynchronous notification)을 기다린다.
    예를 들어 클라이언트에서 데이터를 전송하면, 이를 받은 서버의 소켓은 "read event"를 발생한다. 이벤트는 이벤트 정보를 포함하는 데이터에 대한 포인터도 함께 넘긴다. 이벤트 정보에는 이벤트가 발생한 소켓 지정 번호, 이벤트가 발생한 프로세스와 같은 정보들이 들어있다. 이 정보를 이용해서 데이터를 처리한다.
  3. 봉쇄 소켓 : 소켓 작업이 끝나기 전까지 해당 영역에서 대기한다.

입출력 모델은 "봉쇄/비봉쇄" 와 "동기/ 비 동기"의 조합에 따라 4가지 입출력 모델이 존재한다.

  1. 소켓은 기본적으로 봉쇄&동기 모드로 만들어 진다.
  2. O_NONBLOCK로 동기&비 봉쇄 모드로 만들 수 있다.
  3. 입출력 다중화 기술 자체는 비 동기 모델을 따른다. 소켓이 봉쇄면 "비 동기 & 봉쇄" 모델이 된다. epoll도 마찬가지다.
  4. 리얼 타임 시그널과 AIO는 "비 동기 & 비 봉쇄"모델을 따른다. epoll과 입출력 다중화는 소켓을 "비 봉쇄"로 할경우, "비 동기 & 비 봉쇄"모델이 된다.

입출력 다중화와 epoll을 "비 동기 & 비 봉쇄"모델로 해서 얻을 수 있는 이익이 있는지에 대해서는 회의적이다. 어차피 select나 epoll_wait에서 반환된 후에는 읽을 데이터가 있는게 분명하니, 봉쇄 모드로 작동해도 되기 때문이다. 다만 accept를 위한 "듣기 소켓"은 비동기로 했을 때 얻을 이익이 있을 것으로 생각된다. 연결 대기열에 있는 연결 요청을 한번에 꺼내올 수 있기 때문이다.

1.1.1 동기 봉쇄 모델

  1. read함수를 호출하면, 커널 모드로 요청이 가고 입력을 기다린다.
  2. 애플리케이션은 데이터 입력이 있기 까지 봉쇄된다.
  3. 데이터가 입력되면, 커널 모드에서 유저모드로 데이터가 복사된다.

1.1.2 동기 비 봉쇄 모델

  1. read함수는 바로 반환한다. 데이터가 준비되지 않았다면, errno는 EAGAIN으로 설정된다.
  2. 이를 반복한다.
  3. 만약 데이터가 준비되어 있다면, 데이터를 읽는다.
데이터가 준비되기 전까지 바쁘게 순환해야 하는 busy wait 상태에 놓일 수 있다.

1.1.3 비 동기 봉쇄 모델

  1. 동기 비 봉쇄 모델은 계속 read함수를 호출하기 때문에 busy wait 상태에 놓일 수 있다는 단점이 있다.
  2. 비 동기 봉쇄 모델은 입출력 함수 호출전에 입출력 데이터가 있는지를 검사하는 함수(select 혹은 poll)를 미리 배치한다.
  3. 입출력 데이터가 없을 때는 봉쇄된다.
  4. 입출력 데이터가 있으면, 비로서 입출력 함수를 호출한다. 입출력 함수는 봉쇄 모드로 작동한다.
비 동기 봉쇄 모델중 epoll에 관심을 간다. 현재 리눅스에서 가장 효율적인 네트워크 프로그래밍 도구로 알려져 있다.

1.1.4 비동기 비 봉쇄 모델

aio를 기준으로 설명
  1. aio_read함수를 호출한다.
  2. 다른 일을 한다.
  3. 읽을 데이터가 발생하면, 콜백 함수 혹은 시그널 핸들러로 데이터를 처리한다.


1.2 비 동기 입출력의 장점과 단점

비 동기 입출력을 이용하면 단일 프로세스&단일 쓰레드에서 여러 개의 소켓을 처리할 수 있다. (엄밀히 말해서 입출력 다중화는 비동기 입출력은 아니지만, 비 동기 입출력의 범주에 포함시켰다.) 비 동기 입출력이 가지는 장점과 단점에 대해서 알아보도록 하겠다. 여러 개의 소켓을 처리할 수 있기 때문에 멀티 쓰레드 방식과 많은 비교가 될 것이다.
  1. 쓰레드는 생각만큼 효율적이고 자유로운 도구가 아니다. 생각이상의 제약사항을 가지고 있다. 500개 이상의 클라이언트를 처리하기 위해서 500개의 쓰레드를 만든다고 생각해보자. (이 문제는 쓰레드 풀로 어느 정도 해결할 수 있긴 하다.) 비 동기 입출력은 단일 쓰레드처리 방식이므로 이러한 제한에서 자유롭다.
  2. 비 동기 입출력을 쓰레드와 단순비교하는 건 힘들다. 비 동기 입출력은 쓰레드와 같은 병렬처리 방법을 제공하진 않기 때문이다. 하지만 많은 경우 굳이 병렬로 처리하지 않아도 된다. 예를 들어 채팅 프로그램 같은 경우 굳이 멀티 쓰레드 방식을 사용할 필요는 없다.
  3. 멀티 쓰레드(멀티 프로세스)는 복잡한 프로그래밍 기술을 요구한다. 잠금, 동기화, IPC 등등등... 수많은 문제를 해결해야 한다.
  4. 디버깅이 어렵다는 단점이 있다. (그래도 멀티 쓰레드 프로그램보다는 쉽다)
  5. 데이터 연산이 긴 프로그램의 경우 문제가 될 수 있다. 병렬처리가 아니므로 다음 입출력이 오랜시간 기다릴 수 있기 때문이다. 이 문제는 데이터 처리를 위한 쓰레드 풀을 두는 것으로 해결할 수 있다.

최근의 네트워크 프로그래밍 트랜드는 "단일 쓰레드 기반 & 비동기 입출력"이다.

1.3 소켓을 비 봉쇄 입출력에 대응하도록 하기

소켓은 "봉쇄 & 동기"모드로 만들어진다. fcntl(2)함수로 비 봉쇄 모드로 만들 수 있다.
int fd; 
int flags; 
// 우선 F_GETFL로 파일 지정 번호 fd가 가리키는 파일의 flag값을 가져온다. 
if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 
    flags = 0; 
 
// O_NONBLOCK로 비 봉쇄로 만든다. 
fcntl(fd, F_SETFL, flags | O_NONBLOCK); 
 

소켓을 비 봉쇄로 만들었다면, 해당 소켓을 매개 변수로 사용하는 함수들은 바로 반환한다. 데이터 입출력 관련 함수들인 accept(2), connect(2), read(2), write(2), recv(2), send(2)이다. 반환 값은 -1 이므로 반환 값만을 가지고는 함수가 실패했는지, 아니면 비 봉쇄라서 바로 반환한 것인지 확인할 수 없다. errno 값으로 확인해야 하는데, EAGAIN 혹은 EWOULDBLOCK이면 비 봉쇄 소켓에서 데이터가 준비되지 않아서 반환했음을 의미한다. EAGAIN과 EWOULDBLOCK는 POSIX.1 규격에서 동일한 값으로 사용된다.

간단한 비 봉쇄 소켓 프로그램 예제를 만들어 보았다. 설명은 주석으로 대신한다. 에러 처리는 신경쓰지 않았다.
#include <sys/socket.h> 
#include <sys/stat.h> 
#include <arpa/inet.h> 
#include <stdio.h> 
#include <string.h> 
 
#include <fcntl.h> 
#include <stdlib.h> 
#include <unistd.h> 
 
#include <errno.h> 
 
#define MAXBUF  256  
 
// 비 봉쇄 소켓으로 만들기 위한 함수 
int set_nonblock_socket(int fd) 
{ 
    int flags; 
    if((flags = fcntl(fd, F_GETFL,0)) == -1) 
    { 
        perror("fnctl error"); 
        flags = 0; 
    } 
    fcntl(fd, F_SETFL, flags | O_NONBLOCK); 
} 
 
int main(int argc, char **argv) 
{ 
    int server_sockfd, client_sockfd; 
    int client_len, n; 
    char buf[MAXBUF]; 
    struct sockaddr_in clientaddr, serveraddr; 
 
    client_len = sizeof(clientaddr); 
 
    if ((server_sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) 
    { 
        perror("socket error : "); 
        exit(0); 
    } 
    bzero(&serveraddr, sizeof(serveraddr)); 
    serveraddr.sin_family = AF_INET; 
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serveraddr.sin_port = htons(atoi(argv[1])); 
 
    if(bind (server_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1) 
    { 
        perror("Error"); 
    } 
    if(listen(server_sockfd, 5) == -1) 
    { 
        perror("Error"); 
    } 
    while(1) 
    { 
        memset(buf, 0x00, MAXBUF); 
        client_sockfd = accept(server_sockfd, (struct sockaddr *)&clientaddr, 
                            &client_len); 
 
       // 연결 소켓을 비 봉쇄 소켓으로 만든다. 
       set_nonblock_socket(client_sockfd); 
 
        while(1) 
        { 
            memset(buf, 0x00, sizeof(buf)); 
            if ((n = read(client_sockfd, buf, MAXBUF)) < 0) 
            { 
                // errno 값을 한번 더 계산해 줘야 한다. 
                if(errno == EAGAIN) 
                { 
                } 
                else 
                { 
                    printf("read Error %d\n", errno); 
                    close(client_sockfd); 
                    break; 
                } 
            } 
            else if(n == 0) 
            { 
                printf("close %d\n", errno); 
                close(client_sockfd); 
                break; 
            } 
            else 
            { 
                printf("Read Data %s", buf); 
            } 
        } 
    } 
} 
 
이 프로그램은 read함수에서 바로 반환해버리기 때문에 busy wait상태에 놓이게 된다는 문제점이 있다. 실제 이런 식으로 프로그램을 작성하지는 않는다. busy wait 문제 때문에 "동기 & 비 봉쇄"모델은 거의 사용하지 않는다. 일반적으로 입출력 동기화 혹은 epoll, 리얼 타임 시그널과 같은 비 동기 모델과 함께 사용한다. "이런 식으로 비 비봉쇄 소켓을 만들고 에러를 체크하는 구나"하는 정도만 이해하고 넘어가면 될 것 같다.

1.3.1 accept함수와 비 봉쇄 소켓

accept함수는 즉시 반환한다. accept함수에서 비 봉쇄 소켓은 주로 listen으로 만든 연결 대기열에 있는 연결을 한번게 가져오기 위해서 사용한다. 이 방식은 클라이언트의 연결 요청에 빠르게 반응할 수 있다.
  1. 연결 대기열에 3개의 연결이 있다.
  2. EAGIAN을 만날 때 까지 루프를 돌면서 accept함수를 호출한다.

1.3.2 read/write함수와 비 봉쇄 소켓

역시 즉시 반환한다. read함수와 write함수에서 직접 기다리면 busy wait 상태에 놓이므로 보통은 이들 앞에 상태 혹은 이벤트를 검사하기 위한 함수들을 놓는다. select, poll, sigwaitinfo등의 함수들이다. 특정 소켓에 이벤트가 발생해서 read함수가 호출되면, EAGAIN을 만날 때까지 루프를 돌면서 데이터를 읽는다.
while(1) 
{ 
    select(...); 
    while(1) 
    { 
        read(fd,...); 
        if(errno == EAGAIN) break; 
    } 
} 
 

1.3.3 connect함수와 비 봉쇄 소켓

비 봉쇄 소켓으로 connect함수를 호출하면, connect함수는 즉시 반환한다. 연결이 되었는지는 나중에 getsockopt함수로 확인을 한다. 주로 연결 타임 아웃을 검사하기 위한 목적으로 사용한다. 다음과 같은 과정을 거친다.
  1. 소켓을 비 봉쇄로 한다.
  2. connect함수롤 호출한다. 함수는 즉시 반환할 것이다. 연결 과정은 백그라운드에서 진행된다.
  3. select함수를 타임 아웃을 주고 호출한다.
  4. 타임 아웃 시간전에 연결이 성공하면 select함수는 성공적으로 반환할 것이다. 타임 아웃 시간을 초과해서 연결되지 않으면 에러를 반환한다.

비 봉쇄 소켓과 select를 이용한 연결 타임 아웃 검사 예제는 connect timeout문서를 참고한다.

아래 부분은 정리가 안된 내용들임

1.4 입출력 다중화의 입출력 모델

입출력 다중화를 이용하면 봉쇄 소켓 모드로 두 개 이상의 소켓을 처리할 수 있다. 소켓 함수는 봉쇄모드로 작동하지만 select함수가 이들 함수 앞에서 파일 상태를 체크해 주기 때문이다.

입출력 다중화는 select함수로 파일들에 데이터의 입출력이 있는지 확인해서 신호 (반환)하는 방식으로 작동한다. 그러므로 비 동기 입출력 기술이라고 할 수 있다. select함수에서 봉쇄되기 때문에 비 동기 봉쇄 모델을 따른다.

입출력 다중화에서 비 봉쇄 소켓을 사용할 수도 있다. (그래도 여전히 select에서 봉쇄 되므로 프로그램은 비동기 봉쇄 모델이다.)
  1. 클라이언트 연결을 한꺼번에 처리해서 클라이언트 연결 요청 대기 시간을 단축 시킬 수 있다.
    듣기 소켓에 이벤트가 발생해서 accept함수를 호출 하면, 루프를 돌면서 연결 대기열의 모든 클라이언트의 연결을 한꺼번에 가져올 수 있다. EAGAIN을 만날 때 까지 루프를 돌면 된다. 모아서 처리하기 때문에 그만큼 연결 요청 시간을 단축 시킬 수 있다.

관련 예제는 입출력 다중화 네트워크 프로그램 개발문서를 참고한다.

1.5 epoll의 입출력 모델

epoll도 봉쇄 소켓으로 두 개 이상의 소켓을 처리할 수 있다. 입출력 다중화와 동일하다. 입출력 이벤트를 기다리기 위해서 epoll_wait함수에서 봉쇄된다.

역시 비 봉쇄 소켓을 사용할 수도 있다. 얻을 수 있는 이익은 다음과 같다. (입출력 다중화와 동일한 이익을 얻을 수 있다.)
  1. 클라이언트 연결을 한꺼번에 처리해서 클라이언트 연결 요청 대기 시간을 단축 시킬 수 있다.
    듣기 소켓에 이벤트가 발생해서 accept함수를 호출 하면, 루프를 돌면서 연결 대기열의 모든 클라이언트의 연결을 한꺼번에 가져올 수 있다. EAGAIN을 만날 때 까지 루프를 돌면 된다. 모아서 처리하기 때문에 그만큼 연결 요청 시간을 단축 시킬 수 있다.

"비 동기 & 봉쇄 모델"을 따르는 epoll 프로그램의 예는 epoll문서를 참고하기 바란다.

1.6 리얼 타임 시그널의 입출력 모델

리얼 타임 시그널은 그 자체가 비 동기적 정보 통지 도구다. 관리하고자 하는 소켓에 데이터 입출력이 있으면 "시그널"을 발생시키는 방식으로 작동한다. 때문에 소켓을 비 동기, 비 봉쇄 상태로 만들어야 한다. 리얼 타임 시그널 관련 문서는 리얼 타임 시그널문서들을 참고하기 바란다.

2 개인적으로 선호하는 모델

비 동기 봉쇄 모델을 선호한다. 프로세스가 명확하기 때문이다.

물론 AIO와 같은 비 동기 비 봉쇄 모델의 경우, 입출력 작업과 별개로 다른 작업을 할 수 있다는 장점이 있다. 하지만 대부분의 프로그램이 데이터 입력 -> 처리 -> 데이터 출력의 진행 방식을 따르기 때문에, 딱히 "비 동기 비 봉쇄"가 가지는 장점이 필요 없는 경우가 많기 때문이다.

3 POSIX AIO

리눅스에서 제공하는 비 동기 입출력 매커니즘으로 비교적 최근에 (커널 2.6.x) 추가되었다.

3.1 AIO API

aio_suspend 파일 목록에서 이벤트의 발생을 기다린다.
aio_read 이벤트가 발생한 파일에서 데이터를 읽는다.
aio_write 데이터를 쓴다.
aio_return
aio_error
aio_cancel


4 기타 관련 기술들

  1. Twisted : Event driven framework
    • 게임 네트워크 라이브러리로 시작했음
    • python으로 만들어 졌음. 대략 200K라인
    • web, mail, ssh를 포함한 30개 이상의 프로토콜 지원
    • 실질적인 경쟁자로는 ACE가 있음. ACE를 참고해서 개발되었음.

  2. 하나의 프로세스로 여러 개의 소켓을 다룬다.
  3. 우선 BSD 소켓을 공부해야 한다.
    • 최초 만들어진 소켓은 봉쇄형이다. 이 소켓을 fcntl함수로 nonblocking 소켓으로 변경한다.
  4. 입출력 다중화를 먼저 공부하도록 하자.
  5. 최근 트랜드는 epoll, kqueue, Posix AIO, Twisted 이다.
  6. POSIX aio API

 

 

 

 

 

 

 

출처 : http://blog.naver.com/PostView.nhn?blogId=ahncs0728&logNo=150096223790

 

 

 

 

 

 

신고

'Programming > C+C++' 카테고리의 다른 글

[C언어]strcmp함수  (0) 2014.07.18
다시쓰는 C언어 강좌] 079 - 열거형 - enum  (0) 2014.07.18
비동기 입출력 프로그래밍  (0) 2014.04.16
Chapter 1. C++ 시작하기 pdf 파일  (0) 2014.03.31
[C#] Queue, Thread, AutoReset  (0) 2014.03.31

Comment +0

Chapter 1. C++ 시작하기 완성본 입니다.

나중에 변경될 수도 있겠지요. ~~

pdf 파일로 올려둡니다.

망극!


 

대부분의 컴파일은 리눅스상에서 g++를 사용해서 컴파일하는것으로 구성했어요.

 

 

 

 

 

출처 : http://www.lug.or.kr/m/bbs/list.php?bo_table=cpp#view.php?bo_table=cpp&wr_id=2&page=2
         (해당 링크를 따라가시면 제법 괜찮은 내용이 있습니다)

 

 

 

 

 

 

신고

'Programming > C+C++' 카테고리의 다른 글

[C언어]strcmp함수  (0) 2014.07.18
다시쓰는 C언어 강좌] 079 - 열거형 - enum  (0) 2014.07.18
비동기 입출력 프로그래밍  (0) 2014.04.16
Chapter 1. C++ 시작하기 pdf 파일  (0) 2014.03.31
[C#] Queue, Thread, AutoReset  (0) 2014.03.31

Comment +0

 

운영체제

Windows7

최초 작성일 2013/12/10

프로그램 및 버전

VisualStudio2010/.Net FW 4.0

마지막 수정 작성일 -

※ 주의사항

-

 

1. 작업개요

이 포스팅 역시 제가 나중에 써먹기 위해서 작성하는 글 입니다. 

이 포스팅에서 다뤄지는 내용은 Queue, Thread, AutoReset 입니다. 

 

시나리오는 DB에 저장되는 데이터가 꾸준하게 들어옵니다.(누군가가 팍팍 던져 주겠지요.) 

그것을 받아서 DB에 입력을 해야하는데 들어오는 데이터량이 많아 받고쓰고받고쓰고 하면 속도가 어마어마하게 걸릴 것 입니다. 

그래서 들어오는 데이터는 Queue에 저장하고 다른 Thread로 그 Queue에 담긴 데이터를 DB에 입력하려고 합니다. 

지금은 DB에 입력하는 부분이지만 시리얼통신, TCP, UDP통신에서도 이같은 방법으로 데이터를 받고 처리할 수 있습니다. 

 

2. 작업내용

1) Queue, Thead, AutoReset 선언

소스 및 이미지

line1 :        Queue<String> orderDBQueue = new Queue<String>();
line2 :        Thread orderDBQueueThread = null;//orderDBQueue 처리 스레드
line3 :        bool isRunOrderDBQueueThread = true;
line4 :        AutoResetEvent orderDBQueueThreadEvent = new AutoResetEvent(false);

설명

보시는 봐와 같이 line1에서는 String을 담을 수 있는 큐를 생성하였습니다. 예시로 String을 하였지만 int라든지 만일 통신에서의 

패킷을 담을 것이면 해당 패킷의 DataClass를 넣으셔도 됩니다. 

 

line2에서는 데이터 처리를 하는 스레드 입니다. line3는 스레드 처리 부분에서 사용이 되는데요. 아래 2)에서 스레드를 생성하면 

 

isRunOrderDBQueueThread값을 true로 바꿔 4)에서 큐에 담긴 데이터를 처리할 수 있게 합니다. 

 

StopThread()에서 다시 false값으로 변경하며, StopThread()가 호출된 시점까지의 데이터만 깔끔하게 처리하고 끝날 수 있게 

도와줍니다. 

 

line4의 AutoResetEvent는 대기중인 스레드에 이벤트가 발생하였음을 알려주며, 3)에서 데이터를 큐에 넣고 스레드를 활성화 

할 수 있게 orderDBQueueThreadEvent.set() 을 호출합니다. 4)에서 큐에 데이터가 없으면  

orderDBQueueThreadEvent.WaitOne()을 호출하여 '얼음'을 만들어주고 큐에 데이터가 들어가면 '땡' 해주는 것 입니다.

 

2) 스레드 생성 소멸 함수 만들기

소스 및 이미지

 //생성 함수

        private void StartThread()
        {
            orderDBQueue.Clear();
            if (orderDBQueueThread == null)
            {
                orderDBQueueThread = new Thread(new ThreadStart(DBInsertOrderData));
                orderDBQueueThread.Priority = ThreadPriority.Normal;
                isRunOrderDBQueueThread = true;
                orderDBQueueThread.Start();
            }
        }
 
//소멸 함수
        private void StopThread()
        {
            if (orderDBQueueThread != null)
            {
                isRunOrderDBQueueThread = false;
                orderDBQueueThreadEvent.Set();
                orderDBQueueThread.Abort();
                orderDBQueueThread = null;
            }
        }
 

설명

스레드는 스레드를 생성하는 StartThread()와 이를 다시 소멸하는 StopThread()함수를 만들어 관리하는 것이 편리합니다. 

프로그램 생성자에 StartThread()를 넣어 Thread가 같이 생성하도록 합니다. 

그리고 프로그램이 종료되는 시점에 StopThread를 이용하여 프로그램종료와 동시에 해당 스레드도 소멸되게 합니다. 

만일 생성한 스레드를 소멸시키지 않는다면... 영원히 살아있겠지요 

 

3) Queue에 데이터 넣기

소스 및 이미지

 

        public void InsertErrQueue(String str)
        {
            lock (orderDBQueue)
            {
                orderDBQueue.Enqueue(str);
            }
            orderDBQueueThreadEvent.Set();
        }
 

설명

위 함수는 외부로 노출하여 DB에 넣을 데이터를 넘겨받는 부분입니다. 이부분에서 저는 특별한 작업없이 바로 큐에 넣습니다. 

여기서 보시면 큐에 lock을 걸고 Enqueue를 하게 되는데요. 그 이유는 잘못하면 Dequeue하는 작업과 꼬이게 되어 병목현상? 

이 발생할 수 있어 lock을 걸고 Enqueue를 합니다. 물론 Dequeue할때도 lock을 걸고 진행합니다. 

  

  

4) Thread처리 함수

소스 및 이미지

        private void DBInsertOrderData()
        {
            while (isRunOrderDBQueueThread)
            {
                try
                {
                    int queueCount = 0;
                    lock (orderDBQueue)
                    {
                        queueCount = orderDBQueue.Count;
                    }
 
                    if (queueCount <= 0)
                    {
                        orderDBQueueThreadEvent.WaitOne();
                    }
                    else
                    {
                        String str = string.Empty;
                        lock (orderDBQueue)
                        {
                            str = orderDBQueue.Dequeue();
                        }
                        InsertDBData(str);
                    }
                }
                catch (Exception err)
                {
                    Debug.WriteLine(string.Format("***[{0}]FancINFOMng-INFOMng.cs-DBInsertOrderData() : 오류발생({1})"
                    DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), err.Message));
                }
            }
        }

설명

이제 DB에 넣는 스레드에서 처리하는 부분입니다. 위에서 언급하였던 isRunOrderDBQueueThread도 보이구요.

Dequeue할때 lock하는 것도 보입니다.

소스를 설명하자면 isRunOrderDBQueueThread를 통해 스레드가 시작되었다는 것을 알고 while문이 돕니다.

그리고 lock을 걸어 큐카운트를 확인하여 큐에 데이터가 없으면 orderDBQueueThreadEvent.WaitOne();

을 호출하여 '얼음'을 만들어주고 만일 데이터가 있으면 lock을 걸고 Dequeue를 하여 DB에 insert해주는 InsertDBData함수를

호출합니다.

 

3. 작업결과

-

※ 참고 사이트

-

위 사이트를 참고 하였습니다.

 

 

 
 

 

 

출처 : http://happytomorrow.net/90186225934

 

 

 

 

 

 

신고

'Programming > C+C++' 카테고리의 다른 글

[C언어]strcmp함수  (0) 2014.07.18
다시쓰는 C언어 강좌] 079 - 열거형 - enum  (0) 2014.07.18
비동기 입출력 프로그래밍  (0) 2014.04.16
Chapter 1. C++ 시작하기 pdf 파일  (0) 2014.03.31
[C#] Queue, Thread, AutoReset  (0) 2014.03.31

Comment +0