kbengine中使用了udp跟tcp两种传输方式,udp使用的是可靠udp,也就是kcp,这里我们分析一下kcp是怎么植入到引擎中
1.channel初始化kcp接口
bool Channel::init_kcp()
{
static IUINT32 convID = 1;
// 防止溢出,理论上正常使用不会用完
KBE_ASSERT(convID != 0);
if(id_ == 0)
id_ = convID++;
pKCP_ = ikcp_create((IUINT32)id_, (void*)this);
pKCP_->output = &Channel::kcp_output;
// 配置窗口大小:平均延迟200ms,每20ms发送一个包,
// 而考虑到丢包重发,设置最大收发窗口为128
int sndwnd = this->isExternal() ? Network::g_rudp_extWritePacketsQueueSize : Network::g_rudp_intWritePacketsQueueSize;
int rcvwnd = this->isExternal() ? Network::g_rudp_extReadPacketsQueueSize : Network::g_rudp_intReadPacketsQueueSize;
// nodelay-启用以后若干常规加速将启动
// interval为内部处理时钟,默认设置为 10ms
// resend为快速重传指标,设置为2
// nc为是否禁用常规流控,这里禁止
int nodelay = Network::g_rudp_nodelay ? 1 : 0;
int interval = Network::g_rudp_tickInterval;
int resend = Network::g_rudp_missAcksResend;
int disableNC = (!Network::g_rudp_congestionControl) ? 1 : 0;
int minrto = Network::g_rudp_minRTO;
if (this->isExternal() && Network::g_rudp_mtu > 0 && Network::g_rudp_mtu < (PACKET_MAX_SIZE_UDP * 4))
{
ikcp_setmtu(pKCP_, Network::g_rudp_mtu);
}
else
{
uint32 mtu = PACKET_MAX_SIZE_UDP - 72;
if(pKCP_->mtu != mtu)
ikcp_setmtu(pKCP_, mtu);
}
ikcp_wndsize(pKCP_, sndwnd, rcvwnd);
ikcp_nodelay(pKCP_, nodelay, interval, resend, disableNC);
pKCP_->rx_minrto = minrto;
pKCP_->writelog = &Channel::kcp_writeLog;
/*
pKCP_->logmask |= (IKCP_LOG_OUTPUT | IKCP_LOG_INPUT | IKCP_LOG_SEND | IKCP_LOG_RECV | IKCP_LOG_IN_DATA | IKCP_LOG_IN_ACK |
IKCP_LOG_IN_PROBE | IKCP_LOG_IN_WINS | IKCP_LOG_OUT_DATA | IKCP_LOG_OUT_ACK | IKCP_LOG_OUT_PROBE | IKCP_LOG_OUT_WINS);
*/
hasSetNextKcpUpdate_ = false;
addKcpUpdate();
return true;
}
2.channel中sendto进行发数据
void Channel::sendto(bool reliable, Bundle* pBundle)
{
//..........
if (pPacketSender_ == NULL)
{
//KCP
pPacketSender_ = KCPPacketSender::createPoolObject(OBJECTPOOL_POINT);
pPacketSender_->pEndPoint(pEndPoint_);
pPacketSender_->pNetworkInterface(pNetworkInterface_);
}
else
{
//TCP
if (pPacketSender_->type() != PacketSender::UDP_PACKET_SENDER)
{
TCPPacketSender::reclaimPoolObject((TCPPacketSender*)pPacketSender_);
pPacketSender_ = KCPPacketSender::createPoolObject(OBJECTPOOL_POINT);
pPacketSender_->pEndPoint(pEndPoint_);
pPacketSender_->pNetworkInterface(pNetworkInterface_);
}
}
pPacketSender_->processSend(this, reliable ? 1 : 0);
sendCheck(bundleSize);
}
3.kcp发送数据
bool UDPPacketSender::processSend(Channel* pChannel, int userarg)
{
//..........
Packet* pPacket = (*iter1);
reason = processPacket(pChannel, pPacket, userarg);
//..........
return true;
}
//
Reason KCPPacketSender::processFilterPacket(Channel* pChannel, Packet * pPacket, int userarg)
{
//......
if (ikcp_waitsnd(pChannel->pKCP()) > (int)(pChannel->pKCP()->snd_wnd * 2)/* 发送队列超出发送窗口2倍则提示资源不足 */ ||
ikcp_send(pChannel->pKCP(), (const char*)pPacket->data(), pPacket->length()) < 0)
{
ERROR_MSG(fmt::format("KCPPacketSender::ikcp_send: send error! currPacketSize={}, ikcp_waitsnd={}, snd_wndsize={}\n",
pPacket->length(), ikcp_waitsnd(pChannel->pKCP()), pChannel->pKCP()->snd_wnd));
return REASON_RESOURCE_UNAVAILABLE;
}
//......
}
4.kcp接收数据
Reason KCPPacketReceiver::processPacket(Channel* pChannel, Packet * pPacket)
{
if (pChannel != NULL && pChannel->hasHandshake())
{
pChannel->addKcpUpdate();
if (ikcp_input(pChannel->pKCP(), (const char*)pPacket->data(), pPacket->length()) < 0)
{
RECLAIM_PACKET(pPacket->isTCPPacket(), pPacket);
return REASON_CHANNEL_LOST;
}
RECLAIM_PACKET(pPacket->isTCPPacket(), pPacket);
while (true)
{
Packet* pRcvdUDPPacket = UDPPacket::createPoolObject(OBJECTPOOL_POINT);
int bytes_recvd = ikcp_recv(pChannel->pKCP(), (char*)pRcvdUDPPacket->data(), pRcvdUDPPacket->size());
if (bytes_recvd < 0)
{
//WARNING_MSG(fmt::format("KCPPacketReceiver::processPacket(): recvd_bytes({}) <= 0! addr={}\n", bytes_recvd, pChannel->c_str()));
RECLAIM_PACKET(pRcvdUDPPacket->isTCPPacket(), pRcvdUDPPacket);
return REASON_SUCCESS;
}
else
{
if (bytes_recvd >= (int)pRcvdUDPPacket->size())
{
ERROR_MSG(fmt::format("KCPPacketReceiver::processPacket(): recvd_bytes({}) >= maxBuf({})! addr={}\n", bytes_recvd, pRcvdUDPPacket->size(), pChannel->c_str()));
}
pRcvdUDPPacket->wpos(bytes_recvd);
Reason r = PacketReceiver::processPacket(pChannel, pRcvdUDPPacket);
if (r != REASON_SUCCESS)
{
RECLAIM_PACKET(pRcvdUDPPacket->isTCPPacket(), pRcvdUDPPacket);
return r;
}
}
}
}
else
{
return PacketReceiver::processPacket(pChannel, pPacket);
}
return REASON_SUCCESS;
}