[게임 서버] SetBuffer를 RecvBuffer, SendBuffer로 따로 빼내기 #1
·
공부/게임 서버
패킷 단위로 데이터를 주고 받기 위해서 해야 할 첫 관문은 server 내에 있는 recvBuff를 따로 빼서 관리하는 것이다. 기존 프로그램에서는 SetBuffer를 통해 버퍼 크기, offset을 미리 설정해준 다음에 어떤 변화도 없이 쭉 그대로 사용해왔다. 이렇게 사용하게 되면 문제가 발생한다. TCP 특성상 클라이언트가 보낸 내용이 한 번에 다 오지 않을 수 있다. 앞서 보낸 데이터 중 일부가 버퍼에 데이터가 남아있을 경우 남은 데이터와 새로 보낼 데이터를 함께 보내는데 이때 매우 곤란한 상황이 펼쳐진다. 변화값인 offset을 0으로 설정했기 때문에 남은 데이터를 무시하고 그 위에 새로운 데이터를 덮어쓰게 되는 것이다. 문제를 방지하기 위해서 앞으로는 SetBuffer를 사용하지 않고 RecvB..
[게임 서버] Listener, 네트워크 연결을 기다려보자
·
공부/게임 서버
서버와 클라이언트의 연결을 기다리는 용도의 Listener 클래스는 시작점인 Init, 기다리는 연결이 바로 실행될 수 있음을 확인하는 RegisterAccept, 연결 완료를 담당하는 OnAcceptCompleted, 연결을 받아들이는 Accept 함수로 구성된다. Socket _listenSocket; Action _onAcceptHandler; // Accept가 완료됐을 때 어떻게 처리할 것인지 담당. public void Init(IPEndPoint endPoint, Action onAcceptHandler) { _listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // AddressFami..
[게임 서버] Listener의 반대 개념, Connector에 대해서
·
공부/게임 서버
서버를 만들 때 Listener라는 이름으로 클라이언트의 연결 요청을 기다리는 클래스를 만들게 된다. 그 반대 입장으로 서버에서 서버와 클라이언트의 연결을 직접 하는 클래스를 만들어야 하는데 내가 배운 강의에서는 그 이름을 Connector라 한다. Connector를 따로 만드는 가장 큰 이유 2가지는 1️⃣ 서버 코어 안에서 클라이언트와 연결하는 일이 필요할 때 범용적으로 사용하기 위해, 2️⃣ 나중에 게임 서버를 본격적으로 만들 때 몬스터 관리, 아이템 관리 등 여러가지 서버가 생기게 되는데 이때 그 서버를 서로 연결하고자 할 때 사용하기 위해서다. Connector는 전체적으로 Listener와 흡사한 구조를 갖고 있다. Connector를 호출할 때 사용하게 되는 Connect 함수, 연결을 예..
[C#] ArraySegment를 이용하여 배열 가져오기
·
언어/C#
배열 안에 일부를 가져오기 위해서 새로운 배열을 파는 것 말고도 ArraySegment를 사용하는 방법이 있다. static void Main(string[] args) { int[] arr = { 1, 2, 3, 4, 5 }; int start = 0; int end = 3; ArraySegment segment = new ArraySegment(arr, start, end); Console.WriteLine(String.Join(", ", segment)); // 배열을 이어붙일 수 있는 간단한 메서드. } ArraySegment의 start는 Offset을 의미한다. 그대로 받아들이면 배열의 변화량을 의미하지만 쉽게 이해하기 위해서는 숫자를 가져올 배열의 시작점을 몇 번째로 할 거냐는 의미다. en..
[C#] 이벤트(event)란?
·
언어/C#
이벤트 클래스는 클래스 안에서 특정한 일이 발생했을 때 구독자들에게 그 일을 알리는 메커니즘이다. using System; namespace ServerStudy { class Publisher { public delegate void OnInputKey(); // OnInputKey라는 이름의 이벤트 핸들러 델리게이트 선언 public event OnInputKey InputKey; // InputKey라는 이름의 이벤트 선언 public void Update() { if (Console.KeyAvailable == false) return; ConsoleKeyInfo info = Console.ReadKey(); if (info.Key == ConsoleKey.Enter) InputKey(); } }..
[게임서버] 데드락(DeadLock)이란?
·
공부/게임 서버
데드락을 한글로 풀면 교착상태이다. 두 개 이상의 프로세스가 있다. 모든 프로세스가 원하는 자원이 자신이 아닌 상대에게 있어 자원이 자신에게 오기만을 기다리느라 자원을 얻지 못하고 다음 차례로 넘어가지 못하는 상태가 계속 되는 것이다. C#으로 작성된 내용입니다. using System; namespace ServerStudy { class Program { static object lock1 = new object(); static object lock2 = new object(); static void Test1() { lock (lock1) { Thread.Sleep(1000); lock (lock2) { Console.WriteLine("Test1"); } } } static void Test2(..
[게임서버] Context Switching이란? (Thread.Sleep(1), Thread.Sleep(0), Thread.Yield())
·
공부/게임 서버
영화를 보다가 화장실에 가고 싶은데 다른 사람이 변기를 차지하고 안에서 잠금장치를 걸었다면 우리는 고민한다. 1. 사람이 나올 때까지 그 앞에서 계속 기다릴지 2. 잠시 자리로 돌아갔다가 나중에 다시 와서 다시 문을 두드릴지 3. 영화관 직원에게 부탁하여 화장실에 자리가 나면 나에게 알려달라고 할지 두 번째 상황이 바로 Context Switching이다. Context Switching이란 작업을 진행 중인 스레드가 자신의 작업이 완료되지 않은 상황에서 자신의 작업을 잠시 저장하고 다른 스레드에게 CPU 점유권을 양보하는 작업을 말한다. C#으로 작성된 내용입니다. using System; namespace ServerStudy { class Lock { volatile int _locked = 0; ..
[C#] Dictionary란
·
언어/C#
Dictionary란 Key값과 Value를 사용해 값들을 지정해놓을 수 있는 유용한 클래스를 말한다. 정말 사전처럼 미리 지정해놓은 Key값에 해당하는 Value를 손쉽게 찾아볼 수 있다. Key값은 중복되어서는 안된다. using System; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Dictionary dic = new Dictionary(); // Dictionary 이름 = new Dictionary(); dic.Add(3, "봄"); dic.Add(1, "겨울"); dic.Add(10, "가을"); dic.Add(8, "여름"); foreach(KeyValuePair n in dic) Console.Wr..
[C#] virtual과 abstract (추상 클래스)
·
언어/C#
·virtual 상속 받는 클래스에서 virtual에 있는 내용 중 필요한 것만 골라서 구현해주면 된다. ​ ·abstract (추상 클래스) 상속 받는 클래스에서 abstract에 있는 내용을 반드시 구현해주어야한다. abstract는 추상 클래스다. 추상 클래스에 있는 멤버는 구현할 수 없고 상속받는 곳에서 구현해주어야 한다. using System; namespace ConsoleApp1 { class Program { public abstract class Essential { public virtual void Breath() { Console.WriteLine("숨쉬기"); } public abstract void Fly(); public abstract void Swim(); } public..
[C#] Queue과 Stack
·
언어/C#
Queue와 Stack은 선형자료구조를 사용할 때 유용한 두 구조이다. ​ ·Queue 가장 먼저 들어온 값이 가장 먼저 빠진다. using System; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Queue q = new Queue(); q.Enqueue("첫 번째"); q.Enqueue("두 번째"); q.Enqueue("세 번째"); foreach (string s in q) Console.WriteLine($"{s}", s); Console.WriteLine(); q.Dequeue(); foreach (string s in q) Console.WriteLine($"{s}", s); } } } q.Dequeue..
[C#] 함수, 클래스, 객체, 인스턴스, 생성자
·
언어/C#
· 함수(Method) 함수란 어떤 일을 실행하는 코드를 묶어놓은 코드 블럭을 말한다. ​ · 클래스(Class) 객체를 생성하기 위해 변수와 메소드를 정의하는 틀이다. ​ · 객체(Object) 클래스에 정의된 내용이 메모리에 생성되면 객체라고 말한다. ​ · 인스턴스(Instance) 클래스로부터 만들어진 객체를 그 객체의 인스턴스라고 한다. 클래스로부터 객체를 만드는 과정을 인스턴스화라고 한다. ​ · 생성자 클래스와 같은 이름을 가진 인스턴스 초기화 메서드이다. 반환값이 없고 오버로딩이 가능하다. using System; namespace Test { class Food // 클래스 { public Food() // 생성자 { // 초기화가 필요한 내용 } public void Rice() // ..
[C#] Generic Type 사용하기
·
언어/C#
동일한 내용이지만 데이터 타입만 다른 메소드를 사용할 때, 원래대로라면 매번 데이터 타입만 다르고 내용은 같은 메소드를 만들어주어야 한다. 이때 Generic Type을 사용하면 메소드를 한 번만 호출하고도 원하는 기능을 모두 사용할 수 있다. void Swap (ref T a,ref T b) { var temp = a; a = b; b = temp; } a와 b의 자리를 바꿔주는 간단한 예시문이다. 이름 뒤에 를 붙여 모든 데이터 타입을 아우를 수 있게 됐다. class Program { void Swap (ref T a,ref T b) { var temp = a; a = b; b = temp; } void IntProcess() { int a = 1; int b = 2; Swap (ref a, ref..
[C#] temp를 사용한 자리 바꿔치기
·
언어/C#
static void Main(string[] args) { int[] arr = { 1, 2, 3, 4, 5, }; for (int i = 0; i < arr.Length; i++) Console.Write($"{arr[i]} "); } 여기서 5와 1의 위치를 바꾸고 싶다. static void Main(string[] args) { int[] arr = { 1, 2, 3, 4, 5, }; arr[0] = arr[4]; arr[4] = arr[0]; for (int i = 0; i < arr.Length; i++) Console.WriteLine(arr[i]); } 단순하게 위치를 바꾸어 봤지만 1이 사라지고 5만 두 개로 늘어난 모습을 볼 수 있다. 이럴 때는 임시값인 temp를 사용하면 된다. s..
[C#] CS0122 보호 수준 때문에 액세스할 수 없습니다
·
언어/C#
monster = new Slime();​ class Knight : Player { Knight() : base(PlayerType.Knight) { SetInfo(100, 10); } } 오류가 발생했다. 보호 수준 때문에 엑세스할 수 없다는 내용. class Knight : Player { public Knight() : base(PlayerType.Knight) { SetInfo(100, 10); } } monster = new Slime(); 해결됐다. 한정자를 설정해주지 않아서 발생한 문제였다. public으로 설정해주고 나니 말끔.
[C#] overloading과 overriding
·
언어/C#
· overloading은 같은 이름의 함수를 여러번 호출하면서 다른 방식으로 사용하는 것이다. (정의는 아님. 내 맘대로 정리한 문장) static void Function(int a) { Console.WriteLine(a); } static void Function(int a, int b) { Console.WriteLine(a + b); } static void Main(string[] args) { Function(1); // 1 Function(4, 3); // 7 } Function(int a)와 Function(int a, int b)는 같은 이름을 갖고 있지만 각자 원하는 인자가 다르다. 인자를 넣어주면 알아서 다른 결과값을 출력해준다. 이게 오버로딩이다. ​ · overriding은 부..