개요
기존의 Server 코드를 수정하고 Server에서 Accept 동기 함수가 아닌 AcceptEX 비동기 함수를 이용해 캐릭터 접속을 받고 접속된 캐릭터의 이동을 다른 클라이언트에게 브로드 캐스팅한다.
Server(예전 코드)
기존에는 하나의 CPP 파일과 하나의 헤더 파일에 서버 코드를 모두 작성했기 때문에 가독성이 떨어졌다.
Server(변경 코드)
이제 새로 작업할 부분은 해당하는 Class로 나누면서 가독성과 응집도 결합도를 고려한 설계를 해볼 예정이다.
accept(예전 코드)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
while (1) {
clientSocket = accept(listenSocket, (struct sockaddr *)&clientAddr, &addrLen);
if (clientSocket == INVALID_SOCKET)
{
std::cout << "Error - Accept Failure\n";
return 1;
}
// 동시접속자수 10명
int new_id = -1;
for (int i = 0; i < MAX_USER; ++i) {
// 맵일 때만 가능한 사용법
//if (0 == clients.count(i)) {
if (false == clients[i].in_used) {
new_id = i;
break;
}
}
if (-1 == new_id) {
std::cout << "MAX USER overflow \n";
continue;
}
// 초기화 해줘야하는 인자들
clients[new_id].socket = clientSocket;
clients[new_id].prev_size = 0;
clients[new_id].in_used = false;
clients[new_id].m_lock.lock();
clients[new_id].viewlist.clear();
clients[new_id].m_lock.unlock();
ZeroMemory(&clients[new_id].over_ex.over, sizeof(clients[new_id].over_ex.over));
flags = 0;
// IOCP 객체와 소켓 연결 (socket, hIOCP, key, 0)
CreateIoCompletionPort(reinterpret_cast<HANDLE>(clientSocket), g_iocp, new_id, 0);
//Recv 시작
do_recv(new_id);
}
|
이전에는 accept() 동기 함수를 사용해서 처리를 했다 동기 함수이다 보니 별도의 AcceptThread를 만들어서 진행을 했다.
AcceptEx(변경 코드)
1
2
3
4
5
6
7
8
|
if (AcceptEx(listenSocket_, clientSocket, &over_ex.messageBuffer_, NULL,
sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, NULL,
&over_ex.over_) == FALSE) {
if (WSAGetLastError() != WSA_IO_PENDING) {
std::cout << "Error\n";
}
}
|
변경 후에는 AcceptEx() 함수를 이용해서 처리를 하였다. AcceptEx() 함수는 MS에서 XP부터 지원하는 확장 API이다.
AcceptEx()와 accept()의 차이는 전자는 미리 소켓을 준비해야 한다는 차이가 있다.
Send Recv Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
namespace NETWORK {
#pragma region Standard Recv/Send Function
//WSARecv
void Recv(SOCKET socket, OverEx& overEx);
//WSASend
void SendPacket(SOCKET socket, void* packet);
#pragma endregion
//클라이언트의 접속 성공을 알림
void SendLoginOk(SOCKET socket, const PositionType x, const PositionType y,
const ObjectIDType addID);
//새로운 Object(몬스터, 유저 등)이 생길 때 보내는 패킷
void SendAddObject(SOCKET socket, const PositionType x, const PositionType y,
const ObjectIDType addID, const ObjectClass objType);
//Object(몬스터, 유저 등)이 움직일 때 보내는 패킷
void SendMoveObject(SOCKET socket, const PositionType x, const PositionType y,
const ObjectIDType movedID, const TextureDirection textureDirection);
}
|
기존에는 하나의 CPP에 모든 함수를 집어넣어서 가독성이 떨어졌다. 이번에는 Network Namespace를 따로 만들어서 그 안에 Send/Recv 관련 함수들을 모았다.
이동 동기화 모습
후기
기존에 있던 리뉴얼 글을 지우고 다시 4번을 작성했다. 아무래도 글이 너무 많아지는 거 같아서 줄이는 게 좋을 거 같아서 지웠다. 가장 기본적인 Server와 통신을 끝냈다. 크게 바뀐 건 기존 accept에서 AcceptEx() 비동기 함수로 바꿨으며 하나의 CPP에서 여러 CPP 파일로 나눈 것이 가장 큰 차이이다.
앞으로 크게 남은 구현은
- Lua Script를 이용한 AI (A*포함)
- Memory Pool 기반 Send 및 Thread Safe Log Class
- DB 연결 및 DB 처리
- View List와 Sector 기반 Packet 전송 제한
- Login Server 구축
이 정도인 거 같다. 아무래도 올해는 넘길 거 같은 느낌도 들고 새로 아르바이트 자리도 구했기 때문에 얼마나 걸릴지는 모르겠지만 내년 3월 정도에는 꼭 마무리해서 포트폴리오를 바꾸는 게 목표이다.
리뉴얼 관련 글
[portfolio] - [리뉴얼] 3- 공간 분할을 이용한 Sector Class
[portfolio] - [리뉴얼] 2- 경량 패턴을 이용한 World Class
[portfolio] - [리뉴얼] 1- 중재자 패턴을 이용한 Network Class
[portfolio] - [리뉴얼] 개요 및 목표(20.11.01 수정)
Accept 관련
[게임 서버] AcceptEx와 ConnectEx-IOCP
* 리소스는 알피지 만들기 툴 리소스를 사용했으며 영리 목적으로 사용하지 않을 것입니다.
'portfolio' 카테고리의 다른 글
[리뉴얼] 6- AI의 길찾기(A*) (0) | 2021.01.12 |
---|---|
[리뉴얼] 5- ViewList와 Sector (1) | 2020.11.18 |
[리뉴얼] 3- 공간 분할을 이용한 Sector Class (0) | 2020.08.03 |
[리뉴얼] 2- 경량 패턴을 이용한 World Class (0) | 2020.07.27 |
[리뉴얼] 1- 중재자 패턴을 이용한 Network Class (0) | 2020.07.19 |