目录
成果
运行效果图
左边是在虚拟机里运行的,右边是在Host机上运行的。
最新更改后的界面:
过程
记不起自己为什么要写这个象棋游戏的,大概是因为刚学了点儿Socket ,所以想用用,于是就写个局域网对战的象棋吧。。。
1. 首先的问题是下棋的两端应该是什么样的?
我希望下棋的两个人使用相同的程序。所以就不能像FTP一样,一个客户端,一个服务器端,而只能每个程序都是一样的,既是客户端(Client),又是服务器端(Server)。在通信时,己方的Client 向对方的Server 发送信息,对方的Client 向己方的Server 发送信息。两端都存储棋盘信息,通过通信保持棋盘信息的一致。
然后呢,应该是一端点击界面移子之后,应该能通知对方进行相同的移动。
综合以上两点,运行过程应该是这样的:
当在界面上点击棋子时,先判断当前是否轮到自己落子,如果是,则进行移动,更新界面,并通过Client 向对方Server 发送移动信息。对方Server 收到后,进行同样的移动,更新界面。
这里要求Server能随时接到对方发来的消息,所以Server的监听应该是一个额外的线程。
2. 接下来的问题是怎么表示,怎么存储?
棋盘,应该用二维数组存储比较好,数组坐标(以下所说的"数组坐标 "是指该二维数组中的一个(x,y)数对)对应棋盘坐标。那么数组里存储什么呢,一共有車、马、象、士、将、砲、卒七种棋子,那么设置一个棋子类作为基类,然后设置七个类继承棋子类?基类有一个move函数,每个子类重写该函数?但是移动似乎只是我程序的一小部分,这样似乎没必要。
那么存储整型数值?不同的数值代表不同的棋子?似乎可以。
那么就用7个数代替七种棋子,但是棋子有黑白色,要用一个数表示棋子类型(即是車、马或其他)和棋子颜色两个信息,那就用BLANK =8代表空子,黑方的車、马、象、士、将、砲、卒分别为1到7,白方的車、马、相、士、帅、炮、兵分别为9到15。
这样判断某数组坐标上棋子的颜色,就把其值与BLANK 比较,大于BLANK为白色,否则为黑色。
判断某数组坐标上棋子的类型,则将其值模BLANK 。
另外,因为下棋双方的视角是相反的,所以,棋盘在存储时应该是相反的,移动时的坐标也应该进行转换。
3. 然后应该怎么通信呢?
我希望这个程序打开后,就能找到对方,并确定谁是黑色,谁是白色。
也许可以让Client 在运行之后就对局域网进行端口扫描,然后给出正在运行此程序的IP 地址列表,让用户选择要连接到哪个,如果对方已经有了连接,则对方会拒绝此连接,如果对方没有连接,则对方程序会向对方用户提示该连接请求,如果,对方用户同意,则连接建立,否则依然是拒绝此连接。
但是,我没有采用以上所述方法(因为太复杂,我还是先做好主体工作吧=_=)。
所以在程序开始运行后,会让用户输入对方的IP 地址,然后Server 开始监听。之后Client 开始向对方发出连接请求。
Server 监听时,如果收到连接请求,就看对方的IP 地址是否是用户输入的IP 地址,如果不是,说明连接请求不是用户所希望的对方发送的,那就继续监听。
Client 请求连接时,如果对方同意了,就要开始确定自己的颜色了。
确定颜色这里困扰了我很久,最后采用的解决方法是这样的:
核心思想就是谁先发出连接请求,谁就是黑色。
也就是在Client 连接上对方之后,要判断Server 是不是已经连接了对方,如果Server 已连接,就说明是对方先发出的连接请求,那么对方就是黑色,自己就设为白色。如果Server 没有连接,就说明自己先连接上了对方,也就是自己是黑色。
以上就是编码前及编码时的大致想法。
代码
注: 用 CodeBlocks 编译时若出现类似" undefined reference to `send@16' " 的错误,在Settings->Complier->Global Complier Settings->Linker Settings 中添加 C:\Program Files (x86)\CodeBlocks\MinGW\lib\libwsock32.a
main.cpp
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif #include <tchar.h>
#include <windows.h>
#include <pthread.h>
#include <windowsx.h>
#include "chinese_chess.h"
#include "Server.h"
#include "Client.h" #define WIDTH 600 //界面宽度
#define HEIGHT 600 //界面高度
#define ZERO_X 70 //棋盘左边界
#define ZERO_Y 70 //棋盘上边界
#define PIECE_BKCOLOR RGB(195,163,109) //棋子背景色
#define PIECE_WH 45 //棋盘每个格子的宽度和高度 HWND hwnd; /* This is the handle for our window */
char* ots_ip; //存储对方IP地址的字符串
int port;
bool is_connect_alive=false; //是否连接到对方
Board * chess_board; //棋盘
Server *server;
Client *client;
int chess_sx=-; //移动起始位置的数组坐标
int chess_sy=-;
int chess_dx=-; //移动目标位置的数组坐标
int chess_dy=-; /* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); /* Make the class name into a global variable */
TCHAR szClassName[ ] = _T("Chinese Chess"); int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow) {
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */ /* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX); /* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = ; /* No extra bytes after the window class */
wincl.cbWndExtra = ; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; /* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return ; /* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
, /* Extended possibilites for variation */
szClassName, /* Classname */
_T("Chinese Chess"), /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
WIDTH, /* The programs width */
HEIGHT, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
); /* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow); /* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, , )) {
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
} /* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
//把数组坐标转换为界面坐标
void xy_to_pixel(int x,int y,int*pixelx,int *pixely) {
*pixely=x*PIECE_WH+ZERO_Y;
*pixelx=y*PIECE_WH+ZERO_X;
}
//把界面坐标转换为数组坐标
void pixel_to_xy(int pixelx,int pixely,int*x,int *y) {
int r=PIECE_WH/;
*y=(pixelx-(ZERO_X-r))/PIECE_WH;
*x=(pixely-(ZERO_Y-r))/PIECE_WH;
}
//以数组坐标画线
void draw_line(HDC hdc,int sx,int sy,int dx,int dy) {
int psx,psy,pdx,pdy;
xy_to_pixel(sx,sy,&psx,&psy);
xy_to_pixel(dx,dy,&pdx,&pdy);
MoveToEx (hdc, psx,psy, NULL) ;
LineTo (hdc, pdx, pdy) ;
}
//以数组坐标画棋子
void paint_piece(HDC hdc,int x,int y,int color,int type) {
static HBRUSH piece_brush =CreateSolidBrush (PIECE_BKCOLOR); //棋子的背景色
if(type==||color==BLANK)return ;
int px,py;
xy_to_pixel(x,y,&px,&py);
int r=PIECE_WH/;
SelectObject (hdc,piece_brush ) ;
SelectObject (hdc, GetStockObject (NULL_PEN)) ;
Ellipse(hdc,px-r,py-r,px+r,py+r);
char *text=new char[];
switch(type) {
case JU:
strcpy(text,"車");
break;
case MA:
strcpy(text,"马");
break;
case XIANG:
if(color==BLACK)strcpy(text,"象");
else strcpy(text,"相");
break;
case SHI:
strcpy(text,"士");
break;
case JIANG:
if(color==BLACK)strcpy(text,"将");
else strcpy(text,"帅");
break;
case PAO:
if(color==BLACK)strcpy(text,"砲");
else
strcpy(text,"炮");
break;
case ZU:
if(color==BLACK)strcpy(text,"卒");
else
strcpy(text,"兵");
break;
default:
strcpy(text,"");
}
SetBkColor(hdc,PIECE_BKCOLOR);//设置文字背景色
if(color==BLACK) {
SetTextColor(hdc,RGB(,,)); //设置文字颜色
} else {
SetTextColor(hdc,RGB(,,));
}
TextOut (hdc, px-r/, py-r/,text , strlen("马")) ;
delete text;
} void* main_listen(void *) {
server->listen_message();
return ;
}
//创建线程,使server开始监听
bool start_listen() {
pthread_t listen_p;
int ret;
ret= pthread_create( &listen_p, NULL, main_listen,NULL ); //
if( ret != ) { //创建线程成功返回0
//printf("pthread_create error:error_code=%d\n",ret );
handle_error(THREAD_ERROR,true,true);
return false;
}
return true;
} void* chess_connect(void *) {
client->connect_to_ots(); //client开始连接对方server,连接成功后返回
InvalidateRect(hwnd,NULL,true);
} void init() {
server=new Server();//创建Server对象
client=new Client(); //创建Client对象,
start_listen(); //创建线程,server开始监听
Sleep();
pthread_t connect_p;
int ret;
ret= pthread_create( &connect_p, NULL, chess_connect,NULL); //
if( ret != ) { //创建线程成功返回0
//printf("pthread_create error:error_code=%d\n",ret );
handle_error(THREAD_ERROR,true,true);
return ;
}
} /* This function is called by the Windows function DispatchMessage() */ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
static POINT mouse;
static HDC hdc;
static PAINTSTRUCT ps ;
static int iofip=; //index of ots_ip
switch (message) { /* handle the messages */
case WM_CREATE: {
port=;
ots_ip=new char[];
strcpy(ots_ip,"");
}
break;
case WM_KEYDOWN://识别按键,显示输入的内容(对方IP地址)
if(server!=NULL)break;
if(wParam==) {//如果是ENTER,则初始化server、client,并开始连接,连接成功后初始化board
init();
Sleep();
InvalidateRect(hwnd,NULL,true);
}
if(wParam==VK_BACK) {//删除键
if(iofip==)return ;
iofip--;
ots_ip[iofip]='\0';
}
if(wParam<&&wParam>) {//小键盘数字键
wParam-=;
}
if(wParam<&&wParam>) {//主键盘数字键
ots_ip[iofip]=''-+wParam;
iofip++;
ots_ip[iofip]='\0';
}
if(wParam==||wParam==) {//小数点键,小键盘110,主键盘229
ots_ip[iofip]='.';
iofip++;
ots_ip[iofip]='\0';
}
InvalidateRect(hwnd,NULL,true);
break;
case WM_PAINT: {
static HBRUSH bk_brush =CreateSolidBrush (RGB(,,)); //棋子的背景色
hdc=BeginPaint (hwnd,&ps) ;
static HFONT hFont;
LOGFONT lf;
lf.lfHeight=PIECE_WH/;
lf.lfWidth=;
lf.lfEscapement=;
lf.lfOrientation= ;
lf.lfWeight=;
lf.lfItalic= ;
lf.lfUnderline= ;
lf.lfStrikeOut= ;
lf.lfCharSet=DEFAULT_CHARSET ;
lf.lfOutPrecision= ;
lf.lfClipPrecision= ;
lf.lfQuality= ;
lf.lfPitchAndFamily= ;
lstrcpy (lf.lfFaceName, _T("楷体") );
hFont = CreateFontIndirect (&lf) ;
SelectFont(hdc,hFont);
SelectObject(hdc,bk_brush);
Rectangle(hdc,,,WIDTH,HEIGHT);
SetBkColor(hdc,RGB(,,));
if(chess_board==NULL) {//显示输入的IP地址
char tip[]="请输入对方IP地址:";
Rectangle(hdc,WIDTH/,HEIGHT/-,WIDTH/*,HEIGHT/+);
TextOut(hdc,WIDTH/,HEIGHT/-,tip,strlen(tip));
SetBkColor(hdc,RGB(,,));
TextOut(hdc,WIDTH/+,HEIGHT/,ots_ip,strlen(ots_ip));
if(server!=NULL) { //board==NULL而server!=NULL表示正在连接过程中
char tip[]="正在连接......";
SetBkColor(hdc,RGB(,,));
TextOut(hdc,WIDTH/,HEIGHT/+,tip,strlen(tip));
}
EndPaint(hwnd,&ps);
break;
}
char text[]="你的颜色:";
if(chess_board->get_color()==BLACK) {
strcat(text," 黑");
} else {
strcat(text," 白");
}
TextOut (hdc, , ,text , strlen(text)) ;
int M=chess_board->get_M();
int N=chess_board->get_N();
//画棋盘
for(int i=; i<M; i++) {
draw_line(hdc,i,,i,N-);
}
for(int i=; i<N; i++) {
draw_line(hdc,,i,N/,i);
}
for(int i=; i<N; i++) {
draw_line(hdc,N/+,i,N,i);
}
draw_line(hdc,,,,);
draw_line(hdc,,,,);
draw_line(hdc,,,,);
draw_line(hdc,,,,);
draw_line(hdc,,,,);
draw_line(hdc,,,,);
//画棋子
for(int i=; i<M; i++) {
for(int j=; j<N; j++) {
paint_piece(hdc,i,j,chess_board->get_color(i,j),chess_board->get_type(i,j));
}
}
EndPaint(hwnd,&ps);
}
break;
case WM_LBUTTONUP: {
if(chess_board==NULL)break;
if(!chess_board->is_my_turn())break;//当前没轮到自己下棋
GetCursorPos(&mouse);//获取鼠标的屏幕坐标
ScreenToClient(hwnd,&mouse);//转换为界面坐标
int x,y;
pixel_to_xy(mouse.x,mouse.y,&x,&y);//转换为数组坐标
if(chess_board->get_color(x,y)==chess_board->get_color()) {//点击的是自己的棋子
chess_sx=x;
chess_sy=y;
break;
}
if(chess_sx==-||chess_sy==-) {//起始坐标未赋值且点击的不是自己的棋子,则break
break;
}
chess_dx=x;
chess_dy=y;
if(chess_board->my_move_piece(chess_sx,chess_sy,chess_dx,chess_dy)) { //如果移动棋子合法
client->send_message("move",chess_sx,chess_sy,chess_dx,chess_dy); //向对方发送移子信息
InvalidateRect(hwnd,NULL,true);
if(chess_board->get_is_win()==WIN) {
chess_board->init();//重新初始化棋盘,重下一盘
MessageBox(hwnd,"你赢了","获胜!",NULL);
InvalidateRect(hwnd,NULL,true);
}
}
chess_sx=-;
chess_sy=-;
break;
}
case WM_DESTROY:
if(server!=NULL)server->close();
if(client!=NULL)client->close();
PostQuitMessage (); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
} return ;
}
chinese_chess.h
#ifndef CHINESE_CHESS_H_INCLUDED
#define CHINESE_CHESS_H_INCLUDED #define JU 1
#define MA 2
#define XIANG 3
#define SHI 4
#define JIANG 5
#define PAO 6
#define ZU 7
#define BLANK 8 //空子 #define BLACK -1
#define WHITE 1 #define WIN 1
#define LOSE -1
class Board {
private:
bool turn; //是否轮到自己下棋
int color; //自己的颜色
int M,N; //棋盘行数、列数
int **b; //二维数组
int is_win; //是否胜利 bool is_out(int x,int y) {//坐标是否出界
return x>M||y>N||x<||y<;
} bool is_same_color(int sx,int sy,int dx,int dy) {//源坐标与目的坐标是否是同一颜色
return get_color(sx,sy)==get_color(dx,dy);
}
void swap_num(int & num1,int& num2) {//交换两个数
num1+=num2;
num2=num1-num2;
num1=num1-num2;
}
int get_abs(int num) {//取得绝对值
return num>=?num:-num;
}
int num_of_not_blank_betweenn(int sx,int sy,int dx,int dy) {//返回在起始坐标和目的坐标之间棋子的个数
if(!(sx==dx||sy==dy))return -;
int num=;
if(sy>dy)swap_num(sy,dy);
if(sx>dx)swap_num(sx,dx);
if(sx==dx) {
for(int i=sy+; i<dy; i++) {
if(b[sx][i]!=BLANK)num++;
}
}
if(sy==dy) {
for(int i=sx+; i<dx; i++) {
if(b[i][sy]!=BLANK)num++;
}
}
return num;
}
bool is_correct_move_JU(int sx,int sy,int dx,int dy) {
return num_of_not_blank_betweenn(sx,sy,dx,dy)==;
}
bool is_correct_move_MA(int sx,int sy,int dx,int dy) {
int x=dx-sx,y=dy-sy;
if(get_abs(x)==&&get_abs(y)==) {
if(get_color(sx+x/,sy)==BLANK)return true;//硌马蹄检测
}
if(get_abs(x)==&&get_abs(y)==) {
if(get_color(sx,sy+y/)==BLANK)return true;//硌马蹄检测
}
return false;
}
bool is_correct_move_XIANG(int sx,int sy,int dx,int dy) {
int x=dx-sx,y=dy-sy;
if(!(get_abs(x)==&&get_abs(y)==)) return false;
if(get_color(sx+x/,sy+y/)==BLANK)return true;//硌象蹄检测
return false;
}
bool is_correct_move_SHI(int sx,int sy,int dx,int dy) {
int x=dx-sx,y=dy-sy;
if(!(get_abs(x)==&&get_abs(y)==)) return false;
if(dx<)return false;
if(dy<||dy>)return false;
return true;
}
bool is_correct_move_JIANG(int sx,int sy,int dx,int dy) {
int x=dx-sx,y=dy-sy;
if(!((get_abs(x)==&&get_abs(y)==)||(get_abs(x)==&&get_abs(y)==))) return false;
if(dx<)return false;
if(dy<||dy>)return false;
for(int i=; i<; i++) {//明将检测
if(get_type(i,dy)==JIANG) {
if(num_of_not_blank_betweenn(dx,dy,i,dy)==) return false;
return true;
}
}
return true;
}
bool is_correct_move_PAO(int sx,int sy,int dx,int dy) {
int n=get_color(dx,dy)==BLANK?:;
return num_of_not_blank_betweenn(sx,sy,dx,dy)==n;
}
bool is_correct_move_ZU(int sx,int sy,int dx,int dy) {
if(dx>sx)return false;
int x=dx-sx,y=dy-sy;
if(get_abs(x)+get_abs(y)!=)return false;
if(sx>&&get_abs(x)!=)return false;//过河前只能向前走
return true;
} bool is_correct_move(int sx,int sy,int dx,int dy) {
if(sx==dx&&sy==dy) {
return false;
}
if(is_out(sx,sy)||is_out(dx,dy)) {
return false;
}
if(get_color(sx,sy)!=color) {
return false;
}
if(is_same_color(sx,sy,dx,dy)) {
return false;
}
switch(get_type(sx,sy)) {
case JU:
return is_correct_move_JU(sx,sy,dx,dy);
case MA:
return is_correct_move_MA(sx,sy,dx,dy);
case XIANG:
return is_correct_move_XIANG(sx,sy,dx,dy);
case SHI:
return is_correct_move_SHI(sx,sy,dx,dy);
case JIANG:
return is_correct_move_JIANG(sx,sy,dx,dy);
case PAO:
return is_correct_move_PAO(sx,sy,dx,dy);
case ZU:
return is_correct_move_ZU(sx,sy,dx,dy);
default:
return false;
}
} void move_s_to_d(int sx,int sy,int dx,int dy) { //移动操作
if(get_type(dx,dy)==JIANG) { //如果目的棋子是将
if(get_color(dx,dy)==color)set_win(LOSE);//如果是自己的将,则输
else set_win(WIN);//如果是对方的将,则赢
}
b[dx][dy]=b[sx][sy];
b[sx][sy]=BLANK;
change_turn();
} void init_pieces() {
for(int i=; i<M; i+=M-) {//第一行和最后一行(即车马象士将士象马车)
for(int index=; index<N; index++) {
if(index<N/+)b[i][index]=index+;
else b[i][index]=N-index;
}
}
//卒所在的行
for(int index=; index<N; index+=) {
b[][index]=ZU;
}
for(int index=; index<N; index+=) {
b[][index]=ZU;
}
b[][]=PAO;
b[M--][]=PAO;
b[][N--]=PAO;
b[M--][N--]=PAO;
int s,d;//存储起始行和终点行
if(color==BLACK) {
s=;//从0行到M/2行,即棋盘上半部分
d=M/;
} else {
s=M/;//棋盘下半部分
d=M;
}
//从s行到d行,把非BLANK的值加BLANK,使小于BLANK的代表黑色棋,大于BLANK的代表白色棋
for(int index=s; index<d; index++) {
for(int j=; j<N; j++) {
if(b[index][j]!=BLANK) {
b[index][j]+=BLANK;
}
}
}
} public:
Board(int c) {
color=c;
M=;
N=;
b=new int*[M];
for(int i=; i<M; i++) {
b[i]=new int[N];
}
init();
}
void init() {//棋盘初始化
is_win=;
turn=color==BLACK?true:false;
for(int i=; i<M; i++) {
for(int j=; j<N; j++) {
b[i][j]=BLANK;
}
}
init_pieces();
}
int get_M() {
return M;
}
int get_N() {
return N;
}
int get_color() {//获取己方的颜色
return color;
}
int get_color(int x,int y) {//获取棋盘某一坐标上棋子的颜色
return b[x][y]>BLANK?WHITE:b[x][y]<BLANK?BLACK:BLANK;
}
int get_type(int x,int y) {//获取棋子类型(空、车、马、象、士、将、炮、卒)
return b[x][y]!=BLANK?b[x][y]%BLANK:BLANK;
}
void set_win(int is) {
is_win=is;
}
int get_is_win() {
return is_win;
}
void change_turn() {
turn=turn==true?false:true;
}
bool is_my_turn() {
return turn;
}
void othside_move_piece(int sx,int sy,int dx,int dy) {//对方移子
sx=M--sx;//先进行坐标转换,因对方视角与己方相反
sy=N--sy;
dx=M--dx;
dy=N--dy;
move_s_to_d(sx,sy,dx,dy);
}
bool my_move_piece(int sx,int sy,int dx,int dy) { //己方主动移子
if(!is_correct_move(sx,sy,dx,dy))return false;
move_s_to_d(sx,sy,dx,dy);
return true;
}
}; #endif // CHINESE_CHESS_H_INCLUDED
Server.h
#ifndef SERVER_H_INCLUDED
#define SERVER_H_INCLUDED
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <winsock2.h>
#include"chinese_chess.h" #define INIT_ERROR 1
#define BIND_ERROR 2
#define LISTEN_ERROR 3
#define CONNECT_ERROR 4
#define SEND_ERROR 5
#define ACCEPT_ERROR 6
#define ALIVE_ERROR 7
#define THREAD_ERROR 8
int error_num;
extern HWND hwnd;
extern Board* chess_board;
extern char* ots_ip;
extern int port;
extern bool is_connect_alive; //线程参数结构体
typedef struct server_args {
SOCKET* Com_Sock;
char * rebuf;
} server_args;
//校验检测,与客户端的添加校验是相反操作
//最后一位之前的所有字符相加取模后,如果等于最后一个字符,则校验通过
bool server_check(char * r) {
int len=strlen(r);
len--;
int s=;
for(int i=; i<len; i++) {
s+=r[i];
}
if(r[len]==(s%+'')) {
r[len]='\0';
return true;
}
return false;
}
//错误处理,is_tell控制是否显示错误信息,is_exit控制是否退出程序
int handle_error(int err,bool is_tell,bool is_exit) {
error_num=err;
if(!is_tell)return error_num;
char error[]="";
switch(error_num) {
case INIT_ERROR:
strcpy(error,"初始化错误");
break;
case BIND_ERROR:
strcpy(error,"绑定端口错误");
break;
case LISTEN_ERROR:
strcpy(error,"监听错误");
break;
case ACCEPT_ERROR:
strcpy(error,"接受连接错误");
break;
case CONNECT_ERROR:
strcpy(error,"无法连接");
break;
case ALIVE_ERROR:
strcpy(error,"连接已断开");
break;
case THREAD_ERROR:
strcpy(error,"线程无法创建");
break;
case SEND_ERROR:
strcpy(error,"发送错误");
}
char error_message[];
strcpy(error_message,"错误:");
strcat(error_message,error);
if(is_exit)strcat(error_message,"\n程序将退出。");
MessageBox(hwnd,error_message,"错误",MB_OK);
if(is_exit)exit();
return error_num;
}
void* handle_message(void*ar) {
server_args * serarg=(server_args * )ar;
char *recv=serarg->rebuf;
SOCKET* CommandSock=serarg->Com_Sock;
if(server_check(recv)) {//校验通过发送okok(OK),不通过发送noto(NOTOK)
send(*CommandSock,"okok",,);
} else {
send(*CommandSock,"noto",,);
return ar;
}
if(strncmp(recv,"move",)==) {
char * pch;
//将recvBuf以逗号拆分
pch = strtok (recv,",");
pch = strtok (NULL,",");
int xys[];
int index=;
while (pch != NULL) {
xys[index]=atoi(pch);//char* 转换为int
index++;
pch = strtok (NULL, ",");
}
chess_board->othside_move_piece(xys[],xys[],xys[],xys[]);
if(chess_board->get_is_win()==LOSE) {
chess_board->init();//如果输了,则重新初始化棋盘,再下一盘
MessageBox(hwnd,"你输了","失败!",NULL);
}
InvalidateRect(hwnd,NULL,true);
}
delete recv;
}
class Server {
private:
SOCKET Server_Sock;
SOCKADDR_IN server_addr;
SOCKADDR_IN client_addr;
char recvBuf[]; public:
Server() {
WSADATA wsa;
/*初始化socket资源*/
if (WSAStartup(MAKEWORD(,),&wsa) != ) {
handle_error(INIT_ERROR,true,true);
return;
} if((Server_Sock = socket(AF_INET, SOCK_STREAM, ))==-) {
handle_error(INIT_ERROR,true,true);
return;
}
ZeroMemory((char *)&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); /*本地监听端口*/
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*有IP*/ if(bind(Server_Sock,(struct sockaddr *)&server_addr,sizeof(server_addr))==-) {
handle_error(BIND_ERROR,true,true);
return;
}
if(listen(Server_Sock,)==-) { //其中第二个参数代表能够接收的最多的连接数
handle_error(LISTEN_ERROR,true,true);
return;
}
strcpy(recvBuf,"");
} void listen_message() {
int len=sizeof(SOCKADDR);
while(true) {
SOCKET Command_Sock = accept(Server_Sock, (SOCKADDR*)&client_addr,&len);
if(Command_Sock == INVALID_SOCKET) {
closesocket(Command_Sock);
handle_error(ACCEPT_ERROR,false,false);
continue;
}
if(client_addr.sin_addr.s_addr!=inet_addr(ots_ip)) {//如果接收的socket不是预期的对方的,则发送wron,继续等待
send(Command_Sock,"wron",,);
closesocket(Command_Sock);
continue;
}
send(Command_Sock,"righ",,);
is_connect_alive=true;
while(true) {
if(recv(Command_Sock,recvBuf,,)<=) {//recv返回小于等于0的值,则连接已断开
handle_error(ALIVE_ERROR,true,true);
closesocket(Command_Sock);
close();
return ;
}
char *rbuf=new char[];
strcpy(rbuf,recvBuf);
server_args serarg;
serarg.Com_Sock=&Command_Sock;
serarg.rebuf=rbuf;
pthread_t handle_m;
int ret;
ret= pthread_create( &handle_m, NULL, handle_message,&serarg); //
if( ret != ) { //创建线程成功返回0
// printf("pthread_create error:error_code=%d\n",ret );
handle_error(THREAD_ERROR,true,true);
return ;
}
strcpy(recvBuf,"");
}
closesocket(Command_Sock);
}
}
void close() {
closesocket(Server_Sock);
WSACleanup();
}
}; #endif // SERVER_H_INCLUDED
Client.h
#ifndef CLIENT_H_INCLUDED
#define CLIENT_H_INCLUDED #include <stdio.h>
#include <winsock2.h> #include"chinese_chess.h" //为字符串添加校验信息,对所有字符求和,模5之后转化为字符放在字符串最后
void client_check(char* r) {
int len=strlen(r);
int s=;
for(int i=; i<len; i++) {
s+=r[i];
}
r[len]=s%+'';
r[len+]='\0';
}
class Client {
private:
SOCKET Client_Sock;
SOCKADDR_IN server_addr;
char sendBuf[];
public:
Client() {
WSADATA wsa;
/*初始化socket资源*/
if (WSAStartup(MAKEWORD(,),&wsa) != ) {
handle_error(INIT_ERROR,true,true);
return; //代表失败
}
if((Client_Sock = socket(AF_INET, SOCK_STREAM, ))==-) {
handle_error(INIT_ERROR,true,true);
return; //代表失败
}
server_addr.sin_addr.S_un.S_addr=inet_addr(ots_ip);
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(port);
strcpy(sendBuf,"");
}
void connect_to_ots() {
while(connect(Client_Sock,(SOCKADDR*)&server_addr,sizeof(SOCKADDR)) ==-) {
handle_error(CONNECT_ERROR,false,false);
Sleep();
//printf( "%d ", WSAGetLastError());
}
char rec[];
recv(Client_Sock,rec,,);
if(strncmp(rec,"wron",)==) { //收到wrong,说明对方所输入的IP不是己方IP
MessageBox(hwnd,"对方输入的IP不是你\n程序将退出","错误",NULL);
exit(-);
}
//谁先连接谁是黑色
//如果server已经收到连接,则说明是对方先连接自己,则自己应为白色,否则自己是黑色
if(is_connect_alive) {
chess_board=new Board(WHITE);
} else {
chess_board=new Board(BLACK);
}
}
void close() {
closesocket(Client_Sock);
WSACleanup();
} int send_message(char * message) {
strcpy(sendBuf,message);
client_check(sendBuf);
int len;
int try_time=;
while(true) {
len=send(Client_Sock,sendBuf,strlen(sendBuf)+,);
if(len!=(strlen(sendBuf)+)) {
handle_error(SEND_ERROR,false,false);
//printf( "%d ", WSAGetLastError());
}
char rec[];
recv(Client_Sock,rec,,);
if(strncmp(rec,"okok",)==) {//收到OK说明数据已经正确被对方收到
break;
}
if(try_time>) { //尝试20次,数据仍无法正确送达,则退出
handle_error(SEND_ERROR,true,true);
}
try_time++;
}
return len;
}
int send_message(const char * message,int sx,int sy,int dx,int dy) {
char* message_temp=new char[];
sprintf(message_temp,"%s,%d,%d,%d,%d,",message,sx,sy,dx,dy);
int len=send_message(message_temp);
delete message_temp;
return len;
}
}; #endif // CLIENT_H_INCLUDED
该程序从2016.3.15晚开始,用了四天的空闲时间。