보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

.멀티플렉싱란?
하나의 프로세스를 가지고 여러개의 클라이언트와 통신 할 수 있습니다 즉 전송로 1개로 많은 이들이 통신을 한다는 의미입니다 여러개의 클라이언트와 통신 할수 있는것이 장점이지만 선이 하나이므로 한쪽클라이언트와 통신중일 경우에는 다른 클라이언트는 기다려주어야합니다



참고>>인터럽트와 폴링방식이 결합된 방식



참고>>반이중 전이중?

.반이중 통신
양쪽 방향으로 신호의 전송이 가능하기는 하지만 경우에 따라 반드시 한쪽 방향으로만 전송이 이루어지게 한 방식을 말합니다.

주컴퓨터와 단말기가 반이중 방식으로 통신할 경우, 주컴퓨터가 단말기에 데이터를 보내는 동안은 단말기에서 데이터를 입력할 수 없으며, 반대로 단말기에서 데이터가 입력되고 있는 동안은 주컴퓨터가 단말기로 데이터를 보낼 수 없습니다

.전이중 통신
송신을 하면서 동시에 수신도 할 수 있는 방식을 말한다.


 
.구현기술
1)select함수를 이용하여 파일 디스크립터의 변화를 확인하고 블럭상태로 대기
2)저수준 입출력을 이용하여 데이터 전송 
3)시그널처리(ctrl + c)를 활용 


.select()사용


.server의 랑데뷰 소켓
accept를 하게되면 서버에 랑데뷰 소켓을 1차적으로 사용하게 됩니다 

랑데뷰 소켓이란 ?
 클라이언트에서 보내져서 큐상에서 대기하고 있는 정보(서버와 통신을 원하는 클라이언트의 정보)를 두번째 인자에 연결한 다음 클라이언트소켓과 연결된 새로운 소켓을 반환합니다
앞으로 그 클라이언트와의 통신은 반환된 소켓 디스크립터를 사용하게됩니다
반환값-- 성공시 communication 소켓 디스크립터, 실패시 -1을 반환
이 communicatoin 소켓을 이용하여 통신을 합니다

이러한 랑데뷰소켓은 4번부터 지정이되고 

 I err  랑데뷰   


서버로 접속하는 클라이언트는 5번부터 접속이됩니다

 I err  랑데뷰   클라이언트


.저수준 입출력 : 데이터 송수신


.시그널처리 (ctrl + c)
:통신중 종료를 처리하는 방법에는 여러가지가 있지만 2가지정도를 생가하였습니다 
1. 시그널즉 ctrl + c의 종료메세지를 눌렀을때 처리해주는 방법과
2. 종료 메세지를 입력받았을때 처리하는 방법입니다

2가지 방법으로 구현하는데 있어서 server과 사용하는 방법과 client를 사용하는 방법은 약간의 차이가 있습니다 client경우 열려 있는 소켓만 닫아주고 종료 메세지를 서버에 통지하면  되지만 서버의 경우에는 접속해있는 소켓을 모두 닫아주고 메세지를 통지해야되기 때문입니다
 


일단 시그널 처리를 이용하여 소켓을 닫습니다 시그널 관련 함수 처리는 아래와 같습니다 
Client

#include <signal.h>

/*전연변수 처리*/
int iDs;
unsigned char ucBuf[255];

/*시그널 선언*/
struct sigaction stFuncInfo;

memset(&stFuncInfo,0x00,sizeof(struct sigaction));
stFuncInfo.sa_handler = Socketclose;
sigaction(SIGINT,&stFuncInfo,NULL);


시그널이 발생 했을때의 처리하는 함수(SocketClose)를 만들어 줍니다 

void Socketclose(int iArg)
{
  printf("Client Close(ctrl+c)\n");
  write(iDs,"BYE!!\n",sizeof("BYE!!\n"));
  close(iDs);
  exit(0);
}


시그널이 발생하면 클라이언트에 메세지를 하나 출력하고 서버로 종료 메세지(BYE!!)를 보냅니다 그리고 소켓을 닫고 종료하게 됩니다

Server
client 많이 비슷 하지만 서버쪽에서는 함수에 각접소했는 클라이언트들을 모두 닫아주도록 합니다 

