2013-07-15 3 views
2

Вот простой питон 3.x TCP сервер:Определение определенного количества байтов, отправляемых/полученных в сообщении. (Python)

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() 

и клиент:

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") 

Я не могу понять, как просмотреть точное количество байтов, которые я отправляю в сообщение. Я могу использовать len (data), но он не учитывает нулевой терминатор и т. Д. Также отправляется нулевой ограничитель, или это не имеет значения? Я попытался изучить точное количество байтов отправленного/полученного сообщения, но я не смог найти какую-либо документацию на основе python и видел только примеры людей, использующих len(), которые, как я думаю, не точны ...

Любые идеи?

ответ

4

В строках 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/string. От самых серьезных до наименьших:

  • Вы почти никогда не должны просто звонить decode без аргументов. Если вы отправляете данные UTF-8 с одной стороны, всегда явно decode('utf-8') - с другой.
  • Метод decode гарантирует возврат str, поэтому написание str(message.decode()) просто делает ваш код запутанным.
  • Есть причина, по которой образец кода использует format вместо вызова str на связке объектов и их конкатенации - обычно это намного легче читать.
  • Как правило, более читаемо говорить data.encode('utf-8'), чем bytes(data, 'utf-8').
+0

Хм, так что в перспективе python len (buf), без добавления нулевого ограничителя, является точным измерением размера байта? –

+1

@Noobacode: Да, если вы отправляете байты с именем 'buf' без добавления нулевого терминатора,' len (buf) '- это длина буфера. Если по какой-либо причине вы явно добавляете нулевой ограничитель, 'len (buf + b '\ 0')' - это длина буфера с нулевым терминатором. – abarnert

+1

@Noobacode: Просто имейте в виду, что это длина в байтах, а не длина символов. Если вы хотите _that_, используйте 'len (s)' перед кодировкой '' для UTF-8. – abarnert

Смежные вопросы