네트워크 오목 게임 구현하기

2019. 9. 2. 16:44PL/C++

혼자하는 오목게임을 구현함으로써 전체적인 틀을 구성했다. 여기에 네트워크 Winsock을 추가해서 두 명의 플레이어가 게임을 하도록 만들어볼 것이다

 

먼저 메뉴 폼에서 멀티게임 버튼과 새로운 멀티플레이 창이 열릴 수 있도록 새로운 폼을 생성하도록 한다

 

 

Single_Button 클릭과 마찬가지로 메뉴 폼에서 창 이동에 대한 코드를 똑같이 작성한다. 이동하는 폼에 대한 객체만 변경하면 된다

 

private void Multi_Button_Click(object sender, EventArgs e)
{
    Hide();
    MultiPlay sp = new MultiPlay();
    sp.FormClosed += new FormClosedEventHandler(Form_Closed);
    sp.Show();
}

 

그리고 MultiPlay 폼으로 넘어가 SinglePlay 폼과 비슷하게 만들 것이다. 다만 추가적인 상황은 방을 구분하기 위해 방을 입력하는 textBox를 추가할 것이다

 

 

현재까지 네트워크 코드를 제외하고는 SinglePlay 코드와 비슷하다. 다만 MultiPlay는 두 명 이상 입장해야만 START 버튼을 활성화하도록 할 것이다. 따라서 초기화 코드에 버튼을 disable하는 코드를 추가한다

 

public partial class MultiPlay : Form
{
	...

    public MultiPlay()
    {
        InitializeComponent();
        GameStart.Enabled = false;
    }
}

 

방을 관리하는 서버를 구현하기 위해 C++ 프로젝트로 Server 프로젝트를 생성한다

 

 

Winsock2 라이브러리를 사용하기 위해서 프로젝트 추가 종속성에 ws2_32.lib를 추가하도록 한다

 

 

서버의 main 함수에서는 기본적으로 네트워크 라이브러리는 Winsock 라이브러리를 사용하고, 클라이언트는 멀티 쓰레드 방식으로 구성한다

 

int main() {
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	serv_sock = socket(AF_INET, SOCK_STREAM, NULL);

	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	serv_addr.sin_port = htons(8000);
	serv_addr.sin_family = AF_INET;

	bind(serv_sock, (SOCKADDR*)&serv_addr, sizeof(serv_addr));
	listen(serv_sock, 32);

	int cli_addr_size = sizeof(cli_addr);
	while (true) {
		SOCKET clientSocket;
		if (clientSocket = accept(serv_sock, (SOCKADDR*)&cli_addr, &cli_addr_size)) {
			Client *client = new Client(nextID, clientSocket);
			CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ServerThread, (LPVOID)client, NULL, NULL);
			connections.push_back(*client);
			nextID++;
		}
		Sleep(100);
	}
}

 

ServerThread 함수는 클라이언트 정보를 매개변수로 한다. 서버는 승리판정을 제외하고 클라이언트와 서버가 맞춰진 형식의 문자에 따라서 방을 관리한다. 

 

void ServerThread(Client *client) {
	// 송신, 수신 버퍼 초기화
	char *send_msg = new char[256];
	char *recv_msg = new char[256];
	int size = 0;
	while (true) {
		if ((size = recv(client->getClientSocket(), recv_msg, 256, NULL)) > 0) {
			string receivedString = string(recv_msg);
			vector<string> tokens = getTokens(receivedString, ']');
			if (receivedString.find("[Enter]") != -1) {
				...
			}
			else if (receivedString.find("[Put]") != -1) {
				...
			}
			else if (receivedString.find("[Play]") != -1) {
				...
			}
		}
		else { // 클라이언트가 연결을 끊었을 때
			memset(send_msg, 0, sizeof(send_msg));
			sprintf(send_msg, "클라이언트 sockid:[%i]의 연결이 끊어졌습니다.", client->getClientID());
			cout << send_msg << endl;
			// vector를 통해 게임에서 나간 클라이언트 조회
			for (int i = 0; i < connections.size(); i++) {
				if (connections[i].getClientID() == client->getClientID()) {
					// 게임 중인 경우, 다른 사용자에게 알린다
					if (connections[i].getRoomID() != -1 &&
						clientCountInRoom(connections[i].getRoomID()) == 2) {
						// 남은 유저에게 상대방이 나갔다는 메세지 전송
						exitClient(connections[i].getRoomID());
					}
					// 클라이언트 정보 삭제
					connections.erase(connections.begin() + i);
					break;
				}
			}
			delete client;
			break;
		}
	}
}

 

 

'PL > C++' 카테고리의 다른 글

system("cls") 떨림 현상 줄이는 방법  (0) 2019.09.03
C# 네임스페이스와 열거형  (0) 2019.09.03
C++에서의 string token  (0) 2019.09.02
memset과 ZeroMemory  (0) 2019.09.02
Winsock2 소켓 프로그래밍  (0) 2019.09.01