2017-10-26 17:05:49 +00:00
import os
2017-10-15 21:30:31 +00:00
import socket
2017-10-26 17:05:49 +00:00
import sys
2018-01-10 16:39:28 +00:00
import struct
2019-09-24 20:57:37 +00:00
import selectors
2017-10-26 17:05:49 +00:00
2017-12-30 23:10:14 +00:00
from asciifarm . common . tcommunicate import send , receive
2017-10-15 21:30:31 +00:00
2019-09-24 20:57:37 +00:00
class _BytesBuffer :
def __init__ ( self ) :
self . buff = bytearray ( )
self . msglen = None
def addBytes ( self , data ) :
self . buff . extend ( data )
def readMessages ( self ) :
messages = [ ]
while True :
if self . msglen is None :
if len ( self . buff ) < 4 :
break
header = self . buff [ : 4 ]
self . msglen = int . from_bytes ( header , byteorder = " big " )
self . buff = self . buff [ 4 : ]
elif len ( self . buff ) > = self . msglen :
messages . append ( self . buff [ : self . msglen ] )
self . buff = self . buff [ self . msglen : ]
self . msglen = None
else :
break
return messages
2017-10-22 20:37:45 +00:00
# Class to open a TCP Socket
2017-10-15 21:30:31 +00:00
# will execute callback functions on new connections, closing connections and received messages
# also provides a send function
class Server :
2017-10-22 20:37:45 +00:00
def __init__ ( self , socketType , onConnection = ( lambda * _ : None ) , onMessage = ( lambda * _ : None ) , onConnectionClose = ( lambda * _ : None ) ) :
if socketType == " abstract " or socketType == " unix " :
2018-01-10 16:39:28 +00:00
self . sockType = socket . AF_UNIX
2017-10-22 20:37:45 +00:00
elif socketType == " inet " :
2018-01-10 16:39:28 +00:00
self . sockType = socket . AF_INET
2017-10-22 20:37:45 +00:00
else :
raise ValueError ( " invalid socket type " + str ( socketType ) )
2018-01-10 16:39:28 +00:00
self . sock = socket . socket ( self . sockType , socket . SOCK_STREAM )
2017-10-22 20:37:45 +00:00
self . socketType = socketType
2017-10-15 21:30:31 +00:00
self . onConnection = onConnection
self . onMessage = onMessage
self . onConnectionClose = onConnectionClose
2019-09-24 20:57:37 +00:00
self . sel = selectors . DefaultSelector ( )
2017-10-15 21:30:31 +00:00
2019-09-24 20:57:37 +00:00
def listen ( self , address ) :
2017-10-22 20:37:45 +00:00
print ( " starting {} socket server on address {} " . format ( self . socketType , address ) )
2017-10-15 21:30:31 +00:00
try :
self . sock . bind ( address )
except PermissionError :
print ( " You don ' t have permission to use this socket file. \n Run the server with the ' -s ' option to specify another socket file path. \n WARNING: if an existing file is given, it will be overwritten. " )
sys . exit ( - 1 )
except OSError :
2017-11-10 21:14:56 +00:00
print ( " Unable to bind to the socket address. \n Most likely this means that a server is already running and using the same address. \n Try another socket address (and tell all players to connect to that) " )
2017-10-15 21:30:31 +00:00
sys . exit ( - 1 )
self . sock . listen ( )
2019-09-24 20:57:37 +00:00
self . sock . setblocking ( False )
self . sel . register ( self . sock , selectors . EVENT_READ , " ACCEPT " )
self . connections = { }
2017-10-15 21:30:31 +00:00
print ( " listening " )
while True :
2019-09-24 20:57:37 +00:00
events = self . sel . select ( )
for key , mask in events :
if key . data == " ACCEPT " :
sock = key . fileobj
connection , client_address = sock . accept ( )
connection . setblocking ( False )
self . sel . register ( connection , selectors . EVENT_READ , " RECEIVE " )
self . connections [ connection ] = _BytesBuffer ( )
self . onConnection ( connection )
elif key . data == " RECEIVE " :
connection = key . fileobj
data = connection . recv ( 4096 )
if data :
buff = self . connections [ connection ]
buff . addBytes ( data )
for message in buff . readMessages ( ) :
self . onMessage ( connection , message )
else :
del self . connections [ connection ]
self . onConnectionClose ( connection )
#listener = threading.Thread(target=self._listenCon, args=(connection,), daemon=True)
#listener.start()
2017-10-15 21:30:31 +00:00
2019-09-24 20:57:37 +00:00
#def _listenCon(self, connection):
#self.connections.add(connection)
#self.onConnection(connection)
#data = receive(connection)
#while data:
#self.onMessage(connection, data)
#try:
#data = receive(connection)
#except socket.error:
#break
#if not len(data):
#break
#self.connections.discard(connection)
#self.onConnectionClose(connection)
2017-10-15 21:30:31 +00:00
2018-01-10 16:39:28 +00:00
def getUsername ( self , connection ) :
if self . sockType != socket . AF_UNIX :
return None
peercred = connection . getsockopt ( socket . SOL_SOCKET , socket . SO_PEERCRED , struct . calcsize ( " 3i " ) )
pid , uid , gid = struct . unpack ( " 3i " , peercred )
import pwd
return pwd . getpwuid ( uid ) [ 0 ]
2017-10-15 21:30:31 +00:00
def send ( self , connection , msg ) :
try :
2019-09-24 20:57:37 +00:00
length = len ( msg )
header = length . to_bytes ( 4 , byteorder = " big " )
connection . sendall ( header + msg )
2018-01-11 20:50:21 +00:00
except Exception :
2019-09-24 20:57:37 +00:00
del self . connections [ connection ]
2017-10-15 21:30:31 +00:00
self . onConnectionClose ( connection )
print ( " failed to send to client " )
def broadcast ( self , msg ) :
for connection in frozenset ( self . connections ) :
self . send ( connection , msg )