리눅스 기반의 다양한 프로젝트를 동시에 진행하는데있어 관리가 쉽고 간편한 방법으로 가상머신을 사용하는 것이다. 특히 기초적인 필수프로그램을 설치해 놓은 이미지를 보관하고 있으면 다른 프로젝트를 쉽게 생성해낼 수 있다. 파티션에 할당된 크기가 아닌 실제로 저장된 파일만으로 가상 이미지의 크기가 결정되기 떄문에 백업에 대한 부담이 전혀 없다. 그래서 개인적으로 좋아하는 fedora를 설치해 놓고 이를 파일로 저장한다. - fedora_init.vdi로. 


이를 사용하려면 가상머신을 새로 만들고, 기존 이미지를 불러와서 사용하면 된다. 하지만 바로 백업파일을 불러오면 위와같은 오류가 발생하게 된다. 


하드 디스크 D:\VMs\fedora\fedora_back.vdi을(를) 여는 데 실패했습니다.


Cannot register the hard disk 'D:\VMs\fedora\fedora_back.vdi' {47f2a95a-6e91-4e7b-bfab-39b4a4728f3f} because a hard disk 'D:\VMs\fedora\fedora.vdi' with UUID {47f2a95a-6e91-4e7b-bfab-39b4a4728f3f} already exists.


결과 코드: E_INVALIDARG (0x80070057)

구성 요소: VirtualBox

인터페이스: IVirtualBox {3b2f08eb-b810-4715-bee0-bb06b9880ad2}

호출자 RC: VBOX_E_OBJECT_NOT_FOUND (0x80BB0001)

VirtualBox – Cannot Register the Hard Drive Because a Hard Drive with UUID Already Exists 라는 글에서 답을 찾을 수 있었는데, 간단히 하자면 명령 프롴프트 창(cmd)에서 아래 명령을 입력하면 된다. 

VBOXMANAGE.EXE internalcommands sethduuid <PathOfNewVHD>


예를 들어. fedora_back.vdi라는 파일이 있을 경우 다음과 같은 경우를 생각해볼 수 있다.

"C:\Program Files\Oracle\VirtualBox\VBOXMANAGE.EXE" internalcommands sethduuid D:\VMs\fedora\fedora_back.vdi

명령 실행 후 몇초 후면 'UUID changed to: 595dcc05-3800-4b3a-bfbe-9451b733b76c'라는 식의 메시지가 나오게 될것이다. 



만약, 아래와 같은 오류메시지가 출력될때를 위해,

VBoxManage.exe: error: Failed to create the VirtualBox object!

VBoxManage.exe: error: Code CO_E_SERVER_EXEC_FAILURE (0x80080005) - Server execution failed (extended info not available)

VBoxManage.exe: error: Most likely, the VirtualBox COM server is not running or failed to start.


'VirtuaBox 공유폴더 사용시 Read-only file system 해결하기' 페이지에서 기록했던 부분을 인용해본다.


그리고 여기서 끝나지 않았을 경우가 있다! 위 과정을 마치고 프로그램을 실행시키려고 하면 'Failed to create the VirtualBox COM object. The application will now terminate.' 오류가 발생할 수 있다. 아무래도 관리자 권한으로 실행하는 경우 기존 계정과 설정상에 충돌이 일어나는듯 한데(추측) 이 역시 간단하게 해결할 수 있다. 기존 계정의 .VirtualBox 폴더를 제거하면 이 폴더가 다시 생성되면서 오류가 해결된다. 즉, 윈7에서 USER라는 계정을 사용한다면 C:\Users\USER\.VirtualBox 에서 이 폴더를 찾아 제거하면 된다. 이떄의 문제는 VirtualBox상에서 설정했던 사항들(언어 설정이나 가상장치 기록등)이 사라질 수 있다는것인데 뭐 그것쯤은...


출처:http://frontjang.info/516





TableLayout 속성에
 android:stretchcolumns="0,1,2"

를 추가시킵니다
위 내용은 첫번째,두번째, 세번째 칼럼에 남는 여분의 사이즈를 준다는 의미 

 
하지만, 여기서 컬럼이 아니라 Row(세로)의 크기를 똑같이 주고자 할경우

TableRow 속성에 각각
android:layout_weight = "1" 

주세요. 

위 내용은 만약 TableRow가 5개 있을 경우
전체 TableLayout의 전체 높이에 1/5을 사용한다는 말입니다.


