innopol-protocol-mayak-server/server.py

64 lines
2.3 KiB
Python

#!/usr/bin/env python3
import socket
import struct
import argparse
import threading
def recv_exact(sock, nbytes):
chunks, got = [], 0
while got < nbytes:
chunk = sock.recv(nbytes - got)
if not chunk:
raise ConnectionError("Клиент закрыл соединение")
chunks.append(chunk)
got += len(chunk)
return b"".join(chunks)
def recv_packet(sock):
raw_len = recv_exact(sock, 4)
(length,) = struct.unpack("!I", raw_len)
if length > 16 * 1024 * 1024:
raise ValueError(f"Слишком большой пакет: {length} байт")
return recv_exact(sock, length)
def send_packet(sock, data: bytes):
sock.sendall(struct.pack("!I", len(data)) + data)
def handle_client(conn, addr, response_text: str):
print(f"[+] Подключился {addr[0]}:{addr[1]}")
try:
while True:
data = recv_packet(conn) # ждём следующий пакет
try:
print(f"[{addr[0]}:{addr[1]}] получил: {data.decode('utf-8')!r}")
except UnicodeDecodeError:
print(f"[{addr[0]}:{addr[1]}] получил (bytes): {data!r}")
send_packet(conn, response_text.encode("utf-8"))
except (ConnectionError, OSError) as e:
print(f"[-] {addr[0]}:{addr[1]} отключился: {e}")
finally:
conn.close()
def main():
ap = argparse.ArgumentParser(description="TCP сервер (длина+данные), держит соединение.")
ap.add_argument("--host", default="0.0.0.0")
ap.add_argument("--port", type=int, default=5000)
ap.add_argument("--response", default="OK, пакет получен")
args = ap.parse_args()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as srv:
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind((args.host, args.port))
srv.listen(16)
print(f"Сервер слушает {args.host}:{args.port} ... (Ctrl+C для выхода)")
try:
while True:
conn, addr = srv.accept()
t = threading.Thread(target=handle_client, args=(conn, addr, args.response), daemon=True)
t.start()
except KeyboardInterrupt:
print("\nОстановка сервера.")
if __name__ == "__main__":
main()