Asciifarm/asciifarm/server/server.py

86 lines
3.0 KiB
Python

import os
import socket
import sys
import threading
from .tcommunicate import send, receive
# Class to open a TCP Socket
# will execute callback functions on new connections, closing connections and received messages
# also provides a send function
class Server:
def __init__(self, socketType, onConnection=(lambda *_:None), onMessage=(lambda *_:None), onConnectionClose=(lambda *_:None)):
if socketType == "abstract" or socketType == "unix":
sockType = socket.AF_UNIX
elif socketType == "inet":
sockType = socket.AF_INET
else:
raise ValueError("invalid socket type "+str(socketType))
self.sock = socket.socket(sockType, socket.SOCK_STREAM)
self.socketType = socketType
self.onConnection = onConnection
self.onMessage = onMessage
self.onConnectionClose = onConnectionClose
def start(self, address):
print("starting {} socket server on address {}".format(self.socketType, address))
try:
self.sock.bind(address)
except PermissionError:
print("You don't have permission to use this socket file.\nRun the server with the '-s' option to specify another socket file path.\nWARNING: if an existing file is given, it will be overwritten.")
sys.exit(-1)
except OSError:
print("Unable to create a socket file.\nMost likely this means that a server is already running and using the socket, or the execution of this program didn't clean up well.\nIf no other server is running, try removing "+address+".\nIf you can't then specify another socket (and tell all players to connect to that)")
sys.exit(-1)
self.sock.listen()
self.listener = threading.Thread(target=self._listen, daemon=True)
self.listener.start()
def _listen(self):
self.connections = set()
print("listening")
while True:
connection, client_address = self.sock.accept()
listener = threading.Thread(target=self._listenCon, args=(connection,), daemon=True)
listener.start()
def _listenCon(self, connection):
#print(connection.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED))
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)
def send(self, connection, msg):
try:
send(connection, msg)
except:
self.connections.discard(connection)
self.onConnectionClose(connection)
print("failed to send to client")
def broadcast(self, msg):
for connection in frozenset(self.connections):
self.send(connection, msg)