void sig_handler(int arg)
{
  printf("Server Close\n");
  for(iCnt = 0;iCnt < iCnum ;iCnt++)
  {
    write(iaClient[iCnt],"Server Close!(ctrl+c)",sizeof("Server Close!(ctrl+c)"));
    close(iaClient[iCnt]);
  }  
  close(iDs);
  exit(0);
}


결과보기>>
클라이언트에서 ctrl + c를 눌러 종료를 하니 종료 메세지가 뜨고 종료 하였습니다

다른 클라이언트는 종료되지 않고 대기중입니다

BYE!!란 메세지가 종료되면서 server로 전달되었습니다

>>여기까지 하면 약간의 문제점이 발생합니다 기타 문제점은 아래에서 모아 한꺼번에 다루었습니다

.문자종료 처리
: 문자종료처리란 특정 종료메세지를 입력했을때 종료하게 하는것입니다 이것은 서버에서 적으면 클라이언트와 서버가 종료되고 클라이언트에서 치면 서버에 종료메세지를 보내고 해당클라이언트만 종료되게 됩니다

"quit"를 받으면 종료하게 됩니다

Client - 들어온 메세지를 확인하여 종료합니다

    if(sizeof("quit")==iRet)
      {        
        if(0==memcmp("quit",ucBuf,iRet))
        {
          break;
        }
      }


Server - 각소켓에 메세지를 보내고 종료합니다

     if(sizeof("quit")==iRet)
       {
          if(0==memcmp("quit\n",ucBuf,iRet))
          {
            for(iCnt = 0;iCnum>iCnt;iCnt++)
            {      
              write(iaClient[iCnt],ucBuf,iRet);
            }
            break;
          }
        }


결과보기>>

quit을 누르면 종료됩니다

다른 클라이언트는 종료되지 않았습니다

서버역시 정상적으로 종료가 됩니다 


 
참고>>man sigaction
linux프로그램에 있어서 기본은 man페이지를 보는것과 구조체를 검색하여 보는 것입니다
맨페이지의 내용은 아래와 같습니다
[##_http://smeffect.tistory.com/script/powerEditor/pages/1C%7Ccfile29.uf@114A1C3B4EA7C0861737C9.jpg%7Cwidth="437"_##]
함수포인터로 되어있으면 어떻게 사용해야되는지 잘 나와 있습니다

그리고 sigaction의 구조체는 /usr/include/bits/sigaction.h에 있습니다 grep을 이용하여 검색하면 쉽게 찾을수 있습니다

시그널과 문자처리를 하게되면 몇가지 문제점이 발생하게 됩니다 대응되는 처리는 아래와 같습니다

1문제점) 클라이언트 빈소켓 처리
:닫혀있는 소켓 빈 소켓상태인지확인 을 하기위하여 if문을 걸어주고 iRet == 0과 비교해줍니다 

if(1 == FD_ISSET(iDs,&fsStatus))
{      
  iRet = read(iDs,ucBuf,sizeof(ucBuf));
  if(0==iRet)
  {
    break;
  }


2문제점) 클라이언트에서 시그널을 발생하여 종료시 다른 클라이언트에게도 종료되는 현상 처리
:시그널이 발생하면(BYE!!)라는 메세지가 들어오게되는데 이메세지를 한번만 출력하고 다른 클라이언트에게는 보내지 않으면 됩니다

3문제점) 클라이언트가 서버로  종료메세지를 보내면 남은 클라이언트 모두 메세지가 전달되는 현상 처리
:"BYE!!"를 클라이언트에서 걸러주도록 하거나 서버에서 BYE!! 송신하지 않도록 처리하면됩니다  


















 
Posted by mantwo
.3Way Handshake
TCP 3 Way Handshake는 TCP/IP프로토콜을 이용해서 통신을 하는 응용프로그램이 데이터를 전송하기 전에 먼저 정확한 전송을 보장하기 위해 상대방 컴퓨터와 사전에 세션을 수립하는 과정을 의미합니다.이 과정에서 송신측과 수신측에 총 3개의 패킷이 생성이 되기 때문입니다