1. 키보드 감추기

EditText editText = (EditText) findViewById(R.id.myEdit);
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);

2. 키보드 보여주기
EditText editText = (EditText) findViewById(R.id.myEdit);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);

위 코드로 안보이는 경우 아래코드도 시도해보세요
- imm.showSoftInputFromInputMethod (editText .getApplicationWindowToken(),InputMethodManager.SHOW_FORCED);

onCreate등에서 제대로 동작하지 않는다면 
Handler로 post를 해서 위 코드를 실행시켜보세요.

3. 키보드 토글 - 위 두가지 방법으로 안되는 경우 다음과 같은 코드로 동작하는 경우가 있습니다.
imm.toggleSoftInputFromWindow(editText.getApplicationWindowToken(),  InputMethodManager.SHOW_FORCED, 0); 

4. 액티비티 시작시 자동으로 키보드 보여주기
AndroidManifest.xml의 activity 태그의 속성에 android:windowSoftInputMode="stateVisible" 혹은 "stateAlwaysVisible"삽입

5. 액티비티 시작시 자동으로 키보드 보여주지 않기
AndroidManifest.xml의 activity 태그의 속성에 android:windowSoftInputMode="stateHidden" 혹은 "stateAlwaysHidden" 삽입

6. 에디트 텍스트 선택해도 키보드 안뜨게 하기
EditText xml 속성에서 inputType 을 0으로 주면 안뜹니다.



'부스러기 > 네트워크' 카테고리의 다른 글

윈도우즈 소켓 프로그래밍  (0) 2012.08.01


mysql-connector-java-5.1.25.zip


http://dev.mysql.com/downloads/connector/


다른 언어의 커넥터들은 여기서 받을 수 있다.



드라이버 로드

try{

Class.forName("com.mysql.jdbc.Driver");

}

catch(Exception e){

}


커넥션

String url = "jdbc:mysql://127.0.0.1:8888:test";
String id = "root";
String pw= "password";
Connection conn = null;
try{
    conn =DriverManaer.getConnection(url,id,pw);
}catch(Exception e){

}


url 예시

oracle : jdbc:oracle:thin:@127.0.0.1:8888:test

 mysql : jdbc:mysql://127.0.0.1:8888/test
 MSsql : jdbc:microsoft:sqlserver://127.0.0.1:8888;database=test


IPC별 특성 


 

메일슬롯 

이름없는파이프 

이름있는파이프 

방향성 

단방향 ,브로드캐스팅

단방향 

양방향 

통신범위 

제한 없음 

부모 자식 프로세스 

제한 없음 





예제8-1


이름없는 파이프


#include <windows.h>

#include <tchar.h>

#include <stdio.h>


int _tmain(int argc, LPTSTR argv[])

{

HANDLE hReadPipe, hWritePipe; //pipe handle


TCHAR sendString[] = _T("anonymous pipe");

TCHAR recvString[100];


DWORD bytesWritten;

DWORD bytesRead;          


/* pipe 생성 */

CreatePipe(&hReadPipe, &hWritePipe, NULL, 0);

/* 이름없는 파이프는 단방향성이기에 함수를 호출하면 파이프의 쓰는 부분과 읽는 부분을 반환받는다. */


/* pipe의 한쪽 끝을 이용한 데이터 송신 */ 

WriteFile(hWritePipe, sendString, lstrlen(sendString)*sizeof(TCHAR), &bytesWritten,

                                                                                                                     NULL);

_tprintf( _T("string send: %s \n"), sendString);



/* pipe의 다른 한쪽 끝을 이용한 데이터 수신 */ 

ReadFile(hReadPipe, recvString, bytesWritten, &bytesRead, NULL);

recvString[bytesRead/sizeof(TCHAR)] = 0;

_tprintf( _T("string recv: %s \n"), recvString);



CloseHandle(hReadPipe);

CloseHandle(hWritePipe);


return 0;

}


예제를 보면 CreatePipe함수를 호출하는 프로세스가 파이프의 읽고 쓰는 핸들을 모두 가지고 있다. 

이름없는 파이프는 부모 자식 프로세스간 에만  통신이 가능하다고 위에 나왔다.

 부모 프로세스는 자식 프로세스에 핸들을 상속 할 수 있기에 자식프로세스에 핸들을 상속하여  통신이 가능하다.




이름있는 파이프 프로그래밍 모델





