简单五子棋服务器

来源:互联网 发布:奥数 高斯算法 教师版 编辑:程序博客网 时间:2024/05/21 11:37

介绍

五子棋服务器主要完成两个功能:

  • 保存玩家请求战斗的信息,并为玩家匹配;
  • 匹配好了后直接为两玩家转发坐标数据即可。

五子棋网络对战功能:

  • 指定对手名称;
  • 由服务器随选择对手;

两种数据结构

  • 连接请求数据包

    // 游戏类型:随机玩家还是指定玩家名字enum GamePlayerType{    Nmae_Nobody,  // 用来说明没有游戏对手,链接超时    Name_Random,    Name_Named,};struct CertifyData{    GamePlayerType GameType;    bool ISFIRST;  // 是否先手    char myname[MaxNameLen];  // 自己的名字    char opponame[MaxNameLen];  // 对手名字};
  • 建立连接后转发的数据包

// 开始游戏后传输的坐标数据struct PositionData{    Point pos;};

采用四个线程来处理

  • 主线程专门负责监听连接,将玩家的信息大包,再根据相应的游戏类型保存到相应的队列中:

    // 存放指定对手的玩家map<string,PlayerInfo> NamedGame;// 存放随机对手的玩家list<PlayerInfo> RandomGame;
    • 保存的玩家信息数据结构为
      // 保存玩家信息与套接字struct PlayerInfo{    CertifyData info;    SOCKET sock;};
  • 线程DWORD WINAPI ProgressMap(LPVOID lpParm);专门处理指定对手的玩家
  • 线程DWORD WINAPI ProgressList(LPVOID lpParm);专门处理随机对战的玩家
  • 线程DWORD WINAPI GameRunThread(LPVOID lpParm);专门负责为这种对战的玩家转发消息,每一对玩家开辟一个线程(效率低,但简单实用);传递的参数的数据结构保持了两位玩家的信息和SOCKET号
    // 传递给玩家开始游戏后的线程数据结构struct GameRunParm{    PlayerInfo PlayerA;    PlayerInfo PlayerB;};

临界区

需要创建两个临界区,为对连个玩家队列的互斥访问

// 访问NameGame的临街区CRITICAL_SECTION cri_namedgame;// 访问RandomGame的临界区CRITICAL_SECTION cri_randomgame;

初始化资源

  • winsock资源的初始化

    WORD wVersionRequested;WSADATA wsaData;ZeroMemory(&wsaData,sizeof WSADATA);wVersionRequested = MAKEWORD(2,0);int re = WSAStartup(wVersionRequested,&wsaData);if(re != 0)    return;
  • 临界区的初始化

    // 临界区初始化InitializeCriticalSection(&cri_namedgame);InitializeCriticalSection(&cri_randomgame);

主循环

