простой клиент и сервер tcp

This commit is contained in:
Aleksey Khristolyubov 2025-09-08 16:44:18 +07:00
parent 05bd0435c1
commit 2bdf5fd4fd
3 changed files with 170 additions and 1 deletions

View File

@ -1,2 +1,20 @@
# innopol-protocol-mayak-server
# about
## server.py
Запускаем на удаленном сервере
Слушает указанный порт, ждет подключение клиента, печатает принятое сообщение, при получении отправляет заданное сообщение
```
python3 server.py --host 0.0.0.0 --port 5000 --response "OK, пакет получен"
```
## client.py
Запускаем на локальной машине
Указываем адрес сервера, порт сообщение которое отправляем и интервал повторения отправки
```
python3 client.py --host 127.0.0.1 --port 5000 --message "ping" --interval 5
```

88
client.py Normal file
View File

@ -0,0 +1,88 @@
#!/usr/bin/env python3
import socket
import struct
import argparse
import time
import sys
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 connect(host, port, timeout=10):
return socket.create_connection((host, port), timeout=timeout)
def main():
ap = argparse.ArgumentParser(description="TCP клиент (длина+данные) с периодической отправкой.")
ap.add_argument("--host", default="127.0.0.1", help="Адрес сервера")
ap.add_argument("--port", type=int, default=5000, help="Порт сервера")
ap.add_argument("--message", default="Привет от клиента!", help="Сообщение (UTF-8)")
ap.add_argument("--interval", type=float, default=5.0, help="Интервал отправки, сек (по умолчанию 5)")
args = ap.parse_args()
msg_bytes = args.message.encode("utf-8")
sock = None
try:
while True:
# Подключаемся, если не подключены
if sock is None:
try:
sock = connect(args.host, args.port, timeout=10)
sock.settimeout(15) # таймаут на операции, чтобы не зависать навсегда
print(f"Подключено к {args.host}:{args.port}")
except Exception as e:
print(f"Не удалось подключиться: {e}. Повтор через {args.interval} сек.")
time.sleep(args.interval)
continue
# Пытаемся отправить/получить
try:
send_packet(sock, msg_bytes)
print(f"Отправлено серверу: {args.message!r}")
resp = recv_packet(sock)
try:
print(f"Ответ сервера: {resp.decode('utf-8')!r}")
except UnicodeDecodeError:
print(f"Ответ сервера (bytes): {resp!r}")
time.sleep(args.interval)
except (ConnectionError, BrokenPipeError, TimeoutError, socket.timeout) as e:
print(f"Потеряно соединение: {e}. Переподключение через {args.interval} сек.")
try:
sock.close()
except Exception:
pass
sock = None
time.sleep(args.interval)
except KeyboardInterrupt:
print("\nЗавершение по Ctrl+C.")
finally:
if sock:
try:
sock.close()
except Exception:
pass
if __name__ == "__main__":
main()

63
server.py Normal file
View File

@ -0,0 +1,63 @@
#!/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()