참고>>3way 4way? 뭐가 다른가?
예를 들어 악수를 합니다
3way의 경우 "악수합시다" 상대방에게 요청을 하여 "그래요 합시다"하여 의사를 결정후 손을 내밀어서 악수의 준비를 완료가 되면 악수가 이루어지는 방식이라고 볼 수 있습니다
4way의 경우 "악수합시다" 상대방에게 요청을 하면 "저말씀입니까?" "그래요 당신과 하고 싶습니다"하고 의사를 제차 확인한 후에 상대방이 "그래요 합시다"하고 손을 내밀어서 악수의 준비가 완료되면 악수가 이루어지는 방식입니다

.목표
TCP통신에서 socket으로 생성된 두 프로그램이 일련의 과정을 거쳐서 메세지를 주고 받고 종료될때까지 패킷의 내용을 분석하여 의미를 파악하는 것입니다

.준비단계 - test를 위해 3가지의 파일이 필요합니다
소켓설정은 socket(AF_INET,SOCK_STREAM,IPPROTO_TCP) ;으로 동일합니다

1)client.c 
   socket() ->  connect() ->  메세지 수송신

2)server.c
   socket() ->   bind() ->  listen() -> accept() ->   메세지 수송신

3)packet analyzer.c 
   1계층 ethetnet -> 2계층 IP -> 3계층 TCP
   * 20회 정도를 실행하도록 for문을 걸었습니다
   * 1,2계층의 정보는 출력하지 않습니다 

참고>>고정 아이피 사용?
TCP를 테스트하기 위해서 내부 아이피주소(127.0.0.1)을 사용하면 패킷이 잡히지가 않습니다 그래서 고정아이피로 세팅해주고 자신의 컴퓨터에서 client server를 실행하면 않되고 2대의 서로 다른 컴퓨터에서 테스트를 해야 정상적으로 출력이 됩니다

.실행
server를 실행한후 packet과 client를 실행 하였습니다
15번 실행되면 TCP정보가 아래 그림과 같이 나타났습니다

./server

 

./packet analyzer



.패킷분석표 - 프로그램을 실행하여 나온 20개의 데이터 값을 정리하여 표로 만들었습니다

순서

Source

Destination

Seq Num

Ack Num

Fin

Syn

Ack

1

1682

49152

1100612098

1155894860

0

0

1

2

2169

3000

1306455164

0

0

1

0

3

3000

2169

1306455165

1036455165

0

1

1

4

3697

2869

1156146258

0

0

1

0

5

2869

3697

2068327811

115614259

0

1

1

6

2169

3000

1306455165

1307210305

0

0

1

7

2169

3000

1306455165

1307210305

0

0

1/psh

8

3000

2169

1307210305

1306455170

0

0

1/psh

9

3697

2869

1156146259

02068327812

0

0

1

10

3000

2169

1307210305

1306455170

0

0

1

11

3000

2169

1307210310

1306455170

1

0

1

12

2169

3000

1306455170

1307210310

0

0

1

13

3697

2169

1156146259

2068327812

0

0

1

14

2169

3000

1306455170

1307210311

1

0

1

15

3000

2169

1307210311

1306455171

0

0

1

 

참고>>
실행하던 시점(client 3000)의 server에서 다른 컴퓨처와 연결된 client(3697)가 있어서 표에서는 2가지의 값을 나타나고 있습니다

 

참고>>flag
SYN(Synchronization)
세션을 설정하는 데에 사용되며 초기에 시퀀스 번호를 보내게 된다. 시퀀스 번호는 임의적으로 생성하여 보낸다.

ACK(Acknowledgement)
받는 사람이 보낸 사람 시퀀스 번호에 TCP 계층에서 길이 또는 데이터 양을 더한 것과 같은 ACK를 보냅니다. ACK의 번호와 응답을 통해 보낸 패킷에 대한 손실을 판단하여 재전송 하거나 다음 패킷을 전송한다.

FIN(Finish)     
세션을 종료시키는 데에 사용되며 보낸 사람이 더이상 보낸 데이터가 없음을 보여준다.

RST(Reset)
재설정(Reset)을 하는 과정이며 양방향에서 동시에 일어나는 중단 작업이다. 비 정상적인 세션 연결 끊기에 해당한다.

