목록프로그래밍/C# 게임서버 (25)
有希
추천 방안 : dll을 유니티에서 쓰게되면 편하게 구현해 놓은 기능들을 제한적으로 사용할 수 있지만, 디버깅을 할 수 없다는 문제점이 있다. 그래서 처음 합칠 때에는 웬만하면 코드를 긁어서 따로 만들어주고 디버깅을 하자. 1. 유니티에서는 Span을 인식하지 못한다. 지우고 BitConverter.ToInt 등을 적절히 활용해서 대체해줘야 한다. ArraySegment를 쓰면 될듯 2. TryWriteBytes 사용하지 못한다. byte[]를 생성해서 여기에 복사해주거나 unsafe코드를 이용해서 직접 포인터를 조정해야한다. 3. Async계열의 함수를 이용해서 비동기 함수를 실행할 때 유니티에서는 코드를 실행하는 메인 스레드가 아닌 다른 스레드에서 게임과 관련된 부분을 접근해서 실행하는 것을 원칙적으로..
5천명이 접속하면 5천개의 세션이 생긴다. 이를 한 곳에 몰아넣어 관리하면 편하므로 sessionManager를 하나 둔다. singleton패턴을 사용해 sessionManager를 만들어준다. -> static으로 생성 후, 프로퍼티로 static생성Manager 반환해주기. Dic으로 int(session번호로활용), session을 저장해 관리한다. 10명이 접속하여 10개의 세션이 있다고 가정했을때, A가 채팅을 치는 상황을 생각해보자 1. A가 채팅을 침, 서버로 메시지 전송 2. 서버에서 받고, 10명에게 다시 재전송 3. 10명(A포함)은 각각 메시지를 받아 출력한다. 여기서 알 수 있는 것은 1명이 메시지를 보내면 10번을 다시 서버에서 패킷을 보내야 한다는 것이다. 그러면 단톡방처럼 1..
이 부분이 강의의 핵심 중 하나인 것 같기도 하고, 아직은 완벽히 이해하지 못해서 지금 정리해서 올리는 것은 아닌듯 싶어서 건너뜀. 만약 완벽히 이해해도 정말 중요한 내용이다 싶으면 올리지 않을듯.
간단하게는 볼록한 데이터형(?)을 평평하게 찌그러뜨려서 byte[]안에 담을 수 있도록 하는 것. 원시타입의 경우에는 byte크기가 정해져 있어서 그 사이즈를 예상할 수 있고, 가장 앞쪽에 packet size는 몇 이라고 기입할 수 있다. 하지만 string이나 구조체, 클래스와 같은 경우에는 임의로 사이즈를 줄였다 늘렸다 할 수 있기 때문에 사이즈를 정확히 몇이라고 정의할 수 없다. +멤버 변수를 다 정해놓았더라도 그 중에 string같은게 섞여있으면 또 사이즈가 바뀐다. byte[]에 밀어넣어야 어떻게든 네트워크 상으로 전송할 수 있기 때문에, string이나 클래스처럼 사이즈가 가변적인 녀석을 평평하게 찌그러트려서 버퍼안에 밀어넣는 작업을 직렬화(Serialization)이라고 한다. 반대로, 이..
Buffer에는 맨날 보던 capacity, size등의 속성이 있는데, 이를 적절히 빠진것 들을 구현해 줘야 한다. ArraySegment를 이용하면 Count와 같은 size는 자동으로 주지면 readPos, writePos와 같은 것들은 구현되어 있지 않다.(Java에서는 따로 있던거 같은데 기억이 잘..^^) 이를 int로 선언해주고 읽어야 하는 사이즈, 쓸 수 있는 사이즈를 알 수 있도록 DataSize, FreeSize를 선언해주고 프로퍼티로 writePos-readPos, buffer.Count - writePos를 get해주면 된다. 왜 그런지는 버퍼 그림을 그리고 채워넣는 것을 생각해보면 될듯. readPos, writePos는 이 상태로라면 계속 증가하기만 하므로 초기화 하는 함수도 하..
client에서 connect하는 부분은 처음 구현하게 되면 coonect() 로 통해 구현하게 되는데, 이는 블로킹 함수이다. mmorpg게임에서는 항상 블로킹 함수를 사용하는 것을 지양 해야한다. 또, client나 server나 receive send하는 경우는 공용으로 사용하고 분산서버를 만들 경우에도 서버 간에서 receive send를 한다. 이를 위해서 공용의 인터페이스 역할을 하는 녀석을 하나 만들어주면 코드 재사용에 있어 굉장히 편리하다. 그래서 Connecor라는 클래스를 하나 만들고 이전에 receive send를 하듯이 비동기로 register~함수를 만들고 delegate를 통해 연결 완료시 함수를 실행해준다.
이벤트를 처리하는 방법 2가지 1. EventHandler 생성하여 연결 2. Session을 상속받은 클래스에서 처리
이전에는 Queue에서 byte[] 하나만 빼와서 하나씩 전송했는데, SocketAsynEventArgs.BufferList에 보낼 byte[]들을 넣어주면 한 번에 다 보낼 수 있다. 다만, SetBuffer와 BufferList는 하나만 사용해야 한다. RegisterSend()에서 SetBuffer를 사용하는데, 여기서 BufferList도 넣게 되면 에러가 난다. 상황에 맞게 쓰면서 수정해주자. BufferList에 값을 넣을 때는 List로 만들어서 넣어야 한다. 전역으로 List _pendingList를 생성하고, 는 ArragSegment로 넣어줘야 한다. RegisterSend에서 첫째줄에서 _pendingList를 클리어해주고 while문으로 Queue에 있는 모든 byte[]들을 꺼내온..
스레드가 동시 다발적으로 RegisterRecv를 등록한다면 Lock으로 인해 OnRecvCompleted에 동시다발적으로 들어올 수 없지만, 지금 구현된 간단한 Send는 그렇지 않다. recv처럼 2가지로 쪼개줘야 한다. 1. Send하려는 순간 RegisterSend를 호출한다. 이렇게 하면 Send에서 args를 설정해주고, delegate에 이벤트를 설정해주고 RegisterSend를 설정해주면 된다. 하지만 이전처럼 OnSendCmpleted에서 Register를 다시 해주게 되면 Args를 매 번 다시 만들어서 사용하고, Completed에도 매 번 재등록한다.(Recv도 마찬가지. 이를 재사용하게 수정해줘야 한다.) 또한, MMORPG를 보면 한 보스를 잡을 때 몇십 명씩 모이는데 이럴 때..
Connect, Disconnect를 비동기로 하였으니 이제 receive, send를 비동기로 만들 차례이다. Async를 사용하여 args를 넣어주게 되면 스레드 풀에서 스레드 하나를 데려와서 주 스레드말고 다른 스레드에게 일을 시킨다. Session은 다음과 같은 역할(함수)를 가진다. Start(시작) Send(송신) RegisterRecv(수신) OnRecvCompleted(수신 완료시) Disconnect(연결 끊기) 송신은 하난데, 수신이 2개인 이유가 지금은 수신만 비동기로 만들어서 수신만 2개로 쪼개졌다. 비동기로 만드려고 한다면 비동기를 호출하는 부분과, 호출이 완료됐을 때 함수가 따로 존재해야 한다. 이렇게 해야 비동기로 작업이 완료됐을 때 따로 완료시행동 함수만 호출가능하다. 우선,..