Reputation: 11
I'm trying to code my first client-server application in Python. My client sends different commands to the server side. For instance, one of them is for executing commands in command shell. It looks like:
client.send("execute".encode("utf-8"))
client.send(command.encode("utf-8"))
On the server side I'm trying to receive it this way:
data = client.recv(BUFF_SIZE).decode("utf-8").strip()
if data == "execute":
command = client.recv(BUFF_SIZE).decode("utf-8").strip()
...
But I get "execute{command}" string in 'data' variable, and == "execute" condition is not fulfilled. I'm new to client-server applications and don't know, how to do it properly. What I do wrong?
Upvotes: 0
Views: 7232
Reputation: 326
The thing to realize is that TCP is a byte stream. The guarantee that you get from TCP is that the bytes you send will arrive at the same order. If the byte stream represent a sequence of commands, you get no guarantee that the bytes will arrive in chunks that are aligned with stuff that you sent. You could be sending: "execute", "command" And receiving "e" "xecuteco" "mmand".
(yes, that's extremely unlikely, whereas receiving "executecommand" is extremely likely, because of the nagle algorithm, but I digress. The point is that in order to write robust code you are not to assume anything abut how TCP breaks your data into pieces)
Therefore the first thing you need to decide on is how the request byte stream is partitioned into requests, and how the response byte stream is partitioned into responses. Inside the request you need to decide on its internal structure.
Let's assume you decide that the request looks like: "verb param1 param2...paramN\n" That is:
Since the protocol itself now has an additional layer over TCP, it's best to code this using an abstraction. Something akin to:
class Request(object):
def __init__(self, verb, *args):
self.verb = verb
self.args = [str(x) for x in args]
class Client(object):
def __init__(self, sock):
self.sock = sock
self.rxbuf = ''
def send_request(self, req):
req_str = req.verb
if req.args:
req_str += ' ' + ' '.join(req.args)
req_str += '\n'
self.sock.sendall(req_str.encode("utf-8"))
class Server(object):
def __init__(self, sock):
self.sock = sock
self.rxbuf = ''
def read_request(self):
while True:
s = self.rxbuf.split('\n', 1)
if len(s) == 2:
req_str = s[0]
self.rxbuf = s[1]
req_lst = req_str.split(' ')
return Request(req_lst[0], *req_lst[1:])
data = self.sock.recv(BUF_SIZE).decode("utf-8")
self.rxbuf += data
Of course this has to be complemented by a decision of how the responses will look like and how to delineate the incoming byte stream into a sequence of responses. The main point I was trying to make with this code is that
That assumes the requests are reasonably small and you don't have to stream them, which is a more advanced topic.
Upvotes: 5
Reputation: 476
you could fix this by changing the if statement on the server code to:
if data.split("{")[0] == "execute"
explanation:
data = "execute{command}"
print data.split("{")[0]
prints
"execute"
Since data is a string, you can use the split method to split the string into a list using the "{" character.
so:
data.split("{")
would return
["execute", "commmand}"]
and since you want "execute" you take index 0 in the list
Upvotes: 0