본문 바로가기
Network/HTTP

멀티 스레딩

by 수픽 2020. 3. 16.

멀티 스레드의 정의


하나의 프로세스를 다수의 실행 단위로 구분하여 자원을 공유하고 자원의 생성과 관리의 중복성을 최소화하여 수행 능력을 향상시키는 것

 

멀티 스레드와 멀티 프로세스


자신만의 메모리 영역을 가지며 CPU 또는 프로세서를 시스템에 추가하여 컴퓨팅 속도를 높이는 멀티 프로세서는 멀티 스레드보다 시간과 비용이 더 많이 든다. 따라서 메모리 공간과 시스템 자원 소모가 줄어들어 프로그램의 응답 시간이 단축되는 멀티 스레드를 사용하는 것이다.

 

 

멀티 스레드의 특징


  • 서로 다른 스레드 끼리 데이터 영역, 힙, 스택 중 스택 영역만 비공유한다.
  • 힙 영역을 이용하여 데이터를 주고 받는다.
  • 자신이 속한 프로세스의 메모리를 공유하기 때문에 메모리 공간과 시스템 자원 소모가 줄어든다.
  • 각각의 스레드끼리 별도의 작업이 가능해 사용자와의 응답성이 좋다.

 

뮤텍스


MuTual EXclusion. 상호배제

 

공유된 자원에 여러 스레드가 동시 접근하면서 문제가 발생한다. 이때  Critical Section을 가진 스레드들의 러닝 타임이 겹치지 않게 각각 단독으로 실행되게 하는 기술이다.  공유된 자원의 데이터를 여러 스레드가 접근하는 것을 막아준다. 다중 프로세스들의 공유 리소스에 대한 접근을 조율하기 위해 locking과 unlocking을 사용한다. lock을 가지고 있을 때만 공유 데이터에 접근이 가능하다. 잠긴 문에 대한 열쇠가 하나라고 치면, 열쇠를 가진 사람만 갈 수 있고 그 열쇠를 반납해야만 다음 사람이 사용할 수 있다.

 

비슷한 기술로 세마포어가 있다. 동시에 리소스에 접근할 수 있는 허용 가능한 counter의 개수를 뜻한다. 공유된 자원의 데이터를 여러 스레드가 접근하는 것을 막아주는 점에서는 동일하다. 잠긴 문에 대한 예시로 설명을 이어가면 세마포어는 열쇠가 여러 개이고,  3개라고 치면 3명 이상은 차례를 기다려야한다. 만약 열쇠가 1개인 세마포어라면 뮤텍스와 개념이 같다.

 

*여기서 Critical Section이란 다중 프로그래밍 운영체제에서 여러 프로세스가 데이터를 공유하면서 수행될 때, 각 프로세스에서 공유데이터를 엑세스하는 프로그램 코드 부분을 말한다.

 

멀티 스레딩 예제 - Client


1. 필요 헤더파일 및 라이브러리

 

2. 스레드 생성 함수

스레드를 생성할 함수이다. 먼저 버퍼를 초기화해주고 서버와 통신할 소켓을 생성한다.

 

ZeroMemory는 메모리 영역을 0x00으로 채우는 매크로 이다. 인자로 0으로 채울 메모리 블럭의 시작 주소를 가리키는 포인터와 0으로 채울 크기를 받아온다. 메모리 영역을 0으로 채움을 명시한 것을 제외하고 memset()함수와 동일하게 동작한다.

void ZeroMemory
{
    PVOID Destination,
    SIZE_T Length
};

 

먼저 랜덤 메시지를 생성해서 메시지 버퍼인 randomMessage에 채운다. 서버에 정상적으로 보내지면 랜덤메시지 byte is sent라는 문구가 뜨게 된다. 서버에서 데이터를 정상적으로 받으면 Bytes received: 랜덤메시지가 뜨게된다.

 

3. main 함수

기본 설정이다. 파라미터 값을 확인하고 윈속을 초기화해준다.

 

addrinfo 구조체는 네트워크 주소정보와 호스트 이름을 표현하는데 사용되고, bind(), connect() 호출 시 이 정보를 입력 파라미터에 사용할 수 있다. getaddrinfo 함수를 통해 호출된다.

struct addrinfo {
	int ai_flags; // 추가적인 옵션을 정의 할 때 사용
	int ai_family; // address family
	int ai_socktype; // socket type을 나타냄. SOCK_SREAM, SOCK_DGRAM 
	int ai_protocol; // 소켓 주소가 사용하는 프로토콜
	socklen_t ai_addrlen; // 소켓 주소인 ai_addr의 길이
	char *ai_canonname; // 호스트의 canonical name을 나타냄 
	struct sockaddr *ai_addr; // 소켓 주소를 나타내는 구조체 포인터 
	struct addrinfo *ai_next; 
};

 

