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
#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:
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()
{
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()
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()
{
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()
{
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
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
#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:
bool portmap(int nLocalPort, const std::string &strServerIp, int nServerPort);
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)
{
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
char buf[64] = {0};
#ifdef __GNUC__
snprintf(
#else
_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.参考