2019. 9. 2. 16:44ㆍPL/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 |