您的位置:首页 > 其它

简单五子棋服务器

2015-07-22 23:29 357 查看

介绍

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


保存玩家请求战斗的信息,并为玩家匹配;

匹配好了后直接为两玩家转发坐标数据即可。



五子棋网络对战功能:


指定对手名称;

由服务器随选择对手;



两种数据结构


连接请求数据包


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


建立连接后转发的数据包



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


采用四个线程来处理

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


[code]// 存放指定对手的玩家
map<string,PlayerInfo> NamedGame;
// 存放随机对手的玩家
list<PlayerInfo> RandomGame;


保存的玩家信息数据结构为

[code]// 保存玩家信息与套接字
struct PlayerInfo
{
    CertifyData info;
    SOCKET sock;
};


线程
DWORD WINAPI ProgressMap(LPVOID lpParm);
专门处理指定对手的玩家

线程
DWORD WINAPI ProgressList(LPVOID lpParm);
专门处理随机对战的玩家

线程
DWORD WINAPI GameRunThread(LPVOID lpParm);
专门负责为这种对战的玩家转发消息,每一对玩家开辟一个线程(效率低,但简单实用);传递的参数的数据结构保持了两位玩家的信息和SOCKET号

[code]// 传递给玩家开始游戏后的线程数据结构
struct GameRunParm
{
    PlayerInfo PlayerA;
    PlayerInfo PlayerB;
};




临界区

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


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


初始化资源

winsock资源的初始化


[code]WORD wVersionRequested;
WSADATA wsaData;
ZeroMemory(&wsaData,sizeof WSADATA);
wVersionRequested = MAKEWORD(2,0);
int re = WSAStartup(wVersionRequested,&wsaData);
if(re != 0)
    return;


临界区的初始化


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




主循环

[code]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;
      }
  }
}


子线程

指定对手

[code]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);
}


随机对手

[code]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);
}


正式游戏线程

[code]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);
// 玩家A
recvData = 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;
// 玩家B
recvData = 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;
}
// 关闭资源,线程负责关闭自己的接收socket
re = 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: