본문 바로가기
코스웨어/10년 시스템제어

시스템 제어 4월6일 10번 문현철

by 알 수 없는 사용자 2010. 4. 6.
728x90
반응형

스레드(Thread)

프로세스의 코드를 수행하는 단위이다. 한 프로세스당 복수개의 스레드를 허용함으로써 분리된 개념으로 생각되었음. 즉 스레드를 프로세스에 속하는 자원의 일종으로 생각할수 있다.
 
프로세스(Process)혹은 태스크(Task)

프로세스란 쉽게 생각하면,흔히 윈도우즈에서 실행 중인 파워포인트,MP3 플레이어,아래아 한글 등
이러한 개개의 프로그램을 일컫는다.프로세스가 하드디스크상에 있는 이러한 프로그램 이미지가
아니라 '실행중'인 프로그램을 가리킨다..

WaitForSingleObject()
Win32 프로세스 대기함수. Win32 프로세스, Windows 16비트 프로세스 및 MS-MS-DOS 기반 응용 프로그램을 종료할 때까지 기다립니다.

ws2_32.lib
윈도우와 인터넷을 연결하는 소켓에 관한 32비트 파일을 모아놓은 라이브러리
#pragma comment(lib,"ws2_32.lib")로 호출할수 있다.

동기화
다중 스레드가 실행되는 상황에서 스레드 마다의 실행순서를 제어해서 경쟁 상태나 교착 상태를 해소하는 것을 동기화라고 한다.
경쟁상태: 둘이상의 스레드가 공유 자원을 서로 사용하려는 상태
교착상태: 스레드끼리 서로를 기다리며 블럭된 상태

소켓 주소 구조체(SOCKADDR)
소켓 프로그래밍에서 클라이언트 또는 서버의 구체적인 주소를 표현하기 위해서는 주소체계(address family),IP주소,포트 번호 세가지로 지정되어야 하며 3가지 정보를 묶어서 소켓 주소라고 부른다.
sockaddr_in  구조체 4바이트의 IP주소와 2바이트의 포트번호를 구분하여 엑세스 할수있는  인터넷 전용 소켓 주소 구조체이다. 

WSADATA 구조체
typedef struct WSAData {
  WORD           wVersion;
  WORD           wHighVersion;
  char           szDescription[WSADESCRIPTION_LEN+1];
  char           szSystemStatus[WSASYS_STATUS_LEN+1];
  unsigned short iMaxSockets;
  unsigned short iMaxUdpDg;
  char FAR       *lpVendorInfo;
} WSADATA, *LPWSADATA;
헤더파일  <winsock2.h>


#include
 "stdafx.h"
#include <winsock2.h> //windows.h보다 먼저 선언해야함
#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include <iostream>

#define
 BUFSIZE 512
unsigned int WINAPI ServerProc(LPVOID lpParam);
unsigned int WINAPI ClientContorl(LPVOID lpParam);