getaddrinfo 함수는 도메인 주소를 받아서 네트워크 주소 정보를 가져오는 함수이다. 호스트 이름/주소 문자열, 서비스 이름/10진수로 표현한 포트 번호 문자열을 인자로 받는다. hints는 addrinfo 구조체를 채워서 넘기면 함수가 addrinfo 구조체의 멤버 변수 값을 읽어서 이에 대한 주소 정보를 넘기게 해준다.  hints 값을 참고해서 가져온 네트워크 주소 정보를 res를 통해 가져온다.

int getaddrinfo(const char *node, const char *service,const struct addrinfo *hints, struct addrinfo **res);

void freeaddrinfo(struct addrinfo *res); //메모리 해제

zeromemory로 hints 값을 0으로 채워준 후에, 모든 주소 영역의 정보를 반환하는 AF_UNSPEC, 소켓 형식값인 SOCK_STREAM, 소켓 주소가 사용하는 프로토콜인 ipproto_tcp를 설정해준다.

 

getaddrinfo 함수 안에 받은 인자들을 (구조체 정보들) linked list 형태로 전달한다.

 

linked list는 연결리스트라고 부른다. 각 노드가 데이터와 포인터를 가지고 한 줄로 연결되어 있는 방식으로, 데이터를 저장하는 자료구조이다.

 

서버와 연결할 ConnectSocket 소켓을 생성하고 위에서 지정한 형식들을 가져온다. 그리고 connect( ) 함수로 서버에 연결 요청을 보낸다. 

 

스레드를 생성하고 q를 입력하면 종료된다.

 

CreateThread() 함수는 스레드를 생성하는 함수이다.  인자로 생성하는 스레드의 보안에 관련된 설정, 스택의 크기, 호출되는 함수의 포인터, IpStartAddress가 가리키는 함수 호출시 전달할 인자 지정, 새로운 스레드 생성 시 실행 가능/불가능 지정, 스레드 ID가 리턴되면 저장할 변수의 포인터를 받는다.

HANDLE CreateThread(
	LPSECURITY_ATTRIBUTES lpThreadAttributes,
	SIZE_T dwStackSize,
	LPTHREAD_START_ROUTINE lpStartAddress,
	LPVOID lpParameter,
	DWORD dwCreationFlags,
	LPDWORD lpThreadId        
);

 

더 자세히는 다음 블로그에 세세하게 설명되어 있다.

https://m.blog.naver.com/PostView.nhn?blogId=whentlr&logNo=120127509572&proxyReferer=https%3A%2F%2Fwww.google.com%2F\

 

CreateThread()

HANDLE CreateThread(.... ) 출처 : http://rockdrumy.tistory.com/entry/TCPIP-CreateThre...

blog.naver.com

http://blog.naver.com/PostView.nhn?blogId=bitnang&logNo=70183548976

 

소켓과 윈속을 종료해준다.

 

멀티 스레딩 - server


1. 필요 헤더 및 라이브러리

 

2. 스레드 생성 함수

client와 통신할 소켓인 ClientSocket을 정의해준다.

 

recv() / send() 함수들로 클라이언트로부터 온 랜덤 메시지를 받고 보낸다. 

 

그리고 ClientSocket 소켓을 닫아준다.

 

3. main 함수

소켓과 구조체를 정의해준 후, 윈속을 초기화해준다.

 

클라이언트와 같은 형식에 추가 옵션인 AI_PASSIVE를 지정해준다. AI_PASSIVE 지정시 localhost의 관련 정보들을 얻을 수 있다.

그리고 getaddrinfo 함수 안에 받은 인자들을 linked list 형태로 전달한다. 

 

listen()과 bind() 과정을 걸쳐 클라이언트와 통신한다.

 

 

클라이언트와의 연결 요청을 수락하고 숫자를 문자열로 바꿔주는 WSAAddressToString 함수를 이용하여  IPv4 주소를 출력한다.

int WSAAddressToString(
	LPSOCKADDR IpsaAddress, //숫자 형식의 IP 주소
    DWORD dwAddressLength, //주소 구조체의 길이
    LPWSAPROTOCOL_INFO IpProtocolInfo, //NULL
    LPTSTR IpszAddressString, //IP 주소를 저장할 버퍼
    LPDWORD IpdwAddressStringLength //버퍼의 길이
);

 

멀티 스레드를 만들어주고 윈속과 소켓을 종료시켜준다.

 

결과


랜덤값인 19가 'q'가 입력되기 전까지 계속 서버로 전송된다. 클라이언트 창을 하나 더 띄우면 서버에 코드가 2번 나타난다.

 

서버 창이 먼저 닫히면 클라이언트 창에 각각 이렇게 뜬다.

'Network > HTTP' 카테고리의 다른 글

DDOS Generator 2  (0) 2020.03.30
Thread Pool  (0) 2020.03.22
HTTP 서버  (0) 2020.03.09
Socket Programming  (0) 2020.03.02
DDOS KISA  (0) 2020.02.24