Client/Server TCP Socket programming

I am trying to learn server/client sockets in python. I can establish the server and client and make the connection. I would like the server to ask the client to input a command. I would like the command to be TIME and I would like the response to display the current time. How do I do that?

Here is my server code:

#shell_server.py for python3
import socket
import subprocess

s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("0.0.0.0",444))
s.listen(1)

while True:
    connexion, client_address = s.accept()
    print ("connexion from :" +str(client_address))
    while True:
        connexion.send(bytes("Entrer cmd : ", "utf-8"))
        cmd = connexion.recv(1024).decode()
       # p = subprocess.Popen(cmd.split(" "),shell=True
        p = subprocess.Popen(cmd,shell=True
            ,stdout=subprocess.PIPE,stderr = subprocess.PIPE)
        out , err = p.communicate()
        connexion.send(out)

And here is my client code:

#shell_client.py for python3
import socket

s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("127.0.0.1",444))

while True:
    data = s.recv(4096)
    print(data.decode())
    cmd = input()
    s.send(cmd.encode('utf-8'))

So how do I input TIME and receive the current time?

Fundamentally, there are no “messages” in a TCP socket - it’s just a stream of bytes. So the first thing you have to do is define your protocol. On the internet, a large number of protocols are line-based, with the end of a message being defined by "\r\n" (yes, that’s true even on Unix platforms). Since you can’t know exactly how much you’ll get from one call to s.recv, you’ll have to create an input buffer and parse things out. Also, both your server and your client will have to cope with asynchronous operation - you might receive text from the other side at any time.

(Side point: Using Popen, especially with shell=True, is a very very dangerous thing to do. You’re binding to all IP addresses, so you depend 100% on external firewalling to ensure that someone from the outside doesn’t send you commands to run, because your program will happily just run them. Don’t do that; instead, build something inside Python that responds to the TIME command. Much safer, and also simpler.)

client.py:

#!/usr/bin/env python3

"""shell_client.py for python3.  Read data from a socket and, read a command from stdin, and send the command over the socket."""

import socket
import time

import bufsock

# Give the server time to start up.
time.sleep(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# We use port 4444 so we don't need to be root to run this.
s.connect(("127.0.0.1", 4444))
bs = bufsock.bufsock(s)

while True:
    # Read the prompt and print it.
    data = bs.readto(b'\n')
    print(data)
    # Read a command from the user's terminal.
    cmd = input()
    # Send the command to the server, terminated with a newline.
    bs.send(cmd.encode('utf-8'))
    bs.send(b'\n')
    bs.flush()
    # Read the command's always-one-line response and print it.
    data = bs.readto(b'\n')
    print(data)

server.py:

#!/usr/bin/env python3

"""Shell_server.py for python3.  Accept a connection, read commands, execute them and send data back over the socket."""

import socket
import time

import bufsock

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# We use port 4444 so we don't need to be root to run this.
s.bind(("0.0.0.0", 4444))
s.listen(1)

while True:
    connexion, client_address = s.accept()
    print("connexion from: " + str(client_address))
    bs = bufsock.bufsock(connexion)
    while True:
        bs.send(b"Entrer cmd:\n")
        bs.flush()
        cmd = bs.readto(b'\n').rstrip(b'\n')
        if cmd == b'TIME':
            result = time.ctime().encode('utf-8')
            bs.send(result)
        else:
            bs.send(b'Unrecognized command: %s' % (cmd, ))
        bs.send(b'\n')
        bs.flush()

bufsock.py:
https://stromberg.dnsalias.org/~strombrg/bufsock.html

…and I’m realizing tonight, that I should put bufsock on pypi. :slight_smile:

bufsock should now by pip’able: