Server Remastered

This commit is contained in:
Fierelier 2021-04-22 04:29:12 +02:00
parent bf8505554f
commit eb05c89d47

View File

@ -25,14 +25,15 @@ import subprocess
import configparser import configparser
import time import time
outThreads = {}
inThreads = {}
connections = {} connections = {}
threadId = 0 connectionsId = 0
threadsLock = threading.Lock()
fileLock = threading.Lock()
connectionsLock = threading.Lock() connectionsLock = threading.Lock()
threadCount = 0
threadCountLock = threading.Lock()
fileLock = threading.Lock()
serverAddr = ("127.0.0.1",61920) serverAddr = ("127.0.0.1",61920)
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@ -43,6 +44,18 @@ maxClientsPerIP = 3 # How many clients can be connected at maximum, per IP?
maxAccumulatedData = 50*1000*1000 # How much data can be in an outbound thread's queue at maximum before the connection is closed? The maximum amount of queue data is maxAccumulatedData * maxClients bytes. maxAccumulatedData = 50*1000*1000 # How much data can be in an outbound thread's queue at maximum before the connection is closed? The maximum amount of queue data is maxAccumulatedData * maxClients bytes.
maxInboundTransferRate = 0 # Maximum upload speed for broadcasters. This sucks right now. Set to 0 to disable. maxInboundTransferRate = 0 # Maximum upload speed for broadcasters. This sucks right now. Set to 0 to disable.
def addThread():
global threadCount
with threadCountLock:
threadCount += 1
print("Thread opened. Threads: " +str(threadCount)+ " (Actual: " +str(threading.active_count())+ ")")
def removeThread():
global threadCount
with threadCountLock:
threadCount -= 1
print("Thread closed. Threads: " +str(threadCount)+ " (Actual: " +str(threading.active_count())+ ")")
def commandToList(cmd): def commandToList(cmd):
args = [] args = []
cArg = "" cArg = ""
@ -75,87 +88,62 @@ def commandToList(cmd):
return args return args
class outThread(threading.Thread): def closeConnection(connectionId):
def __init__(self,threadId,user): if connectionId in connections:
threading.Thread.__init__(self) del connections[connectionId]
self.threadId = threadId
self.queue = queue.Queue()
self.user = user
self.ignore = False
def closeConnection(self): class outThread(threading.Thread):
with connectionsLock: def __init__(self,connectionId):
if str(self.threadId) in connections: threading.Thread.__init__(self)
try: self.queue = queue.Queue()
connections[str(self.threadId)][0].close() self.connectionId = connectionId
except Exception as e:
print("Warning, closing connection failed: " +str(e))
del connections[str(self.threadId)]
def getConnection(self): def getConnection(self):
with connectionsLock: with connectionsLock:
if str(self.threadId) in connections: if self.connectionId in connections:
return connections[str(self.threadId)] return connections[self.connectionId]["connection"]
return False return False
def closeThread(self):
with threadsLock:
self.closeConnection()
if str(self.threadId) in outThreads: del outThreads[str(self.threadId)]
def run(self): def run(self):
try: try:
while True: while True:
data = self.queue.get(timeout=15) data = self.queue.get(timeout=15)
if type(data) == tuple: connection = self.getConnection()
data[0](*data[1],**data[2]) if not connection:
if data[0] == self.closeThread: return removeThread()
continue return
self.getConnection()[0].sendall(data) connection.sendall(data)
self.closeThread() with connectionsLock: closeConnection(self.connectionId)
removeThread()
return
except: except:
self.closeThread() with connectionsLock: closeConnection(self.connectionId)
removeThread()
print("Thread closed - Exception:") print("Thread closed - Exception:")
raise raise
class inThread(threading.Thread): class inThread(threading.Thread):
def __init__(self,threadId): def __init__(self,connectionId):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.threadId = threadId self.connectionId = connectionId
def closeConnection(self):
with connectionsLock:
if str(self.threadId) in connections:
try:
connections[str(self.threadId)][0].close()
except:
print("warning, closing connection failed")
del connections[str(self.threadId)]
def getConnection(self): def getConnection(self):
with connectionsLock: with connectionsLock:
if str(self.threadId) in connections: if self.connectionId in connections:
return connections[str(self.threadId)] return connections[self.connectionId]["connection"]
return False
def closeThread(self,closeConnection = True):
with threadsLock:
if closeConnection:
for thread in outThreads:
thread = outThreads[thread]
if thread.user == self.user:
thread.queue.put((thread.closeThread,[],{}))
self.closeConnection()
if str(self.threadId) in inThreads: del inThreads[str(self.threadId)]
def run(self): def run(self):
try: try:
data = self.getConnection()[0].recv(1000) connection = self.getConnection()
if not connection:
removeThread()
return
data = connection.recv(1000)
if data == b"": if data == b"":
self.closeThread() with connectionsLock: closeConnection(self.connectionId)
print("Thread closed - Client disconnected.") print("Thread closed - Client disconnected.")
removeThread()
return return
data = data.decode("utf-8") data = data.decode("utf-8")
while data[-1] == " ": data = data[:-1] while data[-1] == " ": data = data[:-1]
@ -171,8 +159,9 @@ class inThread(threading.Thread):
userPath = p(sp,"users",user+ ".ini") userPath = p(sp,"users",user+ ".ini")
with fileLock: with fileLock:
if not os.path.isfile(userPath): if not os.path.isfile(userPath):
self.closeThread() with connectionsLock: closeConnection(self.connectionId)
print("Thread closed - Invalid user given: " +user) print("Thread closed - Invalid user given: " +user)
removeThread()
return return
config = configparser.ConfigParser() config = configparser.ConfigParser()
@ -180,54 +169,72 @@ class inThread(threading.Thread):
if cmd == "watch": if cmd == "watch":
if cmd in config and "pass" in config[cmd] and config[cmd]["pass"] != "" and config[cmd]["pass"] != password: if cmd in config and "pass" in config[cmd] and config[cmd]["pass"] != "" and config[cmd]["pass"] != password:
self.closeThread() with connectionsLock: closeConnection(self.connectionId)
print("Thread closed - Invalid password given for user: " +user) print("Thread closed - Invalid password given for user: " +user)
removeThread()
return return
with threadsLock: with connectionsLock:
thread = outThread(self.threadId,self.user) if not self.connectionId in connections:
outThreads[str(self.threadId)] = thread removeThread()
return
connections[self.connectionId]["action"] = "watch"
connections[self.connectionId]["user"] = user
thread = outThread(self.connectionId)
connections[self.connectionId]["outThread"] = thread
thread.start() thread.start()
addThread()
self.closeThread(False) connections[self.connectionId]["inThread"] = False
removeThread()
return return
if cmd == "broadcast": if cmd == "broadcast":
if cmd in config and "pass" in config[cmd] and config[cmd]["pass"] != "" and config[cmd]["pass"] != password: if cmd in config and "pass" in config[cmd] and config[cmd]["pass"] != "" and config[cmd]["pass"] != password:
self.closeThread() with connectionsLock: closeConnection(self.connectionId)
print("Thread closed - Invalid password given for user: " +user) print("Thread closed - Invalid password given for user: " +user)
removeThread()
return return
with threadsLock: with connectionsLock:
deleteList = [] deleteList = []
for threadId in inThreads: for connectionId in connections:
if threadId == str(self.threadId): continue if connectionId == self.connectionId: continue
thread = inThreads[threadId] if connections[connectionId]["action"] != "broadcast": continue
thread.closeConnection() if connections[connectionId]["user"] != user: continue
deleteList.append(threadId) deleteList.append(connectionId)
for threadId in deleteList: for connectionId in deleteList:
del inThreads[threadId] closeConnection[connectionId]
connections[connectionId]["action"] = "broadcast"
connections[connectionId]["user"] = user
if maxInboundTransferRate > 0: startTime = time.time() if maxInboundTransferRate > 0: startTime = time.time()
while True: while True:
data = self.getConnection()[0].recv(bufferSize) connection = self.getConnection()
if not connection:
removeThread()
return
data = connection.recv(bufferSize)
if data == b"": if data == b"":
self.closeThread() with connectionsLock: closeConnection(self.connectionId)
print("Thread closed - Client disconnected.") print("Thread closed - Client disconnected.")
removeThread()
return return
with threadsLock: with connectionsLock:
for thread in outThreads: for connectionId in connections:
thread = outThreads[thread] connectionData = connections[connectionId]
if thread.user == user: thread = connectionData["outThread"]
if not thread: continue
if connectionData["user"] != user: continue
accumulatedData = thread.queue.qsize() * bufferSize accumulatedData = thread.queue.qsize() * bufferSize
if accumulatedData > maxAccumulatedData and not thread.ignore: if accumulatedData > maxAccumulatedData:
thread.ignore = True
thread.queue.put((thread.closeThread,[],{}))
thread.closeConnection()
print("Thread closed - Too much accumulated data.") print("Thread closed - Too much accumulated data.")
else: closeConnection(connectionId)
removeThread()
return
thread.queue.put(data) thread.queue.put(data)
if maxInboundTransferRate > 0: if maxInboundTransferRate > 0:
@ -244,11 +251,13 @@ class inThread(threading.Thread):
startTime = endTime startTime = endTime
self.closeThread() with connectionsLock: closeConnection(self.connectionId)
removeThread()
return return
except: except:
with connectionsLock: closeConnection(self.connectionId)
removeThread()
print("Thread closed - Exception:") print("Thread closed - Exception:")
self.closeThread()
raise raise
class debugThread(threading.Thread): class debugThread(threading.Thread):
@ -257,7 +266,7 @@ class debugThread(threading.Thread):
def run(self): def run(self):
while True: while True:
with threadsLock: with connectionsLock:
print("\n---\n") print("\n---\n")
print("Threads - IN: " +str(len(inThreads))) print("Threads - IN: " +str(len(inThreads)))
print("Threads - OUT: " +str(len(outThreads))) print("Threads - OUT: " +str(len(outThreads)))
@ -294,15 +303,15 @@ def readConfig():
serverAddr = tuple(serverAddrSplit) serverAddr = tuple(serverAddrSplit)
def main(): def main():
global threadId global connectionsId
readConfig() readConfig()
serverSocket.bind(serverAddr) serverSocket.bind(serverAddr)
serverSocket.listen(1024) serverSocket.listen(1024)
# DEBUG # DEBUG
debug = debugThread() #debug = debugThread()
debug.start() #debug.start()
# DEBUG END # DEBUG END
while True: while True:
@ -312,25 +321,34 @@ def main():
with connectionsLock: with connectionsLock:
clientCount = 0 clientCount = 0
ipClientCount = 0 ipClientCount = 0
for connId in connections: for connectionId in connections:
clientCount += 1 clientCount += 1
conn = connections[connId] if connections[connectionId]["address"][0] == address[0]:
if conn[1][0] == address[0]:
ipClientCount += 1 ipClientCount += 1
if clientCount + 1 > maxClients or ipClientCount + 1 > maxClientsPerIP: if clientCount + 1 > maxClients:
print("Connection closed - same IP connected too many times.") print("Connection closed - too many clients.")
connection.close() closeConnection(connectionId)
continue continue
with threadsLock: if ipClientCount + 1 > maxClientsPerIP:
threadId += 1 print("Connection closed - same IP connected too many times.")
with connectionsLock: closeConnection(connectionId)
connections[str(threadId)] = (connection,address) continue
thread = inThread(threadId) connectionsId += 1
inThreads[str(threadId)] = thread threadIn = inThread(str(connectionsId))
thread.start() connections[str(connectionsId)] = {
"connection": connection,
"address": address,
"inThread": threadIn,
"outThread": False,
"action": False,
"user": False
}
threadIn.start()
addThread()
if __name__ == '__main__': if __name__ == '__main__':
main() main()