您的位置:首页 > 编程语言 > C语言/C++

游戏服务器防护的一种实现方法(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/5667190

2.http://www.cnblogs.com/Hybird3D/archive/2012/02/04/2337652.html

3.实例下载地址:http://download.csdn.net/detail/zhang_ruiqiang/9808787
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息