1. CreateNamedPipe 함수에 의해 생성

2. ConnectNamedPipe 함수에 의해 연결 대기 상태로 전환

3. Client의 CreateFile 함수 호출에 의한 파이프 오픈


이름 있는 파이프는 양방향 통신이다.

먼저 서버가 파이프를 생성 한다.

CreateNamePipe함수를 호출하면 서버는 파이프를 가지고 있는다. 하지만 말그대로 가지고만 있을 뿐 외부와의 연결은 되지않는 상태이다.

외부와의 연결을 위해

ConnectNamePipe 함수를 호출통하여 외부에 파이프를 노출시킨다.

외부에서 파이프에 연결은 CreateFile 함수를 이용한다.




예제 8-2

이름있는파이프 서버


#include <stdio.h>

#include <stdlib.h>

#include <windows.h> 


#define BUF_SIZE 1024


int CommToClient(HANDLE);


int _tmain(int argc, TCHAR* argv[]) 

{

LPTSTR pipeName = _T("\\\\.\\pipe\\simple_pipe"); 


HANDLE hPipe;

while(1)

{

  hPipe = CreateNamedPipe ( 

  pipeName,            // 파이프 이름

  PIPE_ACCESS_DUPLEX,       // 읽기,쓰기 모드 지정

  PIPE_TYPE_MESSAGE |

  PIPE_READMODE_MESSAGE | PIPE_WAIT,

  PIPE_UNLIMITED_INSTANCES, // 최대 인스턴스 개수.

  BUF_SIZE,                  // 출력버퍼 사이즈.

  BUF_SIZE,                  // 입력버퍼 사이즈 

  20000,                   // 클라이언트 타임-아웃  

  NULL                    // 디폴트 보안 속성

  );

  

  if (hPipe == INVALID_HANDLE_VALUE) 

  {

  _tprintf( _T("CreatePipe failed")); 

  return -1;

  }

  

  BOOL isSuccess; 

  isSuccess = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 

  

  if (isSuccess) 

  CommToClient(hPipe);

  else 

  CloseHandle(hPipe); 

}

return 1; 


int CommToClient(HANDLE hPipe)

TCHAR fileName[MAX_PATH];

TCHAR dataBuf[BUF_SIZE];


BOOL isSuccess;

DWORD fileNameSize;


isSuccess = ReadFile ( 

hPipe,        // 파이프 핸들 

fileName,    // read 버퍼 지정.

MAX_PATH * sizeof(TCHAR), // read 버퍼 사이즈 

&fileNameSize,  // 수신한 데이터 크기

NULL);       


if (!isSuccess || fileNameSize == 0) 

{

_tprintf( _T("Pipe read message error! \n") );

return -1; 

}


FILE * filePtr = _tfopen(fileName, _T("r") );


if(filePtr == NULL)

{

_tprintf( _T("File open fault! \n") );

return -1; 

}


DWORD bytesWritten = 0;

DWORD bytesRead = 0;


while( !feof(filePtr) )

{

bytesRead = fread(dataBuf, 1, BUF_SIZE, filePtr);


WriteFile ( 

hPipe, // 파이프 핸들

dataBuf, // 전송할 데이터 버퍼  

bytesRead, // 전송할 데이터 크기 

&bytesWritten, // 전송된 데이터 크기 

NULL);


       if (bytesRead != bytesWritten)

{

   _tprintf( _T("Pipe write message error! \n") );

   break; 

}

}


FlushFileBuffers(hPipe); 

DisconnectNamedPipe(hPipe); 

CloseHandle(hPipe); 

return 1;

}




예제 8-3

이름있는 파이프 클라이언트



#include <stdio.h>

#include <stdlib.h>

#include <windows.h>


#define BUF_SIZE 1024


int _tmain(int argc, TCHAR *argv[]) 

