64 lines
2.3 KiB
Python
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()
|