服务端代码
dataNode.h
#pragma once
#include
using std::string;
struct dataNode { // 数据包结构
int x, y; // 坐标
int per; // 前一个棋子编号
int turn; // 当前棋子编号
int winner; // 记录胜利者编号
bool isChess; // 判断该点是否是一个棋子
bool isRestart;
dataNode() { // 初始化
winner = -1;
isChess = false;
isRestart = false;
}
};
server.cpp
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "dataNode.h"
#include
#pragma comment (lib,"ws2_32.lib")
#include
#include
#include
#define PORT 4567
using namespace std;
SOCKET li[10]; // 保存客户端信息
int cnt = 1;
void CPlay(void *p)
{
//const char *str = "hello client";
SOCKET *pSock = (SOCKET*)p;
SOCKET socka = *pSock;
// 返回客户端id
dataNode data;
data.x = -10;
data.y = -10;
data.turn = cnt - 1;
data.winner = -1;
send(socka, (char *)&data, sizeof(dataNode), 0);
// 先给第一个客户端发送数据包
if (cnt == 2) {
data.per = cnt;
data.turn = cnt - 1;
data.winner = -1;
int i = send(socka, (char *)&data, sizeof(dataNode), 0);
}
sockaddr_in sa = { AF_INET };
int count = 0;
while (true) // 持续接收和发送数据包
{
data.winner = -1;
char recvData[256];
memset(recvData, 0, 256);
int i = recv(socka, recvData, sizeof(recvData), 0);
count++;
if (i <= 0)
{
if (GetLastError() == 10054) { // 判断客户端是否退出
cout << htons(sa.sin_port) << "客户端退出了:" << socka << endl;
cnt--;
}
break;
}
// char*和结构体转换
memset(&data, 0, sizeof(dataNode));
memcpy(&data, recvData, sizeof(dataNode));
// 计算数据包要发给谁
int tmp = data.turn;
data.turn %= cnt;
if (data.turn == 0)
data.turn = 1;
if (data.turn == 1)
data.per = 2;
else
data.per = 1;
cout << count << "---" << data.x << "---" << data.y << "---" << data.per << "---" << data.turn << "--*" << data.winner << endl;
for (int i = 1; i < 9; i++) { // 数据包不发给自己
if(tmp-1 != i)
send(li[i], (char *)&data, sizeof(dataNode), 0);
}
}
}
int main()
{
WSAData wd;
WSAStartup(0x0202, &wd);
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in sa = { AF_INET };
sa.sin_port = htons(PORT);
bind(sock, (sockaddr*)&sa, sizeof(sa));
listen(sock, 5);
sockaddr_in from = { AF_INET };
int nLen = sizeof(from);
while (true) {
SOCKET socka = accept(sock, (sockaddr*)&from, &nLen);
cout << inet_ntoa(sa.sin_addr) << htons(from.sin_port) << "登录了" << endl;
li[cnt++] = socka;
_beginthread(CPlay, 0, &socka); //开启一个线程 是以CPlay 命名的函数 传输的数据为socka
}
return 0;
}
客户端代码
dataNode.h
#pragma once
#include
using std::string;
struct dataNode { // 数据包结构
int x, y; // 坐标
int per; // 前一个棋子编号
int turn; // 当前棋子编号
int winner; // 记录胜利者编号
bool isChess; // 判断该点是否是一个棋子
bool isRestart;
dataNode() { // 初始化
winner = -1;
isChess = false;
isRestart = false;
}
};
initSock.h
#pragma once
#pragma once
#include
#include
#include
#include
#pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
class CInitSock
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{
// 初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if (::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};
client.h
#pragma once
#include "initSock.h"
#include "dataNode.h"
class client {
public:
void initClient(); // 初始化客户端
void connectServer(const char *ipAddress); // 连接到指定服务器
void sendData(dataNode data); // 发送数据包
dataNode revcData(); // 接收数据包
void closeConnect(); // 关闭连接
CInitSock initSock;
SOCKET s;
sockaddr_in servAddr;
};
void client::initClient() { // 初始化客户端
s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
printf(" Failed socket() \n");
return;
}
}
void client::connectServer(const char *ipAddress) { // 连接到指定服务器
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(4567);
// ipAddress是服务器程序(TCPServer程序)所在机器的IP地址
servAddr.sin_addr.S_un.S_addr = inet_addr(ipAddress);
if (::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
{
printf(" Failed connect() \n");
return;
}
}
void client::sendData(dataNode data) { // 发送数据包
send(s, (char *)&data, sizeof(dataNode), 0);
}
dataNode client::revcData() { // 接收数据包
dataNode data;
data.x = -1;
data.y = -1;
char recvData[256];
memset(recvData, 0, 256);
int i = recv(s, recvData, sizeof(recvData), 0);
if (i <= 0)
return data;
// 清空缓存
memset(&data, 0, sizeof(dataNode));
memcpy(&data, recvData, sizeof(dataNode));
return data;
}
void client::closeConnect() { // 关闭连接
::closesocket(s);
}
grid.h
#pragma once
#include
#include
#include "dataNode.h"
#define mapWidth 512
#define mapHeight 512
#define size 32
dataNode map[800][800];
class grid {
public:
void initGrid(); // 初始化棋盘
void drawGrid(); // 打印棋盘
void drawTips(dataNode data, int step);
void drawBlcak(int x, int y); // 在指定位置打印黑色棋子
void drawWhite(int x, int y); // 在指定位置打印白色棋子
void drawGameOver(int x, int y); // 在指定位置打印结束提示图
int check(int x, int y); // 判断当前棋子下完后游戏是否结束
bool isWin(dataNode data); // 判断是否胜利
private:
IMAGE blackChessRc, blackChess[3]; // 黑色棋子贴图
IMAGE whiteChessRc, whiteChess[3]; // 白色棋子贴图
IMAGE gameOver; // 结束提示图
};
void grid::initGrid() { // 初始化棋盘
// 初始化窗口
initgraph(700, mapHeight + size * 2);
// 加载图片
loadimage(&gameOver, L"rec\\gameOver.jpg");
SetWorkingImage(NULL);
loadimage(&blackChessRc, L"rec\\blackChess.jpg", 62, 31);
SetWorkingImage(&blackChessRc);
for (int i = 0; i < 2; i++)
getimage(&blackChess[i], i * 31, 0, 31, 31);
SetWorkingImage(NULL);
loadimage(&whiteChessRc, L"rec\\whiteChess.jpg", 62, 31);
SetWorkingImage(&whiteChessRc);
for (int i = 0; i < 2; i++)
getimage(&whiteChess[i], i * 31, 0, 31, 31);
SetWorkingImage(NULL);
settextcolor(BLACK);
}
int grid::check(int x, int y) { // 判断当前棋子下完后游戏是否结束
int tmp = map[x][y].turn;
int cnt = 0;
// 上
for (int i = 0; i < 5; i++) {
if (map[x][y - i * size].isChess && map[x][y - i * size].turn == tmp) {
cnt++;
if (cnt >= 6) {
return tmp;
}
}
else
break;
}
// 下
for (int i = 0; i < 5; i++) {
if (map[x][y + i * size].isChess && map[x][y + i * size].turn == tmp) {
cnt++;
if (cnt >= 6) {
return tmp;
}
}
else
break;
}
// 左
cnt = 0;
for (int i = 0; i < 5; i++) {
if (map[x - i * size][y].isChess && map[x - i * size][y].turn == tmp) {
cnt++;
if (cnt >= 6) {
return tmp;
}
}
else
break;
}
// 右
for (int i = 0; i < 5; i++) {
if (map[x + i * size][y].isChess && map[x + i * size][y].turn == tmp) {
cnt++;
if (cnt >= 6) {
return tmp;
}
}
else
break;
}
// 左上
cnt = 0;
for (int i = 0; i < 5; i++) {
if (map[x - i * size][y - i * size].isChess && map[x - i * size][y - i * size].turn == tmp) {
cnt++;
if (cnt >= 6) {
return tmp;
}
}
else
break;
}
// 左下
for (int i = 0; i < 5; i++) {
if (map[x + i * size][y + i * size].isChess && map[x + i * size][y + i * size].turn == tmp) {
cnt++;
if (cnt >= 6) {
return tmp;
}
}
else
break;
}
// 右上
cnt = 0;
for (int i = 0; i < 5; i++) {
if (map[x + i * size][y - i * size].isChess && map[x + i * size][y - i * size].turn == tmp) {
cnt++;
if (cnt >= 6) {
return tmp;
}
}
else
break;
}
// 右下
for (int i = 0; i < 5; i++) {
if (map[x - i * size][y + i * size].isChess && map[x - i * size][y + i * size].turn == tmp) {
cnt++;
if (cnt >= 6) {
return tmp;
}
}
else
break;
}
return -1;
}
bool grid::isWin(dataNode data) { // 判断是否胜利
if (data.winner != -1) {
settextcolor(BLACK);
outtextxy(300, 542, L"胜利");
if (data.winner == 1)
drawBlcak(280, 550);
if (data.winner == 2)
drawWhite(280, 550);
return true;
}
return false;
}
void grid::drawGrid() { // 打印棋盘
// 初始化网格
setbkcolor(RGB(220, 180, 60));
cleardevice();
for (int i = 0; i <= mapWidth / size; i++) {
for (int j = 0; j <= mapHeight / size; j++) {
map[i][j].x = i * size;
map[i][j].y = j * size;
}
}
for (int i = 0; i <= mapWidth; i++) {
for (int j = 0; j <= mapHeight; j++) {
map[i][j].isChess = false;
}
}
// 打印网格
setcolor(BLACK);
for (int i = 0; i < mapWidth / size; i++) {
for (int j = 0; j < mapHeight / size; j++) {
line(map[i][j].x + size / 2, map[i][j].y + size / 2, map[i + 1][j].x + size / 2, map[i + 1][j].y + size / 2);
line(map[i][j].x + size / 2, map[i][j].y + size / 2, map[i][j + 1].x + size / 2, map[i][j + 1].y + size / 2);
}
}
line(size / 2, mapHeight + size / 2, mapWidth + size / 2, mapHeight + size / 2);
line(mapWidth + size / 2, size / 2, mapWidth + size / 2, mapHeight + size / 2);
// 打印棋盘上的5个点
setfillcolor(BLACK);
fillcircle(mapWidth / 2 + size / 2, mapHeight / 2 + size / 2, 3);
setfillcolor(BLACK);
fillcircle(3 * size + 16, 3 * size + 16, 3);
setfillcolor(BLACK);
fillcircle(3 * size + 16, 13 * size + 16, 3);
setfillcolor(BLACK);
fillcircle(13 * size + 16, 3 * size + 16, 3);
setfillcolor(BLACK);
fillcircle(13 * size + 16, 13 * size + 16, 3);
}
void grid::drawTips(dataNode data, int step) {
settextcolor(BLACK);
outtextxy(540, 20, L"现在是:");
if (data.per == 1)
drawBlcak(614, 20 + 8);
if (data.per == 2)
drawWhite(614, 20 + 8);
outtextxy(630, 20, L"的回合");
outtextxy(540, 50, L"当前步数:");
TCHAR s[5];
_stprintf(s, _T("%d"), step);
outtextxy(620, 50, s);
outtextxy(540, 80, L"(1)对局双方各执一");
outtextxy(540, 110, L" 色棋子。");
outtextxy(540, 140, L"(2)空棋盘开局。");
outtextxy(540, 170, L"(3)黑先、白后交替");
outtextxy(540, 200, L" 下子,每次只能");
outtextxy(540, 230, L" 下一子。");
outtextxy(540, 260, L"(4)棋子下在棋盘的");
outtextxy(540, 290, L" 空白点上,棋子");
outtextxy(540, 320, L" 下定后,不得向");
outtextxy(540, 350, L" 其它点移动,不");
outtextxy(540, 380, L" 得从棋盘上拿掉");
outtextxy(540, 410, L" 或拿起落别处。");
outtextxy(540, 440, L"(5)黑方的第一枚棋");
outtextxy(540, 470, L" 子可下在棋盘任");
outtextxy(540, 500, L" 意交叉点上。");
/*
(1)对局双方各执一色棋子。
(2)空棋盘开局。
(3)黑先、白后,交替下子,每次只能下一子。
(4)棋子下在棋盘的空白点上,棋子下定后,不得向其它点移动,不得从棋盘上拿掉或拿起另落别处。
(5)黑方的第一枚棋子可下在棋盘任意交叉点上。
*/
}
void grid::drawWhite(int x, int y) {// 在指定位置打印白色棋子
putimage(x - (size / 2), y - (size / 2), &whiteChess[1], SRCAND);
putimage(x - (size / 2), y - (size / 2), &whiteChess[0], SRCINVERT);
}
void grid::drawBlcak(int x, int y) { // 在指定位置打印黑色棋子
putimage(x - (size / 2), y - (size / 2), &blackChess[1], SRCAND);
putimage(x - (size / 2), y - (size / 2), &blackChess[0], SRCINVERT);
}
void grid::drawGameOver(int x, int y) {
putimage(x, y, &gameOver);
}
client.cpp
#include "client.h"
#include
#include "grid.h"
using namespace std;
int main() {
// 初始化客户端
string ipAddress;
client test;
cout << "请输入服务器IP(127.0.0.1):" << endl;
cin >> ipAddress;
test.initClient();
test.connectServer(ipAddress.c_str());
// 用来记录是第几号客户端
dataNode id;
id = test.revcData();
// 初始化窗口
grid grid;
grid.initGrid();
grid.drawGrid();
int step = 0;
while (1) {
dataNode data;// 用来保存接收和发送的数据
// 打印提示信息
grid.drawTips(data, step);
data = test.revcData(); // 发送数据包
// 打印对方的棋子
if (data.x != -10 && data.y != -10){
if (data.per == 1)
grid.drawBlcak(data.x, data.y);
if (data.per == 2)
grid.drawWhite(data.x, data.y);
map[data.x][data.y].isChess = true;
map[data.x][data.y].turn = data.per;
}
// 判断是否胜利
if (grid.isWin(data)) {
data = test.revcData();
if (data.isRestart) { // 判断对方是否要继续游戏
grid.initGrid();
grid.drawGrid();
data.winner = -1;
data.isRestart = false;
step = 0;
}
else {
_getch();
test.closeConnect();
return 0;
}
}
// 如果到自己的回合了
settextcolor(BLACK);
outtextxy(540, 20, L"现在是");
if (id.turn == 1)
grid.drawBlcak(614, 20 + 8);
if (id.turn == 2)
grid.drawWhite(614, 20 + 8);
outtextxy(630, 20, L"的回合");
while (data.turn == id.turn) {
// 获取鼠标点击信息,先清空鼠标缓存
FlushMouseMsgBuffer();
MOUSEMSG m;
m = GetMouseMsg();
if (m.mkLButton && !map[(m.x / 32) * 32 + 16][(m.y / 32) * 32 + 16].isChess) {
step++;
// 让棋子在格子交点出现
data.x = (m.x / 32) * 32 + 16;
data.y = (m.y / 32) * 32 + 16;
// 判断棋子颜色
if (id.turn == 1)
grid.drawBlcak(data.x, data.y);
if (id.turn == 2)
grid.drawWhite(data.x, data.y);
// 发送数据包
map[data.x][data.y].isChess = true;
map[data.x][data.y].turn = id.turn;
data.winner = grid.check(data.x, data.y);
data.turn = id.turn + 1;
test.sendData(data);
break;
}
}
// 判断是否胜利
if (grid.isWin(data)) {
grid.drawGameOver(250, 242);
char isRestart = getch();
if (isRestart == 'y' || isRestart == 'Y') { // 判断是否要继续游戏
grid.initGrid();
grid.drawGrid();
data.winner = -1;
data.isRestart = true;
test.sendData(data);
data.isRestart = false;
step = 0;
}
else {
_getch();
test.closeConnect();
return 0;
}
}
}
_getch();
test.closeConnect();
return 0;
}
Comments | NOTHING