要意识到 TCP 是一个字节流。您从 TCP 获得的保证是您发送的字节将以相同的顺序到达。如果字节流表示一系列命令,则无法保证字节会以与您发送的内容对齐的块的形式到达。
您可以发送:“执行”、“命令”
并接收“e”“xecuteco”“mmand”。
(是的,这是极不可能的,而接收“executecommand”是极有可能的,因为 nagle 算法,但我离题了。关键是,为了编写健壮的代码,你不要假设 TCP 是如何破坏你的数据分片)
因此,您需要决定的第一件事是如何将请求字节流划分为请求,以及如何将响应字节流划分为响应。在请求内部,您需要决定其内部结构。
假设您决定请求如下所示:
“动词 param1 param2...paramN\n”
那就是:
- 请求是一系列非换行字节,后跟换行符
- 请求由一个初始动词(非空格字符)后跟零个或多个参数组成
由于协议本身现在在 TCP 上多了一个层,因此最好使用抽象对其进行编码。类似于:
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
当然,这必须通过决定响应的外观以及如何将传入的字节流描述为一系列响应来补充。我试图用这段代码表达的主要观点是
- 你读取字节
- 你积累它们
- 试着看看你目前得到的东西是否是一个完整的请求
- 如果是 - 分析一下,剩下的留到下一次
假设请求相当小并且您不必流式传输它们,这是一个更高级的主题。