PSH(Push)
대화형 트랙픽에 사용되는 것으로 버퍼가 채워지기를 기다리지 않고 데이터를 전달한다. 데이터는 버퍼링 없이 바로 위 Layer가 아닌 7 Layer의 응용프로그램으로 바로 전달한다.

.Handshake의 단계
1) 연결요청 단계 - 3way handshake



위의 패킷분석표에서 2,3,6에 해당합니다

2

2169

3000

1306455164

0

0

1

0

3

3000

2169

1306455165

1036455165

0

1

1


2 번에서는 비어있던 3000포트 향해서 채워지고 syn이1 이되었습니다
 여기는 "저와 악수를 하겠습니까"에 해당합니다

3 번에서는 3000포트에서 부터 syn1 ack1을 보냅니다
 여기는 "그래요 합시다"에 해당합니다

6

2169

3000

1306455165

1307210305

0

0

1


6번에서는 3000포트를 향해서 ack1을 다시 보냅니다
 손을 내밀며 연결 요청이 되었음을 가리킵니다 

2) 연결종료 단계 - 4way handshake


위의 패킷분석표에서 11,12,14,15에 해당합니다

11

3000

2169

1307210310

1306455170

1

0

1

12

2169

3000

1306455170

1307210310

0

0

1

11번에서 fin 1 ack 1을 보내어 종료를 하겠다는 의사를 밝힙니다
12번에서 일단 1번 생각하고

14

2169

3000

1306455170

1307210311

1

0

1

15

3000

2169

1307210311

1306455171

0

0

1

14번에서 fin 1 ack 1을 보내서 확실한 결정을 내립니다
15번에서 종료메세지 처리를 완료하게됩니다

문제점>>ack 카운트
11 12번의 데이터를 보면 akc가 카운트 되지 않고 있습니다 분석해본 결과로는 fin 으로 받은 다음 2번을 보낸 후에 최종결과를 보내기 때문에 받아 들이는 입장에서는 14번이 되어야지만 데이터를 처리 할 수 있습니다 그래서 14번부터 ack 가num에 카운트 됩니다 

참고>>데이터 전달 psh

7

2169

3000

1306455165

1307210305

0

0

1/psh

8

3000

2169

1307210305

1306455170

0

0

1/psh


7번과 8번에서는 psh(push)가 1세팅되었습니다 이것은 상위 응용프로그램 단계에 데이터가 쓰여졌다는 의미입니다 즉 read write로 데이터를 주고 받았다는 것을 알수 있습니다
Posted by mantwo

.Bitmap?
: 비트맵(영어: Bitmap, 문화어: 비트매프, 비트배렬표)은 컴퓨터 분야에서 디지털 이미지를 저장하는 데 쓰이는 이미지 파일 포맷 또는 메모리 저장 방식의 한 형태이다. 보다 일반적으로는 래스터 그래픽스(점방식)라고 한다. 화면 상의 각 점들을 직교좌표계를 사용하여 화소 단위로 나타낸다. 그림을 확대하면 각 점이 그대로 커져 경계선 부분이 오돌도돌하게 보이는 계단 현상이 나타나며, 이를 좀 더 부드럽게 처리 하기 위한 알고리즘들(쌍삼차 필터링, 이중선형 필터링 등)이 있다.
가로 곱하기 세로 만큼의 픽셀 정보를 다 저장해야 하기 때문에 벡터 방식의 이미지나 텍스트 자료에 비해 상대적으로 용량이 크고 처리 속도가 느리다. 이를 개선하기 위해 JPEG, GIF, PNG 등의 다양한 파일 형식이 개발되었다.

.Bitmap viewer 그리고 목표
: Bitmap을 열어서 1차적으로 파일내의 정보를 불러와서 화면에 나타내어주며 2차적으로는 data를 분석하여 그림이 출력되게 합니다 그리고 추가로 기타 회전 색반전등의 기능을 넣습니다

.진행계획
총 6단계로 나누어 비트맵뷰어를 작성해 나갈 계획입니다
각단계별 내용은 아래 표와 같습니다

 1_ 비트맵 정보 출력 및 구조파악
 2_ 비트맵 이미지 출력
 3_ 원하는 비트맵 선택하여 출력
 4_ 부가기능1 - 회전
 5_ 부가기능2 - 확대축소
 6_ 부가기능3 - 기타기능

