This commit is contained in:
aixianling
2025-01-09 17:45:40 +08:00
commit 5c9f1dae4a
3482 changed files with 1146531 additions and 0 deletions

View File

@@ -0,0 +1,399 @@
#include "StdAfx.h"
#include "client.h"
#include "base/sha1.h"
#include "base64.h"
BaseAllocator Client::alloc("ClientAlloc");
Client::Client(GameClientMgr *cli_mgr, SOCKET s, sockaddr_in* addr) {
shake_hands_ = false;
fd_ = s;
cli_mgr_ = cli_mgr;
send_packet_ = new DataPacket(&alloc);
send_packet_->reserve(SEND_PACKET_MAX);
send_packet_->setLength(0);
memcpy(&remote_addr_, addr, sizeof(sockaddr_in));
net_id_.socket_ = s;
conn_status_ = ConnStatusDef::CS_CONNED;
skey_ = 0;
is_close_ = false;
real_ip[0] = 0;
}
Client ::~Client() {
for (auto it : recv_list_) {
SafeDelete(it);
}
SafeDelete(send_packet_);
recv_list_.clear();
for (auto it : gw_recv_list_) {
SafeDelete(it);
}
real_ip[0] = 0;
}
int Client::SetBlockMode(const bool block) {
unsigned long flag = block ? 0 : 1;
#ifdef _MSC_VER
return ioctlsocket(fd_, FIONBIO, &flag);
#else
return ioctl(fd_, FIONBIO, &flag);//改为非阻塞;
#endif
}
void Client::RecvData(void) {
if (is_close_) return;
static char buf[1024];
while (true) {
int buflen = recv(fd_, buf, sizeof(buf), 0);
if (buflen < 0) {
// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读
// 在这里就当作是该次事件已处理
#ifdef _MSC_VER
if (WSAGetLastError() == WSAECONNABORTED) {
Close();
return;
} else if (WSAGetLastError() == WSAEWOULDBLOCK) {
#else
if (WSAGetLastError() == EINPROGRESS || WSAGetLastError() == EAGAIN) {
#endif
break;
} else {
MSG_ERR("not handle errno:[%d]%s", WSAGetLastError(), strerror(WSAGetLastError()));
Close();
return;
}
} else if (buflen == 0) {
// 这里表示对端的socket已正常关闭.
Close();
return;
}
if (shake_hands_) {
DataPacket* dp = new DataPacket(&alloc);
dp->writeBuf(buf, buflen);
dp->setPosition(0);
recv_list_.push_back(dp);
} else {
//进行websocket握手
ShakeHandsHandle(buf, buflen);
return;
}
}
}
bool Client::SendData(void) {
size_t splen = send_packet_->getAvaliableLength();
if (splen <= 0) { return true; }
int lsed = send(fd_, send_packet_->getOffsetPtr(), (int)splen, 0);
if(lsed <= 0) { return true; }
if (lsed < splen) {
send_packet_->setPosition(send_packet_->getPosition() + lsed);
return false;
} else {
send_packet_->setLength(0);
}
return true;
}
void Client::WriteEvent(void) {
if (is_close_) return;
DataPacket* dp = NULL;
while (true) {
if (gw_recv_list_.empty()) { break; }
dp = *(gw_recv_list_.begin());
gw_recv_list_.pop_front();
FlushWsPack(dp->getOffsetPtr(), dp->getAvaliableLength());
SafeDelete(dp);
}
if(send_packet_->getAvaliableLength() > 0) {
cli_mgr_->HaveData(this);
}
}
void Client::Flush(const char *buf, size_t len) {
if (send_packet_->getAvaliableLength() + len > SEND_PACKET_MAX) {
MSG_ERR("send packet is max, fd:%d", fd_);
Close();
return;
} else {
size_t pos = send_packet_->getPosition();
send_packet_->setPosition(pos + send_packet_->getAvaliableLength());
send_packet_->writeBuf(buf, len);
send_packet_->setPosition(pos);
}
}
void Client::SendNewSession(void) {
static GameWorldClient* wgc = cli_mgr_->GetGameWorldClient();
DataPacket &dp = wgc->AllocProtoPacket(GM_OPEN,net_id_.socket_,net_id_.index_,net_id_.gate_id_);
if (real_ip[0] != 0)
dp.writeBuf(real_ip, 32);
else
dp.writeBuf(inet_ntoa(remote_addr_.sin_addr), 32);
wgc->flushProtoPacket(dp);
OutputMsg(rmTip, _T("[Login] (2) 握手成功通知LogicServer创建一个玩家Gate gatekey(%d)socket(%lld)GateIdx(%d)LogicIdx(%d)CurrentThreadId(%d)。"),
skey_, net_id_.socket_, net_id_.index_, net_id_.gate_id_, GetCurrentThreadId());
}
void Client::SendSessionClose(void) {
static GameWorldClient* wgc = cli_mgr_->GetGameWorldClient();
DataPacket &dp = wgc->AllocProtoPacket(GM_CLOSE,net_id_.socket_,net_id_.index_,net_id_.gate_id_);
wgc->flushProtoPacket(dp);
}
void Client::Close(void) {
MSG_ERR("close client, fd:%d,index:%d", net_id_.socket_, net_id_.index_);
cli_mgr_->CloseClient(this);
SendSessionClose();
is_close_ = true;
}
#define WEB_SOCKET_HANDS_RE "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n"
#define MAGIC_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
void Client::ShakeHandsHandle(const char* buf, int buflen) {
char key[512]; memset(key, 0, 512);
for (int i = 0; i < buflen; ++i)
{
if (FindHttpParam("Sec-WebSocket-Key", buf+i)) {
short k = i + 17, ki = 0;
while(*(buf + k) != '\r' && *(buf + k) != '\n' && *(buf + k) != '\0') {
if (*(buf + k) == ':' || *(buf + k) == ' '){
++k; continue;
} else {
key[ki++] = *(buf + k);
}
++k;
}
break;
}
}
memset(real_ip, 0, sizeof(real_ip));
for (int i = 0; i < buflen; ++i)
{
if (FindHttpParam("X-Real-IP", buf+i)) {
short k = i + 9, ki = 0;
while(*(buf + k) != '\r' && *(buf + k) != '\n' && *(buf + k) != '\0') {
if (*(buf + k) == ':' || *(buf + k) == ' '){
++k; continue;
} else {
real_ip[ki++] = *(buf + k);
}
++k;
}
break;
}
}
//MSG_LOG("key:%s...", key);
memcpy(key + strlen(key), MAGIC_KEY, sizeof(MAGIC_KEY));
//MSG_LOG("megerkey:%s...", key);
//求哈希1
SHA1 sha;
unsigned int message_digest[5];
sha.Reset();
sha << key;
sha.Result(message_digest);
for (int i = 0; i < 5; i++) {
message_digest[i] = htonl(message_digest[i]);
}
memset(key, 0, 512);
base64_encode(key, reinterpret_cast<const char*>(message_digest), 20);
char http_res[512] = "";
sprintf(http_res, WEB_SOCKET_HANDS_RE, key);
Flush(http_res, strlen(http_res));
//MSG_LOG("res:%s...",http_res);//fkYTdNEVkParesYkrM4S
SendData();
shake_hands_ = true;
SendNewSession();//发送到gameworld新增连接
}
void Client::PacketHandle(void) {
if (is_close_) return;
while (!recv_list_.empty()) {
auto dpit = recv_list_.begin();
DataPacket* dp = *dpit;
//读取websocket固定包头
if (!ws_head_.rh) {
//这个包不够一个头部的大小
if (dp->getAvaliableLength() < 2) {
if (MergePacketList(dpit)) continue;
else break;
}
//读取
uint8_t head = 0;
dp->readBuf(&head, 1);
ws_head_.fin = head >> 7;
ws_head_.opcode = head & 0xF;
dp->readBuf(&head, 1);
ws_head_.len = head & 0x7F;
ws_head_.mask = head >> 7;
ws_head_.rh = 1;//标记头部读取完成
}
//读取长度
if (!ws_head_.rl) {
uint8_t nsize = ws_head_.GetLenNeedByte();
if (nsize) {
//这个包不够一个长度
if (dp->getAvaliableLength() < nsize) {
if (MergePacketList(dpit)) continue;
else break;
}
if (nsize == 2) {
(*dp) >> ws_head_.ex_len.v16;
ws_head_.ex_len.v16 = ntohs(ws_head_.ex_len.v16);
} else {
(*dp) >> ws_head_.ex_len.v32;
ws_head_.ex_len.v32 = ntohl((u_long)ws_head_.ex_len.v32);
}
}
ws_head_.rl = 1;
}
//读取MKEY
if (!ws_head_.rk) {
if (ws_head_.mask) {
//这个包不够一个key
if (dp->getAvaliableLength() < 4) {
if (MergePacketList(dpit)) continue;
else break;
}
(*dp) >> ws_head_.mkey[0];
(*dp) >> ws_head_.mkey[1];
(*dp) >> ws_head_.mkey[2];
(*dp) >> ws_head_.mkey[3];
}
ws_head_.rk = 1;
}
//读取数据段
uint64_t data_len = ws_head_.GetLen();
if (dp->getAvaliableLength() < data_len) {
if (MergePacketList(dpit)) continue;
else break;
}
if (ws_head_.mask) {
char* dptr = dp->getOffsetPtr();
for (size_t i = 0; i < data_len; ++i) {
dptr[i] = dptr[i] ^ ws_head_.mkey[i % 4];
}
}
if (!OnRecv(dp->getOffsetPtr(), data_len)) {
return;
}
ws_head_.reset();
dp->adjustOffset(data_len);
//这个包读取完了
if (dp->getAvaliableLength() <= 0) {
recv_list_.pop_front();
SafeDelete(dp);
}
}
}
bool Client::OnRecv(const char* buf, size_t size) {
if (conn_status_ == CS_COMMUNICATE) {
if (ws_head_.opcode == OPCODE_CLR) {
Close();
return false;
}
if (size < 4 + 2) {
MSG_ERR("size is min");
return true;
}
//截去key
unsigned short tag = *((unsigned short*)buf);
if (tag != skey_) {
Close();
return false;
}
char* pdata = (char*)buf + sizeof(unsigned short);
size -= 2;
//截去验证
uint32_t check = *((uint32_t*)pdata);
pdata = pdata + sizeof(uint32_t);
size -= 4;
//发送给游戏服
static GameWorldClient* wgc = cli_mgr_->GetGameWorldClient();
DataPacket &pack = wgc->AllocProtoPacket(GM_DATA,net_id_.socket_,net_id_.index_,net_id_.gate_id_);
#ifdef _DEBUG
const unsigned char* proto = pdata;
OutputMsg(rmTip, "<[sock:%d,idx:%d] Recv(%d,%d)", fd_, GetSessionId(), *proto, *(proto+1));
#endif
pack.writeBuf(pdata, size);
wgc->flushProtoPacket(pack);
//MSG_ERR("size is [%d + 4 + 2]", size);
return true;
}
if (conn_status_ == CS_CONNED) { //刚连接上来
uint32_t tag = *((uint32_t*)buf);
if (tag != DEFAULT_TAG) {
Close();
return false;
}
skey_ = wrand(SHRT_MAX);
FlushWsPack((char *)&skey_, 2);
SendData();
conn_status_ = CS_COMMUNICATE;
}
return true;
}
void Client::FlushWsPack(const char* buf, size_t size) {
static DataPacket pack(&alloc);
pack.setLength(0);
pack << (uint8_t)0x82;//写头部
//写长度
if (size >= 126) {//7位放不下
if (size <= 0xFFFF) {//16位放
pack << (uint8_t)126;
pack << (uint16_t)htons((u_short)size);
} else {//64位放
pack << (uint8_t)127;
pack << (uint64_t)OrderSwap64(size);
}
} else {
pack << (uint8_t)size;
}
//写数据
pack.writeBuf(buf, size);
pack.setPosition(0);
Flush(pack.getOffsetPtr(), pack.getAvaliableLength());
}
void Client::OnGameWorldRecv(const char* buf, size_t size) {
DataPacket *dp = new DataPacket(&alloc);
(*dp) << (uint16_t)skey_;
#ifdef _DEBUG
const unsigned char* proto = buf;
OutputMsg(rmTip, ">[sock:%d,idx:%d] Send(%d,%d)", fd_, GetSessionId(), *proto, *(proto+1));
#endif
dp->writeBuf(buf, size);
dp->setPosition(0);
gw_recv_list_.push_back(dp);
}
bool Client::MergePacketList(std::list<DataPacket*>::iterator &dpit) {
auto sdpit = dpit++;
if (dpit == recv_list_.end()) return false;
DataPacket* sdp = *sdpit;
//合并第二个到第一个
sdp->writeBuf((*dpit)->getOffsetPtr(), (*dpit)->getAvaliableLength());
SafeDelete((*dpit));
recv_list_.erase(dpit);
return true;
}
bool Client::FindHttpParam(const char * param, const char * buf) {
while (*param == *buf) {
if (*(param + 1) == '\0') return true;
++param; ++buf;
}
return false;
}

View File

@@ -0,0 +1,106 @@
#pragma once
#include <list>
#define SEND_PACKET_MAX 1024*1024*8
#define DEFAULT_TAG 0xCA0FFFFF
enum WebSocketOpcode { //操作码定义类型
OPCODE_MID = 0x0,//标识一个中间数据包
OPCODE_TXT = 0x1,//标识一个text类型数据包
OPCODE_BIN = 0x2,//标识一个binary类型数据包
//0x3 - 7保留
OPCODE_CLR = 0x8,//标识一个断开连接类型数据包
OPCODE_PIN = 0x9,//标识一个ping类型数据包
OPCODE_PON = 0xA,//表示一个pong类型数据包
};
#pragma pack(push,1)
struct WebSocketHead {
uint8_t fin : 1;//标识是否为此消息的最后一个数据包
uint8_t rsv1 : 1;//保留位1
uint8_t rsv2 : 1;//保留位2
uint8_t rsv3 : 1;//保留位3
uint8_t opcode : 4;//操作码
uint8_t mask : 1; //是否需要掩码
uint8_t len : 7;//长度
union {
uint16_t v16;//长度为126时
uint32_t v32;//长度为127时
} ex_len;
uint8_t mkey[4];
uint8_t rh : 1;//head读取完成
uint8_t rl : 1;//len读取完成
uint8_t rk : 1;//mkey读取完成
uint8_t rs : 5;//扩展保留
WebSocketHead(void) { reset(); }
void reset(void) { memset(this,0,sizeof(WebSocketHead)); }
inline uint32_t GetLen(void) {
if (len == 126) {
return ex_len.v16;
} else if (len == 127) {
return ex_len.v32;
}
return len;
}
inline uint8_t GetLenNeedByte(void) {
if (len == 126) {
return 2;
} else if (len == 127) {
return 8;
}
return 0;
}
};
#pragma pack(pop)
enum ConnStatusDef {
CS_CONNED,//连接上
CS_COMMUNICATE,//通信状态
};
typedef std::list<DataPacket*> DataPacketList;
class Client {
public:
Client(GameClientMgr *cli_mgr, SOCKET s, sockaddr_in* addr);
~Client();
//设置阻塞模式。true表示阻塞false表示非阻塞返回值为socket错误号0表示成功
int SetBlockMode(const bool block);
void SetSessionId(const uint16_t sid) { net_id_.index_ = sid; }
uint16_t GetSessionId(void) { return net_id_.index_; }
NetId &GetNetId(void) { return net_id_; }
void RecvData(void);
bool SendData(void);
void WriteEvent(void);
void Flush(const char *buf, size_t len);
void SendNewSession(void);
void SendSessionClose(void);
void Close(void);
bool OnRecv(const char* buf, size_t size);
void FlushWsPack(const char* buf, size_t size);
void OnGameWorldRecv(const char* buf, size_t size);
inline SOCKET GetFd(void) { return fd_; }
inline bool IsClose(void) { return is_close_; }
void PacketHandle(void);
uint16_t GetSKey() { return skey_; }
protected:
void ShakeHandsHandle(const char* buf, int buflen);
private:
inline bool MergePacketList(std::list<DataPacket*>::iterator &dpit);
bool FindHttpParam(const char* param, const char* buf);
bool shake_hands_;//是否已经握手
SOCKET fd_; //套接字
bool is_close_; //是否已经关闭连接
sockaddr_in remote_addr_;//远程地址
DataPacketList recv_list_;//收到的包列表
DataPacket* send_packet_;//需要发包的缓冲区
DataPacketList gw_recv_list_;//从游戏服收到的包列表
WebSocketHead ws_head_;//包头
NetId net_id_;
uint8_t conn_status_;//连接状态
uint16_t skey_;//服务端KEY
static BaseAllocator alloc;
GameClientMgr* cli_mgr_;
char real_ip[64];
};

View File

@@ -0,0 +1,67 @@
#include "StdAfx.h"
#include "client_obj_mgr.h"
#include <map>
ClientObjMgr::ClientObjMgr() {
cur_idx_ = 0;
max_session_ = 0;
}
ClientObjMgr::~ClientObjMgr() {
flush();
for (auto cli:client_list_) {
SafeDelete(cli.second);
}
client_list_.clear();
}
void ClientObjMgr::SetMaxSession(int max_session) {
max_session_ = max_session;
}
bool ClientObjMgr::setNewClient(Client *cli) {
client_lock_.Lock();
if (max_session_ <= cur_idx_) return false;
uint16_t index = 0;
if (free_cli_idx_.empty()) {
index = cur_idx_++;
} else {
index = free_cli_idx_.front();
free_cli_idx_.pop_front();
}
cli->SetSessionId(index);
client_append_list_.push_back(cli);
client_lock_.Unlock();
return true;
}
void ClientObjMgr::flush(void) {
client_lock_.Lock();
for (auto it:client_append_list_) {
client_list_.insert(std::make_pair(it->GetSessionId(), it));
}
client_append_list_.clear();
client_lock_.Unlock();
}
Client* ClientObjMgr::get(uint16_t index) {
auto it = client_list_.find(index);
if (it == client_list_.end()) return NULL;
return it->second;
}
Client * ClientObjMgr::remove(uint16_t index) {
auto it = client_list_.find(index);
if (it == client_list_.end()) return NULL;
Client *cli = it->second;
client_list_.erase(it);
client_lock_.Lock();
if (index == cur_idx_ - 1) {
--cur_idx_;
} else {
free_cli_idx_.push_back(index);
}
client_lock_.Unlock();
return cli;
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <map>
class ClientObjMgr {
public:
ClientObjMgr();
~ClientObjMgr();
void SetMaxSession(int max_session);
bool setNewClient(Client *cli);
void flush(void);
std::map<uint16_t, Client*> &getClientMap(void) {
return client_list_;
}
Client* get(uint16_t index);
Client* remove(uint16_t index);
private:
uint16_t max_session_;//最大客户端连接数
uint16_t cur_idx_;
std::list<uint16_t> free_cli_idx_;//空闲的客户端索引
std::vector<Client*> client_append_list_;
Mutex client_lock_; //客户端列表锁给append_list用
std::map<uint16_t, Client*> client_list_;
};