#python #network-programming #socketserver
#питон #сетевое программирование #socketserver
Вопрос:
Вывод клиента
Чтобы показать проблему, вот код в действии при запуске на клиенте. Как видно, первая команда выполняется успешно (она всегда выполняется), и она выполняется даже в случае ошибочной команды, с помощью которой она вернет stderr.
Pwny ~gt;df Filesystem 1K-blocks Used Available Use% Mounted on udev 1895788 0 1895788 0% /dev tmpfs 386392 1416 384976 1% /run /dev/sda1 478612200 43470808 410755756 10% / tmpfs 1931948 78200 1853748 5% /dev/shm tmpfs 5120 0 5120 0% /run/lock tmpfs 386388 60 386328 1% /run/user/1000 Pwny ~gt;pwd Pwny ~gt;pwd [~] Connection aborted Pwny ~gt;
Вывод на сервер
Вот вывод с моего сервера.
└─$ /bin/python3 /home/user/Desktop/tcpserver/socketserver.py [*] Started server 0.0.0.0:9999 [*] 192.168.137.1:1051 connected [~] 192.168.137.1:1051 df
Клиентская программа TCP
Это мой клиентский код:
import socket class TCPClient: @staticmethod def run(host, port, buffer_size=1024, encoding='utf-8'): HOST = host PORT = port BUFFER_SIZE = buffer_size ENCODING = encoding with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) while True: tx: bytes = bytes(input("Pwny ~gt;"), ENCODING) tx = b"n" if tx == b"exit": break else: try: s.sendall(tx) full_msg: bytes = b'' while True: msg: bytes = s.recv(BUFFER_SIZE) if len(msg) == 0: break full_msg = msg print(full_msg.decode(ENCODING)) except ConnectionAbortedError: print("[~] Connection aborted") except OSError: print("[~] An OS error occurred") if __name__ == '__main__': tcp_client = TCPClient() tcp_client.run(host='192.168.137.2', port=9999, buffer_size=128, encoding='utf-8')
Программа TCP SocketServer
И это программа socketserver (я подозреваю, что я что-то здесь делаю, очень признательна за помощь)
import socketserver import subprocess from subprocess import CompletedProcess class ThreadingServer(socketserver.ThreadingMixIn,socketserver.TCPServer): pass class TCPRequestHandler(socketserver.StreamRequestHandler): def setup(self) -gt; None: return super().setup() def handle(self) -gt; None: ENCODING: str = 'utf-8' BUFFER_SIZE: int = 128 client_address: str = self.request.getpeername()[0] client_port: int = self.request.getpeername()[1] print(f"[*] {client_address}:{client_port} connected") client_cmd: str = self.rfile.readline().decode(ENCODING).strip() print(f"[~] {client_address}:{client_port} {client_cmd}") output: CompletedProcess = subprocess.run(client_cmd,shell=True,capture_output=True) """ Returns 0 for success and gt;= 1 for failure""" if output.returncode == 0: # success self.wfile.write(output.stdout) else: # failure when gt; 0 self.wfile.write(output.stderr) def finish(self) -gt; None: return super().finish() if __name__ == '__main__': with ThreadingServer(('0.0.0.0',9999),TCPRequestHandler) as server: print(f"[*] Started server {server.server_address[0]}:{server.server_address[1]}") server.serve_forever()
Комментарии:
1.На несвязанной заметке, в клиентском коде действительно ли имеет смысл проверять
if tx == b"exit"
после добавления новой строкиtx
?2. Логику следует поменять местами. Пропустил это, спасибо
3. Что касается вашей проблемы, если я понимаю документацию и найденные примеры,
handle
функция обрабатывает один запрос на подключение . Ни одного ввода. Поэтому вамhandle
нужно прочитать ввод и обработать этот ввод в цикле.4. Вы неправильно понимаете, что означает «просьба». С точки зрения
StreamRequestHandler
, запрос — это соединение. Иserve_forever()
просто заставляет сервер продолжать принимать новые соединения .
Ответ №1:
У вас есть проблемы как на клиенте, так и на сервере.
Клиент использует сокет .recv()
для чтения данных, но это будет заблокировано навсегда в конце первой команды, когда сервер завершит вывод. он вернется b''
только в EOF, когда сокет будет закрыт сервером. Из-за проблемы с сервером (ниже), похоже, это работает.
Ваш серверный код считывает только одну строку, создает команду, отправляет выходные данные, а затем возвращается, handle()
после чего соединение закрывается. Из-за этого закрытия клиент действительно работает (он получает b''
от recv()
).
Чтобы исправить сервер, делайте readline()
в цикле, пока он не вернется b''
(сокет закрыт клиентом), и предпочтительно flush()
поток записи после отправки вывода команды.
Пример обработчика сервера:
def handle(self) -gt; None: ENCODING: str = 'utf-8' BUFFER_SIZE: int = 128 client_address: str = self.request.getpeername()[0] client_port: int = self.request.getpeername()[1] print(f"[*] {client_address}:{client_port} connected") while True: line: str = self.rfile.readline() if line == b'': break client_cmd: str = line.decode(ENCODING).strip() print(f"[~] {client_address}:{client_port} {client_cmd}") output: CompletedProcess = subprocess.run(client_cmd,shell=True,capture_output=True) """ Returns 0 for success and gt;= 1 for failure""" if output.returncode == 0: # success self.wfile.write(output.stdout) else: # failure when gt; 0 self.wfile.write(output.stderr) self.wfile.flush()
Чтобы исправить клиент и всю настройку, вам, вероятно, потребуется реализовать какое-то обрамление, для клиентских команд в настоящее время это новая строка (потому что вы используете строку чтения), но для вывода на сервер вам нужно придумать отдельное обрамление.