void main(){  WORD wVersionRequested;  WSADATA wsaData;  ZeroMemory(&wsaData,sizeof WSADATA);  wVersionRequested = MAKEWORD(2,0);  int re = WSAStartup(wVersionRequested,&wsaData);  if(re != 0)      return;  // 临界区初始化  InitializeCriticalSection(&cri_namedgame);  InitializeCriticalSection(&cri_randomgame);  SOCKET svrScok = socket(AF_INET,SOCK_STREAM,0);  SOCKADDR_IN addSvr;  addSvr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  addSvr.sin_family = AF_INET;  addSvr.sin_port = htons(SERVER_PORT);  bind(svrScok,(SOCKADDR*)&addSvr,sizeof(SOCKADDR));  re = listen(svrScok,MAX_CON);  assert(re != SOCKET_ERROR);  // 创建对map与list玩家列表处理的线程  MapProThread = CreateThread(NULL,0,ProgressMap,NULL,0,NULL);  ListProThread = CreateThread(NULL,0,ProgressList,NULL,0,NULL);  // 接收玩家连接  while(1)  {      cout<<"监听中..."<<endl;      SOCKADDR_IN cliAddr;      int len = sizeof(SOCKADDR_IN);      ZeroMemory(&cliAddr,len);      CertifyData revData;      memset(&revData,'\0',CrtDataSize);      // 阻塞接收连接      SOCKET cliSock = accept(svrScok,(SOCKADDR*)&cliAddr,&len);      assert(cliSock != INVALID_SOCKET);      // 接收玩家信息      recv(cliSock,(char*)&revData,CrtDataSize,0);      cout<<"玩家"<<revData.myname<<"连接建立成功"<<endl;      // 将玩家放入相应的列表里      PlayerInfo plyerInfo = {revData,cliSock};      string str  = revData.myname;      switch(revData.GameType)      {      case Name_Named:          EnterCriticalSection(&cri_namedgame);          NamedGame.insert(make_pair(str,plyerInfo));          LeaveCriticalSection(&cri_namedgame);          break;      case Name_Random:          EnterCriticalSection(&cri_randomgame);          RandomGame.push_back(plyerInfo);          LeaveCriticalSection(&cri_randomgame);          break;      }  }}

子线程

  • 指定对手

    DWORD WINAPI ProgressMap(LPVOID lpParm){  typedef map<string,PlayerInfo>::iterator mapItr;  do   {      // 每隔200ms处理一次      Sleep(200);      EnterCriticalSection(&cri_namedgame);      // 遍历map,查找指定对手      for (mapItr itr = NamedGame.begin();itr != NamedGame.end();)      {          string str = itr->second.info.opponame;          mapItr OppoItr = NamedGame.find(str);          // 如果找到对手          if (OppoItr != NamedGame.end())          {              // 大包玩家信息              GameRunParm runparm = {itr->second,OppoItr->second};              // 创建游戏线程              CreateThread(NULL,0,GameRunThread,&runparm,0,NULL);              // 从玩家列表中将两者删除              NamedGame.erase(itr++);              if(itr == OppoItr) ++itr;              NamedGame.erase(OppoItr++);              if(itr == OppoItr && OppoItr != NamedGame.end()) ++ OppoItr;          }          else              ++itr;      }  } while (1);  LeaveCriticalSection(&cri_namedgame);}
  • 随机对手

    DWORD WINAPI ProgressList(LPVOID lpParm){typedef list<PlayerInfo>::iterator listItr;do {    Sleep(200);    EnterCriticalSection(&cri_randomgame);    listItr plyA = RandomGame.end();    if (plyA != RandomGame.end())    {        listItr plyB = plyA;        ++plyB;        // 当两者都存在时        while(plyA != RandomGame.end() && plyB != RandomGame.end())        {            // 打包玩家信息            GameRunParm runparm = {*plyA,*plyB};            // 创建游戏线程            CreateThread(NULL,0,GameRunThread,&runparm,0,NULL);            // 删除对象            RandomGame.erase(plyA++);            RandomGame.erase(plyB++);            plyA = plyB;            if(plyB != RandomGame.end())                ++plyB;        }    }    LeaveCriticalSection(&cri_randomgame);} while (1);}
  • 正式游戏线程

    DWORD WINAPI GameRunThread(LPVOID lpParm){GameRunParm *plyInfo = (GameRunParm*)lpParm;SOCKET SockA = plyInfo->PlayerA.sock;SOCKET SockB = plyInfo->PlayerB.sock;// 首先返回确认信息给两位玩家PlayerInfo recvData;memset(&recvData,'\0',CrtDataSize);// 玩家ArecvData = plyInfo->PlayerA;recvData.info.ISFIRST = true;int re = send(SockA,(char*)&recvData,CrtDataSize,0);cout<<"server told to "<<recvData.info.myname<<" game begin,and you play first hand!"<<endl;// 玩家BrecvData = plyInfo->PlayerB;recvData.info.ISFIRST = false;re = send(SockB,(char*)&recvData,CrtDataSize,0);cout<<"server told to "<<recvData.info.myname<<" game begin,and you play second hand!"<<endl;// 进入循环开始游戏int PSize = sizeof(Point);while(1){    Point point = {-1,-1};    re = send(SockA,(char*)&point,PSize,0);    if (re == SOCKET_ERROR)    {        cout<<"game over"<<endl;        break;    }    send(SockB,(char*)&point,PSize,0);    cout<<"player "<<plyInfo->PlayerA.info.myname<<" send to player "        <<plyInfo->PlayerB.info.myname<<" position:( "<<point.x        <<" , "<<point.y<<" )"<<endl;    re = recv(SockB,(char*)&point,PSize,0);    if (re == SOCKET_ERROR)    {        cout<<"game over"<<endl;        break;    }    send(SockA,(char*)&point,PSize,0);    cout<<"player "<<plyInfo->PlayerB.info.myname<<" send to player "        <<plyInfo->PlayerA.info.myname<<" position:( "<<point.x        <<" , "<<point.y<<" )"<<endl;}// 关闭资源,线程负责关闭自己的接收socketre = shutdown(SockA,SD_SEND);re = shutdown(SockB,SD_SEND);Point data;while(send(SockA,(char*)&data,PSize,0) > 0);while(send(SockB,(char*)&data,PSize,0) > 0);closesocket(SockA);closesocket(SockB);cout<<"connection "<<plyInfo->PlayerA.info.myname<<" exit."<<endl;cout<<"connection "<<plyInfo->PlayerB.info.myname<<" exit."<<endl;return 0;}
0 0
原创粉丝点击