.Bitmap의 기본적인 구조
 FILE HEADER : 파일에 대한 정보를 저장
 INFORMATION HEADER : 실제 비트맵의 세부 정보 저장
 PALETTL : 각 빅셀이 가지고 있는 RGB값을 저장
                 색상이 256이하일 경우에만 해당
 DATA : 각 픽셀의 색상값 저장

한가지 색으로만 되어 있는 비트맵을 헥사뷰로 출력하였습니다>>

위 그림의 정보를 토대로 분석하도록 하겠습니다

.Bitmap의 구조체 HEADER
1. FILEHEADER 구조체 - 헤더에 속하며 3가지의 정보를 가지고 있습니다 정보는 아래와 같습니다

typedef struct tagBITMAPFILEHEADER
{
          WORD               bfType;                  // "BM" 이라는 값을 저장함
          DWORD             bfSize;                   // 바이트 단위로 전체파일 크기
          WORD               bfReserved1;          // 예약된 변수
          WORD               bfReserved2;          // 예약된 변수
          DWORD             bfOffBits;                // 영상데이터 위치까지의 거리
} BITMAPFILEHEADER;

오픈한 파일이 비트맵 파일인지를 가리키는 것이 bfType입니다  "BM" 문자가 있다면 Bitmap입니다.
bfOffBits 는 파일 시작부분에서 실제 데이터가 존재하는 위치까지 바이트 단위의 거리를 나타내며. 오프셋 (offset) 이라고 합니다.

참고>> 
WORD 는 2바이트 (unsigned short), DWORD 는 4 바이트 (unsigned long) 
 

2. INFOHEADER - bmp의 상세한 정보를 가지고 있고 그내요은 아래와 같습니다

typedef struct tagBITMAPINFOHEADER
{
          DWORD              biSize;                       // 이 구조체의 크기 
          LONG                biWidth;                      // 픽셀단위로 영상의 폭
          LONG                biHeight;                     // 영상의 높이
          WORD                biplanes;                    // 비트 플레인 수 (항상 1)
          WORD                biBitCount;                 // 픽셀당 비트수 (컬러, 흑백 구별)
          DWORD              biCompression;          // 압축유무
          DWORD              biSizeImage;              // 영상의 크기 (바이트 단위)
          LONG                biXPelsPerMeter;        // 가로 해상도
          LONG                biYPelsPerMeter;        // 세로 해상도
          DWORD              biClrUsed;                 // 실제 사용 색상 수
          DWORD              biClrImportant;            // 중요한 색상 인덱스
} BITMAPINFOHEADER;

3. 팔레트 구조체  - 잘 사용되지 않습니다 내용은 아래와 같습니다 
typedef struct tagRGBQUAD
{
           BYTE          rgbBlue;                  // B 성분 (파란색)
           BYTE          rgbGreen;                // G 성분 (녹색)
           BYTE          rgbRed;                   // R 성분 (빨간색)
           BYTE          rgbReserved1;          // 예약된 변수
} RGBQUAD;

 팔레트는 인덱스에 의한 컬러값을 저장하기 위한 구조체이다. 이 구조체를 사용하여 팔레트의 수 만큼 배열을 할당하여 저장한다. 256 컬러모드의 영상은 팔레트배열 크기가 256 개, 16 비트 컬러 영상은 팔레트 크기가 2^16 개이다. biClrUsed 변수를 참조하면 된다. 흑백영상의 경우 팔레트는 256 개이며, 트루컬러의 경우는 인덱스 저장이 아니라 데이터값을 직접 저장하므로 팔레트가 없다.

.구조체 정보 출력
위의 구조체 정보는 bitmap의 파일에 형성하는데 있어서 중요한 정보들을 지니고 있습니다 이러한 정보들을 정해진 위치에서 불러들인 파일에 따라 그 값만 변화됩니다 불러들인 정보를 가지고 실제로 비트맵을 화면에 나타내는데에도 사용하고 어떻게 구성되어있는지에 대한 정보도 확인 할 수 있습니다

