我们怎么知道在tcp套接字上读取的前4个字节是消息的长度?

时间:2015-08-21 13:29:22

标签: python sockets tcp

python logging cookbook的"Sending and receiving logging events across a network"部分演示了客户端如何通过TCP会话发送日志。

由于socket handler,日志消息被腌制并发送到服务器。然后服务器取消删除消息并记录它们。

从tcp套接字获取消息的代码是:

Sub RowInserter()
Dim d As Integer
d = Range("C:C").End(xlDown).Row
Dim c As Range
  For i = d To 2 Step -1
    If Cells(i, 1).Value = "1" Then
      Rows(Cells(i, 1).Row).Insert shift:=xlDown
    End If
  Next
End Sub

在这个例子中我不明白的是:我们怎么知道从套接字读取的前4个字节构成了消息的长度?我查看了class LogRecordStreamHandler(SocketServer.StreamRequestHandler): """Handler for a streaming logging request. This basically logs the record using whatever logging policy is configured locally. """ def handle(self): """ Handle multiple requests - each expected to be a 4-byte length, followed by the LogRecord in pickle format. Logs the record according to whatever policy is configured locally. """ while True: chunk = self.connection.recv(4) if len(chunk) < 4: break slen = struct.unpack('>L', chunk)[0] chunk = self.connection.recv(slen) while len(chunk) < slen: chunk = chunk + self.connection.recv(slen - len(chunk)) obj = self.unPickle(chunk) record = logging.makeLogRecord(obj) self.handleLogRecord(record) # then, methods to handle the record, but that's not the interesting part class LogRecordSocketReceiver(SocketServer.ThreadingTCPServer): """ Simple TCP socket-based logging receiver suitable for testing. """ allow_reuse_address = 1 def __init__(self, host='localhost', port=logging.handlers.DEFAULT_TCP_LOGGING_PORT, handler=LogRecordStreamHandler): SocketServer.ThreadingTCPServer.__init__(self, (host, port), handler) self.abort = 0 self.timeout = 1 self.logname = None def serve_until_stopped(self): import select abort = 0 while not abort: rd, wr, ex = select.select([self.socket.fileno()], [], [], self.timeout) if rd: self.handle_request() abort = self.abort socket文档但未找到提及。

此外,docstring隐含地声明此代码对于生产来说还不够好。这段代码有什么不好的?

1 个答案:

答案 0 :(得分:0)

正如评论中所建议的,答案在于发送代码(python 2.7.10中的handlers.py)。我刚刚删除了与此问题明显或无关的文档字符串/注释,以使代码更具可读性。

def makePickle(self, record):
    ei = record.exc_info
    if ei:
        dummy = self.format(record)
        record.exc_info = None
    d = dict(record.__dict__)
    d['msg'] = record.getMessage()
    d['args'] = None
    s = cPickle.dumps(d, 1)
    if ei:
        record.exc_info = ei

    # slen represents a "long integer", which is usually 32 bits large.
    slen = struct.pack(">L", len(s))

    # Here is where the 4 byte length is prepended to the message
    return slen + s


def emit(self, record):
    try:
        s = self.makePickle(record)
        # s is actually (length of the message) + (message)
        self.send(s)
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        self.handleError(record)



def send(self, s):
    """
    Send a pickled string to the socket.

    This function allows for partial sends which can happen when the
    network is busy.
    """
    if self.sock is None:
        self.createSocket()

    if self.sock:
        try:
            if hasattr(self.sock, "sendall"):
                self.sock.sendall(s)
            else:
                sentsofar = 0
                left = len(s)
                while left > 0:
                    sent = self.sock.send(s[sentsofar:])
                    sentsofar = sentsofar + sent
                    left = left - sent
        except socket.error:
            self.sock.close()
            self.sock = None  # so we can call createSocket next time