游戏服务器防护的一种实现方法(IOCP)
2017-04-09 22:29
363 查看
1.前言
目前市面上的服务器防护软件大部分都使用节点转发来实现,我们第一期产品也采用简单的单节点转发,代码比较简单。但是如果玩家端连接的节点被攻击,玩家必须重新启动切换新的节点后才能正常操作,体验很不好,所以第二期对代码进行了重构,独创“一王多后”模式,实现当前所连节点被攻击后,自动切换为下一个节点,且游戏不会掉线。
2.解决方案
一王多后要满足如下要求:
1.A节点被攻击后,A节点和同域内节点通信正常2.A节点被攻击后,A节点和游戏服务器通信正常
我们假设现在有A节点(王),B节点(后),C节点(后),D节点(后)
创建通道如下:
1.玩家—A节点—游戏服务器2.玩家—B节点—A节点
3.玩家—C节点—A节点
4.玩家—D节点—A节点
当A节点被攻击无法连接时,玩家端自动切换附加线路,如玩家—B节点—A节点—游戏服务器
3.代码实现
节点端部分代码:
multinode.h/* * Copyright (c) 2017,宁波云户信息科技有限公司 * All rights reserved. * * 文件名称: multinode.h * 文件标识: * 摘 要: 节点端通过IOCP实现高效数据转发 * * 当前版本: 1.0.0.1 * 作 者: 张瑞强 * 完成日期: 2017年04月08日 * 修改日期: 2017年04月09日 */ #pragma once #include "myiocp.h" #include "mydefine.h" #include <map> #include <string> #include <vector> class CMULTINODE : public CMYIOCP { public: typedef struct stSocketGuid{ SOCKET server; std::string strGuid; }S_SOCKET_GUID; typedef struct stNodeData{ std::string strNodeIp; int nNodePort; std::string strServerIp; int nServerPort; std::string strTransNodeIp; int nTransNodePort; std::map<SOCKET, SOCKET> mapLocalNode; std::map<std::string, SOCKET> mapGuidLocal; }S_NODE_DATA; typedef struct stIpPort{ std::string strip; int nport; }S_IP_PORT; typedef struct stMsgInfo{ std::string strheader; std::vector<S_IP_PORT> vecall; std::string stroldguid; std::string strnewguid; }S_MSG_INFO; public: CMULTINODE(); ~CMULTINODE(); public: /* * 函数介绍:创建本地监听 * 输入参数:nLocalPort 本地监听端口 * 输出参数: * 返 回 值:true 成功 * false 失败 */ bool portmap(int nLocalPort); private: void OnAccept(SOCKET server, SOCKET client); void OnRead(SOCKET s, char *buf, int len); void OnClose(SOCKET s); private: std::string Msg2String(const S_MSG_INFO &msginfo); bool String2Msg(const std::string &strmsg, S_MSG_INFO &msginfo); private: S_NODE_DATA m_stNodeData; S_NODE_DATA m_stTransNodeData; };
multinode.cpp
#include "multinode.h" CMULTINODE::CMULTINODE() { } CMULTINODE::~CMULTINODE() { } bool CMULTINODE::portmap(int nLocalPort) { bool bret = false; if (nLocalPort > 0) { SOCKET server; bret = CMYIOCP::Listen(nLocalPort, server); } return bret; } void CMULTINODE::OnAccept(SOCKET server, SOCKET local) { if (local != INVALID_SOCKET) { char szbuf[1024] = {0}; recv(local, szbuf, 1024, 0); if (strlen(szbuf) > 0) { S_MSG_INFO msginfo; if (String2Msg(szbuf, msginfo)) { if (msginfo.strheader.compare(gstrSendHeader) == 0 && msginfo.vecall.size() == 1) { SOCKET game_server; if (CMYIOCP::Connect((char *)msginfo.vecall[0].strip.c_str(), msginfo.vecall[0].nport, game_server)){ m_stNodeData.mapLocalNode[local] = game_server; m_stNodeData.mapGuidLocal[msginfo.stroldguid] = local; //printf("OnAccept local:%d guid:%s.\r\n", local, msginfo.stroldguid.c_str()); } }else if (msginfo.strheader.compare(gstrTransHeader) == 0 && msginfo.vecall.size() == 2){ SOCKET node; if (CMYIOCP::Connect((char *)msginfo.vecall[0].strip.c_str(), msginfo.vecall[0].nport, node)){ m_stNodeData.mapLocalNode[local] = node; m_stNodeData.mapGuidLocal[msginfo.stroldguid] = local; S_MSG_INFO passinfo; passinfo.strheader = gstrPassHeader; passinfo.stroldguid = msginfo.stroldguid; passinfo.strnewguid = msginfo.strnewguid; std::string strsendinfo = Msg2String(passinfo); send(node, strsendinfo.c_str(), strsendinfo.size(), 0); } }else if (msginfo.strheader.compare(gstrPassHeader) == 0){ //这里只是信息的记录没什么用的 //m_stTransNodeData.mapLocalNode[local] = local; m_stTransNodeData.mapGuidLocal[msginfo.stroldguid+msginfo.strnewguid] = local; //printf("gstrPassHeader oldguid:%s newguid:%s.\r\n", msginfo.stroldguid.c_str(), msginfo.strnewguid.c_str()); }else if (msginfo.strheader.compare(gstrChangeHeader) == 0 && msginfo.vecall.size() == 1){ //这里要发送到代理节点上去 SOCKET node; if (CMYIOCP::Connect((char *)msginfo.vecall[0].strip.c_str(), msginfo.vecall[0].nport, node)){ S_MSG_INFO passinfo; passinfo.strheader = gstrSwitchHeader; passinfo.strnewguid = msginfo.strnewguid; passinfo.stroldguid = msginfo.stroldguid; std::string strsendinfo = Msg2String(passinfo); send(node, strsendinfo.c_str(), strsendinfo.size(), 0); //printf("gstrChangeHeader oldguid:%s newguid:%s.\r\n", msginfo.stroldguid.c_str(), msginfo.strnewguid.c_str()); } }else if (msginfo.strheader.compare(gstrSwitchHeader) == 0){ //std::string stroldguid = msginfo.stroldguid; //printf("gstrSwitchHeader oldguid:%s newguid:%s.\r\n", msginfo.stroldguid.c_str(), msginfo.strnewguid.c_str()); SOCKET old_local = m_stNodeData.mapGuidLocal[msginfo.stroldguid]; SOCKET new_local = m_stTransNodeData.mapGuidLocal[msginfo.stroldguid+msginfo.strnewguid]; if (old_local > 0 && new_local > 0) { SOCKET old_server = m_stNodeData.mapLocalNode[old_local]; if (old_server > 0) { //这里要同步的否则可能会有问题哦 m_stNodeData.mapLocalNode.erase(old_local); m_stNodeData.mapLocalNode[new_local] = old_server; m_stNodeData.mapGuidLocal[msginfo.stroldguid] = new_local; //printf("gstrSwitchEnd oldguid:%s newguid:%s.\r\n", msginfo.stroldguid.c_str(), msginfo.strnewguid.c_str()); } } } }else { printf("failed in String2Msg.\r\n"); } } } } void CMULTINODE::OnRead(SOCKET s, char *buf, int len) { if (s != INVALID_SOCKET && buf != NULL && len > 0) { SOCKET node = m_stNodeData.mapLocalNode[s]; if (node > 0) { char szbuf[8192] = {0}; memcpy(szbuf, buf, len); send(node, szbuf, len, 0); return; } else { for (std::map<SOCKET, SOCKET>::iterator it = m_stNodeData.mapLocalNode.begin(); it != m_stNodeData.mapLocalNode.end(); ++it) { if (it->second == s) { char szbuf[8192] = {0}; memcpy(szbuf, buf, len); send(it->first, szbuf, len, 0); return; } } } } } void CMULTINODE::OnClose(SOCKET s) { auto delguid = [&](SOCKET local){ for (std::map<std::string, SOCKET>::iterator it = m_stNodeData.mapGuidLocal.begin(); it != m_stNodeData.mapGuidLocal.end();++it){ if (it->second == local) { m_stNodeData.mapGuidLocal.erase(it); break; } } }; SOCKET node = m_stNodeData.mapLocalNode[s]; if (node > 0) { delguid(s); closesocket(node); m_stNodeData.mapLocalNode.erase(s); //printf("OnClose local:%d.\r\n", s); } else { for (std::map<SOCKET, SOCKET>::iterator it = m_stNodeData.mapLocalNode.begin(); it != m_stNodeData.mapLocalNode.end(); ++it) { if (it->second == s) { //printf("OnClose local:%d.\r\n", it->first); delguid(it->first); closesocket(it->first); m_stNodeData.mapLocalNode.erase(it); break; } } } } #include "enc_dec.h" std::string CMULTINODE::Msg2String(const S_MSG_INFO &msginfo) { std::string strmsg = msginfo.strheader; for (std::vector<S_IP_PORT>::const_iterator it = msginfo.vecall.begin(); it != msginfo.vecall.end(); ++it) { strmsg += CENCDEC::new_ip_encode(it->strip); char szport[64] = {0}; sprintf(szport, "%05d", it->nport); strmsg += CENCDEC::new_port_encode(szport); } strmsg += msginfo.stroldguid; strmsg += msginfo.strnewguid; return strmsg; } bool CMULTINODE::String2Msg(const std::string &strmsg, S_MSG_INFO &msginfo) { bool bret = false; if (!strmsg.empty()) { msginfo.strheader = strmsg.substr(0, 4); std::string strallinfo = strmsg.substr(4); if (strallinfo.size() >= 32) { msginfo.stroldguid = strallinfo.substr(strallinfo.size() - 64, 32); msginfo.strnewguid = strallinfo.substr(strallinfo.size() - 32, 32); std::string strallip = strallinfo.substr(0, strallinfo.size() - 64); if (!strallip.empty() && strallip.size() % 17 == 0) { int ncount = strallip.size() / 17; for (int nindex = 0; nindex < ncount; ++nindex){ std::string strencip = strallip.substr(nindex * 17, 12); std::string strencport = strallip.substr(nindex * 17 + 12, 5); S_IP_PORT stport; stport.strip = CENCDEC::new_ip_decode(strencip); stport.nport = atoi(CENCDEC::new_port_decode(strencport).c_str()); msginfo.vecall.push_back(stport); } } bret = true; } } return bret; }
客户端部分代码:
mymultiportmap.h/* * Copyright (c) 2017,宁波云户信息科技有限公司 * All rights reserved. * * 文件名称: mymultiportmap.h * 文件标识: 客户端创建映射 * 摘 要: 客户端创建映射 * * 当前版本: 1.0.0.1 * 作 者: 张瑞强 * 完成日期: 2017年04月08日 * 修改日期: 2017年04月09日 */ #pragma once #include "myiocp.h" #include "mydefine.h" #include <vector> #include <map> class CMYMULTIPORTMAP : public CMYIOCP { public: typedef struct stNodeData{ std::string strNodeIp; int nNodePort; std::string strServerIp; int nServerPort; std::string strTransNodeIp; int nTransNodePort; std::map<SOCKET, SOCKET> mapLocalNode; std::map<SOCKET, std::string> mapNodeOldGuid; std::map<SOCKET, std::string> mapNodeNewGuid; }S_NODE_DATA; typedef struct stIpPort{ std::string strip; int nport; }S_IP_PORT; typedef struct stMsgInfo{ std::string strheader; std::vector<S_IP_PORT> vecall; std::string stroldguid; std::string strnewguid; }S_MSG_INFO; public: CMYMULTIPORTMAP(); ~CMYMULTIPORTMAP(); public: /* * 函数介绍:创建和节点端映射 * 输入参数:nLocalPort 本地监听端口 * strServerIp 游戏服务器ip地址 * nServerPort 游戏服务器端口 * 输出参数:bool * 返 回 值:true 成功 * false 失败 */ bool portmap(int nLocalPort, const std::string &strServerIp, int nServerPort); /* * 函数介绍:添加节点 * 输入参数:strNodeIp 节点地址 * nNodePort 节点端口号 * 输出参数: * 返 回 值: */ void insertnode(const std::string &strNodeIp, int nNodePort); /* * 函数介绍:切换节点 * 输入参数: * 输出参数: * 返 回 值: */ void changenode(); public: void OnAccept(SOCKET server, SOCKET client); void OnRead(SOCKET s, char *buf, int len); void OnClose(SOCKET s); private: std::string getGuid(); std::string Msg2String(const S_MSG_INFO &msginfo); bool String2Msg(const std::string &strmsg, S_MSG_INFO &msginfo); private: S_NODE_DATA m_stNodeData; std::vector<S_NODE_DATA*> m_vecNodeData; std::map<SOCKET, S_SERVER_INFO*> m_mapSocketInfo; int m_nNodeIndex; //这是当前切换的序号 std::string m_strGameIp; int m_nGamePort; };
mymultiportmap.cpp
#include "stdafx.h" #include "mymultiportmap.h" #include <Objbase.h> #include "enc_dec.h" #pragma comment(lib, "Ole32.lib") CMYMULTIPORTMAP::CMYMULTIPORTMAP() { m_nNodeIndex = 0; m_strGameIp = ""; } CMYMULTIPORTMAP::~CMYMULTIPORTMAP() { CMYIOCP::m_workThread = false; CMYIOCP::m_acceptThread = false; for (std::map<SOCKET, S_SERVER_INFO*>::iterator it = m_mapSocketInfo.begin(); it != m_mapSocketInfo.end(); ++it){ closesocket(it->first); delete it->second; } for (std::map<SOCKET, SOCKET>::iterator it = m_stNodeData.mapLocalNode.begin(); it != m_stNodeData.mapLocalNode.end(); ++it){ closesocket(it->first); } for (std::vector<S_NODE_DATA *>::iterator it = m_vecNodeData.begin(); it != m_vecNodeData.end(); ++it){ S_NODE_DATA *pdata = *it; for (std::map<SOCKET, SOCKET>::iterator itmap = pdata->mapLocalNode.begin(); itmap != pdata->mapLocalNode.end(); ++itmap){ closesocket(itmap->second); } delete pdata; } m_vecNodeData.clear(); } bool CMYMULTIPORTMAP::portmap(int nLocalPort, const std::string &strServerIp, int nServerPort) { bool bret = false; if (nLocalPort > 0 && !strServerIp.empty() && nServerPort > 0) { SOCKET server; if (CMYIOCP::Listen(nLocalPort, server)) { char szinfo[256] = {0}; sprintf(szinfo, "%s:%d", strServerIp.c_str(), nServerPort); S_SERVER_INFO *pstInfo = new S_SERVER_INFO; pstInfo->strSendInfo = szinfo; pstInfo->strServerIp = strServerIp; pstInfo->nServerPort = nServerPort; m_mapSocketInfo[server] = pstInfo; bret = true; } } return bret; } void CMYMULTIPORTMAP::OnAccept(SOCKET server, SOCKET local) { SOCKET node = m_stNodeData.mapLocalNode[local]; if (node <= 0) { //SOCKET node; S_SERVER_INFO *pstInfo = (S_SERVER_INFO *)m_mapSocketInfo[server]; if (pstInfo != NULL) { std::string stroldguid = getGuid(); if (CMYIOCP::Connect((char *)m_stNodeData.strNodeIp.c_str(), m_stNodeData.nNodePort, node)) { m_stNodeData.mapLocalNode[local] = node; m_stNodeData.mapNodeOldGuid[node] = stroldguid; m_stNodeData.mapNodeNewGuid[node] = stroldguid; S_MSG_INFO msginfo; msginfo.stroldguid = stroldguid; msginfo.strnewguid = stroldguid; msginfo.strheader = gstrSendHeader; S_IP_PORT ip_node; ip_node.strip = pstInfo->strServerIp; ip_node.nport = pstInfo->nServerPort; msginfo.vecall.push_back(ip_node); std::string strsendinfo = Msg2String(msginfo); send(node, strsendinfo.c_str(), strsendinfo.size(), 0); } int nsize = m_vecNodeData.size(); for (int nindex = 0; nindex < nsize; ++nindex){ S_NODE_DATA *pdata = m_vecNodeData[nindex]; if (CMYIOCP::Connect((char *)(pdata->strNodeIp.c_str()), pdata->nNodePort, node)){ std::string strguid = getGuid(); S_MSG_INFO msginfo; msginfo.strheader = gstrTransHeader; msginfo.stroldguid = stroldguid; msginfo.strnewguid = strguid; S_IP_PORT ip_node; ip_node.strip = m_stNodeData.strNodeIp; ip_node.nport = m_stNodeData.nNodePort; msginfo.vecall.push_back(ip_node); S_IP_PORT ip_server; ip_server.strip = pstInfo->strServerIp; ip_server.nport = pstInfo->nServerPort; msginfo.vecall.push_back(ip_server); std::string strsendinfo = Msg2String(msginfo); pdata->mapLocalNode[local] = node; pdata->mapNodeOldGuid[node] = stroldguid; pdata->mapNodeNewGuid[node] = strguid; pdata->strServerIp = pstInfo->strServerIp; pdata->nServerPort = pstInfo->nServerPort; pdata->strTransNodeIp = m_stNodeData.strNodeIp; pdata->nTransNodePort = m_stNodeData.nNodePort; send(node, strsendinfo.c_str(), strsendinfo.size(), 0); } } } } } void CMYMULTIPORTMAP::OnRead(SOCKET s, char *buf, int len) { if (s != INVALID_SOCKET && buf != NULL && len > 0) { SOCKET node = m_stNodeData.mapLocalNode[s]; if (node > 0) { char szbuf[8192] = {0}; memcpy(szbuf, buf, len); send(node, szbuf, len, 0); return; } else { for (std::map<SOCKET, SOCKET>::iterator it = m_stNodeData.mapLocalNode.begin(); it != m_stNodeData.mapLocalNode.end(); ++it) { if (it->second == s) { char szbuf[8192] = {0}; memcpy(szbuf, buf, len); send(it->first, szbuf, len, 0); return; } } } } } void CMYMULTIPORTMAP::OnClose(SOCKET s) { SOCKET node = m_stNodeData.mapLocalNode[s]; if (node > 0) { closesocket(node); m_stNodeData.mapLocalNode.erase(s); } else { for (std::map<SOCKET, SOCKET>::iterator it = m_stNodeData.mapLocalNode.begin(); it != m_stNodeData.mapLocalNode.end(); ++it) { if (it->second == s) { closesocket(it->first); m_stNodeData.mapLocalNode.erase(it); break; } } } } void CMYMULTIPORTMAP::changenode() { if (!m_vecNodeData.empty()) { if (m_nNodeIndex + 1 >= m_vecNodeData.size()) m_nNodeIndex = 0; else m_nNodeIndex += 1; S_NODE_DATA *pdata = m_vecNodeData[m_nNodeIndex]; if (pdata != NULL) { //这里没有乱换阵容的时候 要求直接返回 否则有问题的. if (pdata->strNodeIp.compare(m_stNodeData.strNodeIp) == 0 && pdata->nNodePort == m_stNodeData.nNodePort) return; for (std::map<SOCKET, std::string>::iterator it = pdata->mapNodeOldGuid.begin(); it != pdata->mapNodeOldGuid.end(); ++it) { S_MSG_INFO msginfo; msginfo.strheader = gstrChangeHeader; msginfo.stroldguid = it->second; msginfo.strnewguid = pdata->mapNodeNewGuid[it->first]; S_IP_PORT ip_server; ip_server.strip = pdata->strTransNodeIp; ip_server.nport = pdata->nTransNodePort; msginfo.vecall.push_back(ip_server); std::string strsendinfo = Msg2String(msginfo); SOCKET node; if (CMYIOCP::Connect((char *)pdata->strNodeIp.c_str(), pdata->nNodePort, node)) send(node, strsendinfo.c_str(), strsendinfo.size(), 0); } if (m_stNodeData.strNodeIp.compare(m_strGameIp) == 0 && m_stNodeData.nNodePort == m_nGamePort) m_stNodeData = *pdata; else { S_NODE_DATA tempdata = *pdata; *pdata = m_stNodeData; m_stNodeData = tempdata; } } } } void CMYMULTIPORTMAP::insertnode(const std::string &strNodeIp, int nNodePort) { if (m_stNodeData.strNodeIp.empty()) { m_stNodeData.strNodeIp = strNodeIp; m_stNodeData.nNodePort = nNodePort; m_strGameIp = strNodeIp; m_nGamePort = nNodePort; } else { S_NODE_DATA *pdata = new S_NODE_DATA; pdata->nNodePort = nNodePort; pdata->strNodeIp = strNodeIp; m_vecNodeData.push_back(pdata); } } std::string CMYMULTIPORTMAP::getGuid() { GUID guid; #ifdef WIN32 CoCreateGuid(&guid); #else uuid_generate(reinterpret_cast<unsigned char *>(&guid)); #endif //return guid; char buf[64] = {0}; #ifdef __GNUC__ snprintf( #else // MSVC _snprintf_s( #endif buf, sizeof(buf), "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); return std::string(buf); } std::string CMYMULTIPORTMAP::Msg2String(const S_MSG_INFO &msginfo) { std::string strmsg = msginfo.strheader; for (std::vector<S_IP_PORT>::const_iterator it = msginfo.vecall.begin(); it != msginfo.vecall.end(); ++it) { strmsg += CENCDEC::new_ip_encode(it->strip); char szport[64] = {0}; sprintf(szport, "%05d", it->nport); strmsg += CENCDEC::new_port_encode(szport); } strmsg += msginfo.stroldguid; strmsg += msginfo.strnewguid; return strmsg; } bool CMYMULTIPORTMAP::String2Msg(const std::string &strmsg, S_MSG_INFO &msginfo) { bool bret = false; if (!strmsg.empty()) { msginfo.strheader = strmsg.substr(0, 4); std::string strallinfo = strmsg.substr(4); if (strallinfo.size() >= 64) { msginfo.stroldguid = strallinfo.substr(strallinfo.size() - 64, 32); msginfo.strnewguid = strallinfo.substr(strallinfo.size() - 32, 32); std::string strallip = strallinfo.substr(0, strallinfo.size() - 64); if (!strallip.empty() && strallip.size() % 17 == 0) { int ncount = strallip.size() / 17; for (int nindex = 0; nindex < ncount; ++nindex){ std::string strencip = strallip.substr(nindex * 17, 12); std::string strencport = strallip.substr(nindex * 17 + 12, 5); S_IP_PORT stport; stport.strip = CENCDEC::new_ip_decode(strencip); stport.nport = atoi(CENCDEC::new_port_decode(strencport).c_str()); msginfo.vecall.push_back(stport); } } bret = true; } } return bret; }
4.参考
1.http://blog.csdn.net/sky04/article/details/56671902.http://www.cnblogs.com/Hybird3D/archive/2012/02/04/2337652.html
3.实例下载地址:http://download.csdn.net/detail/zhang_ruiqiang/9808787
相关文章推荐
- 基于Java代码实现游戏服务器生成全局唯一ID的方法汇总
- EasyDarwin开源流媒体服务器中一种实现对作用域内new对象自动释放的方法(值得借鉴)
- EasyDarwin开源流媒体服务器中一种实现对作用域内new对象自动释放的方法(值得借鉴)
- Unity 全局游戏脚本的一种实现方法
- Android自定义“图片+文字”控件实现方法之 --------个人最推荐的一种
- opencv 之一种二值图像实现方法
- 实现游戏在3秒钟打开的三种方法
- 一种2D游戏引擎的设计与实现
- unity 实现炸弹人放炸弹后只进不出的一种方法
- 一种基于im客户端实现消息图片管理的方法和装置
- 实现对gridview删除行时弹出确认对话框的一种简单方法
- 游戏架构 一种经典的服务器架构
- 企业实现服务器负载均衡常见的四种方法
- 对象语义的一种实现方法
- Unix与Windows共享资源的一种实现方法
- Apache服务器中使用.htaccess实现伪静态URL的方法
- C语言实现多态的一种方法
- C# 两种方法实现HTTP协议迷你服务器
- python基于pygame实现响应游戏中事件的方法(附源码)
- Windows7装上Windows8双系统的一种实现方法