介绍
五子棋服务器主要完成两个功能:
- 保存玩家请求战斗的信息,并为玩家匹配;
- 匹配好了后直接为两玩家转发坐标数据即可。
五子棋网络对战功能:
- 指定对手名称;
- 由服务器随选择对手;
两种数据结构
- // 开始游戏后传输的坐标数据
- 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,NULL);
- ListProThread = CreateThread(NULL,ProgressList,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,GameRunThread,&runparm,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,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,CrtDataSize);
- // 玩家A
- recvData = plyInfo->PlayerA;
- recvData.info.ISFIRST = true;
- int re = send(SockA,(char*)&recvData,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,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,0);
- cout<<"player "<<plyInfo->PlayerA.info.myname<<" send to player "
- <<plyInfo->PlayerB.info.myname<<" position:( "<<point.x
- <<","<<point.y<<" )"<<endl;
- re = recv(SockB,0);
- if (re == SOCKET_ERROR)
- {
- cout<<"game over"<<endl;
- break;
- }
- send(SockA,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,0) > 0);
- while(send(SockB,0) > 0);
- closesocket(SockA);
- closesocket(SockB);
- cout<<"connection "<<plyInfo->PlayerA.info.myname<<" exit."<<endl;
- cout<<"connection "<<plyInfo->PlayerB.info.myname<<" exit."<<endl;
- return 0;
- }