mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-28 13:01:31 +00:00
[Performance] Significantly Improve Client Network Resends
This commit is contained in:
parent
2b9d248aec
commit
b171572631
@ -44,7 +44,12 @@ void EQ::Net::DaybreakConnectionManager::Attach(uv_loop_t *loop)
|
||||
DaybreakConnectionManager *c = (DaybreakConnectionManager*)handle->data;
|
||||
c->UpdateDataBudget();
|
||||
c->Process();
|
||||
c->ProcessResend();
|
||||
auto now = Clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - c->m_last_resend_check).count() > c->m_resend_check_interval) {
|
||||
c->ProcessResend();
|
||||
c->m_last_resend_check = now;
|
||||
return;
|
||||
}
|
||||
}, update_rate, update_rate);
|
||||
|
||||
uv_udp_init(loop, &m_socket);
|
||||
@ -1091,69 +1096,72 @@ void EQ::Net::DaybreakConnection::ProcessResend()
|
||||
}
|
||||
}
|
||||
|
||||
// observed client receive window is 300 packets, 140KB
|
||||
constexpr size_t MAX_CLIENT_RECV_PACKETS_PER_WINDOW = 300;
|
||||
constexpr size_t MAX_CLIENT_RECV_BYTES_PER_WINDOW = 140 * 1024;
|
||||
|
||||
void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
||||
{
|
||||
if (m_status == DbProtocolStatus::StatusDisconnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto resends = 0;
|
||||
auto now = Clock::now();
|
||||
auto s = &m_streams[stream];
|
||||
for (auto &entry : s->sent_packets) {
|
||||
auto time_since_last_send = std::chrono::duration_cast<std::chrono::milliseconds>(now - entry.second.last_sent);
|
||||
if (entry.second.times_resent == 0) {
|
||||
if ((size_t)time_since_last_send.count() > entry.second.resend_delay) {
|
||||
auto &p = entry.second.packet;
|
||||
if (p.Length() >= DaybreakHeader::size()) {
|
||||
if (p.GetInt8(0) == 0 && p.GetInt8(1) >= OP_Fragment && p.GetInt8(1) <= OP_Fragment4) {
|
||||
m_stats.resent_fragments++;
|
||||
}
|
||||
else {
|
||||
m_stats.resent_full++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_stats.resent_full++;
|
||||
}
|
||||
m_stats.resent_packets++;
|
||||
if (m_streams[stream].sent_packets.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
InternalBufferedSend(p);
|
||||
entry.second.last_sent = now;
|
||||
entry.second.times_resent++;
|
||||
entry.second.resend_delay = EQ::Clamp(entry.second.resend_delay * 2, m_owner->m_options.resend_delay_min, m_owner->m_options.resend_delay_max);
|
||||
resends++;
|
||||
m_resend_packets_sent = 0;
|
||||
m_resend_bytes_sent = 0;
|
||||
|
||||
auto now = Clock::now(); // Current time
|
||||
auto s = &m_streams[stream];
|
||||
|
||||
// Get a reference resend delay (assume first packet represents the typical case)
|
||||
size_t reference_resend_delay = 0;
|
||||
if (!s->sent_packets.empty()) {
|
||||
reference_resend_delay = s->sent_packets.begin()->second.resend_delay;
|
||||
|
||||
// Check if the first packet has timed out
|
||||
auto time_since_first_sent = std::chrono::duration_cast<std::chrono::milliseconds>(now - s->sent_packets.begin()->second.first_sent).count();
|
||||
if (time_since_first_sent >= m_owner->m_options.resend_timeout) {
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &e: s->sent_packets) {
|
||||
if (m_resend_packets_sent >= MAX_CLIENT_RECV_PACKETS_PER_WINDOW ||
|
||||
m_resend_bytes_sent >= MAX_CLIENT_RECV_BYTES_PER_WINDOW) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto &sp = e.second;
|
||||
auto &p = sp.packet;
|
||||
if (p.Length() >= DaybreakHeader::size()) {
|
||||
if (p.GetInt8(0) == 0 && p.GetInt8(1) >= OP_Fragment && p.GetInt8(1) <= OP_Fragment4) {
|
||||
m_stats.resent_fragments++;
|
||||
}
|
||||
else {
|
||||
m_stats.resent_full++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto time_since_first_sent = std::chrono::duration_cast<std::chrono::milliseconds>(now - entry.second.first_sent);
|
||||
if (time_since_first_sent.count() >= m_owner->m_options.resend_timeout) {
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((size_t)time_since_last_send.count() > entry.second.resend_delay) {
|
||||
auto &p = entry.second.packet;
|
||||
if (p.Length() >= DaybreakHeader::size()) {
|
||||
if (p.GetInt8(0) == 0 && p.GetInt8(1) >= OP_Fragment && p.GetInt8(1) <= OP_Fragment4) {
|
||||
m_stats.resent_fragments++;
|
||||
}
|
||||
else {
|
||||
m_stats.resent_full++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_stats.resent_full++;
|
||||
}
|
||||
m_stats.resent_packets++;
|
||||
|
||||
InternalBufferedSend(p);
|
||||
entry.second.last_sent = now;
|
||||
entry.second.times_resent++;
|
||||
entry.second.resend_delay = EQ::Clamp(entry.second.resend_delay * 2, m_owner->m_options.resend_delay_min, m_owner->m_options.resend_delay_max);
|
||||
resends++;
|
||||
}
|
||||
m_stats.resent_full++;
|
||||
}
|
||||
m_stats.resent_packets++;
|
||||
|
||||
// Resend the packet
|
||||
InternalBufferedSend(p);
|
||||
|
||||
m_resend_packets_sent++;
|
||||
m_resend_bytes_sent += p.Length();
|
||||
sp.last_sent = now;
|
||||
sp.times_resent++;
|
||||
sp.resend_delay = EQ::Clamp(
|
||||
sp.resend_delay * 2,
|
||||
m_owner->m_options.resend_delay_min,
|
||||
m_owner->m_options.resend_delay_max
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -181,6 +181,10 @@ namespace EQ
|
||||
Timestamp m_close_time;
|
||||
double m_outgoing_budget;
|
||||
|
||||
// resend tracking
|
||||
size_t m_resend_packets_sent = 0;
|
||||
size_t m_resend_bytes_sent = 0;
|
||||
|
||||
struct DaybreakSentPacket
|
||||
{
|
||||
DynamicPacket packet;
|
||||
@ -318,6 +322,10 @@ namespace EQ
|
||||
void Attach(uv_loop_t *loop);
|
||||
void Detach();
|
||||
|
||||
// resend
|
||||
Timestamp m_last_resend_check;
|
||||
int m_resend_check_interval = 50;
|
||||
|
||||
EQ::Random m_rand;
|
||||
uv_timer_t m_timer;
|
||||
uv_udp_t m_socket;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user