Python ‘socketserver’ обрабатывает только первый запрос

#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()  

Чтобы исправить клиент и всю настройку, вам, вероятно, потребуется реализовать какое-то обрамление, для клиентских команд в настоящее время это новая строка (потому что вы используете строку чтения), но для вывода на сервер вам нужно придумать отдельное обрамление.