Files
mir_server/Gateway/gateway/client/client.cc
aixianling 5c9f1dae4a init
2025-01-09 17:45:40 +08:00

400 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}