{

    HANDLE hPipe;

    TCHAR readDataBuf[BUF_SIZE + 1];

    LPTSTR pipeName = _T("\\\\.\\pipe\\simple_pipe"); 

    

    while(1)

    {

      hPipe = CreateFile( 

      pipeName,             // 파이프 이름 

      GENERIC_READ | GENERIC_WRITE, // 읽기, 쓰기 모드 동                         

                                                                                                               시 지정 

      0,

      NULL,

      OPEN_EXISTING,

      0,

      NULL

      );

     

      if (hPipe != INVALID_HANDLE_VALUE)

         break;

         

      if (GetLastError() != ERROR_PIPE_BUSY)

      {

         _tprintf( _T("Could not open pipe \n") );        

         return 0;

      }

     

      if (!WaitNamedPipe(pipeName, 20000))

      {

         _tprintf( _T("Could not open pipe \n") );

         return 0;

      }

    }

    

    DWORD pipeMode = PIPE_READMODE_MESSAGE|PIPE_WAIT; // 메시지 기반으로 모드 변경.

    BOOL isSuccess = SetNamedPipeHandleState (

                       hPipe,      // 파이프 핸들

                       &pipeMode,  // 변경할 모드 정보.  

                       NULL,     // 설정하지 않는다. 

                       NULL);    // 설정하지 않는다. 

    

    if (!isSuccess)

    {

        _tprintf( _T("SetNamedPipeHandleState failed") ); 

        return 0;

    }

    

    LPCTSTR fileName = _T("news.txt");

    DWORD bytesWritten = 0;

    

    isSuccess = WriteFile (

                    hPipe,                // 파이프 핸들

                    fileName,             // 전송할 메시지 

                    (lstrlen(fileName)+1) * sizeof(TCHAR), // 메시지 길이 

                    &bytesWritten,             // 전송된 바이트 수

                    NULL);

    

    if (!isSuccess) 

    {

      _tprintf( _T("WriteFile failed") ); 

      return 0;

    }

    

    DWORD bytesRead = 0;

    while(1) 

    {

        isSuccess = ReadFile(

                        hPipe, // 파이프 핸들

                        readDataBuf, // 데이터 수신할 버퍼

                        BUF_SIZE * sizeof(TCHAR),  // 버퍼 사이즈

                        &bytesRead, // 수신한 바이트 수

                        NULL);      

        if (! isSuccess && GetLastError() != ERROR_MORE_DATA) 

            break; 

        

        readDataBuf[bytesRead] = 0; 

        _tprintf( _T("%s \n"), readDataBuf ); 

    }

    

    CloseHandle(hPipe); 

    return 0; 

}






핸들 테이블의 상속


BOOL CreateProcess(

LPCTSTR lpApplicationName , 

LPTSTR lpCommandLine ,                                                                                    

LPSECURITY_ATTRIBUTES lpProcessAttributes ,

LPSECURITY_ATTRIBUTES lpTreadAttributes ,

BOOL binheritHandles , // 핸들테이블의 상속여부

DWORD dwCreationFlags ,

LPVOID lpEnvironment ,

LPCTSTR lpCurrentDirectory ,

LPSTARTUPINFO lpstrartupInfo ,

LPPROCESS_INFORMATION lpProcessInformation

);


핸들테이블은 5장 과 6장에서 각 리소스에 독립적이라 하였는데 핸들테이블은 조건에 따라 상속도 가능하다. 

상속될때 핸들같이 동일하게 상속된다.

핸들테이블에 핸들 정보에 상속가능여부가 있는데 이 값이 가능상태여야 상속이가능하다.

ex)


 핸들

주소 

상속여부 

127 

0x1200 

168 

0x1680 

 ......

 ......

 ......

이런 식으로 상속 가능여부가 핸들값마다 저장이 되어진다.



CreateProcess함수를 살펴보면 5번째 인자에 BOOL binheritHandles 가있는데 이 인자값을 true 을 주면 부모프로세스의 핸들테이블에 상속가능한 핸들들이 상속된다.



그러하다면 어떻게하면 핸들값의 상속여부를 가능하게 할 수 있을 것인가.

 

ex)


SECURITY_ATTRIBUTES sa;

sa.nLength=sizeof(sa);

sa.lpSecurityDescriptor=NULL;

sa.bInheritHandle=TRUE;  // 이런식으로 하면 상속가능하게 할 수 있다.


.................


CreateMailslot(......... &sa); //4번째 전달인자이다.


대부분의 리소스 생성과정에서 보면 SECURITY_ATTRIBUTES 즉 보안관리자 설정을 위한 구조체가 사용된다.

이보안설정을 하는과정에서 앞의 강의에서 NULL 설정을 하였다.

보안 설정을 하지 않을 경우 상속가능여부는 가능하지 않게 된다.


메일 슬롯 뿐아리라 대부분 다른 리소스를 생성할 경우에도 보안관리구조체의 포인터를 전달하면 동일하게 설정 할 수 있다.



핸들의 상속과 UC