오프셋 : 위 구조체를 바탕으로 위치를 정리하였습니다


예제>magic number(signature)를 출력하기
1) 파일을 엽니다
 hFile=CreateFile(Sub,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);   

2)edit컨트롤을 사용하여 값이 들어갈곳을 만들어 줍니다
hEdit = CreateWindow(TEXT("edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_READONLY | ES_CENTER, 750, 105, 110, 25, hWnd,(HMENU)ID_EDIT,g_hInst,NULL);  

3)원하는 위치를 wsprintf를 이용하여 입력하고  SetWindowText를 이용하여 출력합니다
ReadFile(hFile,buf,BSIZE,&dwRead,NULL);
 wsprintf(tt, TEXT(" %c%c"),buf[0],buf[1]);
 SetWindowText(hEdit,tt);

결과보기>>추가로 file size 가로 세로 폭을 출력하였습니다

참고>>가로출력
 wsprintf(tt,TEXT("%d "),*(int *)(&buf[22])) ;
 SetWindowText(hEdit3,tt);


4. DATA - Bitmap 이미지의 정보가 들어있습니다


 1)RGB
Bitmap의 저장 방식 RGB세개의 색상으로 저장이 되는데 위 헥사뷰 36번부터 시작하며

 B
 FF 00 00 

위의 값이 반복되는것으로 보아 단순히 파란색인 그림임을 알 수 있습니다 

 2)4의배수(가로길이)와 패딩
 비트맵은 메모리 저장시, 가로줄의 크기는 항상 4 바이트의 배수가 되어야 한다. 실제 사용하는 영상의 가로길이는 4 바이트의 배수가 아닐 수 있으므로 이럴 경우는 4 의 배수바이트로 바꾸어 저장합니다

  예를 들어 지금 BMP 로 저장할  데이터의 실제 크기가 78 x 60 이라면 가로픽셀 78 은 78 byte 이고, 4 의 배수가 아니므로 80 바이트로 만들고 나머지 두 바이트는 아무 값이나 넣어줍니다. 실제 저장되는 메모리는 80 x 60 픽셀의 크기가 됩니다 여기서 남은 2바이트는 패딩값으로 비어 있는 쓰레기 값입니다


 3)반전
 우리가 보는 그림의 아래부터 순서대로 그림이 들어가기 때문에 실제로 저장된 상태는 뒤집혀 있습니다 이것을 바로 잡도록 해주어야 합니다

 


.비트맵 출력
비트맵을 정확하게 출력하기 위해서는 이상 3가지의 상황을 고려해야 합니다

고려하지 않고 출력하면 대체로 심각한 노이즈에 상하가 사선으로 짤려서 나오게 됩니다



제대로 출력을 하기 위해서는 
 1) setpixel명령을 사용하여  1pixel로 찍어주면됩니다 
 2) 2중 for를 사용하여 좌우 상하로 데이터를 찍어주는 중요한것은 3칸(rgb)씩 전진해야 합니다
 3) 데이터가 시작하는 부분 부터 iOff= *((int *)(&buf[10])); 시작하도록 합니다
 4) 패딩값을 계산하여 좌우 좌표가 패딩값이 시작하는 위치에 왔을때 건너뛰게 합니다
     공식 : (전체사이즈 - 픽셀사이즈)/높이 => iBMsize = (iBMsize - iW*iH)/iH;

소스예제는 아래와 같습니다

소스코드
/* 비트맵 가로 세로 계산 */
  iW = *((int *)(&buf[18])); 
  iH = *((int *)(&buf[22]));
      
  iW = iW*3;
  iH = iH;
    
  iOff= *((int *)(&buf[10]));
        
  iBMsize=*((int *)(&buf[34]));
    
  iBMsize = (iBMsize - iW*iH)/iH;
  
  hdc =GetDC(hWnd);
  hMemDC = CreateCompatibleDC(hdc);

  MyBitmap = CreateCompatibleBitmap(hdc,iW/3,iH);    
  OldBitmap = (HBITMAP)SelectObject(hMemDC,MyBitmap);
  for(iY=1;iY<=iH;iY++)
  {        
    for(iX=0;iX<iW;iX+=3)
    {  
      SetPixel(hMemDC,iX/3,iH-iY,RGB(*(char *)(&buf[iOff+2]),*(char *)(&buf[iOff+1]),*(char *)(&buf[iOff])));
      iOff +=3;
    }
    iOff=iOff+iBMsize;
  }

  SelectObject(hMemDC,OldBitmap);
  DeleteDC(hMemDC);
  ReleaseDC(hWnd,hdc);
  
  InvalidateRect(hWnd,NULL,TRUE); 