int
 main(int argc, char* argv[])
{
  DWORD dwID;
  
  HANDLE h = (HANDLE)_beginthreadex(NULL, 0, ServerProc, NULL, 0, (unsigned *)&dwID); //스레드 생성
  
  WaitForSingleObject(h,INFINITE);  //스레드 종료를 기다림 
  printf("Main Thread 종료\n");
  CloseHandle(h);           //핸들을 반환
  return 0;
}
unsigned int WINAPI ServerProc(LPVOID lpParam)
{
  WSADATA wsa;
  int retval;
  //윈속 초기화
  
/*함수 : int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData );
    기능 : WS2_32.DLL의 사용을 초기화한다.
    인수 : 버전, WSADATA 구조체의 번지
   
설명 : 요즘은 보통 Winsock 2.2를 사용하는데, 이렇게 설정하기
          위해서 MAKEWORD(2,2)를 사용한다.

    반환 : 성공 시 0, 실패 시 에러 코드
   
주의 : 이 함수는 특별한 경우가 아니면, 한 프로그램에 한 번만 호출하면 된다.*/
  
  if(WSAStartup(MAKEWORD(2,2),&wsa) !=0)
    return -1;
  
/*
  함수 : SOCKET socket( int af, int type, int protocol );
  기능 : 소켓을 생성한다.
  인수 : 1. AF_INET은 TCP/IP, UDP일 경우 사용.
        2. SOCK_STREAM(TCP/IP) 또는 SOCK_DGRAM(UDP)
        3. IPPROTO_TCP(TCP/IP) 또는 IPPROTO_UDP(UDP), 일반적으로 0 사용. 
  설명 : 소켓 디스크립터를 생성하고, 필요 자원을 할당한다.
  반환 : 성공 시 소켓 디스크립터(descriptor), 실패 시 INVALID_SOCKET
    */

  SOCKET listen_sock = socket(AF_INET,SOCK_STREAM,0);
  
  if(listen_sock == INVALID_SOCKET) 
    printf("소켓 초기화 실패\n");
  
  SOCKADDR_IN serveraddr;
  
  ZeroMemory(&serveraddr,sizeof(serveraddr));
  serveraddr.sin_family= AF_INET;     //주소체계
  serveraddr.sin_port=htons(9000);     //포트번호
  serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);   //네트워크 카드 설정
  
  //bind 서버의 지역 IP주소와 지역 포트번호를 결정
  //(클라이언트의 접속을 수용할 소켓,이변수의 주소와 지역포트 번호로 초기화 시킴,
     소켓주소 구조체변수의 길이)

  
/*
  
    클라이언트 프로그램은 필요한 경우만 bind를 사용하면 된다.
    함수 : int bind(SOCKET s, const struct sockaddr FAR*  name, int namelen );
    기능 : 생성된 소켓 s에 네트워크 주소와 포트 번호를 연결한다.
    인수 : 1. socket 함수에 의해 생성된 소켓 디스크립터(s)
    2. SOCKADDR_IN 구조체. (SOCKADDR*는 struct sockaddr FAR*)
    구조체는 주소체계(AF_INET), Port, 네트워크 주소로 채워져야 함.
    3. SOCKADDR_IN 구조체의 크기 
    설명 : 생성된 소켓 s 디스크립터에 포트번호(9999)와 네트워크 주소를 설정.
    일반적으로 네트워크 카드는 INADDR_ANY를 사용하는데, INADDR_ANY를 설정하면 
    운영체제가 자동으로 네트워크 주소를 설정한다. 만약 네트워크 
카드가 2개 이상
    인 경우, 특정 네트워크 IP를 설정하려면, INADDR_ANY 
대신 "220.x.x.x"처럼 
    직접 설정해야 함.

    반환 : 성공 시 0, 실패 시 SOCKET_ERROR
    보충 : htons, htonl은 호스트(내 PC)의 바이트 순서를 네트워크 바이트순서로 
    바꿔
주는 함수이다. 이 함수를 사용하지 않고, 그냥 9999를 대입하면 포트번호가 
    잘못 설정된다.
    inet_addr 함수는 네트워크 주소를 4바이트의 IN_ADDR 구조로 바꿔 준다.
    */

  
  retval=bind(listen_sock,(SOCKADDR*)&serveraddr,sizeof(serveraddr));
  if(retval==SOCKET_ERROR)
    printf("bind error\n");
  
  //listen
  //client의 접속을 받아들일수 있는 상태로 포트 상태를 변경한다.
  retval=listen(listen_sock,SOMAXCONN);
  if(retval==SOCKET_ERROR)
    printf("listen error\n");
  
 
