Skip to content

Commit

Permalink
move ping and info packet handling to a mixin
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@18529 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Feb 22, 2018
1 parent a151df3 commit 90e1a04
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 141 deletions.
185 changes: 185 additions & 0 deletions src/xpra/client/mixins/network_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# This file is part of Xpra.
# Copyright (C) 2010-2018 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import re
from collections import deque

from xpra.log import Logger
log = Logger("network")
bandwidthlog = Logger("bandwidth")

from xpra.os_util import monotonic_time, POSIX
from xpra.util import envint, csv
from xpra.exit_codes import EXIT_TIMEOUT


FAKE_BROKEN_CONNECTION = envint("XPRA_FAKE_BROKEN_CONNECTION")
PING_TIMEOUT = envint("XPRA_PING_TIMEOUT", 60)
#LOG_INFO_RESPONSE = ("^window.*position", "^window.*size$")
LOG_INFO_RESPONSE = os.environ.get("XPRA_LOG_INFO_RESPONSE", "")


"""
Mixin for adding server / network state monitoring functions:
- ping and echo
- info request and response
"""
class NetworkState(object):

def __init__(self):
self.start_time = monotonic_time()
self.server_start_time = -1

#setting:
self.pings = False

#bandwidth
self.server_bandwidth_limit_change = False
self.server_bandwidth_limit = 0
self.server_session_name = None

#info requests
self.server_last_info = None
self.info_request_pending = False

#network state:
self.server_ping_latency = deque(maxlen=1000)
self.server_load = None
self.client_ping_latency = deque(maxlen=1000)
self._server_ok = True
self.last_ping_echoed_time = 0


def init(self, opts):
self.pings = opts.pings


def cleanup(self):
pass


def get_caps(self):
return {"info-namespace" : True}

def parse_server_capabilities(self):
c = self.server_capabilities
self.server_start_time = c.intget("start_time", -1)
self.server_bandwidth_limit_change = c.boolget("network.bandwidth-limit-change")
self.server_bandwidth_limit = c.intget("network.bandwidth-limit")
bandwidthlog("server_bandwidth_limit_change=%s, server_bandwidth_limit=%s", self.server_bandwidth_limit_change, self.server_bandwidth_limit)

def process_ui_capabilities(self):
self.send_deflate_level()
self.send_ping()
if self.pings>0:
self.timeout_add(1000*self.pings, self.send_ping)


######################################################################
# info:
def _process_info_response(self, packet):
self.info_request_pending = False
self.server_last_info = packet[1]
log("info-response: %s", self.server_last_info)
if LOG_INFO_RESPONSE:
items = LOG_INFO_RESPONSE.split(",")
logres = [re.compile(v) for v in items]
log.info("info-response debug for %s:", csv(["'%s'" % x for x in items]))
for k in sorted(self.server_last_info.keys()):
if any(lr.match(k) for lr in logres):
log.info(" %s=%s", k, self.server_last_info[k])

def send_info_request(self, *categories):
if not self.info_request_pending:
self.info_request_pending = True
self.send("info-request", [self.uuid], tuple(self._id_to_window.keys()), categories)


######################################################################
# network and status:
def server_ok(self):
return self._server_ok

def check_server_echo(self, ping_sent_time):
if self._protocol is None:
#no longer connected!
return False
last = self._server_ok
if FAKE_BROKEN_CONNECTION>0:
self._server_ok = (int(monotonic_time()) % FAKE_BROKEN_CONNECTION) <= (FAKE_BROKEN_CONNECTION//2)
else:
self._server_ok = self.last_ping_echoed_time>=ping_sent_time
log("check_server_echo(%s) last=%s, server_ok=%s (last_ping_echoed_time=%s)", ping_sent_time, last, self._server_ok, self.last_ping_echoed_time)
self.server_state_change()
return False

def server_state_change(self):
log("server_state_change() ok=%s", self._server_ok)

def check_echo_timeout(self, ping_time):
log("check_echo_timeout(%s) last_ping_echoed_time=%s", ping_time, self.last_ping_echoed_time)
if self.last_ping_echoed_time<ping_time:
#no point trying to use disconnect_and_quit() to tell the server here..
self.warn_and_quit(EXIT_TIMEOUT, "server ping timeout - waited %s seconds without a response" % PING_TIMEOUT)

def send_ping(self):
now_ms = int(1000.0*monotonic_time())
self.send("ping", now_ms)
self.timeout_add(PING_TIMEOUT*1000, self.check_echo_timeout, now_ms)
wait = 2.0
if len(self.server_ping_latency)>0:
l = [x for _,x in tuple(self.server_ping_latency)]
avg = sum(l) / len(l)
wait = min(5, 1.0+avg*2.0)
log("send_ping() timestamp=%s, average server latency=%.1f, using max wait %.2fs", now_ms, 1000.0*avg, wait)
self.timeout_add(int(1000.0*wait), self.check_server_echo, now_ms)
return True

def _process_ping_echo(self, packet):
echoedtime, l1, l2, l3, cl = packet[1:6]
self.last_ping_echoed_time = echoedtime
self.check_server_echo(0)
server_ping_latency = monotonic_time()-echoedtime/1000.0
self.server_ping_latency.append((monotonic_time(), server_ping_latency))
self.server_load = l1, l2, l3
if cl>=0:
self.client_ping_latency.append((monotonic_time(), cl/1000.0))
log("ping echo server load=%s, measured client latency=%sms", self.server_load, cl)

def _process_ping(self, packet):
echotime = packet[1]
l1,l2,l3 = 0,0,0
if POSIX:
try:
(fl1, fl2, fl3) = os.getloadavg()
l1,l2,l3 = int(fl1*1000), int(fl2*1000), int(fl3*1000)
except (OSError, AttributeError):
pass
sl = -1
if len(self.server_ping_latency)>0:
_, sl = self.server_ping_latency[-1]
self.send("ping_echo", echotime, l1, l2, l3, int(1000.0*sl))


######################################################################
# network level packet compression:
def set_deflate_level(self, level):
self.compression_level = level
self.send_deflate_level()

def send_deflate_level(self):
self._protocol.set_compression_level(self.compression_level)
self.send("set_deflate", self.compression_level)


######################################################################
# packets:
def init_authenticated_packet_handlers(self):
self.set_packet_handlers(self._packet_handlers, {
"ping": self._process_ping,
"ping_echo": self._process_ping_echo,
"info-response": self._process_info_response,
})
Loading

0 comments on commit 90e1a04

Please sign in to comment.