핸들 값을 상속하게 되면 당연하게도 UC값이 증가한다.








Pseudo 핸들과 핸들의 중복


6장에 나왓듯이 프로세스가 자기자신의 핸들값은 핸들테이블에 저장시켜서 사용하는것이 아니라 GetCurrentProcess함수를 이용하여 특정 상수를 이용한다라고 하였는데 이를 Pseudo 핸들 이라한다.즉 가짜핸들이다.


하지만 이런 일이 일어난다고 해보자.


A프로세스가 B라는 자식프로세스에게 A프로세스의 핸들을 상속하고싶다.

하지만 A프로세스의 핸들테이블에는 A의 핸들정보가 존재하지않는다.

그래서 A프로세스의 핸들테이블에 자기자신의 핸들정보를 저장하여야 한다.



DuplicateHandle 함수가 있다


대략 하는일은 DulpicateHandle(A 핸들, 256,B핸들,&val,.......)  

                                                                        HANDLE val=364

이런식으로 함수를 호출하면 밑에 처럼 A프로세스의 핸들테이블에 있는 256 핸들이    B프로세스의 핸들테이블에 364 값으로 복사가 된다. 

여기서 다시한번 핸들테이블은 각 리소스에 독립적이라는 것을 인식하자.


프로세스 A     

 프로세스B

 

핸들 

주소 

 

핸들 

주소 

... 

... 

 

..0

... 

256 

0x2400 

 ------------>

364 

0x2400 

 ...

... 

 복사

... 

 ...


이것으로 이제 Pseudo 핸들을 핸들테이블에 저장해보자.



DuplicateHandle(

프로세스A핸들,

256,

프로세스A핸들,

&val,

.....

);


 핸들 

주소 

 

 

... 

...   

 256

0x2400 

 ---------------->

 Usage count : 2

 ...

 ...  

 284

  0x2400  ----------------> 

 ...

...   


한테이블에서 핸들을 복사 할 수도 있다.

이러면 같은 주소를 가리키는 핸들이 하나 더 생긴다. 

※물론  UC도 늘어남으로 나중에 핸들을 닫을때 두번 닫아야 한다.


이제 자신의 핸들값을 상속 할 수있게 Pseudo 핸들을 핸들테이블에 저장해보자.


DuplicateHandle(

GetCurrentProcess(),

GetCurrentProcess(),

GetCurrentProcess(),

&hProcess,

0,

TRUE,

DUPLICATE_SAME_ACCESS

);


앞에 3인자에 모두 GetCurrentProcess함수를 넣으면 함수를 호출한 프로세스의 핸들이 반환되니까 호출한 프로세스의 핸들테이블에 호출한프로세스의 핸들값이 저장이 되는것이다. 





DWORD WINAPI WaitForSingleObject(
  __in  HANDLE hHandle,
  __in  DWORD dwMilliseconds
);

위 함수는 커널 오브젝트의 상태 정보를 확인하는데 사용 됩니다.
즉, 해당 리소스의 커널 오브젝트가 Signaled인지 Non-Signaled인지 알 수 있습니다.

해당 리소스가 살아있으면 Non-Signaled (FALSE)이고 
해당 리소스가 종료되면 Signaled(TRUE)를 커널 오브젝트안에 가지고 있습니다.

첫번째 인자로는 해당 커널 오브젝트 핸들을 지정해 주고
두번째 인자는 Signaled 상태가 될 때까지 기다릴 수 있는 최대 시간을 밀리초 단위로 지정해 줍니다.
(INFINITE라고 인자를 주시면 해당 리소스가 종료 될 때까지 기다리고 있습니다. 
 즉, Signaled가 될 때까지 기다린다는 뜻입니다.)




간단한 예제 소스로 결과를 확인해 봅시다.

===========================
예제 소스
===========================
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>

int _tmain(int argc,TCHAR* args[])
{
STARTUPINFO start_info={0,};
PROCESS_INFORMATION process_info;

SetCurrentDirectory(_T("C:\\WINDOWS\\system32"));
TCHAR Command[] = _T("notepad.exe");

        /*메모장을 생성*/
CreateProcess(NULL,Command,NULL,NULL,TRUE,0,NULL,NULL,&start_info,&process_info);
       
       /*메모장이 종료 될때까지 멈춰있음*/
WaitForSingleObject(process_info.hProcess,INFINITE);

_tprintf(_T("Exit notepad. \n"));

return 0;
}