참고>>hMemDC?
위코드에서는 그려지는 대상이 hMemDC입니다 이유는 고속복사 BitBlt를 사용하여 메모리DC에 저장한다음 출력해주기 위해서 입니다 BitBlt를 사용하지 않아도 출력이 되지만 화면을 움직일때마다 다시 그리게 되면 아래서 부터 위로 그려지는 속도가 느껴질 정도로 느려지게 됩니다 
BitBlt(hdc,30,50,(*(int *)(&buf[18])),(*(int *)(&buf[22])),hMemDC,0,0,SRCCOPY);

고>>MyBitmap = CreateCompatibleBitmap(hdc,iW/3,iH);  
비트맵을 그리기위해서는 비트맵을 그릴 공간이 필요한데 그공간을 마련해주는 역활을 하는것이 CreateCompatibleBitmap(hdc,iW/3,iH); 입니다

.API 전체적 흐름
: 프로그램을 제작하기전에 어떻게 구현해 나갈것인가 미리 구상을 해보았습니다 단순히 기본문에서 소스들을 추가해나가니 소스가 깔끔하지 못하고 복잡해지며 정리가 잘되지 않았습니다 그리고 에러 발생시 처리를 해야될 상황에서 곤란하여 기본형에서 시작하되 원하는 기능과 관련 소스들을 정리하여 만들어 나가는 것이 가장 좋은 방법이 아닐까 생각이되어 그려보게 되었습니다

전체적인 흐름>>

1) Create : 창을 생성하고 초기 값을 세팅하여 미리 지정해둔 비트맵을 화면에 뿌려줍니다

2) 버튼 컨트롤
   WM_KEYDOWN : 버튼을 눌력 상하로 스크롤이 가능 합니다 간단합니다 원하는 키값을 받으면 원하는 동작을 하도록 하면됩니다 아주 기본입니다

   WM_MOUSEWHEEL : 스크롤시 마우스 휠을 가능하도록 하였습니다
   case WM_MOUSEWHEEL:
    SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,&Lines,0);
    for(i=0;i<Lines;i++)
    {
      if((short)HIWORD(wParam)>0)
      {
        ylnc = -Lines*20;
      }
      else
      {
        ylnc = Lines*20;
      }
    }
    ylnc = max(-yPos,min(ylnc,(*(int *)(&buf[22]))-yPos));
    yPos = yPos + ylnc;
    GetClientRect(hWnd,&crt);
    SetRect(&rt,10,50,(*(int *)(&buf[18]))+50,(*(int *)(&buf[22]))+50);
    ScrollWindow(hWnd,0,-ylnc,&rt,&rt);
    SetScrollPos(hWnd,SB_VERT,yPos,TRUE);
    return 0;  

   WM_VSCROLL : 그림이 화면 밖으로 나가게 되면 스크롤을 하여 화면 밖의 이미지를 볼수 있도록 합니다
  case WM_VSCROLL:
    ylnc = 0;
    switch(LOWORD(wParam))
    {
      case SB_LINEUP:
        ylnc = -20;
        break;
      case SB_LINEDOWN:
        ylnc = 20;
        break;
      case SB_PAGEUP:
        ylnc = -200;
        break;
      case SB_PAGEDOWN:
        ylnc = 200;
        break;
    }
    if(0 > yPos+ylnc)
      ylnc =-yPos;
    if((*(int *)(&buf[22]))<yPos+ylnc)
      ylnc=(*(int *)(&buf[22]))-yPos;
      yPos = yPos + ylnc;
    
    GetClientRect(hWnd,&crt);
    SetRect(&rt,10,50,(*(int *)(&buf[18]))+50,(*(int *)(&buf[22]))+50);
    ScrollWindow(hWnd,0,-ylnc,&rt,&rt);
    SetScrollPos(hWnd,SB_VERT,yPos,TRUE);
    return 0;