SOCKET client_sock;
  SOCKADDR_IN clientaddr;
  int addrlen;
  HANDLE hThread;
  DWORD THreadID;
  //accept()
  addrlen = sizeof(clientaddr);
  //클라이언트에서 접근을 하면 연결을 허락해준다.
  //(소켓 서버, 클라이언트의 주소(sockaddr 타입), 클라이언트 주소의사이즈)
  do
  { 
    printf("client 접속대기\n");
    client_sock = accept(listen_sock,(SOCKADDR *)&clientaddr,&addrlen);
    if(client_sock ==INVALID_SOCKET)
    {
      printf("accept error\n");
    }
    Sleep(1);
  }while(client_sock==INVALID_SOCKET);
  
  printf("[TCP 서버]클라이언트 접속: IP 주소=%s, 포트번호 %d\n",\
    inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
  hThread = (HANDLE)_beginthreadex(NULL, 0, ClientContorl,(LPVOID)client_sock, 0, (unsigned *)&THreadID); //스레드 생성
  
  if(hThread==NULL)
    printf("Client Thread Error\n");
  
  WaitForSingleObject(hThread,INFINITE);  //스레드 종료를 기다림 
  
  printf("server thread 종료\n");
  CloseHandle(hThread);
  closesocket(listen_sock);
  
/*
    함수 : int closesocket( SOCKET s );
    기능 : 소켓을 닫고, 자원을 해제한다.
    인수 : 1. 닫을 소켓 디스크립터(s 또는 cs)
    설명 : 소켓을 닫는다.
    반환 : 성공 시 0, 실패 시 SOCKET_ERROR.
    주의 : closesocket 함수 사용 시 SO_LINGER 옵션과 SO_DONTLINGER 옵션의 
    영향을 받기 때문에 주의하자.아래의 표에 보면,SO_LINGER의 설정 값이 l_onoff
    는 0이고, l_linger도 0일 때에는
즉시 닫힘(hard close)이 발생하여 소켓은 
    즉시 제거되고,
대기 중인 송수신 데이터는 모두 잃게 된다.
    안전한 데이터 송수신의 종료를 위해서는 shutdown 함수를 사용하는 것이 좋다.
  
  옵션           | 타임아웃간격    닫힘유형    닫힘대기여부    비고
  ------------------------------------------------------------------------
  SO_DONTLINGER  |  상관 없음     정상       블록 안됨    데이터 안전 전송
  SO_LINGER      |     0         hard       블록 안됨     데이터 손실 가능
  SO_LINGER      |    >0         정상       블록 됨     일정시간 데이터 전송
    
  SO_LINGER 옵션은 다음과 같이 설정할 수 있다. 이렇게 설정하면 closesocket 함수
  사용 시 60초간 대기하면서 큐에 있는 데이터를 전송한다. 만약 전송이 완료되면,
  60초가 되기 전에 블록이 해제된다. 중요한 데이터를 송신 중일 때는 이 방법을 
  사용하는 것이 좋다. 물론 closesocket 함수는 기본적으로 SO_DONTLINGER 옵션이
  있기 때문에 이 방법을 사용하지 않아도 된다.
      
  LINGER linger = { 1, 60 }; // 1(기능 사용), 종료 시 60 초간 대기 설정
  if( setsockopt( s, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof         
  (linger) ) !
= 0 )
  {
      printf( "종료 설정 실패, 에러코드 = %d \n", WSAGetLastError() );
      closesocket( s );    // 소켓 제거
      WSACleanup();        // WS2_32.DLL 사용 종료
      return;
  }
    */

 WSACleanup(); //윈속 종료
  
/*
   함수 : int  WSACleanup( void );
   기능 : WS2_32.DLL의 사용을 종료한다.
   인수 : 1. 닫을 소켓 디스크립터(s 또는 cs)
   설명 : WS2_32.DLL의 사용을 종료한다.
   반환 : 성공 시 0, 실패 시 SOCKET_ERROR.
   주의 : 프로그램이 닫혀지기 전에 한 번만 호출하면 된다.
   만약 프로그램이 실행 중에 이 함수를 호출한다면 모든 소켓은 종료된다.
    */

  return 0;
}
unsigned int WINAPI ClientContorl(LPVOID lpParam)
//클라이언트 접속시 데이타 처리부분
{
  return 0;
}

728x90