SlideShare ist ein Scribd-Unternehmen logo
1 von 25
Downloaden Sie, um offline zu lesen
C# Server 만들기
2013. 06. 07.
최재영
Why?
• 빌드 속도
• 표현력
2
async
await
extension
method
linq
Observable
TPL
DynamicObject
Reflection
Attribute
IEnumerable
흐름
3
Network
Datasheet
Database
Logic
async, await
TaskCompletionSource
Reflection
Attribute
Dynamic
XmlLinq
IEnumerable
Network
• 빠른 패킷 처리를 위해 비동기 IO 사용
4
var socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.IPv4);
// preprocess socket
var buffer = new byte[4096];
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None,
result => {
var received = socket.EndReceive(result);
// process packet
}, null /* state */);
TaskCompletionSource
• C++ Future + C# Task Awaitable Future
5
Network
static Task<int> ReceiveAsync(this Socket sock, byte[] buf, int off, int size)
{
var source = new TaskCompletionSource<int>(sock);
sock.BeginReceive(buf, off, size, SocketFlags.None, state =>
{
try
{
source.SetResult(socket.EndReceive(state));
}
catch (Exception e)
{
source.SetException(e);
}
}, source);
return source.Task;
}
async, await
• Task.Result 비동기 대기(await), 그런 코드가 있는 함수(async)
6
Network
static async Task<byte[]> ReceiveAsync(this Socket socket, int count)
{
var buffer = new byte[count];
var length = 0;
do
{
var num = await ReceiveAsync(socket, buffer, length, count);
if (num == 0)
break;
length += num;
count -= num;
} while (count > 0);
if (length != buffer.Length) throw new IOException("packet is truncated.");
return buffer;
}
async, await
7
Network
async void ReceiveLoop(Socket socket)
{
while (true)
{
var lengthBytes = await socket.ReceiveAsync(sizeof (int));
var packetBytes = await socket.ReceiveAsync(
BitConverter.ToInt32(lengthBytes, 0));
// process packet
var packet = ReadPacket(packetBytes);
_handlerMap[packet.GetType()](packet);
}
}
await하는 지점에 아직 IO signal이 없다면, 해당 Task는 잠시 멈추고,
가용한 다른 Task를 찾아 수행함
Listener (Server)
• ClientSocket을 비동기로 Accept해서,
• 각 Socket마다 비동기로 Packet을 대기해서 처리함
8
Network
var listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
var localEndPoint = new IPEndPoint(IPAddress.Any, Port);
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
var clientSocket = await listener.AcceptAsync();
ReceiveLoop(clientSocket);
}
async method
Summary
• Socket의 Async 계열 함수를 사용 (.NET 내부는 IOCP로 처리)
• async, await은 TaskContinuation의 Syntax sugar
• async, await Keyword로 Callback 없이 편하게 Network 코드 작성
• 그러면서도 Thread Pool에 의한 효율적으로 수행됨
(.NET Thread Pool도 내부에서 IOCP로 관리)
9
Network
Datasheet
• dynamic을 사용한 일반적인 Xml 읽기
• code generator를 사용
10
Xml 작성
Xml Model
구현
Xml Parser
구현
자동 생성 자동 생성
code generator
DynamicObject
XmlDefinition (자동 생성)
general-loader
DynamicObject
• RuntimeType으로 동적으로 멤버 접근 가능
• 올바른 Type의 값을 미리 준비해야 하므로 XmlDefinition 필요
11
Datasheet
public class XmlObject : DynamicObject
{
private readonly Dictionary<string, object> _attributes;
private readonly Dictionary<string, IEnumerable<XmlObject>>
_multipleChildren;
private readonly Dictionary<string, XmlObject> _singleChildren;
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
return TryGetValue(_attributes, binder.Name, out result) ||
TryGetValue(_singleChildren, binder.Name, out result) ||
TryGetValue(_multipleChildren, binder.Name, out result);
}
DynamicObject
• Attribute를 읽을 때 Type 변환을 미리 수행
• dynamic으로 접근하여 Model 없이 접근 가능
12
Datasheet
_attributes = element.Attributes.OfType<XmlAttribute>()
.ToDictionary(e => e.Name,
e => defNode.SelectAttribute(e.Name)
.ReadValue(e.Value));
<?xml version="1.0" encoding="utf-8" ?>
<World>
<Config port="40123"/>
</World>
World.Config@port : int
dynamic world = XmlObject.Load("World.xml", _def);
_listener.Port = world.Config.port;
Summary
• dynamic을 사용하여 코딩 시간 단축(Model, Parser 작성 불필요)
• 오타로 인한 접근 위반은 Runtime에 확인 가능
• XmlDefinition이 필요함(자동 생성 가능)
• 보다 빠른 속도를 원할 경우에는 Model, Parser를 Generate
(IVsSingleFileGenerator)
13
Datasheet
Database
• Reflection과 Attribute 사용으로 일반적인 Bind 구현
• scheme 작업이 불필요할 경우 model 작성만으로 모든 구현 해결 가능
14
Scheme
작성
DataModel
작성
Bind 구현
nosql or generator Reflection
Reflection
• Runtime에 model의 type정보로 scheme를 구축
• 각 데이터의 Serialize/Deserialize 구현 필요 (String과 object 상호 변환)
• 모든 Model 객체를 Xml로 변환
15
Database
new XElement("Objects",
_gameObjects.Values.Select(
obj =>
new XElement("Object",
obj.GetType().GetProperties()
.Where(e => e.CanRead && e.CanWrite)
.Select(e => new XAttribute(e.Name,
SerializeValue(e.PropertyType, e.GetValue(obj, null))
)))));
모든 Property에 대해 출력
출력할 때에는 string으로, 읽을 때에는 다시 object로
Attribute
• Runtime에 접근 가능한 metadata를 코드에 주입
16
Database
[CommandHandler("npc", "새로운 Npc를 생성합니다")]
internal bool SpawnNpc(Entity admin,
[CommandArgument("Npc의 이름")] string npcName,
[CommandArgument("Npc의 X 위치", 0)] double newX,
[CommandArgument("Npc의 Y 위치", 0)] double newY)
{
if (!admin.Has<Pos>())
return false;
var npc = EntityManager.Instance.Create(EntityTemplate.Ids.Npc);
npc.Get<Motion>().Dir = admin.Get<Motion>().Dir;
npc.Get<Nameplate>().Name = npcName;
npc.Get<Pos>().Assign(new Pos {X = newX, Y = newY});
명령어와 설명을 코드에 기록
인자 설명과 기본 값, type을 코드에 기록
Summary
• model 객체의 type 정보를 최대한 사용
• Attribute를 부여하여 가능한 많은 정보를 코드에 주입
(DSL, 문서, 주석 등 외부 정보는 추가 유지 보수가 필요함)
• Dirty나 Lazy를 사용하여 최적화 가능
• 역시 보다 빠른 속도를 원할 경우에는 Code Generate를 사용
(partial class를 사용하여 사용자 코드와 혼합 가능)
17
Database
Logic
• yield return을 사용하여 State Machine 제거
• context 유지를 위한 별도 코딩이 필요 없음
18
행동
A
행동
B
행동
C
1초 뒤
3초 뒤
5초 뒤
Logic
Engine
Logic #1 Logic #2
IEnumerable (Coroutine)
• IEnumerable을 반환 type으로 설정하여 yield return 사용
• 다음 로직 수행까지의 대기 시간을 반환
19
Logic
public IEnumerable<int> RegenerateEntry()
{
while (true)
{
var newNpc = _context.NewGameObject(ObjectType.Npc);
_context.AddGameObject(newNpc);
_context.BroadcastPacket(newNpc.ToSpawnPacket());
var newAi = new EachAi(this, newNpc);
_context.AddEntry(newAi.AiLogicEntry);
var nextInterval = _random.Next(interval) + base;
yield return nextInterval;
}
제어권이 호출자에게 넘어감
다음 호출 시 이 지점부터 수행
IEnumerator (Coroutine)
• IEnumerator의 MoveNext() 함수로 코드 실행
• Current로 yield return 결과 값 확인
20
Logic
var enumerator = RegenerateEntry().GetEnumerator();
while (enumerator.MoveNext())
{
Thread.Sleep(enumerator.Current);
}
IEnumerable로부터 IEnumerator를 가져옴
MoveNext()로 yield 사이 구간 코드 수행
yield return 반환 값을 얻음. 대기 시간만큼 쉼
여러 IEnumerator를 관리하고 Thread.Sleep()을 보다 작은 단위로 수행
LogicEngine (Coroutine)
21
Logic
public void EntryLoop()
{
var prev = DateTime.Now;
while (true)
{
var now = DateTime.Now;
var delta = (now - prev).Milliseconds;
foreach (var newOne in _newLogicEntries)
{
var newEntry = new LogicEntry
{
Enumerator = newOne().GetEnumerator(),
SleepTime = 0
};
_logicEntries.Add(newEntry);
}
_newLogicEntries.Clear();
새로 추가된 Entry로부터
IEnumerator객체를 생성
LogicEngine (Coroutine)
22
Logic
var removals = new List<LogicEntry>();
foreach (var each in _logicEntries)
{
each.SleepTime -= delta;
if (each.SleepTime >= 0)
continue;
if (!each.Enumerator.MoveNext())
removals.Add(each);
else each.SleepTime = each.Enumerator.Current;
}
_logicEntries.RemoveAll(removals.Contains);
prev = now;
const int logicInterval = 16;
Thread.Sleep(logicInterval);
}
}
수행 가능한 IEnumerator 집합
수행할 시간이 된 로직을 찾아서 실행
로직 함수가 return되어 완료되면 MoveNext()가 fasle를 반환
수행이 완료된 로직 삭제
Summary
• IEnumerable과 yield return의 조합으로 coroutine 구현
• 간단한 coroutine이지만 많은 boiler plate 코드 작성 회피 가능
• yield return으로 많은 정보를 전달하여 다양한 활용 (Unity3D Engine)
• 여러 Thread가 LogicEngine을 수행하여 Entry 수행 분산 가능
(여러 Thread가 수행할 경우 Lfe 등 객체별 수행 동기화 고려가 필요함)
• Script를 C#으로 작성 시 도움이 될 듯(?)
(c# script + roslyn + linqpad + nuget)
23
Logic
Summary
• async, await을 사용한 동기적 Network(IO) 프로그래밍
• dynamic을 사용한 Runtime type dispatch
• Reflection, Attribute를 사용한 boiler plate 코드 줄이기
• Attribute로 metadata를 코드로 기록하여 유지 보수 비용 줄이기
• coroutine을 사용한 동기적 로직 프로그래밍
24
보다 적은, 그리고 직관적인(동기적) 코딩으로 유지 보수 비용 줄이기
높은 표현력
성능 문제는 회로의 발전이 해결해 줄 것입니다 [...]
How much faster is C++ than C#?
25
C# may not be faster, but it makes
YOU/ME faster. That's the most
important measure for what I do. :)
http://stackoverflow.com/questions/138361/how-much-faster-is-c-than-c

Weitere ähnliche Inhalte

Was ist angesagt?

임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012
devCAT Studio, NEXON
 
임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013
devCAT Studio, NEXON
 
테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템
QooJuice
 
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
흥배 최
 

Was ist angesagt? (20)

임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012
 
Iocp 기본 구조 이해
Iocp 기본 구조 이해Iocp 기본 구조 이해
Iocp 기본 구조 이해
 
중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직
 
Ndc14 분산 서버 구축의 ABC
Ndc14 분산 서버 구축의 ABCNdc14 분산 서버 구축의 ABC
Ndc14 분산 서버 구축의 ABC
 
Multiplayer Game Sync Techniques through CAP theorem
Multiplayer Game Sync Techniques through CAP theoremMultiplayer Game Sync Techniques through CAP theorem
Multiplayer Game Sync Techniques through CAP theorem
 
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
 
임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013
 
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이  왜 이리 힘드나요?  (Lock-free에서 Transactional Memory까지)Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이  왜 이리 힘드나요?  (Lock-free에서 Transactional Memory까지)
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
 
MMOG Server-Side 충돌 및 이동처리 설계와 구현
MMOG Server-Side 충돌 및 이동처리 설계와 구현MMOG Server-Side 충돌 및 이동처리 설계와 구현
MMOG Server-Side 충돌 및 이동처리 설계와 구현
 
GCGC- CGCII 서버 엔진에 적용된 기술 (1)
GCGC- CGCII 서버 엔진에 적용된 기술 (1)GCGC- CGCII 서버 엔진에 적용된 기술 (1)
GCGC- CGCII 서버 엔진에 적용된 기술 (1)
 
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
시즌 2: 멀티쓰레드 프로그래밍이 왜이리 힘드나요?
 
Next-generation MMORPG service architecture
Next-generation MMORPG service architectureNext-generation MMORPG service architecture
Next-generation MMORPG service architecture
 
KGC 2014: 클라이언트 개발자를 위한 컴퓨터 네트워크 기초 배현직
KGC 2014: 클라이언트 개발자를 위한 컴퓨터 네트워크 기초 배현직KGC 2014: 클라이언트 개발자를 위한 컴퓨터 네트워크 기초 배현직
KGC 2014: 클라이언트 개발자를 위한 컴퓨터 네트워크 기초 배현직
 
게임 분산 서버 구조
게임 분산 서버 구조게임 분산 서버 구조
게임 분산 서버 구조
 
Scalable Gaming with AWS - GDC 2014
Scalable Gaming with AWS - GDC 2014Scalable Gaming with AWS - GDC 2014
Scalable Gaming with AWS - GDC 2014
 
테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템
 
Multithread & shared_ptr
Multithread & shared_ptrMultithread & shared_ptr
Multithread & shared_ptr
 
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지
[IGC 2017] 아마존 구승모 - 게임 엔진으로 서버 제작 및 운영까지
 
실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략
 
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
 

Ähnlich wie C# Game Server

Naver api for android
Naver api for androidNaver api for android
Naver api for android
Sangon Lee
 
Android xml parsing
Android xml parsingAndroid xml parsing
Android xml parsing
Sangon Lee
 
SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8
Sangmin Lee
 

Ähnlich wie C# Game Server (20)

.NET에서 비동기 프로그래밍 배우기
.NET에서 비동기 프로그래밍 배우기.NET에서 비동기 프로그래밍 배우기
.NET에서 비동기 프로그래밍 배우기
 
Naver api for android
Naver api for androidNaver api for android
Naver api for android
 
Gcd ppt
Gcd pptGcd ppt
Gcd ppt
 
Android xml parsing
Android xml parsingAndroid xml parsing
Android xml parsing
 
android_thread
android_threadandroid_thread
android_thread
 
Javascript 조금 더 잘 알기
Javascript 조금 더 잘 알기Javascript 조금 더 잘 알기
Javascript 조금 더 잘 알기
 
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
 
Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)
 
pgday-2023
pgday-2023pgday-2023
pgday-2023
 
ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!
 
5-4. html5 offline and storage
5-4. html5 offline and storage5-4. html5 offline and storage
5-4. html5 offline and storage
 
I phone 2 release
I phone 2 releaseI phone 2 release
I phone 2 release
 
TenforFlow Internals
TenforFlow InternalsTenforFlow Internals
TenforFlow Internals
 
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
 
C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발
 
Rx java intro
Rx java introRx java intro
Rx java intro
 
SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8
 
Tensorflow service & Machine Learning
Tensorflow service & Machine LearningTensorflow service & Machine Learning
Tensorflow service & Machine Learning
 
Nodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjsNodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjs
 
20140528 AWS Meister BlackBelt - Amazon Kinesis (Korean)
20140528 AWS Meister BlackBelt - Amazon Kinesis (Korean)20140528 AWS Meister BlackBelt - Amazon Kinesis (Korean)
20140528 AWS Meister BlackBelt - Amazon Kinesis (Korean)
 

Mehr von lactrious (7)

Layered System prototype
Layered System prototypeLayered System prototype
Layered System prototype
 
Policy based Class Design
Policy based Class DesignPolicy based Class Design
Policy based Class Design
 
Preprocessor Programming
Preprocessor ProgrammingPreprocessor Programming
Preprocessor Programming
 
AWS GameServer Management
AWS GameServer ManagementAWS GameServer Management
AWS GameServer Management
 
Index Analysis
Index AnalysisIndex Analysis
Index Analysis
 
Synchronizing concurrent threads
Synchronizing concurrent threadsSynchronizing concurrent threads
Synchronizing concurrent threads
 
omega design proposal
omega design proposalomega design proposal
omega design proposal
 

Kürzlich hochgeladen

Kürzlich hochgeladen (8)

(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
 
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
 
JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례
 
데이터 분석 문제 해결을 위한 나의 JMP 활용법
데이터 분석 문제 해결을 위한 나의 JMP 활용법데이터 분석 문제 해결을 위한 나의 JMP 활용법
데이터 분석 문제 해결을 위한 나의 JMP 활용법
 
JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement MethodologyJMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
 
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
 
JMP가 걸어온 여정, 새로운 도약 JMP 18!
JMP가 걸어온 여정, 새로운 도약 JMP 18!JMP가 걸어온 여정, 새로운 도약 JMP 18!
JMP가 걸어온 여정, 새로운 도약 JMP 18!
 
공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화
 

C# Game Server

  • 1. C# Server 만들기 2013. 06. 07. 최재영
  • 2. Why? • 빌드 속도 • 표현력 2 async await extension method linq Observable TPL DynamicObject Reflection Attribute IEnumerable
  • 4. Network • 빠른 패킷 처리를 위해 비동기 IO 사용 4 var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IPv4); // preprocess socket var buffer = new byte[4096]; socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, result => { var received = socket.EndReceive(result); // process packet }, null /* state */);
  • 5. TaskCompletionSource • C++ Future + C# Task Awaitable Future 5 Network static Task<int> ReceiveAsync(this Socket sock, byte[] buf, int off, int size) { var source = new TaskCompletionSource<int>(sock); sock.BeginReceive(buf, off, size, SocketFlags.None, state => { try { source.SetResult(socket.EndReceive(state)); } catch (Exception e) { source.SetException(e); } }, source); return source.Task; }
  • 6. async, await • Task.Result 비동기 대기(await), 그런 코드가 있는 함수(async) 6 Network static async Task<byte[]> ReceiveAsync(this Socket socket, int count) { var buffer = new byte[count]; var length = 0; do { var num = await ReceiveAsync(socket, buffer, length, count); if (num == 0) break; length += num; count -= num; } while (count > 0); if (length != buffer.Length) throw new IOException("packet is truncated."); return buffer; }
  • 7. async, await 7 Network async void ReceiveLoop(Socket socket) { while (true) { var lengthBytes = await socket.ReceiveAsync(sizeof (int)); var packetBytes = await socket.ReceiveAsync( BitConverter.ToInt32(lengthBytes, 0)); // process packet var packet = ReadPacket(packetBytes); _handlerMap[packet.GetType()](packet); } } await하는 지점에 아직 IO signal이 없다면, 해당 Task는 잠시 멈추고, 가용한 다른 Task를 찾아 수행함
  • 8. Listener (Server) • ClientSocket을 비동기로 Accept해서, • 각 Socket마다 비동기로 Packet을 대기해서 처리함 8 Network var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var localEndPoint = new IPEndPoint(IPAddress.Any, Port); listener.Bind(localEndPoint); listener.Listen(100); while (true) { var clientSocket = await listener.AcceptAsync(); ReceiveLoop(clientSocket); } async method
  • 9. Summary • Socket의 Async 계열 함수를 사용 (.NET 내부는 IOCP로 처리) • async, await은 TaskContinuation의 Syntax sugar • async, await Keyword로 Callback 없이 편하게 Network 코드 작성 • 그러면서도 Thread Pool에 의한 효율적으로 수행됨 (.NET Thread Pool도 내부에서 IOCP로 관리) 9 Network
  • 10. Datasheet • dynamic을 사용한 일반적인 Xml 읽기 • code generator를 사용 10 Xml 작성 Xml Model 구현 Xml Parser 구현 자동 생성 자동 생성 code generator DynamicObject XmlDefinition (자동 생성) general-loader
  • 11. DynamicObject • RuntimeType으로 동적으로 멤버 접근 가능 • 올바른 Type의 값을 미리 준비해야 하므로 XmlDefinition 필요 11 Datasheet public class XmlObject : DynamicObject { private readonly Dictionary<string, object> _attributes; private readonly Dictionary<string, IEnumerable<XmlObject>> _multipleChildren; private readonly Dictionary<string, XmlObject> _singleChildren; public override bool TryGetMember(GetMemberBinder binder, out object result) { return TryGetValue(_attributes, binder.Name, out result) || TryGetValue(_singleChildren, binder.Name, out result) || TryGetValue(_multipleChildren, binder.Name, out result); }
  • 12. DynamicObject • Attribute를 읽을 때 Type 변환을 미리 수행 • dynamic으로 접근하여 Model 없이 접근 가능 12 Datasheet _attributes = element.Attributes.OfType<XmlAttribute>() .ToDictionary(e => e.Name, e => defNode.SelectAttribute(e.Name) .ReadValue(e.Value)); <?xml version="1.0" encoding="utf-8" ?> <World> <Config port="40123"/> </World> World.Config@port : int dynamic world = XmlObject.Load("World.xml", _def); _listener.Port = world.Config.port;
  • 13. Summary • dynamic을 사용하여 코딩 시간 단축(Model, Parser 작성 불필요) • 오타로 인한 접근 위반은 Runtime에 확인 가능 • XmlDefinition이 필요함(자동 생성 가능) • 보다 빠른 속도를 원할 경우에는 Model, Parser를 Generate (IVsSingleFileGenerator) 13 Datasheet
  • 14. Database • Reflection과 Attribute 사용으로 일반적인 Bind 구현 • scheme 작업이 불필요할 경우 model 작성만으로 모든 구현 해결 가능 14 Scheme 작성 DataModel 작성 Bind 구현 nosql or generator Reflection
  • 15. Reflection • Runtime에 model의 type정보로 scheme를 구축 • 각 데이터의 Serialize/Deserialize 구현 필요 (String과 object 상호 변환) • 모든 Model 객체를 Xml로 변환 15 Database new XElement("Objects", _gameObjects.Values.Select( obj => new XElement("Object", obj.GetType().GetProperties() .Where(e => e.CanRead && e.CanWrite) .Select(e => new XAttribute(e.Name, SerializeValue(e.PropertyType, e.GetValue(obj, null)) ))))); 모든 Property에 대해 출력 출력할 때에는 string으로, 읽을 때에는 다시 object로
  • 16. Attribute • Runtime에 접근 가능한 metadata를 코드에 주입 16 Database [CommandHandler("npc", "새로운 Npc를 생성합니다")] internal bool SpawnNpc(Entity admin, [CommandArgument("Npc의 이름")] string npcName, [CommandArgument("Npc의 X 위치", 0)] double newX, [CommandArgument("Npc의 Y 위치", 0)] double newY) { if (!admin.Has<Pos>()) return false; var npc = EntityManager.Instance.Create(EntityTemplate.Ids.Npc); npc.Get<Motion>().Dir = admin.Get<Motion>().Dir; npc.Get<Nameplate>().Name = npcName; npc.Get<Pos>().Assign(new Pos {X = newX, Y = newY}); 명령어와 설명을 코드에 기록 인자 설명과 기본 값, type을 코드에 기록
  • 17. Summary • model 객체의 type 정보를 최대한 사용 • Attribute를 부여하여 가능한 많은 정보를 코드에 주입 (DSL, 문서, 주석 등 외부 정보는 추가 유지 보수가 필요함) • Dirty나 Lazy를 사용하여 최적화 가능 • 역시 보다 빠른 속도를 원할 경우에는 Code Generate를 사용 (partial class를 사용하여 사용자 코드와 혼합 가능) 17 Database
  • 18. Logic • yield return을 사용하여 State Machine 제거 • context 유지를 위한 별도 코딩이 필요 없음 18 행동 A 행동 B 행동 C 1초 뒤 3초 뒤 5초 뒤 Logic Engine Logic #1 Logic #2
  • 19. IEnumerable (Coroutine) • IEnumerable을 반환 type으로 설정하여 yield return 사용 • 다음 로직 수행까지의 대기 시간을 반환 19 Logic public IEnumerable<int> RegenerateEntry() { while (true) { var newNpc = _context.NewGameObject(ObjectType.Npc); _context.AddGameObject(newNpc); _context.BroadcastPacket(newNpc.ToSpawnPacket()); var newAi = new EachAi(this, newNpc); _context.AddEntry(newAi.AiLogicEntry); var nextInterval = _random.Next(interval) + base; yield return nextInterval; } 제어권이 호출자에게 넘어감 다음 호출 시 이 지점부터 수행
  • 20. IEnumerator (Coroutine) • IEnumerator의 MoveNext() 함수로 코드 실행 • Current로 yield return 결과 값 확인 20 Logic var enumerator = RegenerateEntry().GetEnumerator(); while (enumerator.MoveNext()) { Thread.Sleep(enumerator.Current); } IEnumerable로부터 IEnumerator를 가져옴 MoveNext()로 yield 사이 구간 코드 수행 yield return 반환 값을 얻음. 대기 시간만큼 쉼 여러 IEnumerator를 관리하고 Thread.Sleep()을 보다 작은 단위로 수행
  • 21. LogicEngine (Coroutine) 21 Logic public void EntryLoop() { var prev = DateTime.Now; while (true) { var now = DateTime.Now; var delta = (now - prev).Milliseconds; foreach (var newOne in _newLogicEntries) { var newEntry = new LogicEntry { Enumerator = newOne().GetEnumerator(), SleepTime = 0 }; _logicEntries.Add(newEntry); } _newLogicEntries.Clear(); 새로 추가된 Entry로부터 IEnumerator객체를 생성
  • 22. LogicEngine (Coroutine) 22 Logic var removals = new List<LogicEntry>(); foreach (var each in _logicEntries) { each.SleepTime -= delta; if (each.SleepTime >= 0) continue; if (!each.Enumerator.MoveNext()) removals.Add(each); else each.SleepTime = each.Enumerator.Current; } _logicEntries.RemoveAll(removals.Contains); prev = now; const int logicInterval = 16; Thread.Sleep(logicInterval); } } 수행 가능한 IEnumerator 집합 수행할 시간이 된 로직을 찾아서 실행 로직 함수가 return되어 완료되면 MoveNext()가 fasle를 반환 수행이 완료된 로직 삭제
  • 23. Summary • IEnumerable과 yield return의 조합으로 coroutine 구현 • 간단한 coroutine이지만 많은 boiler plate 코드 작성 회피 가능 • yield return으로 많은 정보를 전달하여 다양한 활용 (Unity3D Engine) • 여러 Thread가 LogicEngine을 수행하여 Entry 수행 분산 가능 (여러 Thread가 수행할 경우 Lfe 등 객체별 수행 동기화 고려가 필요함) • Script를 C#으로 작성 시 도움이 될 듯(?) (c# script + roslyn + linqpad + nuget) 23 Logic
  • 24. Summary • async, await을 사용한 동기적 Network(IO) 프로그래밍 • dynamic을 사용한 Runtime type dispatch • Reflection, Attribute를 사용한 boiler plate 코드 줄이기 • Attribute로 metadata를 코드로 기록하여 유지 보수 비용 줄이기 • coroutine을 사용한 동기적 로직 프로그래밍 24 보다 적은, 그리고 직관적인(동기적) 코딩으로 유지 보수 비용 줄이기 높은 표현력 성능 문제는 회로의 발전이 해결해 줄 것입니다 [...]
  • 25. How much faster is C++ than C#? 25 C# may not be faster, but it makes YOU/ME faster. That's the most important measure for what I do. :) http://stackoverflow.com/questions/138361/how-much-faster-is-c-than-c