windows网络版象棋的实现

要构建网络版象棋,首先应该创建服务器与客户端,建立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
");


        }
    }

服务器与客户端互相发送数据,需要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));
            }


        }
    }
原文地址:https://www.cnblogs.com/jjx2013/p/6223761.html