참고>> 휠과 스크롤
휠과 스크롤은 약간 관계가 있습니다 키보드를 눌려 한칸씩 스트롤이 되는것과 휠을 돌려서 한칸씩스크롤 되는것은 비슷하기 때문입니다 위의 2코드는 버튼과 마우스휠 동시에도 잘작동하도록 각각의 케이스 문에다가 scroll함수를 넣어두었습니다 휠이 스크롤이 아닌 다른용도로 사용한다면 스크롤이 아닌 다른 기능을 추가하여 스크롤을 사용합니다 확대 축소나 혹은 휠로 파일을 이동하도록 하는 것도 가능합니다

3)PAINT : 비트맵을 계산하여 화면에 뿌려줍니다 여기서 2가지 파트로 나누었습니다
Paint : hMemDC를 가지고 BitBlt를 이용하여 고속복사를 수행합니다 고속복사를 사용하지 않게 되면 화면 에 그림이 떠지는 속도가 상당히 느려집니다 아마 아래에서 부터 위로 그려지는 에니메이션을 보게 될것입니다

void BitmapDraw(hWnd) : 비트맵의 정보를 뿌려주는 역활과 데이터를 계산하여 hMemDC에 넣어주는 역활을 합니다 위에 있는 이중for문이 여기에 해당합니다 이렇게 따로 함수 처리를 해준 이유는 open을 눌러 비트맵을 선택했을때나 초기 시작화면 기타 처리 상황이 발생 했을 경우에 그림을 표시하기 위해서 만들었습니다

4)COMMAND : radio와 Menu.rc를 처리하고 BitmapDraw() switch에 들어온 명령에 따라 호출됩니다

추가>> 경로 출력
:비트맵을 불러드릴때 sample.bmp를 처음 불러들이고 이후에 선택된 값을 불러드릴때 불러드린 경로를 출력하도록 하였습니다

먼처 배열을 선언하고 초기화를 하여 줍니다

static TCHAR *Sub=TEXT("park.bmp");

그리고 열어주는 처리를 합니다
  case ID_FILE_OPEN1:
    memset(&OFN,0,sizeof(OPENFILENAME));
    OFN.lStructSize=sizeof(OPENFILENAME);
    OFN.hwndOwner=hWnd;
    OFN.lpstrFilter=TEXT("모든 파일(*.*)\0*.*\0");
    OFN.lpstrFile=lpstrFile;
    OFN.nMaxFile=MAX_PATH;
    Sub=OFN.lpstrFile;

    hFile=CreateFile(Sub,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);


결과보기>> 현재까지 구현된 기본적인 비트맵 뷰어입니다
    1)기본 출력화면입니다

    2)90도 회전한 화면입니다

   3)180도 회전한 화면입니다

4)270도 회전한 화면입니다

   5)흑백처리한 화면입니다


업데이트1.확대와 축소
: 버튼을 눌러 확대와 축소의 기능을 넣으려고 하였습니다 제일처음 생각난것이 StretchBlt입니다 코드를 작성하고 테스트를 해보니 문제점이 발견되었습니다 확대는 잘되지만 축소를 하게되면 몇몇 부분에서 색깔값이 겹쳐 그림이 이상하게 출력이 됩니다

그래서 생각한것이 현재 그림의 상태보다 확대 될때는 StretchBlt를 사용하고 축소가 될때는 지정한 값보다 반만큼 그려주도록 하였습니다

먼저 버튼으로 확대 축소 버튼을 만들었습니다

원본이미지>>

축소이미지>> 정상적으로 잘 출력됩니다

 


후기>>
지금까지 비트맵의 정보를 불러오고 비트맵의 이미지를 출력하여 보고 추가로 부가적인 기능들을 구현해 보았습니다 시작은 그림판과 동일한 구현이 목표였지만 쉽지 않았습니다 하지만 기본적인 뷰어로써의 기능은 구현이 되어 만족스럽습니다!!



Posted by mantwo

 


Posted by mantwo

Posted by mantwo


티스토리 툴바