NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버Heungsub Lee
NDC14에서 발표한 "[야생의 땅: 듀랑고] 서버 아키텍처" 세션의 슬라이드입니다.
슬라이드에 설명이 많지 않은데, 디스이즈게임에서 발표 내용을 잘 정리해주었습니다. 기사도 함께 보시면 좋을 것 같습니다.
http://www.thisisgame.com/webzine/news/nboard/4/?n=54955
NHN NEXT 게임 서버 프로그래밍 강의 자료입니다. 최소한의 필요한 이론 내용은 질문 위주로 구성되어 있고 (답은 학생들 개별로 고민해와서 피드백 받는 방식) 해당 내용에 맞는 실습(구현) 과제가 포함되어 있습니다.
참고로, 서버 아키텍처에 관한 과목은 따로 있어서 본 강의에는 포함되어 있지 않습니다.
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버Heungsub Lee
NDC14에서 발표한 "[야생의 땅: 듀랑고] 서버 아키텍처" 세션의 슬라이드입니다.
슬라이드에 설명이 많지 않은데, 디스이즈게임에서 발표 내용을 잘 정리해주었습니다. 기사도 함께 보시면 좋을 것 같습니다.
http://www.thisisgame.com/webzine/news/nboard/4/?n=54955
boost라이브러리 중에서 가장 많이 사용하는 기능인 BOOST_FOREACH()와 shared_ptr의 내부 구조를 분석합니다. 그리고 boost의 내부 구현에 사용된 이 기능을 프로그래밍에 응용하는 방법을 제시합니다.
* BOOST_FOREACH 구조 분석 및 응용
* shared_ptr 구조 분석 및 응용
6. 1. 변수 정의 때 명시적으로 type을 지정하지 않아도 된다.
2. auto로 정의한 변수는 초기화할 때 type이 결정.
3. 컴파일 타임 때 type이 결정.
4. 템플릿 프로그래밍에 사용하면 코딩이 간편.
5. 코드 가독성이 향상
11. C++ 14의 새로운 기능.
일반 함수에서도 반환 값 타임을 추론할 수 있는 기능.
반환 값 타입으로 auto를 사용한다.
14. 원래는 C++ 11에 들어갈 예정인 'Concept'과 같이 들어갈 예정이었으나
Concept이 표준에 들어가지 못하게 되어서 A이(Argument Dependent name
Lookup)에 의해서 구현되어 있다.
C++11 기능 중 'auto'와 더불어 간단하면서 유용한 기능.
반복문을 아주 쉽고, 안전하게 사용할 수 있다.
VC의 'for each'와 유사
C++ STL의 컨테이너, 배열 등을 사용할 수 있다.
for ( for-range-선언 : for-range-초기화자 ) 문
18. C++11의 enum은 C++03 표준과 다르게 두 종류의 enum으로 바뀌었다.
강한 형 사용과 범위를 가진다.
'unscoped enumeration'과 'scoped enumeration'
unscoped enumeration은 기존(C++03) enum과 비슷
27. 초기화 리스트 전용 type
함수의 인자, 유저 정의형, STL 컨테이너의 초기화에 사용할 수 있다.
30. default
컴파일러가 함수를 자동으로 생성하도록 명시적으로 지정
delete
컴파일러가 함수를 자동으로 생성하지 않도록 명시적으로 지정
33. override 라는 키워드를 사용하여 컴파일러에게 부모 클래스의 멤버 함수를
재 정의 함을 알린다.
final
부모 클래스의 특정 멤버 함수를 자식 클래스에서 재정의 하지 못하다록 막을
때 사용한다.
36. 오브젝트 생성과 컨테이너 추가를 한번에 할 수 있다.
STL의 대부분의 컨테이너에서 지원.
요소의 생성자 인수를 받아서 컨테이너 내에서 오브젝트를 만든다.
push_bak() -> emplace_back()
push_front() -> emplace_front()
insert() -> emplace()
push_back에 비해 요소 추가 비용을 줄일 수 있다.
임시 오브젝트의 복사와 파괴 비용이 발생하지 않는다.
39. constexpr는 변수, 함수, 클래스를 컴파일 타임에 정수로 사용할 수 있다.
즉 상수로 취급할 수 있는 작업은 컴파일 타임에 처리하도록 한다.
#define 이나 템플릿을 대체 할 수 있다.
42. ‘lambda 함수’ 또는 ‘무명 함수’ 라고 부르기도 한다.
lambda는 함수 오브젝트 이다.
C++의 표현력을 증가 시켜 준다.
STL의 알고리즘을 더 간편하게 사용할 수 있다.
규격에서는 lambda는 특별한 타입을 가지고 있다고 한다.
단 decltype나 sizeof에서 사용 할 수 없다.
43. int main()
{
[] // lambda capture
() // 함수의 인수정의
{} // 함수의 본체
() // 함수 호출;
}
51. lambda를 정의한 scope 내의 변수를 capture 할 수 있다.
모든 변수를 참조로 capture 할 때는 [&], 특정 변수만 참조로 capture 할 때
는 [&변수]
모든 변수를 복사로 capture 할 때는 [=], 특정 변수만 복사로 capture 할 때
는 [변수]
56. 클래스의 멤버 함수에서 lambda 사용 가능.
public, protected, private 멤버도 접근 가능.
lambda는 클래스에서 friend로 인식.
lambda에서 클래스 멤버를 호출 할 때는 this를 사용한다.
57. C++ 14
인수 타입을 auto를 사용할 수 있다.
템플릿 인수와 같이 형 추론된다.
[](const auto& x, auto& y) { return x + y; }
가변 인수를 사용할 수 있다.
[](auto&&... args){cout << sizeof...(args) << endl; }(1U, 2.1, nullptr, hoge{});
auto은 사양 및 구현에서 필수는 아니다.
다만 가독성을 위해서 붙이는 것으로 되었다.
58. // generic한 형 표현
auto Sum = [](auto a, decltype(a) b) { return a + b; };
int i = Sum(3, 4);
double d = Sum(3.14, 2.77);
// 캡쳐하지 않은 람다는 함수 포인터로 형 변환 가능
auto L = [](auto a, decltype(a) b) { return a + b; };
int (*fp)(int, int) = L;
62. C++03
C 런타임 난수를 사용.
전역 함수 사용.
의사 난수 주기가 짧음. 최대 값이 32767
균등하게 분포되지 않음.
기능적으로 아주 빈약.
C++11
고품질의 난수 생성기와 분포 클래스를 사용.
난수의 형, 범위, 분포 형태를 세세하게 조절 가능.
65. 필요한 헤더 파일
난수 생성기:
Mersenne twister(32비트 버전),
std::mt19937_64(64비트)
호출마다 난수 생성
67. 보안 등의 목적으로 절대 예측할 수 없는 진정한 의미의 난수를 생성하기 위
해서는 비 결정적 난수 생성기를 사용해야 한다.
- std::mt19937는 의사 난수
random_device
비 결정적인 난수를 생성하므로 다른 의사 난수 생성 엔진의 시드 초기화나
암호화 용도로 사용할 수 있다
random_device는 Mersenne twister와 달리 예측 불가능한 난수를 생성해야
하므로 소프트웨어로 구현하지 않고 하드웨어 리소스를 사용하여 만든다.
예) 하드웨어 노이즈나 마우스 움직임 등을 사용.
- Windows에서는 CryptGenRandom() 함수를 랩핑하고,
- Unix에서는 /dev/random 이나 /dex/urandom 값을 사용
69. 보통 특정 조건 안에 만족하는 난수를 원한다.
예) 아이템 드랍율을 위해 1 ~ 100 사이의 난수
난수를 특정 타입과 특정 범위 안에서 생성하기 위해서는 난수 생성기에 난수
분포기를 더하여 난수를 생성한다.
정수 타입의 난수를 분포 할 때는 uniform_int_distribution,
실수 타입은 uniform_real_distribution을 사용한다.
71. uniform_int_distribution dist와 std::uniform_real_distribution는 포함 범위가
다르므로 주의해야 한다.
std::uniform_int_distribution dist(-3, 3)
-3 이상, 3
std::uniform_real_distribution<double> dist(0.0, 1.0)
0.0 이상, 1.0 미만의 범위다.
72. bernoulli_distribution 분포기
: 확률을 지정하면 이 확률에 근거하여 true와 false를 반환한다.
예) ‘몬스터를 잡으면 n%의 확률로 XX 아이템을 드롭시켜라’.
binomial_distribution 난수 분포기
특정 확률로 n회 실시 했을 때 몇 번 성공할 것인가를 반환.
예) 사망 가능성(확률)이 있는 백신을 N 사람에게 투여할 때 살 수 있는 사람
의 수를 구해라.
normal_distribution 난수 분포기
평균과 표준편차로 정규 분포 난수를 생성한다.
예)’평균 키 173cm, 표준편차 5cm’의 신장 데이터 생성
76. C++ 표준 스레드 라이브러리.
<thread> 헤더 파일만 있으면 사용.
각 OS API의 스레드 사용 보다 사용하기 쉽다.
81. thread 클래스의 join 함수를 사용하여 스레드가 종료할 때까지 대기한다.
Thread1.join();
join 함수를 호출하면 블럭킹 된다.
join 함수를 호출할 수 있는지 알기 위해서는 joinable 함수를 사용한다.
83. get_id() 함수를 사용하면 해당 스레드의 식별자를 얻을 수 있다.
get_id()를 통해서 멀티스레드에서 각각의 스레드를 구별 한다.
get_id()를 사용하면 멀티스레드에서 공용 리소스에 접근하는 스레드를 알수
있고, 특정 스레드만 접근할 수 있게 한다.
85. detach 함수를 사용하면 thread 오브젝트와 스레드 연결 고리를 떼어낸다.
detach 이후에는 thread 오브젝트는 스레드를 제어할 수 없다.
detach 와 스레드의 종료와는 상관 없다.
87. sleep_for와 sleep_until을 사용하면 스레드를 일시 중지 시킬 수 있다.
sleep_for는 지정한 시간 동안(예 100밀리세컨드 동안만 정지),
sleep_until은 지정 시간이까지(예 16시 10분까지 정지)
yield를 사용하여 자신(스레드)의 활동을 포기하고 다른 스레드에게 양보한다.
std::this_thread::yield();
88. 스레드가 실행 중에 프로그램이 종료되면 프로그램이 crash가 발생할 수 있다.
프로그램 종료 전에 꼭 스레드를 먼저 종료 시키고 프로그램을 종료하도록 한
다.
90. 멀티스레드 프로그래밍에서는 공유 리소스 관리가 가장 큰 문제
너무 빡빡하게 관리하면 성능 하락의 위험,
너무 느슨하게 관리하면 시한 폭탄 동작
mutex를 사용하여 공유 리소스를 관리하는 것이 가장 일반적(?)인 방법
mutex는 Windows에 구현에서는 크리티컬섹션을 사용한다.
92. 스레드가 A가 mutex의 lock을 호출 했을 때,
이미 스레드 B에서 lock을 호출했다면
A는 B가 lock을 풀어 줄 때까지 대기한다.
스레드 A는 다른 일을 하고 싶어도 할 수가 없다.
이럴 때 try_lock()을 사용!!!
다른 스레드가 먼저 락을 걸었다면 대기하지 않고 즉시 false를 반환.
반대로 true를 반환한 경우 공유 리소스의 소유권을 가진다.
93. 실수로 lock 호출 후 unlock을 호출하지 않고 나와버리면 데드락 상황에 빠져
버린다.
또는 코드 실행 중 예외가 발생하여 lock을 풀지 못하고 나와버릴 수도 있다.
이런 문제를 풀기 위해 lock_guard 라는 유틸리티 클래스를 사용한다.
scope를 벗어날 때 자동으로 unlock을 호출한다.
97. 멀티스레드 환경에서 프로그램 실행 중에서 단 한번만의 코드 실행이 필요할
때가 있다.
보통 이중 조사로 구현하기도 한다.
실수 위험이 있음
std::call_once을 사용하면 더 쉽고, 안전하게 구현할 수 있다.
99. C++은 하루를 다 보낸 후에 불필요한 노력이
라고 느끼는 경우가 없다. 코드를 쓰는 시간이
길게 걸리더라도 (컨트롤 할 수 있으므로)
100% 자신의 의도를 반영할 수 있으므로 좋다.
from: Ruby/Python 프로그래머는 Go로 넘어오는데 왜 C++ 개발자는 넘어오지 않는가?