아마 실행해 보시면 메모장이 띄워지고 WaitForSingleObject 함수에서 멈춰 있는 것을 알 수 있습니다.
그리고 메모장을 끄시면 "Exit notepad."라는 문장이 출력되면서 예제소스 프로그램이 종료되는 것을 보실 수 있습니다.

======================================
쓰레드 동기화 시 사용되는 경우
======================================
쓰레드 동기화 시키는 방법에는 유저모드와 커널모드로 나눠지지만 WaitForSingleObject함수를 사용하는 경우는 
커널모드 쓰레드 동기화일 때만 사용합니다. 그에대한 2가지 방법으로  뮤텍스, 세마포어가 있습니다.

뮤텍스와 세마포어는 커널모드 쓰레드 동기화 방법 이므로 생성하면 커널 오브젝트 상태를 지닙니다.

1. 뮤텍스
임계영역에 들어가기 위해서는 무작정 들어가는 것이 아니라 키를 가지고 있어야만 들어갈 수 있는 것 입니다.
그럴 때 키는 얻는 함수로 사용되는 것이 WaitForSingleObject가 될 수 있습니다.
임계영역을 빠져나오면 키를 반환해야 하므로 그때 사용하는 함수는 ReleaseMutex가 되겠습니다.
간단하게 뮤텍스를 이용해 쓰레드들이 임계영역에 동시접근 하는 것을 막아보겠습니다.

상황을 간단하게 말하면
하나의 ATM기계가 있는데 10명의 사람들이 일을 보고 나오려고 하는 상황입니다.
각 사람들이 일보는 시간은 10~20초 사이입니다. ( 잘 안보이시면 클릭 )

위에 Working이라는 함수에서 WaitForSingleObject라는 함수를 사용하고 있는데 들어갈 수 있는 권한을 얻는 것입니다.
뮤텍스 커널 오브젝트도 마찬가지로 Non-Signed상태와 Signed상태가 있는데 해당 뮤텍스 커널 오브젝트가 Signed상태가 될때까지 기다리는 것입니다. Signed상태가 되면 기다리는 것들 중지하고 해당 쓰레드가 임계영역으로 들어가는 것입니다.

실행 결과 입니다.

동기화 방법 중에 뮤텍스 말고도 세마포어라는 것이 있는데 세마포어는 뮤텍스에 없는 카운트 기능이 있습니다.
즉, 임계영역에 동시에 들어 갈 수 있는 쓰레드의 개수를 정할 수 있는 기능이 있습니다.
하지만 WaitForSingleObject를 사용하는 방법은 같으므로 생략하도록 하겠습니다.


 Signaled vs Non-Signaled



커널 오브젝트의 상태


이곳은 프로세스 기준이다.

커널오브젝트의 구성멤버 변수중에 커널오브젝의 상태정보 변수가있는데 이 변수는

프로세스가 실행중이면 프로세스의 커널오브젝트가 Non-signaled 상태 이고

                실행 종료되면  프로세스의 커널오브젝트가 signaled 상태가 된다.  


프로세스가 실행되면 그 프로세스의 커널 오브젝트는 바로  Non-signaled 가 된다.


커널 오브젝트의 상태가 필요한 이유는 프로세스의 핸들을 가지고 있는 무언가가 프로세스의 실행 여부를 판단하기 위해서이다.


상태관찰 시나리오


부모프로세스가 자식 프로세스 생성 - >자식프로세스의 핸들값반환 


WaitForSingleObject(자식프로세스핸들인자){

Non-signaled 면 멈춰있는다.

signaled 상태면 동작한다.

}


윈속을 위한 헤더 및 라이브러리 설정


winsock2.h 헤더 파일을 포함 한다.


ws2_32.lib 라이브러리를 링크 시킨다.


윈속 사용을 위한 라이브러리 초기화 및 해제



윈속 관련 라이브러리 초기화 함수


#include<winsock2.h>


int WSAStartup(

WORD wVersionRequested,

LPWSADATA lpWSAData

);

WORD wVersionRequested 에 버전에 맞는 16진수를 전달해야한다.

예 Version 3.4 이면 0x0403 식으로 주버전이 뒤로간다.

 WORD MAKEWORD(BYTE bLow,BYTE bHigh);

bLow에 주 버전 bHigh 에 부 버전을 넣으면 16진수로 반환된다.

