2

Here's a simple python 3.x TCP server:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        print(str(self.client_address[0]) + " wrote: " + str(self.data.decode()))

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

and client:

import socket
import sys

HOST, PORT = "localhost", 9999

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))

while( True ):
    data = input("Msg: ")

    if data == "exit()":
        print("Exiting...")
        sock.close()
        exit();

    sock.sendall(bytes(data, "utf-8"))

#numBytes = ....?
#print("Sent: " + str( numBytes ) + " bytes\n")

I can't figure out how to view the exact number of bytes that I send in a message. I can use len(data), but it doesn't account for the null terminator and such.... Is null terminator being sent as well, or is it irrelevant? I tried researching on an exact byte count of a sent/received message, but I couldn't find any python-specific documentation and only have seen examples of people using len(), which I don't think is exact...

Any ideas?

4

1 回答 1

5

Python 字符串中没有空终止符。如果你想发送一个,你必须明确地这样做:sock.sendall(bytes(data, "utf-8") + b'\0').

但是,没有充分的理由首先添加空终止符,除非您打算将其用作消息之间​​的分隔符。(请注意,这不适用于一般的 Python 字符串,因为它们可以在中间包含空字节……但它当然适用于真正的人类可读文本。)

使用空字节作为分隔符并不是一个坏主意……但是您现有的代码需要实际处理它。您不能只是打电话recv(1024)并假设这是一条完整的信息;您必须不断recv(1024)地在循环中调用并附加到缓冲区,直到找到一个空值——然后保存该空值之后的所有内容,以供下次循环使用。


无论如何,该sendall方法不会返回发送的字节数,因为它总是准确地发送您给它的字节(除非有错误,在这种情况下会引发错误)。所以:

buf = bytes(data, "utf-8") + b'\0'
sock.sendall(buf)
bytes_sent = len(buf)

在服务器端,您可能想要编写一个 NullTerminatedHandler 类,如下所示:

class NullTerminatedHandler(socketserver.BaseRequestHandler):
    def __init__(self):
        self.buf = b''
    def handle(self):
        self.buf += self.request.recv(1024)
        messages = self.buf.split(b'\0')
        for message in messages[:-1]:
            self.handle_message(message)
        self.buf = self.buf[:-1]

然后你可以像这样使用它:

class MyTCPHandler(NullTerminatedHandler):
    def handle_message(self, message):
        print(str(self.client_address[0]) + " wrote: " + str(message.decode()))

当我们这样做时,您遇到了一些 Unicode/字符串问题。从最严重到最不严重:

  • 你几乎不应该直接打电话decode而没有争论。如果您在一侧发送 UTF-8 数据,则始终在另一侧显式发送decode('utf-8')
  • decode方法保证返回 a str,因此编写str(message.decode())只会使您的代码混乱。
  • 示例代码使用format而不是调用str一堆对象并将它们连接起来是有原因的——它通常更容易阅读。
  • 通常说起来data.encode('utf-8')bytes(data, 'utf-8').
于 2013-07-15T23:59:56.710 回答