SlideShare a Scribd company logo
오픈소스 네트워크
엔진
SuperSocket
사용하기
NHN Next 겸임 교수(게임)
최흥배
https://github.com/jacking75/choiHeungbae
Windows 플랫폼에서
고성능 네트워크
프로그램을 만들 때 가장
자주 사용하는 기술은
C++ & IOCP
... PC 온라인 게임 시대 때...
KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기
C#의 비동기 Socket은
Windows에서는 내부적으로
IOCP로 구현되어 있음.
즉 비동기 네트워크 측면만
보았을 때는 C++로 IOCP를
사용하는 것과 비슷
사용하기 쉽고,
고 성능이고,
안전하다
http://www.supersocket.net/
● .NET 플랫폼용 오픈 소스 네트워크 라이브러리
3.5 ~ 4.5까지 지원.
● Windows, Linux, Mono, Azure를 지원한다.
● 비동기 I/O를 지원. TCP, UDP
● SSL/TLS 지원, 확장 기능 등 다양한 기능 지원
● 공식 사이트 http://www.supersocket.net
문서는 http://docs.supersocket.net/
● 현재(2016.09.02) 기준
● nugget 최신 버전은 1.6.6.1
https://www.nugget.org/packages/SuperSocket/
● GitHub 버전은 1. 6.7
https://github.com/kerryjiang/SuperSocket
● 2.0 버전을 준비 중
Visual Studio Code 지원.
어쩌면 .NET Core도 지원?
오픈 소스 답지 않게(?) 문서화와
예제 코드가 잘 만들어져 있어서
분석이 쉽다
http://docs.supersocket.net/v1-6/en-US/Architecture-Diagrams
http://docs.supersocket.net/v1-6/en-US/Architecture-Diagrams
http://docs.supersocket.net/v1-6/en-US/Architecture-Diagrams
http://docs.supersocket.net/v1-6/en-US/Architecture-Diagrams
설치하기 – 소스 코드에서
SuperSocket 소스에 있는 log4net.dll을 포함한다.
SuperSocket.Common, SuperSocket.SocketBase,
SuperSocket.SocketEngine는 왼쪽 그림처럼 dll을 참조에 포함해도 되
고 아니면 프로젝트를 바로 포함해도 된다(아래).
필요한 클래스 라이브러리
설치하기 – NuGet에서
CountSpliterReceiveFilter, FixedSizeReceiveFilter,
BeginEndMarkReceiveFilter, FixedHeaderReceiveFilter 를 사용하기
위해서는 'SuperSocket.Facility'가 필요한데 기본으로 등록되지 않으
므로 NuGet으로 받은 package 디렉토리에서 선택해서 추가한다.
서버 실행 – 설정
void InitConfig()
{
m_Config = new ServerConfig
{
Port = 23478,
Ip = "Any",
MaxConnectionNumber = 100,
Mode = SocketMode.Tcp,
Name = "BoardServerNet"
};
}
void CreateServer()
{
m_Server = new BoardServerNet();
bool bResult = m_Server.Setup(new RootConfig(),
m_Config,
logFactory: new Log4NetLogFactory()
);
if (bResult == false)
{
}
......
}
class BoardServerNet : AppServer<NetworkSession,
EFBinaryRequestInfo>
{
}
// 포트 번호 2012로 설정
if (! m_Server.Setup(2012))
{
return;
}
....
// 네트워크 시작
if (! m_Server.Start())
{
return;
}
....
// 네트워크 중지
m_Server.Stop();
서버 실행 - 네트워크 설정 및 시작/중단
서버 실행 - 핸들러 등록
새로운 클라이언트가 연결되면 호출될 핸들러 등록
appServer.NewSessionConnected += new
SessionHandler<AppSession>(appServer_NewSessionConnected);
....
static void appServer_NewSessionConnected(AppSession session)
{
session.Send("Welcome to SuperSocket Telnet Server");
}
클라이언트가 보낸 데이터를 받으면 호출될 핸들러 등록
appServer.NewRequestReceived += new RequestHandler<AppSession,
StringRequestInfo>(appServer_NewRequestReceived);
....
static void appServer_NewRequestReceived(AppSession session,
StringRequestInfo requestInfo)
{
switch (requestInfo.Key.ToUpper())
{
case("ECHO"):
session.Send(requestInfo.Body);
break;
........
}
AppServer와 AppSession 구현
• AppSession
서버에 연결된 Socket의 로직 클래스.
이 클래스를 통해 클라이언트의 연결,끊어짐, 데이터 주고 받기를 한다.
• AppServer
네트워크 서버 클래스. 모든 AppSession 객체를 관리한다.
SuperSocket의 몸통이다.
public class TelnetSession : AppSession<TelnetSession>
{
protected override void OnSessionStarted()
{
this.Send("Welcome to SuperSocket Telnet Server");
}
protected override void HandleUnknownRequest(StringRequestInfo requestInfo)
{
this.Send("Unknow request");
}
protected override void HandleException(Exception e)
{
this.Send("Application error: {0}", e.Message);
}
protected override void OnSessionClosed(CloseReason reason)
{
//add you logics which will be executed after the session is closed
base.OnSessionClosed(reason);
}
}
public class TelnetServer : AppServer<TelnetSession>
{
protected override bool Setup(IRootConfig rootConfig, IServerConfig config)
{
return base.Setup(rootConfig, config);
}
protected override void OnStartup()
{
base.OnStartup();
}
protected override void OnStopped()
{
base.OnStopped();
}
}
서버 네트워크 옵션 설정하기
name: the name of appServer instance
serverType: the full name of the AppServer your want to run
ip: listen ip
port: listen port
위 설정을 아래와 같이 config 파일에 정의할 수 있다.
<superSocket>
<servers>
<server name="TelnetServer"
serverType="SuperSocket.QuickStart.TelnetServer_StartByConfig.TelnetServer,
SuperSocket.QuickStart.TelnetServer_StartByConfig"
ip="Any" port="2020">
</server>
</servers>
</superSocket>
static void Main(string[] args)
{
var bootstrap =
BootstrapFactory.CreateBootstrap(
);
if (!bootstrap.Initialize())
{
return;
}
var result = bootstrap.Start();
if (result == StartResult.Failed)
{
return;
}
//Stop the appServer
bootstrap.Stop();
}
위의 Config 파일 사용 예
서버 네트워크 옵션 설정하기
Config 파일 - 멀티 인스턴스 사용 예
<superSocket>
<servers>
<server name="TelnetServerA"
serverTypeName="TelnetServer"
ip="Any" port="2020">
</server>
<server name="TelnetServerB"
serverTypeName="TelnetServer"
ip="Any" port="2021">
</server>
</servers>
<serverTypes>
<add name="TelnetServer"
type="SuperSocket.QuickStart.TelnetServer_StartByConfig.TelnetServer,
SuperSocket.QuickStart.TelnetServer_StartByConfig"/>
</serverTypes>
</superSocket>
App.Config 파일 이외의 설정 파일을 사용하기 위해서는 아래처럼 파일 이름을 지정한다.
m_Bootstrap = BootstrapFactory.CreateBootstrapFromConfigFile("SuperSocket.config");
SuperSocket 라이브러리가 호스트 프로그램이 아닌 다른 프로젝트에서 사용하는 경우의 설정
SuperSocket의 'AppServer'
클래스를 상속한 클래스
Config 파일의 설정 값 확인
네트워크 옵션 파라미터
루트 설정(모든 서버 네트워크에 적용)에 사용하는 파리미터 IRootConfig
maxWorkingThreads: maximum working threads count of .NET thread pool;
minWorkingThreads: minimum working threads count of .NET thread pool;
maxCompletionPortThreads: maximum completion threads count of .NET thread pool;
minCompletionPortThreads: minimum completion threads count of .NET thread pool;
disablePerformanceDataCollector: whether disable performance data collector;
performanceDataCollectInterval: performance data collecting interval (in seconds, default value: 60);
isolation: SuperSocket instances isolation level
None - no isolation
AppDomain - server instances will be isolated by AppDomains
Process - server instances will be isolated by processes
logFactory: the name of default logFactory, all log factories are defined in the child node "logFactories"
which will be introduced in following documentation;
defaultCulture: default thread culture for the global application, only available in .Net 4.5;
서버 인스턴스 옵션 파라미터 IServerconfig
name: the name of the server instance;
serverType: the full name the AppServer's type which you want to run;
serverTypeName: the name of the selected server types, all server types should be defined in
serverTypes node which will be introduced in following documentation;
ip: the ip of the server instance listens. You can set an exact ip, you also can set the below values Any -
all IPv4 address IPv6Any - all IPv6 address
port: the port of the server instance listens;
listenBacklog: the listen back log size;
mode: the socket server's running mode, Tcp (default) or Udp;
disabled: whether the server instance is disabled;
startupOrder: the server instance start order, the bootstrap will start all server instances order by this
value;
서버 인스턴스 옵션 파라미터
sendTimeOut: sending data timeout;
sendingQueueSize: the sending queue's maximum size;
maxConnectionNumber: maximum connection number the server instance allow to connect at the
same time;
receiveBufferSize: receiving buffer size; 세션당
sendBufferSize: sending buffer size; 세션당
syncSend: sending data in sync mode, default value: false;
logCommand: whether log command execution record;
logBasicSessionActivity: whether log the session's basic activities like connected and closed;
clearIdleSession: true or false, whether clear idle sessions, default value is false;
clearIdleSessionInterval: the clearing timeout idle session interval, default value is 120, in seconds;
idleSessionTimeOut: The session timeout period. Default value is 300, in seconds;
security: Empty, Tls, Ssl3. The security option of the socket server, default value is empty;
서버 인스턴스 옵션 파라미터
maxRequestLength: The maximum allowed request length, default value is 1024;
textEncoding: The default text encoding in the server instance, default value is ASCII;
defaultCulture: default thread culture for this appserver instance, only available in .Net 4.5 and cannot
be set if the isolation model is 'None';
disableSessionSnapshot: Indicate whether disable session snapshot, default value is false. 세션 수 기
록
sessionSnapshotInterval: The interval of taking session snapshot, default value is 5, in seconds;
keepAliveTime: The interval of keeping alive, default value is 600, in seconds;
keepAliveInterval: The interval of retry after keep alive fail, default value is 60, in seconds;
Commnad-Line Protocol
"rn" 로 끝나는 라인 단위 문자열을 패킷 프로토콜로 사용할 수 있다.
문자열의 인코딩은 기본은 Ascii. UTF-8 등의 다른 인코딩으로 바꿀 수 있다.
public class StringRequestInfo
{
public string Key { get; }
public string Body { get; }
public string[] Parameters { get; }
//Other properties and methods
}
"LOGIN kerry 123456" + NewLine
Key: "LOGIN"
Body: "kerry 123456";
Parameters: ["kerry", "123456"]
public class LOGIN : CommandBase<AppSession, StringRequestInfo>
{
public override void ExecuteCommand( AppSession session, StringRequestInfo requestInfo)
{
//Implement your business logic
}
}
독자적으로 변경하기 "LOGIN:kerry,12345" + NewLine
public class YourServer : AppServer<YourSession>
{
public YourServer()
: base(new CommandLineReceiveFilterFactory(Encoding.Default,
new BasicRequestInfoParser(":", ",")))
{
}
}
public class YourServer : AppServer<YourSession>
{
public YourServer()
: base(new CommandLineReceiveFilterFactory(Encoding.Default,
new YourRequestInfoParser()))
{
}
}
AppSession 조작
AppServer에서 세션 찾기
- GetSessionByID 멤버를 사용한다.
var session = appServer.GetSessionByID(sessionID);
if(session != null)
session.Send(data, 0, data.Length);
sessionID는 AppSession 객체를 생성할 때 GUID를 string으로 할당한다.
UDP의 경우 UdpRequestInfo를 사용하면 GUID로 만들고, 아니면 리모
트의 IP와 Port로 만든다.
데이터 보내기
session.Send(data, 0, data.Length);
or
session.Send("Welcome to use SuperSocket!");
연결된 모든 세션에 메시지 보내기
foreach(var session in appServer.GetAllSessions())
{
session.Send(data, 0, data.Length);
}
자작용 Key로 세션들 찾기
- 아래의 CompanyId 처럼 새로운 Key를 사용하여 검색이 가능하다.
var sessions = appServer.GetSessions(s => s.CompanyId == companyId);
foreach(var s in sessions)
{
s.Send(data, 0, data.Length);
}
Connection Filter
• IConnectionFilter라는 인터페이스를 통해서 접속한 클라이언트를 접속 허용할
건지 차단할건지 정의할 수 있다. ip 범위대를 지정하여 특정 ip 범위에서만 접속
을 허용할 수 있다.
public class IPConnectionFilter : IConnectionFilter
{
private Tuple<long, long>[] m_IpRanges;
public bool Initialize(string name, IAppServer appServer)
{
Name = name;
var ipRange = appServer.Config.Options.GetValue("ipRange");
string[] ipRangeArray;
if (string.IsNullOrEmpty(ipRange)
|| (ipRangeArray = ipRange.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)).Length <= 0)
{
throw new ArgumentException("The ipRange doesn't exist in configuration!");
}
m_IpRanges = new Tuple<long, long>[ipRangeArray.Length];
for (int i = 0; i < ipRangeArray.Length; i++)
{
var range = ipRangeArray[i];
m_IpRanges[i] = GenerateIpRange(range);
}
return true;
}
private Tuple<long, long> GenerateIpRange(string range)
{
var ipArray = range.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
if(ipArray.Length != 2)
throw new ArgumentException("Invalid ipRange exist in configuration!");
return new Tuple<long, long>(ConvertIpToLong(ipArray[0]), ConvertIpToLong(ipArray[1]));
}
private long ConvertIpToLong(string ip)
{
var points = ip.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
if(points.Length != 4)
throw new ArgumentException("Invalid ipRange exist in configuration!");
long value = 0;
long unit = 1;
for (int i = points.Length - 1; i >= 0; i--)
{
value += unit * points[i].ToInt32();
unit *= 256;
}
return value;
}
public string Name { get; private set; }
public bool AllowConnect(IPEndPoint remoteAddress)
{
var ip = remoteAddress.Address.ToString();
var ipValue = ConvertIpToLong(ip);
for (var i = 0; i < m_IpRanges.Length; i++)
{
var range = m_IpRanges[i];
if (ipValue > range.Item2)
return false;
if (ipValue < range.Item1)
return false;
}
return true;
}
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="superSocket" type="SuperSocket.SocketEngine.Configuration.SocketServiceConfig,
SuperSocket.SocketEngine"/>
</configSections>
<appSettings>
<add key="ServiceName" value="EchoService"/>
</appSettings>
<superSocket>
<servers>
<server name="EchoServer"
serverTypeName="EchoService"
ip="Any" port="2012"
connectionFilter="IpRangeFilter"
ipRange="127.0.1.0-127.0.1.255">
</server>
</servers>
<serverTypes>
<add name="EchoService"
type="SuperSocket.QuickStart.EchoService.EchoServer, SuperSocket.QuickStart.EchoService" />
</serverTypes>
<connectionFilters>
<add name="IpRangeFilter"
type="SuperSocket.QuickStart.ConnectionFilter.IPConnectionFilter, SuperSocket.QuickStart.ConnectionFilter" />
</connectionFilters>
</superSocket>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup>
</configuration>
다중 Listeners
• 하나의 서버인스턴스에서 복수의 listen을 할 수 있다.
<superSocket>
<servers>
<server name="EchoServer" serverTypeName="EchoService">
<listeners>
<add ip="127.0.0.2" port="2012" />
<add ip="IPv6Any" port="2012" />
</listeners>
</server>
</servers>
<serverTypes>
<add name="EchoService"
type="SuperSocket.QuickStart.EchoService.EchoServer, SuperSocket.QuickStart.EchoService" />
</serverTypes>
</superSocket>
<superSocket>
<servers>
<server name="EchoServer" serverTypeName="EchoService">
<certificate filePath="localhost.pfx" password="supersocket"></certificate>
<listeners>
<add ip="Any" port="80" />
<add ip="Any" port="443" security="tls" />
</listeners>
</server>
</servers>
<serverTypes>
<add name="EchoService"
type="SuperSocket.QuickStart.EchoService.EchoServer, SuperSocket.QuickStart.EchoService" />
</serverTypes>
</superSocket>
동적 언어 지원
• 닷넷에서 지원하는 동적언어들은 SuperSocket을 사용할 수 있다.
• 대표적인 닷넷용 동적언어는 IronPython, Ironruby, F# 등이 있다.
The Built-in Common Format Protocol
Implementation Templates
● http://docs.supersocket.net/v1-6/en-US/The-Built-in-Common-Format-Protocol-
Implementation-Templates
● TerminatorReceiveFilter - Terminator Protocol
: 특정 지시어를 지정하여 패킷을 구분한다.
● CountSpliterReceiveFilter - Fixed Number Split Parts with Separator Protocol
: 특정 지시어로 구분된 단위의 크기를 숫자로 지정
● FixedSizeReceiveFilter - Fixed Size Request Protocol
: 고정된 바이너리 크기로 패킷을 구분한다.
● BeginEndMarkReceiveFilter - The Protocol with Begin and End Mark
: 시작과 끝을 구분하는 지시어를 사용하여 패킷을 구분한다.
● FixedHeaderReceiveFilter - Fixed Header with Body Length Protocol
: 헤더와 보디로 나누어서 이것들의 크기에 의해서 패킷을 구분한다.
Custome 프로토콜 정의(binary 기반)
• SuperSocket 예제를 보면 대부분 string 기반의 프로토콜을 사용하고 있으나
binary 기반의 프로토콜을 정의해서 사용할 수 있다.
1. BinaryRequestInfo 클래스와 FixedHeaderReceuveFilter 클래스를 재 정의 한다.
// 헤더는 4 바이트 정수값으로 key, 그 다음 body byte[]의 크기를 가리키는 4 바이트 정수값
public class EFBinaryRequestInfo : BinaryRequestInfo
{
public int nKey
{
get;
private set;
}
public EFBinaryRequestInfo(int nKey, byte[] body)
: base(null, body)
{
this.nKey = nKey;
}
}
참조: http://www.gamecodi.com/board/zboard.php?id=GAMECODI_Talkdev&no=1981
class ReceiveFilter : FixedHeaderReceiveFilter<EFBinaryRequestInfo>
{
public ReceiveFilter()
: base(8)
{
}
protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
{
if (!BitConverter.IsLittleEndian)
Array.Reverse(header, offset + 4, 4);
var nBodySize = BitConverter.ToInt32(header, offset+4);
return nBodySize;
}
protected override EFBinaryRequestInfo ResolveRequestInfo(ArraySegment<byte> header,
byte[] bodyBuffer, int offset, int length)
{
if (!BitConverter.IsLittleEndian)
Array.Reverse(header.Array, 0, 4);
return new EFBinaryRequestInfo(BitConverter.ToInt32(header.Array, 0), bodyBuffer.CloneRange(offset,
length));
}
}
2. 패킷 핸들러 정의
public class PacketData
{
public NetworkSession session;
public EFBinaryRequestInfo reqInfo;
}
public enum PACKETID : int
{
REQ_DUMMY_CHAT = 1,
REQ_LOGIN = 11,
}
public class CommonHandler
{
public void RequestLogin(NetworkSession session, EFBinaryRequestInfo requestInfo)
{
}
public void RequestDummyChat(NetworkSession session, EFBinaryRequestInfo requestInfo)
{
string jsonstring = System.Text.Encoding.GetEncoding("utf-8").GetString(requestInfo.Body);
var deserializedProduct = JsonConvert.DeserializeObject<PK_CHAT>(jsonstring);
session.Send(deserializedProduct.sender + ":" + deserializedProduct.msg);
}
}
public class PK_LOGON
{
public string ID;
public string PW;
}
public class PK_CHAT
{
public string sender;
public string msg;
}
3. 데이터 받기 이벤트 등록 및 프로토콜 해석하기
.....
var HandlerMap = new Dictionary<int, Action<NetworkSession, EFBinaryRequestInfo>>();
CommonHandler CommonHan = new CommonHandler();
.....
public BoardServerNet()
: base(new DefaultReceiveFilterFactory<ReceiveFilter, EFBinaryRequestInfo>())
{
NewSessionConnected += new SessionHandler<NetworkSession>(OnConnected);
SessionClosed += new SessionHandler<NetworkSession, CloseReason>(OnClosed);
NewRequestReceived += new RequestHandler<NetworkSession, EFBinaryRequestInfo>(RequestReceived);
}
public void RegistHandler()
{
HandlerMap.Add( (int)PACKETID.REQ_LOGIN, CommonHan.RequestLogin );
HandlerMap.Add((int)PACKETID.REQ_DUMMY_CHAT, CommonHan.RequestDummyChat);
}
public void StartPacketThread()
{
IsRunningPacketThread = true;
PakcetThread = new System.Threading.Thread(this.ProcessPacket);
PakcetThread.Start();
}
public void ProcessPacket()
{
while (IsRunningPacketThread)
{
PacketData packet;
if (PacketQueue.TryDequeue(out packet))
{
var PacketID = packet.reqInfo.nKey;
if (HandlerMap.ContainsKey(PacketID))
{
HandlerMap[PacketID](packet.session, packet.reqInfo);
}
}
System.Threading.Thread.Sleep(1);
}
}
로그 시스템
• log4net 라이브러리를 사용한다.
: 설정 파일에 따로 설정하지 않으면 ‘log4NetLogFactory’ 생성된다.
• 로그 관련 설정 파일은 log4net.config 혹은 log4nte.unix.config 이다. 이 파일
은 'Config' 라는 폴더 안에 있어야 한다.
로그 설정 파일을 솔루션에 등록하고, 출력 디렉토리로 복사하도록 하면
빌드 할 때마다 아래처럼 복사해 준다.
● 로그 시스템 사용을 위해 SuperSocket에 있는 log4net.dll 이 필요하다.
● log4net의 객체를 가져오는 방법
log4net.ILog log = log4net.LogManager.GetLogger(
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public class RemoteProcessSession : AppSession<RemoteProcessSession>
{
protected override void HandleUnknownRequest(StringRequestInfo requestInfo)
{
Logger.Error("Unknow request");
}
}
<appender name="myBusinessAppender">
<!--Your appender details-->
</appender>
<logger name="MyBusiness" additivity="false">
<level value="ALL" />
<appender-ref ref="myBusinessAppender" />
</logger>
var myLogger = server.LogFactory.GetLog("MyBusiness");
● 직접 만든 로그 시스템을 사용하고 싶으면 ILogFactory와 ILog 인터페이스를
구현한다.
Azure, Mono, 그 외
• Work Role을 사용하면 손쉽게 Azure에서 사용할 수 있다.
• Mono 기반을 사용하여 Unix/Linux에서 사용할 수 있다. Mono 2.10 이상 필요.
• 1.6 버전에서는 설정에 의해서 클라이언트와 주고 받는 텍스트 메시지의 포맷
을 UTF-8 이외의 것으로 설정할 수 있다.
• 닷넷플랫폼 4.5 이상이라면 각 서버 인스턴스 별로 defaultCulture 설정 가능.
• Process level isolation 에 의해 하나의 애플리케이션에서 복수의 인스턴스를
생성하는 경우 각 인스턴스 별로 프로세스를 만들 수 있다.
Ubuntu + mono에서 사용
• Linux에서는 mono를 사용하면 SuperSocket으로 만든 프로그램을 실행 가
능(다만 mono가 지원하는 닷넷 라이브러리를 사용해야 한다)
• 실행 방법
Windows으로 빌드한 파일을 그대로 가져와서 실행한다.
"mono 실행파일 이름.exe"
실행 권한만 있다면 "실행파일 이름.exe" 만으로도 실행 가능
• 클라이언트와 서버간에 CommandText를 사용할 때 에러가 발생할 수 있음.
이유는 Windows와 Linux 간의 개행 코드가 다르기 때문.
Windows는 CRLF, Linux는 LF
SuperSocket을 사용한 SuperWebSocket의 경우
session.SocketSession.SendResponse(responseBuilder.ToString());
->
session.SocketSession.SendResponse(responseBuilder.ToString().Replace(En
vironment.NewLine, "rn"));
SuperSocket를 Mono에서 사용하기 http://blog.livedoor.jp/tukuruyo/tag/SuperWebSocket
SuperSocket ServerManager
• 서버 모니터링 컴포넌트를 지원한다.
• 현재 클라이언트는 실버라이트, WPF용 클라이언트를 지원한다.
SuperSocket을 더 잘 이해하려면
혹은 C# 고성능 네트워크 프로그래밍을 하려면
• C# SocketAsyncEventArgs High Performance Socket Code
http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod
• (e-book)유니티 개발자를 위한 C#으로 온라인 게임 서버 만들기
http://www.hanbit.co.kr/realtime/books/book_view.html?p_code=E6015792502
강연 문서와 예제 코드는 아래에...
https://github.com/jacking75/kgc2016_SuperSocket

More Related Content

What's hot (20)

PDF
MMOG Server-Side 충돌 및 이동처리 설계와 구현
YEONG-CHEON YOU
 
PDF
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
Heungsub Lee
 
PPTX
[Unite17] 유니티에서차세대프로그래밍을 UniRx 소개 및 활용
MinGeun Park
 
PDF
쿠키런 1년, 서버개발 분투기
Brian Hong
 
PDF
NoSQL 위에서 MMORPG 개발하기
Hoyoung Choi
 
PDF
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
Amazon Web Services Korea
 
PDF
[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기
Chanwoong Kim
 
PDF
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
Heungsub Lee
 
PDF
게임 애셋 스트리밍 패치
Seungjae Lee
 
PPT
GCGC- CGCII 서버 엔진에 적용된 기술 (2) - Perfornance
상현 조
 
PDF
NDC2017 언리얼엔진4 디버깅 101 - 게임 기획자, 프로그래머가 버그와 만났을 때 사용할 수 있는 지침들
영욱 오
 
PDF
Iocp advanced
Nam Hyeonuk
 
PPTX
KGC 2016: HTTPS 로 모바일 게임 서버 구축한다는 것 - Korea Games Conference
Xionglong Jin
 
PPTX
게임 분산 서버 구조
Hyunjik Bae
 
PPTX
[160402_데브루키_박민근] UniRx 소개
MinGeun Park
 
PDF
NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기
Jaeseung Ha
 
PDF
조정훈, 게임 프로그래머를 위한 클래스 설계, NDC2012
devCAT Studio, NEXON
 
PDF
How to build massive service for advance
DaeMyung Kang
 
PDF
실시간 게임 서버 최적화 전략
YEONG-CHEON YOU
 
PPTX
Next-generation MMORPG service architecture
Jongwon Kim
 
MMOG Server-Side 충돌 및 이동처리 설계와 구현
YEONG-CHEON YOU
 
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
Heungsub Lee
 
[Unite17] 유니티에서차세대프로그래밍을 UniRx 소개 및 활용
MinGeun Park
 
쿠키런 1년, 서버개발 분투기
Brian Hong
 
NoSQL 위에서 MMORPG 개발하기
Hoyoung Choi
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
Amazon Web Services Korea
 
[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기
Chanwoong Kim
 
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
Heungsub Lee
 
게임 애셋 스트리밍 패치
Seungjae Lee
 
GCGC- CGCII 서버 엔진에 적용된 기술 (2) - Perfornance
상현 조
 
NDC2017 언리얼엔진4 디버깅 101 - 게임 기획자, 프로그래머가 버그와 만났을 때 사용할 수 있는 지침들
영욱 오
 
Iocp advanced
Nam Hyeonuk
 
KGC 2016: HTTPS 로 모바일 게임 서버 구축한다는 것 - Korea Games Conference
Xionglong Jin
 
게임 분산 서버 구조
Hyunjik Bae
 
[160402_데브루키_박민근] UniRx 소개
MinGeun Park
 
NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기
Jaeseung Ha
 
조정훈, 게임 프로그래머를 위한 클래스 설계, NDC2012
devCAT Studio, NEXON
 
How to build massive service for advance
DaeMyung Kang
 
실시간 게임 서버 최적화 전략
YEONG-CHEON YOU
 
Next-generation MMORPG service architecture
Jongwon Kim
 

Similar to KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기 (20)

PPTX
Easy gameserver
진상 문
 
PDF
(C#,네트워크강좌)간단한 TCP 클라이언트/서버 구현, 멀티쓰레드 기반 에코우 클라이언트/서버_C추천#/WPF/자마린실무교육학원
탑크리에듀(구로디지털단지역3번출구 2분거리)
 
PPTX
11_웹서비스활용
noerror
 
PDF
Python socket programming
Tae Young Lee
 
PPT
Rhea_MMO_SNG_Convergence_Server_Architecture
Rhea Strike
 
PDF
Python으로 채팅 구현하기
Tae Young Lee
 
PPTX
Concurrent servers
TonyYoon12
 
PDF
NODE.JS 글로벌 기업 적용 사례 그리고, real-time 어플리케이션 개발하기
John Kim
 
PDF
Tcp ip & io model
Nam Hyeonuk
 
PDF
[오픈소스컨설팅]파일럿진행예제 on AWS
Ji-Woong Choi
 
PPTX
잭블랙이블랙잭을한다 - 최종발표
Seokmin No
 
PDF
[NodeJS] - NET 모듈 소개
문학청년
 
PPTX
Rhea mmo node_for_share
Rhea Strike
 
PPTX
Tcp summary
Jooho Lee
 
PDF
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
devCAT Studio, NEXON
 
PDF
세션3 node.js의 의미와 자바의 대안
Lee Ji Eun
 
PPTX
파이썬+네트워크 20160210
Yong Joon Moon
 
PPTX
Startup JavaScript 9 - Socket.IO 실시간 통신
Circulus
 
PPTX
스마트폰 온라인 게임에서 고려해야 할 것들
Hyunjik Bae
 
PPTX
Network researching
hyeok gyu Kwon
 
Easy gameserver
진상 문
 
(C#,네트워크강좌)간단한 TCP 클라이언트/서버 구현, 멀티쓰레드 기반 에코우 클라이언트/서버_C추천#/WPF/자마린실무교육학원
탑크리에듀(구로디지털단지역3번출구 2분거리)
 
11_웹서비스활용
noerror
 
Python socket programming
Tae Young Lee
 
Rhea_MMO_SNG_Convergence_Server_Architecture
Rhea Strike
 
Python으로 채팅 구현하기
Tae Young Lee
 
Concurrent servers
TonyYoon12
 
NODE.JS 글로벌 기업 적용 사례 그리고, real-time 어플리케이션 개발하기
John Kim
 
Tcp ip & io model
Nam Hyeonuk
 
[오픈소스컨설팅]파일럿진행예제 on AWS
Ji-Woong Choi
 
잭블랙이블랙잭을한다 - 최종발표
Seokmin No
 
[NodeJS] - NET 모듈 소개
문학청년
 
Rhea mmo node_for_share
Rhea Strike
 
Tcp summary
Jooho Lee
 
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
devCAT Studio, NEXON
 
세션3 node.js의 의미와 자바의 대안
Lee Ji Eun
 
파이썬+네트워크 20160210
Yong Joon Moon
 
Startup JavaScript 9 - Socket.IO 실시간 통신
Circulus
 
스마트폰 온라인 게임에서 고려해야 할 것들
Hyunjik Bae
 
Network researching
hyeok gyu Kwon
 
Ad

More from 흥배 최 (20)

PDF
Twitter의 snowflake 소개 및 활용
흥배 최
 
PDF
Go web framework 비교[번역 정리]
흥배 최
 
PDF
Bash on Ubuntu on Windows
흥배 최
 
PDF
잘 알려지지 않은 숨은 진주, Winsock API - WSAPoll, Fast Loopback
흥배 최
 
PDF
Wtl 개요와 설치
흥배 최
 
PDF
KGC2015_C# 스크립트를 사용한 게임서버 모니터링 시스템개발
흥배 최
 
PDF
Modern C++ 프로그래머를 위한 CPP11/14 핵심
흥배 최
 
PDF
NLog 소개
흥배 최
 
PDF
닷넷 Apache avro
흥배 최
 
PDF
Mongodb2.2와 2.4의 신 기능 소개
흥배 최
 
PPTX
Mongodb 관리
흥배 최
 
PPTX
Mongodb 개발 포인트
흥배 최
 
PDF
NET 최선단 기술에 의한 고성능 웹 애플리케이션
흥배 최
 
PDF
ASP.NET과 C#으로 개발하는 대규모 소셜 게임
흥배 최
 
PDF
닷넷프레임워크에서 Redis 사용하기
흥배 최
 
PDF
Twitter의 대규모 시스템 운용 기술 어느 고래의 배속에서
흥배 최
 
PDF
Twitter에 있어서 대규모 시스템 구성, 3개의 원칙과 시스템
흥배 최
 
PDF
MongoDB 모바일 게임 개발에 사용
흥배 최
 
PDF
Tdc2013 선배들에게 배우는 server scalability
흥배 최
 
PDF
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
흥배 최
 
Twitter의 snowflake 소개 및 활용
흥배 최
 
Go web framework 비교[번역 정리]
흥배 최
 
Bash on Ubuntu on Windows
흥배 최
 
잘 알려지지 않은 숨은 진주, Winsock API - WSAPoll, Fast Loopback
흥배 최
 
Wtl 개요와 설치
흥배 최
 
KGC2015_C# 스크립트를 사용한 게임서버 모니터링 시스템개발
흥배 최
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
흥배 최
 
NLog 소개
흥배 최
 
닷넷 Apache avro
흥배 최
 
Mongodb2.2와 2.4의 신 기능 소개
흥배 최
 
Mongodb 관리
흥배 최
 
Mongodb 개발 포인트
흥배 최
 
NET 최선단 기술에 의한 고성능 웹 애플리케이션
흥배 최
 
ASP.NET과 C#으로 개발하는 대규모 소셜 게임
흥배 최
 
닷넷프레임워크에서 Redis 사용하기
흥배 최
 
Twitter의 대규모 시스템 운용 기술 어느 고래의 배속에서
흥배 최
 
Twitter에 있어서 대규모 시스템 구성, 3개의 원칙과 시스템
흥배 최
 
MongoDB 모바일 게임 개발에 사용
흥배 최
 
Tdc2013 선배들에게 배우는 server scalability
흥배 최
 
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
흥배 최
 
Ad

KGC 2016 오픈소스 네트워크 엔진 Super socket 사용하기

  • 1. 오픈소스 네트워크 엔진 SuperSocket 사용하기 NHN Next 겸임 교수(게임) 최흥배 https://github.com/jacking75/choiHeungbae
  • 2. Windows 플랫폼에서 고성능 네트워크 프로그램을 만들 때 가장 자주 사용하는 기술은 C++ & IOCP ... PC 온라인 게임 시대 때...
  • 4. C#의 비동기 Socket은 Windows에서는 내부적으로 IOCP로 구현되어 있음. 즉 비동기 네트워크 측면만 보았을 때는 C++로 IOCP를 사용하는 것과 비슷
  • 6. ● .NET 플랫폼용 오픈 소스 네트워크 라이브러리 3.5 ~ 4.5까지 지원. ● Windows, Linux, Mono, Azure를 지원한다. ● 비동기 I/O를 지원. TCP, UDP ● SSL/TLS 지원, 확장 기능 등 다양한 기능 지원 ● 공식 사이트 http://www.supersocket.net 문서는 http://docs.supersocket.net/
  • 7. ● 현재(2016.09.02) 기준 ● nugget 최신 버전은 1.6.6.1 https://www.nugget.org/packages/SuperSocket/ ● GitHub 버전은 1. 6.7 https://github.com/kerryjiang/SuperSocket ● 2.0 버전을 준비 중 Visual Studio Code 지원. 어쩌면 .NET Core도 지원?
  • 8. 오픈 소스 답지 않게(?) 문서화와 예제 코드가 잘 만들어져 있어서 분석이 쉽다
  • 13. 설치하기 – 소스 코드에서 SuperSocket 소스에 있는 log4net.dll을 포함한다. SuperSocket.Common, SuperSocket.SocketBase, SuperSocket.SocketEngine는 왼쪽 그림처럼 dll을 참조에 포함해도 되 고 아니면 프로젝트를 바로 포함해도 된다(아래). 필요한 클래스 라이브러리
  • 15. CountSpliterReceiveFilter, FixedSizeReceiveFilter, BeginEndMarkReceiveFilter, FixedHeaderReceiveFilter 를 사용하기 위해서는 'SuperSocket.Facility'가 필요한데 기본으로 등록되지 않으 므로 NuGet으로 받은 package 디렉토리에서 선택해서 추가한다.
  • 16. 서버 실행 – 설정 void InitConfig() { m_Config = new ServerConfig { Port = 23478, Ip = "Any", MaxConnectionNumber = 100, Mode = SocketMode.Tcp, Name = "BoardServerNet" }; }
  • 17. void CreateServer() { m_Server = new BoardServerNet(); bool bResult = m_Server.Setup(new RootConfig(), m_Config, logFactory: new Log4NetLogFactory() ); if (bResult == false) { } ...... } class BoardServerNet : AppServer<NetworkSession, EFBinaryRequestInfo> { }
  • 18. // 포트 번호 2012로 설정 if (! m_Server.Setup(2012)) { return; } .... // 네트워크 시작 if (! m_Server.Start()) { return; } .... // 네트워크 중지 m_Server.Stop(); 서버 실행 - 네트워크 설정 및 시작/중단
  • 19. 서버 실행 - 핸들러 등록 새로운 클라이언트가 연결되면 호출될 핸들러 등록 appServer.NewSessionConnected += new SessionHandler<AppSession>(appServer_NewSessionConnected); .... static void appServer_NewSessionConnected(AppSession session) { session.Send("Welcome to SuperSocket Telnet Server"); }
  • 20. 클라이언트가 보낸 데이터를 받으면 호출될 핸들러 등록 appServer.NewRequestReceived += new RequestHandler<AppSession, StringRequestInfo>(appServer_NewRequestReceived); .... static void appServer_NewRequestReceived(AppSession session, StringRequestInfo requestInfo) { switch (requestInfo.Key.ToUpper()) { case("ECHO"): session.Send(requestInfo.Body); break; ........ }
  • 21. AppServer와 AppSession 구현 • AppSession 서버에 연결된 Socket의 로직 클래스. 이 클래스를 통해 클라이언트의 연결,끊어짐, 데이터 주고 받기를 한다. • AppServer 네트워크 서버 클래스. 모든 AppSession 객체를 관리한다. SuperSocket의 몸통이다.
  • 22. public class TelnetSession : AppSession<TelnetSession> { protected override void OnSessionStarted() { this.Send("Welcome to SuperSocket Telnet Server"); } protected override void HandleUnknownRequest(StringRequestInfo requestInfo) { this.Send("Unknow request"); } protected override void HandleException(Exception e) { this.Send("Application error: {0}", e.Message); } protected override void OnSessionClosed(CloseReason reason) { //add you logics which will be executed after the session is closed base.OnSessionClosed(reason); } }
  • 23. public class TelnetServer : AppServer<TelnetSession> { protected override bool Setup(IRootConfig rootConfig, IServerConfig config) { return base.Setup(rootConfig, config); } protected override void OnStartup() { base.OnStartup(); } protected override void OnStopped() { base.OnStopped(); } }
  • 24. 서버 네트워크 옵션 설정하기 name: the name of appServer instance serverType: the full name of the AppServer your want to run ip: listen ip port: listen port 위 설정을 아래와 같이 config 파일에 정의할 수 있다. <superSocket> <servers> <server name="TelnetServer" serverType="SuperSocket.QuickStart.TelnetServer_StartByConfig.TelnetServer, SuperSocket.QuickStart.TelnetServer_StartByConfig" ip="Any" port="2020"> </server> </servers> </superSocket>
  • 25. static void Main(string[] args) { var bootstrap = BootstrapFactory.CreateBootstrap( ); if (!bootstrap.Initialize()) { return; } var result = bootstrap.Start(); if (result == StartResult.Failed) { return; } //Stop the appServer bootstrap.Stop(); } 위의 Config 파일 사용 예
  • 26. 서버 네트워크 옵션 설정하기 Config 파일 - 멀티 인스턴스 사용 예 <superSocket> <servers> <server name="TelnetServerA" serverTypeName="TelnetServer" ip="Any" port="2020"> </server> <server name="TelnetServerB" serverTypeName="TelnetServer" ip="Any" port="2021"> </server> </servers> <serverTypes> <add name="TelnetServer" type="SuperSocket.QuickStart.TelnetServer_StartByConfig.TelnetServer, SuperSocket.QuickStart.TelnetServer_StartByConfig"/> </serverTypes> </superSocket> App.Config 파일 이외의 설정 파일을 사용하기 위해서는 아래처럼 파일 이름을 지정한다. m_Bootstrap = BootstrapFactory.CreateBootstrapFromConfigFile("SuperSocket.config");
  • 27. SuperSocket 라이브러리가 호스트 프로그램이 아닌 다른 프로젝트에서 사용하는 경우의 설정 SuperSocket의 'AppServer' 클래스를 상속한 클래스
  • 29. 네트워크 옵션 파라미터 루트 설정(모든 서버 네트워크에 적용)에 사용하는 파리미터 IRootConfig maxWorkingThreads: maximum working threads count of .NET thread pool; minWorkingThreads: minimum working threads count of .NET thread pool; maxCompletionPortThreads: maximum completion threads count of .NET thread pool; minCompletionPortThreads: minimum completion threads count of .NET thread pool; disablePerformanceDataCollector: whether disable performance data collector; performanceDataCollectInterval: performance data collecting interval (in seconds, default value: 60); isolation: SuperSocket instances isolation level None - no isolation AppDomain - server instances will be isolated by AppDomains Process - server instances will be isolated by processes logFactory: the name of default logFactory, all log factories are defined in the child node "logFactories" which will be introduced in following documentation; defaultCulture: default thread culture for the global application, only available in .Net 4.5;
  • 30. 서버 인스턴스 옵션 파라미터 IServerconfig name: the name of the server instance; serverType: the full name the AppServer's type which you want to run; serverTypeName: the name of the selected server types, all server types should be defined in serverTypes node which will be introduced in following documentation; ip: the ip of the server instance listens. You can set an exact ip, you also can set the below values Any - all IPv4 address IPv6Any - all IPv6 address port: the port of the server instance listens; listenBacklog: the listen back log size; mode: the socket server's running mode, Tcp (default) or Udp; disabled: whether the server instance is disabled; startupOrder: the server instance start order, the bootstrap will start all server instances order by this value;
  • 31. 서버 인스턴스 옵션 파라미터 sendTimeOut: sending data timeout; sendingQueueSize: the sending queue's maximum size; maxConnectionNumber: maximum connection number the server instance allow to connect at the same time; receiveBufferSize: receiving buffer size; 세션당 sendBufferSize: sending buffer size; 세션당 syncSend: sending data in sync mode, default value: false; logCommand: whether log command execution record; logBasicSessionActivity: whether log the session's basic activities like connected and closed; clearIdleSession: true or false, whether clear idle sessions, default value is false; clearIdleSessionInterval: the clearing timeout idle session interval, default value is 120, in seconds; idleSessionTimeOut: The session timeout period. Default value is 300, in seconds; security: Empty, Tls, Ssl3. The security option of the socket server, default value is empty;
  • 32. 서버 인스턴스 옵션 파라미터 maxRequestLength: The maximum allowed request length, default value is 1024; textEncoding: The default text encoding in the server instance, default value is ASCII; defaultCulture: default thread culture for this appserver instance, only available in .Net 4.5 and cannot be set if the isolation model is 'None'; disableSessionSnapshot: Indicate whether disable session snapshot, default value is false. 세션 수 기 록 sessionSnapshotInterval: The interval of taking session snapshot, default value is 5, in seconds; keepAliveTime: The interval of keeping alive, default value is 600, in seconds; keepAliveInterval: The interval of retry after keep alive fail, default value is 60, in seconds;
  • 33. Commnad-Line Protocol "rn" 로 끝나는 라인 단위 문자열을 패킷 프로토콜로 사용할 수 있다. 문자열의 인코딩은 기본은 Ascii. UTF-8 등의 다른 인코딩으로 바꿀 수 있다. public class StringRequestInfo { public string Key { get; } public string Body { get; } public string[] Parameters { get; } //Other properties and methods } "LOGIN kerry 123456" + NewLine Key: "LOGIN" Body: "kerry 123456"; Parameters: ["kerry", "123456"] public class LOGIN : CommandBase<AppSession, StringRequestInfo> { public override void ExecuteCommand( AppSession session, StringRequestInfo requestInfo) { //Implement your business logic } }
  • 34. 독자적으로 변경하기 "LOGIN:kerry,12345" + NewLine public class YourServer : AppServer<YourSession> { public YourServer() : base(new CommandLineReceiveFilterFactory(Encoding.Default, new BasicRequestInfoParser(":", ","))) { } } public class YourServer : AppServer<YourSession> { public YourServer() : base(new CommandLineReceiveFilterFactory(Encoding.Default, new YourRequestInfoParser())) { } }
  • 35. AppSession 조작 AppServer에서 세션 찾기 - GetSessionByID 멤버를 사용한다. var session = appServer.GetSessionByID(sessionID); if(session != null) session.Send(data, 0, data.Length); sessionID는 AppSession 객체를 생성할 때 GUID를 string으로 할당한다. UDP의 경우 UdpRequestInfo를 사용하면 GUID로 만들고, 아니면 리모 트의 IP와 Port로 만든다. 데이터 보내기 session.Send(data, 0, data.Length); or session.Send("Welcome to use SuperSocket!");
  • 36. 연결된 모든 세션에 메시지 보내기 foreach(var session in appServer.GetAllSessions()) { session.Send(data, 0, data.Length); } 자작용 Key로 세션들 찾기 - 아래의 CompanyId 처럼 새로운 Key를 사용하여 검색이 가능하다. var sessions = appServer.GetSessions(s => s.CompanyId == companyId); foreach(var s in sessions) { s.Send(data, 0, data.Length); }
  • 37. Connection Filter • IConnectionFilter라는 인터페이스를 통해서 접속한 클라이언트를 접속 허용할 건지 차단할건지 정의할 수 있다. ip 범위대를 지정하여 특정 ip 범위에서만 접속 을 허용할 수 있다. public class IPConnectionFilter : IConnectionFilter { private Tuple<long, long>[] m_IpRanges; public bool Initialize(string name, IAppServer appServer) { Name = name; var ipRange = appServer.Config.Options.GetValue("ipRange"); string[] ipRangeArray; if (string.IsNullOrEmpty(ipRange) || (ipRangeArray = ipRange.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)).Length <= 0) { throw new ArgumentException("The ipRange doesn't exist in configuration!"); } m_IpRanges = new Tuple<long, long>[ipRangeArray.Length]; for (int i = 0; i < ipRangeArray.Length; i++) { var range = ipRangeArray[i]; m_IpRanges[i] = GenerateIpRange(range); } return true; }
  • 38. private Tuple<long, long> GenerateIpRange(string range) { var ipArray = range.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries); if(ipArray.Length != 2) throw new ArgumentException("Invalid ipRange exist in configuration!"); return new Tuple<long, long>(ConvertIpToLong(ipArray[0]), ConvertIpToLong(ipArray[1])); } private long ConvertIpToLong(string ip) { var points = ip.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); if(points.Length != 4) throw new ArgumentException("Invalid ipRange exist in configuration!"); long value = 0; long unit = 1; for (int i = points.Length - 1; i >= 0; i--) { value += unit * points[i].ToInt32(); unit *= 256; } return value; } public string Name { get; private set; }
  • 39. public bool AllowConnect(IPEndPoint remoteAddress) { var ip = remoteAddress.Address.ToString(); var ipValue = ConvertIpToLong(ip); for (var i = 0; i < m_IpRanges.Length; i++) { var range = m_IpRanges[i]; if (ipValue > range.Item2) return false; if (ipValue < range.Item1) return false; } return true; } }
  • 40. <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="superSocket" type="SuperSocket.SocketEngine.Configuration.SocketServiceConfig, SuperSocket.SocketEngine"/> </configSections> <appSettings> <add key="ServiceName" value="EchoService"/> </appSettings> <superSocket> <servers> <server name="EchoServer" serverTypeName="EchoService" ip="Any" port="2012" connectionFilter="IpRangeFilter" ipRange="127.0.1.0-127.0.1.255"> </server> </servers> <serverTypes> <add name="EchoService" type="SuperSocket.QuickStart.EchoService.EchoServer, SuperSocket.QuickStart.EchoService" /> </serverTypes> <connectionFilters> <add name="IpRangeFilter" type="SuperSocket.QuickStart.ConnectionFilter.IPConnectionFilter, SuperSocket.QuickStart.ConnectionFilter" /> </connectionFilters> </superSocket> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" /> </startup> </configuration>
  • 41. 다중 Listeners • 하나의 서버인스턴스에서 복수의 listen을 할 수 있다. <superSocket> <servers> <server name="EchoServer" serverTypeName="EchoService"> <listeners> <add ip="127.0.0.2" port="2012" /> <add ip="IPv6Any" port="2012" /> </listeners> </server> </servers> <serverTypes> <add name="EchoService" type="SuperSocket.QuickStart.EchoService.EchoServer, SuperSocket.QuickStart.EchoService" /> </serverTypes> </superSocket> <superSocket> <servers> <server name="EchoServer" serverTypeName="EchoService"> <certificate filePath="localhost.pfx" password="supersocket"></certificate> <listeners> <add ip="Any" port="80" /> <add ip="Any" port="443" security="tls" /> </listeners> </server> </servers> <serverTypes> <add name="EchoService" type="SuperSocket.QuickStart.EchoService.EchoServer, SuperSocket.QuickStart.EchoService" /> </serverTypes> </superSocket>
  • 42. 동적 언어 지원 • 닷넷에서 지원하는 동적언어들은 SuperSocket을 사용할 수 있다. • 대표적인 닷넷용 동적언어는 IronPython, Ironruby, F# 등이 있다.
  • 43. The Built-in Common Format Protocol Implementation Templates ● http://docs.supersocket.net/v1-6/en-US/The-Built-in-Common-Format-Protocol- Implementation-Templates ● TerminatorReceiveFilter - Terminator Protocol : 특정 지시어를 지정하여 패킷을 구분한다. ● CountSpliterReceiveFilter - Fixed Number Split Parts with Separator Protocol : 특정 지시어로 구분된 단위의 크기를 숫자로 지정 ● FixedSizeReceiveFilter - Fixed Size Request Protocol : 고정된 바이너리 크기로 패킷을 구분한다. ● BeginEndMarkReceiveFilter - The Protocol with Begin and End Mark : 시작과 끝을 구분하는 지시어를 사용하여 패킷을 구분한다. ● FixedHeaderReceiveFilter - Fixed Header with Body Length Protocol : 헤더와 보디로 나누어서 이것들의 크기에 의해서 패킷을 구분한다.
  • 44. Custome 프로토콜 정의(binary 기반) • SuperSocket 예제를 보면 대부분 string 기반의 프로토콜을 사용하고 있으나 binary 기반의 프로토콜을 정의해서 사용할 수 있다. 1. BinaryRequestInfo 클래스와 FixedHeaderReceuveFilter 클래스를 재 정의 한다. // 헤더는 4 바이트 정수값으로 key, 그 다음 body byte[]의 크기를 가리키는 4 바이트 정수값 public class EFBinaryRequestInfo : BinaryRequestInfo { public int nKey { get; private set; } public EFBinaryRequestInfo(int nKey, byte[] body) : base(null, body) { this.nKey = nKey; } } 참조: http://www.gamecodi.com/board/zboard.php?id=GAMECODI_Talkdev&no=1981
  • 45. class ReceiveFilter : FixedHeaderReceiveFilter<EFBinaryRequestInfo> { public ReceiveFilter() : base(8) { } protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length) { if (!BitConverter.IsLittleEndian) Array.Reverse(header, offset + 4, 4); var nBodySize = BitConverter.ToInt32(header, offset+4); return nBodySize; } protected override EFBinaryRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length) { if (!BitConverter.IsLittleEndian) Array.Reverse(header.Array, 0, 4); return new EFBinaryRequestInfo(BitConverter.ToInt32(header.Array, 0), bodyBuffer.CloneRange(offset, length)); } }
  • 46. 2. 패킷 핸들러 정의 public class PacketData { public NetworkSession session; public EFBinaryRequestInfo reqInfo; } public enum PACKETID : int { REQ_DUMMY_CHAT = 1, REQ_LOGIN = 11, } public class CommonHandler { public void RequestLogin(NetworkSession session, EFBinaryRequestInfo requestInfo) { } public void RequestDummyChat(NetworkSession session, EFBinaryRequestInfo requestInfo) { string jsonstring = System.Text.Encoding.GetEncoding("utf-8").GetString(requestInfo.Body); var deserializedProduct = JsonConvert.DeserializeObject<PK_CHAT>(jsonstring); session.Send(deserializedProduct.sender + ":" + deserializedProduct.msg); } } public class PK_LOGON { public string ID; public string PW; } public class PK_CHAT { public string sender; public string msg; }
  • 47. 3. 데이터 받기 이벤트 등록 및 프로토콜 해석하기 ..... var HandlerMap = new Dictionary<int, Action<NetworkSession, EFBinaryRequestInfo>>(); CommonHandler CommonHan = new CommonHandler(); ..... public BoardServerNet() : base(new DefaultReceiveFilterFactory<ReceiveFilter, EFBinaryRequestInfo>()) { NewSessionConnected += new SessionHandler<NetworkSession>(OnConnected); SessionClosed += new SessionHandler<NetworkSession, CloseReason>(OnClosed); NewRequestReceived += new RequestHandler<NetworkSession, EFBinaryRequestInfo>(RequestReceived); } public void RegistHandler() { HandlerMap.Add( (int)PACKETID.REQ_LOGIN, CommonHan.RequestLogin ); HandlerMap.Add((int)PACKETID.REQ_DUMMY_CHAT, CommonHan.RequestDummyChat); } public void StartPacketThread() { IsRunningPacketThread = true; PakcetThread = new System.Threading.Thread(this.ProcessPacket); PakcetThread.Start(); }
  • 48. public void ProcessPacket() { while (IsRunningPacketThread) { PacketData packet; if (PacketQueue.TryDequeue(out packet)) { var PacketID = packet.reqInfo.nKey; if (HandlerMap.ContainsKey(PacketID)) { HandlerMap[PacketID](packet.session, packet.reqInfo); } } System.Threading.Thread.Sleep(1); } }
  • 49. 로그 시스템 • log4net 라이브러리를 사용한다. : 설정 파일에 따로 설정하지 않으면 ‘log4NetLogFactory’ 생성된다. • 로그 관련 설정 파일은 log4net.config 혹은 log4nte.unix.config 이다. 이 파일 은 'Config' 라는 폴더 안에 있어야 한다. 로그 설정 파일을 솔루션에 등록하고, 출력 디렉토리로 복사하도록 하면 빌드 할 때마다 아래처럼 복사해 준다.
  • 50. ● 로그 시스템 사용을 위해 SuperSocket에 있는 log4net.dll 이 필요하다. ● log4net의 객체를 가져오는 방법 log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  • 51. public class RemoteProcessSession : AppSession<RemoteProcessSession> { protected override void HandleUnknownRequest(StringRequestInfo requestInfo) { Logger.Error("Unknow request"); } } <appender name="myBusinessAppender"> <!--Your appender details--> </appender> <logger name="MyBusiness" additivity="false"> <level value="ALL" /> <appender-ref ref="myBusinessAppender" /> </logger> var myLogger = server.LogFactory.GetLog("MyBusiness"); ● 직접 만든 로그 시스템을 사용하고 싶으면 ILogFactory와 ILog 인터페이스를 구현한다.
  • 52. Azure, Mono, 그 외 • Work Role을 사용하면 손쉽게 Azure에서 사용할 수 있다. • Mono 기반을 사용하여 Unix/Linux에서 사용할 수 있다. Mono 2.10 이상 필요. • 1.6 버전에서는 설정에 의해서 클라이언트와 주고 받는 텍스트 메시지의 포맷 을 UTF-8 이외의 것으로 설정할 수 있다. • 닷넷플랫폼 4.5 이상이라면 각 서버 인스턴스 별로 defaultCulture 설정 가능. • Process level isolation 에 의해 하나의 애플리케이션에서 복수의 인스턴스를 생성하는 경우 각 인스턴스 별로 프로세스를 만들 수 있다.
  • 53. Ubuntu + mono에서 사용 • Linux에서는 mono를 사용하면 SuperSocket으로 만든 프로그램을 실행 가 능(다만 mono가 지원하는 닷넷 라이브러리를 사용해야 한다) • 실행 방법 Windows으로 빌드한 파일을 그대로 가져와서 실행한다. "mono 실행파일 이름.exe" 실행 권한만 있다면 "실행파일 이름.exe" 만으로도 실행 가능 • 클라이언트와 서버간에 CommandText를 사용할 때 에러가 발생할 수 있음. 이유는 Windows와 Linux 간의 개행 코드가 다르기 때문. Windows는 CRLF, Linux는 LF SuperSocket을 사용한 SuperWebSocket의 경우 session.SocketSession.SendResponse(responseBuilder.ToString()); -> session.SocketSession.SendResponse(responseBuilder.ToString().Replace(En vironment.NewLine, "rn")); SuperSocket를 Mono에서 사용하기 http://blog.livedoor.jp/tukuruyo/tag/SuperWebSocket
  • 54. SuperSocket ServerManager • 서버 모니터링 컴포넌트를 지원한다. • 현재 클라이언트는 실버라이트, WPF용 클라이언트를 지원한다.
  • 55. SuperSocket을 더 잘 이해하려면 혹은 C# 고성능 네트워크 프로그래밍을 하려면 • C# SocketAsyncEventArgs High Performance Socket Code http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod • (e-book)유니티 개발자를 위한 C#으로 온라인 게임 서버 만들기 http://www.hanbit.co.kr/realtime/books/book_view.html?p_code=E6015792502
  • 56. 강연 문서와 예제 코드는 아래에... https://github.com/jacking75/kgc2016_SuperSocket