Rewrote timeout system
This commit is contained in:
parent
e9ef282c95
commit
a30b9f1a62
|
@ -1,5 +1,7 @@
|
||||||
import struct
|
import struct
|
||||||
import socket
|
import socket
|
||||||
|
import asyncio
|
||||||
|
from time import time
|
||||||
|
|
||||||
from . import base
|
from . import base
|
||||||
from . import query
|
from . import query
|
||||||
|
@ -9,6 +11,8 @@ STATE_UNKNOWN = (0, base.BaseClient)
|
||||||
STATE_QUERY = (1, query.QueryClient)
|
STATE_QUERY = (1, query.QueryClient)
|
||||||
STATE_PLAYER = (2, player.PlayerClient)
|
STATE_PLAYER = (2, player.PlayerClient)
|
||||||
|
|
||||||
|
TIMEOUT = 10 # assume connection is closed after 10 seconds of inactivity (change this to a higher value so you dont timeout while debugging might be a good idea)
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
|
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
|
||||||
self.server = server
|
self.server = server
|
||||||
|
@ -17,18 +21,32 @@ class Client:
|
||||||
|
|
||||||
self.set_state(STATE_UNKNOWN)
|
self.set_state(STATE_UNKNOWN)
|
||||||
|
|
||||||
|
self.last_active = time()
|
||||||
|
self.keep_alive_task = asyncio.create_task( self.keep_alive() )
|
||||||
|
self.connected = True # keep_alive will set this to False if connection has not been interacted with for a while (allowing server loop to remove their reference)
|
||||||
|
|
||||||
def set_state(self, state: tuple):
|
def set_state(self, state: tuple):
|
||||||
|
#self.keep_alive_task.cancel()
|
||||||
self.state = state
|
self.state = state
|
||||||
self.client = self.state[1](self.server, self.ip, self.port)
|
self.client = self.state[1](self.server, self.ip, self.port)
|
||||||
|
|
||||||
async def on_packet(self, packet: bytes):
|
async def on_packet(self, packet: bytes):
|
||||||
|
self.last_active = time()
|
||||||
|
|
||||||
if self.state == STATE_UNKNOWN:
|
if self.state == STATE_UNKNOWN:
|
||||||
# We are currently unaware if this is a player client or query client, but we got a packet that will be our check to know
|
# We are currently unaware if this is a player client or query client, but we got a packet that will be our check to know
|
||||||
if packet.startswith(b"SAMP"):
|
if packet.startswith(b"SAMP"):
|
||||||
await self.client.on_state_change()
|
|
||||||
self.set_state(STATE_QUERY)
|
self.set_state(STATE_QUERY)
|
||||||
else:
|
else:
|
||||||
await self.client.on_state_change()
|
|
||||||
self.set_state(STATE_PLAYER)
|
self.set_state(STATE_PLAYER)
|
||||||
|
|
||||||
await self.client.on_packet(packet)
|
await self.client.on_packet(packet)
|
||||||
|
|
||||||
|
async def keep_alive(self): # Maybe bad name for this method as it rather checks if connection is dropped
|
||||||
|
while True:
|
||||||
|
timestamp = time()
|
||||||
|
if self.last_active + TIMEOUT - timestamp < 0:
|
||||||
|
self.connected = False
|
||||||
|
return
|
||||||
|
|
||||||
|
await asyncio.sleep(self.last_active + TIMEOUT - timestamp)
|
|
@ -1,13 +1,9 @@
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import asyncio
|
|
||||||
from time import time
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
TIMEOUT = 30 # assume connection is closed after 30 seconds if inactivity
|
|
||||||
|
|
||||||
class BaseClient:
|
class BaseClient:
|
||||||
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
|
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
|
||||||
self.server = server
|
self.server = server
|
||||||
|
@ -16,26 +12,9 @@ class BaseClient:
|
||||||
|
|
||||||
self.ip_uint, = struct.unpack(b"<I", bytes(int(x) for x in self.ip.split(".")))
|
self.ip_uint, = struct.unpack(b"<I", bytes(int(x) for x in self.ip.split(".")))
|
||||||
|
|
||||||
self.last_active = time()
|
|
||||||
self.keep_alive_task = asyncio.create_task( self.keep_alive() )
|
|
||||||
self.connected = True # keep_alive will set this to False if connection has not been interacted with for a while (allowing server loop to remove their reference)
|
|
||||||
|
|
||||||
async def on_packet(self, packet: bytes):
|
async def on_packet(self, packet: bytes):
|
||||||
logger.debug("on_packet(%s)" % packet)
|
logger.debug("on_packet(%s)" % packet)
|
||||||
self.last_active = time()
|
|
||||||
|
|
||||||
async def send(self, packet: bytes):
|
async def send(self, packet: bytes):
|
||||||
sock: socket.socket = self.server.socket
|
sock: socket.socket = self.server.socket
|
||||||
sock.sendto(packet, (self.ip, self.port))
|
sock.sendto(packet, (self.ip, self.port))
|
||||||
|
|
||||||
async def on_state_change(self): # Stop the keep alive task as the whole class is being replaced, TODO: Find out if this could be automated with a magic method
|
|
||||||
self.keep_alive_task.cancel()
|
|
||||||
|
|
||||||
async def keep_alive(self):
|
|
||||||
while True:
|
|
||||||
timestamp = time()
|
|
||||||
if self.last_active + TIMEOUT - timestamp < 0:
|
|
||||||
self.connected = False
|
|
||||||
return
|
|
||||||
|
|
||||||
await asyncio.sleep(self.last_active + TIMEOUT - timestamp)
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ class QueryClient(BaseClient):
|
||||||
}
|
}
|
||||||
|
|
||||||
async def on_packet(self, packet: bytes):
|
async def on_packet(self, packet: bytes):
|
||||||
await super().on_packet(packet)
|
logger.debug("on_packet(%s)" % packet)
|
||||||
|
|
||||||
if len(packet) <= 10: # Invalid
|
if len(packet) <= 10: # Invalid
|
||||||
return
|
return
|
||||||
|
|
|
@ -50,4 +50,11 @@ class Server:
|
||||||
self.clients[addr] = Client(self, ip, port)
|
self.clients[addr] = Client(self, ip, port)
|
||||||
await self.clients[addr].on_packet(data)
|
await self.clients[addr].on_packet(data)
|
||||||
|
|
||||||
|
disconnected = [c for c in self.clients.values() if c.connected == False]
|
||||||
|
for c in disconnected: # Remove dead connections
|
||||||
|
addr = (c.ip, c.port)
|
||||||
|
if addr in self.clients:
|
||||||
|
del self.clients[addr]
|
||||||
|
logger.debug("free(%s)" % c)
|
||||||
|
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user