윈속 관련 라이브러리 해제 함수


int WSACleanup(void);



윈속 프로그램 코드 Template


#include<winsock2.h>


............


int main(int argc, char **argv)

{

WSADATA wsaData;

...............

if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)

error_handling("WSAStartup() error!");

..................


WSACleanup()

retune 0;

}



윈속 기반의 소켓 관련 함수


SOCKET socket(int af, int type ,int protocol);

윈도우에서도 socket을 생성하면 리눅스와 마찬가지로 파일(소켓)핸들이 만들어져서 리턴된다. 

※파일(소켓) 핸들도 정수형 데이터이다.

int bind(SOCKET s,const struct sockaddr FAR * name, int namelen);


int listen(SOCKET s, int backlog);


SOCKET accept(SOCKET s, struct sockaddr FAR *addr,int FAR *addrlen);


int connect(SOCKET s, const sockaddr FAR * name, int namelen);




윈속 기반의 데이터 입출력 함수


데이터 출력함수 

#include<winsock2.h>


int send(SOCKET s, const char FAR *buf, int len, int flags);


데이터 입력함수

#include<winsock2,h>


int recv(SOCKET s, char FAR *buf, int len, int flags);


send,recv 함수는 리눅스에도 존재하는 함수로 윈도우에서는 리눅스의 read,write함수가 없기에 send,recv함수를 사용한다.




예제

서버

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<winsock2.h>


void ErrorHandling(char *message);


int main(int argc, char **argv)

{

WSADATA wsaData;

SOCKET hServSock;

SOCKET hClntSock;

SOCKADDR_IN servAddr;

SOCKADDR_IN clntAddr;

int szClntAddr;

char message[]="hello World\n";


if(argc!=2){

printf("Usage : %s <port>\n",argv[0]);

}


if(WSAStartup(MAKEWORD(2,2), &wsaData) !=0)

ErrorHandling("WSAStartup() error!");


hServSock=socket(PF_INET, SOCK_STREAM, 0); //서버 소켓 생성

if(hServSock==INVALID_SOCKET)

ErrorHandling("socket() error");


memset(&servAddr, 0 ,sizeof(servAddr));

servAddr.sin_family=AF_INET;

servAddr.sin_addr.s_addr=htonl(INADDR_ANY);

servAddr.sin_port=htons(atoi(argv[1]));


if(bind(hServSock, (SOCKADDR*) &servAddr,sizeof(servAddr))==SOCKET_ERROR)

ErrorHandling("bind() error");


if(listen(hServSock,5)==SOCKET_ERROR)

ErrorHandling("listen() error");                  //연결 요청 대기


szClntAddr=sizeof(clntAddr);

hClntSock=accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr); //연결요청수락

if(hClntSock==INVALID_SOCKET)

ErrorHandling("accept() error");


send(hClntSock, message, sizof(message), 0);


closesocket(hClntSock);

WASCleanup();


클라이언트


#include<stdio,h>

#include<stdlib.h>

#incldue<string.h>

#incldue<winsock2.h>


void ErrorHandling(char *message);


int main(int argc, char **argv)

{

WSADATA wsaData;

SOCKET hSocket;

char message[30];

int strLen;

SOCKADDR_IN servAddr;


if(argc!=3){

printf("Usage : %s <IP> <port> \n",argv[0]);

exit(1);

}


if(WSAStartup(MAKEWORD(2,2), &wsadata) !=0)

ErrorHandling(WSAStartup() error!");


hSocket=socket(PF_INET, SOCK_STREAM, 0);

if(hSocket==INVALID_SOCKET)

ErrorHandling("hSocketet() error");


memset(&servAddr, 0, sizeof(servAddr));

servAddr.sin_family=AF_INET;

servAddr.sin_addr,s_addr=inet_addr(argv[1]);

servaddr.sin_port=htons(atoi(argv[2]));


if(connect(hSocket,  (SOCKADDR*)&servAddr, sizeof(servAddr))==SOCKET_ERROR)

Erroerhandling(:connect() error!);


strLen=recv(hSocket, message, sizeof(message)-1,0);

if(strLen==-1)

ErrorHandling("read() error!");

message[strLen]=0;

printf("Message from server : %s \n", message);


closesocket(hSocket);

WSACleanup();

return 0;

}


void ErrorHandling(char *message)

{

fputs(message, stderr);

fputc('\n',stderr);

exit(1);

}


+ Recent posts