I have a problem with sockets
in python
. Here my code:
File server.py
:
import subprocess
import socket
def run_command(command):
process = subprocess.run(command.split(), stdout = subprocess.PIPE, stdin = subprocess.PIPE,)
return process.stdout
skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
skt.bind(('localhost', 9999))
skt.listen(5)
while True:
client_socket, addr = skt.accept()
while True:
received = client_socket.recv(1024)
client_socket.send(run_command(received))
File client.py
:
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('localhost', 9999))
while True:
client_socket.send(
input('>> ').encode('utf-8')
)
received = client_socket.recv(2024)
print(received)
client_socket.close()
The purpose is to be able to execute commands, but when doing so the first problem arises, the problem consists in how it prints the output of the terminal, example:
>> ls
b'client.py\nproyect.py\nserver.py\ntest.py\n'
>> apt
b'apt 1.6.11 (amd64)\nUso: apt [opciones] orden\n\napt es un gestor de paquetes de l\xc3\xadnea de \xc3\xb3rdenes y proporciona \xc3\xb3rdenes\npara la b....
How is it done to be able to print the output of the terminal in a "normal" way, so to speak, and not make it look ugly?
I also run into the problem that when executing many commands an error occurs. Here an example:
>> ls
b'client.py\nproyect.py\nserver.py\ntest.py\n'
>> ls
b'client.py\nproyect.py\nserver.py\ntest.py\n'
>> ls
b'client.py\nproyect.py\nserver.py\ntest.py\n'
>> ls
b'client.py\nproyect.py\nserver.py\ntest.py\n'
>> cd ..
b''
>> ls
b''
>> ls
Traceback (most recent call last):
File "client.py", line 8, in <module>
input('>> ').encode('utf-8')
BrokenPipeError: [Errno 32] Broken pipe
Is there a way to fix this? I'm pretty new to this theme sockets
. Thanks in advance for your answers!.
Update:
I managed to solve the problem of aesthetics, making the output of the terminal look "pretty", by adding .decode('utf-8')
data received from the socket
server.
What is printed is a string of bytes, which is what subprocess returns and what you receive from the server as such. To make it look "normal" you must decode that string using the proper encoding. You can use the argument
universal_newlines=True
ortext=True
(Python aliases >=3.7)subprocess.run
to tell Python to use system encoding to decode the incoming byte string and convert it to a UTF-8 string. But you are not guaranteed that a given thread will return in a given encoding or use the system encoding , so this may fail.This is because your client tries to send data to the server when the server has closed the connection. But why?. If you notice you have tried to make thread execute
cd
,cd
it is not a thread, it is a shell functionality . This causes an exception and the server terminates its execution, thereby closing the socket on its side. If you want to use shell commands you should use shell=True in threads.run . This makes any command run through the terminal instead of launching a new thread directly (with all that that implies in terms of syntax and security).Another thing you should consider is that on your client (also on the server) you read 2024 bytes only, this may or may not be enough. Nothing assures you that a certain thread will generate a larger output, in which case you leave yourself to read part of it, which will be read in the next call to
recv
or you can even end up with the buffer full (whose size depends on the system) andsend
blocked in the server side. You should make sure to read all the bytes sent by the server. The same applies to the server, if you consider that it is possible to send commands that exceed the 1024 bytes that you have assigned torecv
on the server side.With all that said, something like this should work for you:
Server
Client
A little note about calling
cd
on the thread, this will never change the server's working directory, it just calls the shell, runscd
and closes the shell, no more. Note that you can ssh via thread, or use paramiko, fabric, etc.Never use
str.split
to convert the string with the command to the list with the separate arguments to pass tosubprocess.run/Popen
. This is insecure useshlex.split
instead.