108 lines
3.1 KiB
Python
108 lines
3.1 KiB
Python
import json
|
|
import socket
|
|
import struct
|
|
import sys
|
|
import ssl
|
|
import threading
|
|
from datetime import datetime
|
|
|
|
PORT = 1234
|
|
HEADER_LENGTH = 2
|
|
|
|
|
|
def setup_SSL_context():
|
|
#uporabi samo TLS, ne SSL
|
|
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
|
|
# certifikat je obvezen
|
|
context.verify_mode = ssl.CERT_REQUIRED
|
|
#nalozi svoje certifikate
|
|
context.load_cert_chain(certfile="certs/janez_cert.crt", keyfile="certs/janez_private.key")
|
|
# nalozi certifikate CAjev (samopodp. cert.= svoja CA!)
|
|
context.load_verify_locations('certs/server_cert.crt')
|
|
# nastavi SSL CipherSuites (nacin kriptiranja)
|
|
context.set_ciphers('ECDHE-RSA-AES128-GCM-SHA256')
|
|
return context
|
|
|
|
|
|
def receive_fixed_length_msg(sock, msglen):
|
|
message = b''
|
|
while len(message) < msglen:
|
|
chunk = sock.recv(msglen - len(message)) # preberi nekaj bajtov
|
|
if chunk == b'':
|
|
raise RuntimeError("socket connection broken")
|
|
message = message + chunk # pripni prebrane bajte sporocilu
|
|
|
|
return message
|
|
|
|
|
|
def receive_json(sock):
|
|
# preberi glavo sporocila (v prvih 2 bytih je dolzina sporocila)
|
|
header = receive_fixed_length_msg(sock, HEADER_LENGTH)
|
|
message_length = struct.unpack("!H", header)[0] # pretvori dolzino sporocila v int
|
|
|
|
message = None
|
|
if message_length > 0: # ce je vse OK
|
|
message = receive_fixed_length_msg(sock, message_length) # preberi sporocilo
|
|
message = message.decode("utf-8")
|
|
message = json.loads(message)
|
|
|
|
return message
|
|
|
|
|
|
def send_json(sock, data):
|
|
data["time"] = datetime.now().strftime("%c")
|
|
|
|
# pretvori sporocilo v niz bajtov, uporabi UTF-8 kodno tabelo
|
|
encoded_data = json.dumps(data).encode("utf-8")
|
|
|
|
# ustvari glavo v prvih 2 bytih je dolzina sporocila (HEADER_LENGTH)
|
|
# metoda pack "!H" : !=network byte order, H=unsigned short
|
|
header = struct.pack("!H", len(encoded_data))
|
|
|
|
data = header + encoded_data # najprj posljemo dolzino sporocilo, sele nato sporocilo samo
|
|
sock.sendall(data)
|
|
|
|
|
|
def send_message(sock, message):
|
|
send_json(sock, {
|
|
"type": "message",
|
|
"data": message,
|
|
})
|
|
|
|
|
|
# message_receiver funkcija tece v loceni niti
|
|
def message_receiver():
|
|
while True:
|
|
msg_received = receive_json(sock)
|
|
|
|
msg_type = msg_received["type"]
|
|
data = msg_received["data"]
|
|
time = msg_received["time"]
|
|
match msg_type:
|
|
case "message":
|
|
username = msg_received["username"]
|
|
print(f'[{time}] [{username}] : {data}')
|
|
case "error":
|
|
print(f'Error: {data}')
|
|
|
|
|
|
# povezi se na streznik
|
|
print("[system] connecting to chat server ...")
|
|
my_ssl_ctx = setup_SSL_context()
|
|
sock = my_ssl_ctx.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
|
|
sock.connect(("localhost", PORT))
|
|
print("[system] connected!")
|
|
|
|
# zazeni message_receiver funkcijo v loceni niti
|
|
thread = threading.Thread(target=message_receiver)
|
|
thread.daemon = True
|
|
thread.start()
|
|
|
|
# pocakaj da uporabnik nekaj natipka in poslji na streznik
|
|
while True:
|
|
try:
|
|
msg_send = input("")
|
|
send_message(sock, msg_send)
|
|
except KeyboardInterrupt:
|
|
sys.exit()
|