简单五子棋服务器
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; }
相关文章推荐
- Codeforces Amr and Chemistry(数学+乱搞)
- 2015072201 - 越来越不像技术博客
- 沙盒获得根目录
- Upload your app to IonicView
- Nginx 笔记与总结(2)信号控制
- zabbix 的安装
- [原创] Zookeeper注册节点的掉线自动重新注册及测试方法
- C++析构函数,This指针,函数链
- 数据库中的悲观锁和乐观锁详解
- LeetCode Single Number II 单元素2
- com.opensymphony.xwork2.inject.DependencyException: com.opensymphony.xwork2.inject.ContainerImpl$Mis
- 用java实现 小孩围圈数三退一游戏
- 多重背包 HDU2191
- Tr A
- Shell练习-计算一下所有进程所占用内存大小的和
- 互联网营销、高管创业、一夜成名,“小米式”创业能改变世界?
- C语言声明一览
- 各种极致的树莓派玩法
- QGraphicsView 重写drawBackGround刷新问题
- Linux与Windows的不同