RK - 2021/22 - naloga 7 - resitev Starc Aljazs¶
Nalogo sem naredil "from scrath" z malo pomoci iz ze podanega serverja ter clienta.
Client
import socket as Socket
import os as Os
import threading as Threading
import datetime as Datetime
import json as Json
import time as Time
import sys as Sys
import re as Re
from typing import Dict
SOCKET_PORT = int(Os.getenv('PORT', 1234))
SOCKET_HOST = Os.getenv('HOST', "127.0.0.1")
TERM_COLS, TERM_ROWS = Os.get_terminal_size()
socket: Socket.socket = None
displayname: str = None
def log (level: str, line: str, prefix: str = ""):
colors = {
"info": u"\u001b[34m",
"success": u"\u001b[32m",
"warn": u"\u001b[33m",
"error": u"\u001b[31m",
"log": u"\u001b[37m"
}
print(u"%s%-12s [%s%s\u001b[0m] %s" % (
prefix,
Datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
colors[level],
level.upper().center(7),
line
), flush=True)
def receiver(socket: Socket.socket):
rfile = socket.makefile()
while True:
try:
raw = rfile.readline()
if not raw:
continue
data: Dict = Json.loads(raw)
if data.get("action") == "message" or data.get("action") == "dm":
print("\033[%d;%dH[%12s%s %s" % (TERM_ROWS - 1, 0, data.get("from"), ">" if data.get("action") == "dm" else "]", data.get("data")), flush=True)
print("[%12s] " % displayname, flush=True)
print("\033[%d;%dH" % (TERM_ROWS - 1, 16), end='', flush=True)
except Exception as e:
print("Error")
print(e)
pass
def send(data: Dict):
bdata = bytes(Json.dumps(data) + "\n", "UTF-8")
socket.send(bdata)
def command(msg: str):
cmd, *args = msg[1:].split(" ")
if cmd == "help":
print("""
\u001b[33mCommands\u001b[0m:
\u001b[34m/help\u001b[0m
Display this help menu
\u001b[34m/msg\u001b[0m <\u001b[34msession\u001b[0m> <\u001b[34mcontent\u001b[0m>
Send a direct message to a specific session
<\u001b[34msession\u001b[0m> is the session identificator. You can get list of them with /list
<\u001b[34mcontent\u001b[0m> is your message content
""")
elif cmd == "msg":
send({ "action": "dm", "to": args[0], "data": " ".join(args[1:]) })
else:
print("\u001b[31mERROR!\u001b[0m Invalid command. Try \u001b[34m/help\u001b[0m for a list of commands")
def defineDisplayname():
global displayname
try:
while not displayname:
print(chr(27) + "[2J")
print("\033[%d;%dH" % (TERM_ROWS / 2, TERM_COLS / 2 - 30), flush=True, end="")
vpis = input("Displayname ^[a-zA-Z0-9]{3,8}$: ")
if Re.match("^[a-zA-Z0-9]{3,8}$", vpis):
displayname = vpis
print(chr(27) + "[2J")
except KeyboardInterrupt:
print()
Sys.exit()
defineDisplayname()
while True:
try:
print("\033[%d;%dH" % (TERM_ROWS - 1, 0), flush=True)
print("[ \u001b[34msystem\u001b[0m] Use \u001b[34m/help\u001b[0m for a list of commands")
print("[ \u001b[34msystem\u001b[0m] connecting to chat server ... ", end="", flush=True)
socket = Socket.socket(Socket.AF_INET, Socket.SOCK_STREAM)
connected = False
while not connected:
try:
socket.connect((SOCKET_HOST, SOCKET_PORT))
connected = True
send({ "action": "setname", "data": displayname })
except Exception:
False
Time.sleep(1.0)
print("\u001b[32mCONNECTED\u001b[0m!")
thread = Threading.Thread(target=receiver, args=(socket,))
thread.daemon = True
thread.start()
while True:
try:
print("[%12s] " % displayname)
print("\033[%d;%dH" % (TERM_ROWS - 1, 16), end='', flush=True)
vpis = input("")
if not vpis:
print ("\033[A" + " " * TERM_COLS + "\033[A", flush=True)
continue
if vpis.startswith("/"):
command(vpis)
else:
send({ "action": "message", "data": vpis })
except Exception as e:
log("error", e)
print("Something went wrong! Restarting...")
break
except KeyboardInterrupt:
print()
Sys.exit()
Server
import socket as Socket
import os as Os
import threading as Threading
import datetime as Datetime
import json as Json
import time as Time
from typing import Dict, Set, TextIO
SOCKET_PORT = int(Os.getenv('PORT', 1234))
SOCKET_BIND = Os.getenv('BIND', "0.0.0.0")
def log (level: str, line: str, prefix: str = ""):
colors = {
"info": u"\u001b[34m",
"success": u"\u001b[32m",
"warn": u"\u001b[33m",
"error": u"\u001b[31m",
"log": u"\u001b[37m"
}
print(u"%s%-12s [%s%s\u001b[0m] %s" % (
prefix,
Datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
colors[level],
level.upper().center(7),
line
), flush=True)
class Client():
socket: Socket.socket = None
rfile: TextIO = None
address = None
name: str = None
thread: Threading.Thread = None
def __init__(self, socket: Socket.socket, address) -> None:
self.socket = socket
self.address = address
self.rfile = socket.makefile()
self.name = address[0] + ":" + str(address[1])
self.thread = Threading.Thread(target=self.entrypoint)
self.thread.daemon = True
self.thread.start()
def entrypoint (self):
"""
The client thread entrypoint
"""
log("log", "[client] %s connected " % self.name)
emptyCount = 0
while True:
try:
content = self.rfile.readline()
if not content:
emptyCount += 1
if emptyCount > 10: break
continue
emptyCount = 0
data: Dict = Json.loads(content)
if (data.get('action') == 'setname'):
self.name = data.get("data")
if (data.get('action') == 'message'):
for client in clients:
if client.name != self.name:
client.send({ "action": "message", "from": self.name, "data": data.get('data') })
if (data.get('action') == 'dm'):
print(data.get('to'))
print(data.get('data'))
for client in clients:
if client.name == data.get('to'):
client.send({ "action": "dm", "from": self.name, "data": data.get('data') })
except:
pass
self.close()
def send(self, data: Dict):
"""
Send data to client
"""
bdata = bytes(Json.dumps(data) + "\n", "UTF-8")
self.socket.send(bdata)
def close(self):
"""
Gracefully close the client socket
"""
with server_lock:
log("log", "[client] %s closing ..." % self.name)
self.socket.close()
clients.remove(self)
log("warn", "[client] %s closed" % self.name)
log("info", "[system] Starting server on port %d ..." % SOCKET_PORT)
clients: Set[Client] = set()
server_lock = Threading.Lock()
server_socket = Socket.socket(Socket.AF_INET, Socket.SOCK_STREAM)
server_socket.bind((SOCKET_BIND, SOCKET_PORT))
server_socket.listen(10)
log("success", "[system] Started server on port %d ..." % SOCKET_PORT)
log("info", "[system] Starting healthchecker")
def healthchecker():
while True:
for client in list(clients):
try:
client.send({"action": "healthcheck"})
# client.send({"action": "message", "sender": "server", "content": "healthcheck!"})
except: pass
Time.sleep(5)
healthchecker_thread = Threading.Thread(target=healthchecker)
healthchecker_thread.daemon = True
healthchecker_thread.start()
log("success", "[system] Started healthchecker")
while True:
try:
socket, address = server_socket.accept()
with server_lock:
client = Client(socket, address)
clients.add(client)
except KeyboardInterrupt:
break
except:
pass
log("warn", "[system] Closing server ...", "\n")
for client in list(clients):
client.close()
server_socket.close()
log("info", "[system] Server closed")
Zadnja posodobitev:
May 2, 2022