要构建网络版象棋,首先应该创建服务器与客户端,建立socket连接
1) 开局,你是什么颜色
2)选择棋子,
3)走棋
4)悔棋(悔棋悔两步)
5)认输
网络实现:
1)建立连接
a.主机,建立监听socket
b.accept
c.关闭监听socket
a.客户端,连接服务器
2)发送报文
1)服务器给客户端发送开局报文
2)选棋 发送选棋报文
3)走棋 发送走棋报文
4)悔棋
5)认输 重新开始
报文格式定义:
第一个字节表示命令字 0表示开具,1表示选其,2表示走棋,3表示悔棋,4表示认输
开局报文 [0][0或者1] 收到0表示走黑棋,收到1表示走红旗 [0][0] [0][1]
选棋报文 [1][0~31]
走棋报文 [2][0~31][x][y] x和y已经转换好的
悔棋 [3]
认输 [4][0或者1]
运行时设计
定义一个全局变量 char packet[4]用来保存报文
定义一个定时器,每秒去收socket
#ifndef _Net_H__
#define _Net_H__
#ifdef WIN32
#include <WinSock2.h>
#else
//linux and android
#endif // WIN32
class Net
{
public:
static SOCKET _server;
static SOCKET _connet;
static int _isConnected;
static int _isRecvComplete;
static char* _recvData;
static bool Listen(short port=9999);
static bool isConnected();
static bool Connect(const char*ip,short port=9999);
static int Send(const char*buffer,int len);
//接收数据的接口
static bool RecvStart();
static bool isRecvComplete();
static char *RecvData(int &len);
static DWORD WINAPI AcceptThreadFunc(void *arg);
static DWORD WINAPI RecvThreadFunc(void *arg);
};
#endif
首先,服务器需要listen函数,一直监听,以得到与客户端的连接
bool Net::Listen(short port)
{
SOCKET sock=socket(AF_INET,SOCK_STREAM,0);
if (sock==INVALID_SOCKET)
{
return false;
}
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.S_un.S_addr=INADDR_ANY;
int ret=bind(sock,(struct sockaddr*)&addr,sizeof(addr));
if (ret != 0)
{
closesocket(sock);
return false;
}
listen(sock,10); //10 means listen count
//create a therad to accept socket
_server = sock;
_isConnected = 0;
HANDLE hThread = CreateThread(NULL, 0, AcceptThreadFunc, NULL, 0, NULL);
CloseHandle(hThread);
return true;
}
DWORD Net::AcceptThreadFunc(void *arg)
{
_connet = accept(_server, NULL, NULL);
_isConnected = 1;
return 0;
}
客户端则需要connect函数与服务器建立连接
bool Net::Connect(const char*ip,short port)
{
_connet=socket(AF_INET,SOCK_STREAM,0);
if (_connet==INVALID_SOCKET)
{
return false;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.S_un.S_addr = inet_addr(ip);
int ret = connect(_connet, (struct sockaddr*)&addr, sizeof(addr));
if (ret != 0)
{
closesocket(_connet);
return false;
}
return true;
}
如果建立连接成功,则可以互相传递数据了,此时服务器可以关闭连接监听
void SceneGame::startServer(CCObject*)
{
_bredSide=true;
Net::Listen();
schedule(schedule_selector(SceneGame::CheckListen));
}
void SceneGame::CheckListen(float)
{
if (Net::isConnected())
{
unschedule(schedule_selector(SceneGame::CheckListen));
//game start and do some initiate game
CCLog("server start game\n");
}
}
服务器与客户端互相发送数据,需要send函数
int Net::Send(const char*buffer,int len)
{
return send(_connet,buffer,len,0);
}
接收数据需要receve函数保持监听
bool Net::RecvStart()
{
_isRecvComplete=0;
HANDLE hThread=CreateThread(NULL,0,RecvThreadFunc,NULL,0,NULL);
CloseHandle(hThread);
return true;
}
bool Net::isRecvComplete()
{
return _isRecvComplete;
}
char *Net::RecvData(int &len)
{
len=0;
_isRecvComplete=0;
return _recvData;
}
DWORD Net::RecvThreadFunc(void *arg)
{
static char buf[16];
recv(_connet,buf,1,0);
if (buf[0]==1)
{
recv(_connet,&buf[1],1,0);
}else if(buf[0]==2)
{
for (int i=1;i<=3;i++)
{
recv(_connet,&buf[i],1,0);
}
}
//stop receve
_recvData=buf;
_isRecvComplete=1;
return 0;
}
在象棋中调用发送数据,发送完毕之后轮到对方走了,此时要打开接收数据监听
//send step
Step* step = *_steps.rbegin();
char buf[4];
buf[0] = 2;
buf[1] = step->moveid;
buf[2] = step->rowTo;
buf[3] = step->colTo;
Net::Send(buf, 4);
//receive message
Net::RecvStart();
schedule(schedule_selector(SceneGame::CheckRecv));
接收到数据之后要关闭接收监听,然后开始发送数据,象棋根据传递的报文判断对方的信息,分别以1,2,3开头,表示选择,走棋,其中1与3还需要继续接收数据,2可以关闭数据接收。
void SceneGame::CheckRecv(float)
{
if (Net::isRecvComplete())
{
unschedule(schedule_selector(SceneGame::CheckRecv));
int len;
char *data=Net::RecvData(len);
//accord to the data protocoal do some work
if (data[0]==1)
{
//selected
_selectid=data[1];
_selectSprite->setPosition(_s[_selectid]->fromPlate());
_selectSprite->setVisible(true);
//continue receive
Net::RecvStart();
schedule(schedule_selector(SceneGame::CheckRecv));
}
else if(data[0]==2)
{
//move stone
Stone* s = _s[data[1]];
int row = 9 - data[2];
int col = 8 - data[3];
int killid = getStoneFromRowCol(row, col);
recordStep(_selectid, killid, _s[_selectid]->_row, _s[_selectid]->_col, row, col);
// 移动棋子
s->_row = row;
s->_col = col;
s->setPosition(s->fromPlate());
if (killid != -1)
{
Stone* ks = _s[killid];
ks->_dead = true;
ks->setVisible(false);
}
_selectid = -1;
_selectSprite->setVisible(false);
_bRedTurn = !_bRedTurn;
}else if(data[0]==3)
{
doRegret2();
//continue receive
Net::RecvStart();
schedule(schedule_selector(SceneGame::CheckRecv));
}
}
}