SlideShare una empresa de Scribd logo
1 de 25
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
흥배 최
 
임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012
devCAT Studio, NEXON
 
ASP.NET과 C#으로 개발하는 대규모 소셜 게임
ASP.NET과 C#으로 개발하는 대규모 소셜 게임ASP.NET과 C#으로 개발하는 대규모 소셜 게임
ASP.NET과 C#으로 개발하는 대규모 소셜 게임
흥배 최
 

La actualidad más candente (20)

Iocp 기본 구조 이해
Iocp 기본 구조 이해Iocp 기본 구조 이해
Iocp 기본 구조 이해
 
MMOG Server-Side 충돌 및 이동처리 설계와 구현
MMOG Server-Side 충돌 및 이동처리 설계와 구현MMOG Server-Side 충돌 및 이동처리 설계와 구현
MMOG Server-Side 충돌 및 이동처리 설계와 구현
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
 
중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직중앙 서버 없는 게임 로직
중앙 서버 없는 게임 로직
 
유니티3D 그리고 웹통신
유니티3D 그리고 웹통신유니티3D 그리고 웹통신
유니티3D 그리고 웹통신
 
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
 
임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012임태현, MMO 서버 개발 포스트 모템, NDC2012
임태현, MMO 서버 개발 포스트 모템, NDC2012
 
게임서버프로그래밍 #2 - IOCP Adv
게임서버프로그래밍 #2 - IOCP Adv게임서버프로그래밍 #2 - IOCP Adv
게임서버프로그래밍 #2 - IOCP Adv
 
Iocp advanced
Iocp advancedIocp advanced
Iocp advanced
 
게임서버프로그래밍 #1 - IOCP
게임서버프로그래밍 #1 - IOCP게임서버프로그래밍 #1 - IOCP
게임서버프로그래밍 #1 - IOCP
 
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
 
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
 
ASP.NET과 C#으로 개발하는 대규모 소셜 게임
ASP.NET과 C#으로 개발하는 대규모 소셜 게임ASP.NET과 C#으로 개발하는 대규모 소셜 게임
ASP.NET과 C#으로 개발하는 대규모 소셜 게임
 
Windows Registered I/O (RIO) vs IOCP
Windows Registered I/O (RIO) vs IOCPWindows Registered I/O (RIO) vs IOCP
Windows Registered I/O (RIO) vs IOCP
 
Twitter의 snowflake 소개 및 활용
Twitter의 snowflake 소개 및 활용Twitter의 snowflake 소개 및 활용
Twitter의 snowflake 소개 및 활용
 
게임서버프로그래밍 #8 - 성능 평가
게임서버프로그래밍 #8 - 성능 평가게임서버프로그래밍 #8 - 성능 평가
게임서버프로그래밍 #8 - 성능 평가
 
NDC 11 자이언트 서버의 비밀
NDC 11 자이언트 서버의 비밀NDC 11 자이언트 서버의 비밀
NDC 11 자이언트 서버의 비밀
 
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
 
동기화 시대를 뛰어넘는 비동기 프로그래밍
동기화 시대를 뛰어넘는 비동기 프로그래밍동기화 시대를 뛰어넘는 비동기 프로그래밍
동기화 시대를 뛰어넘는 비동기 프로그래밍
 
NoSQL 위에서 MMORPG 개발하기
NoSQL 위에서 MMORPG 개발하기NoSQL 위에서 MMORPG 개발하기
NoSQL 위에서 MMORPG 개발하기
 

Similar a 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
 

Similar a 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)
 

Más de 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
 

Último

Último (7)

JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement MethodologyJMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
 
JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례
 
공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화
 
실험 설계의 평가 방법: 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-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
 
데이터 분석 문제 해결을 위한 나의 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