Python日志数据报处理器

2 投票
2 回答
3449 浏览
提问于 2025-04-16 08:17

我在使用Python文档中关于日志的DatagramHandler示例代码时遇到了问题,下面的代码在每次接收到数据报时都会出现EOFError异常。

import socket
import logging
import cPickle
import struct
import sys

sock = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
sock.bind (('localhost', 9000))

handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("UDP LogViewer %(asctime)s %(message)s"))
logger = logging.getLogger("Test")
logger.addHandler(handler)

try:
    while True:
        dgram_size = sock.recv(4)
        if len(dgram_size) < 4:
            break
        slen = struct.unpack(">L", dgram_size)[0]
        data = sock.recv(slen)

        while len(data) < slen:
            data = data + sock.recv(slen - len(data))

        try:
            obj = cPickle.loads(data)
            record = logging.makeLogRecord(obj)
            logger.handle(record)
        except:
            print "exception", sys.exc_info()[0]



finally:
    sock.close()

不过这段代码是可以正常工作的,你们有什么想法吗?

data, address = sock.recvfrom(8192)
rec = logging.makeLogRecord(cPickle.loads(data[4:]))
logger.handle(rec)

祝好

2 个回答

0

一个完整的MWA(消息写入适配器)看起来大概是这样的。

注意,我们直接忽略了前四个字节(这部分是pickle数据的大小),这些字节存在是因为DatagramHandler是从SocketHandler继承过来的,而SocketHandler是用TCP模式工作的。由于我们现在使用的是UDP,所以可以直接读取数据包的全部内容。不过,正如Sarnold所指出的,如果数据量超过了一个数据包的大小,我们就需要找到更好的解决办法。而且,由于UDP可能会丢失数据包,这就需要在服务器端进行一些定制化的处理。或许最简单的服务器端解决方案就是把消息拆分开,这样就不会一开始就超过限制了!

import socketserver
import socket
import pickle
import logging

class MyDatagramRequestHandler( socketserver.DatagramRequestHandler ):
    def handle( self ):
        try:
            while True:
                chunk = self.socket.recv( 2048 )
                chunk = chunk[4:]
                obj = self.unPickle( chunk )
                record = logging.makeLogRecord( obj )
                self.on_handle( record )
        except socket.timeout:
            pass

    def unPickle( self, data ):
        return pickle.loads( data )
        
    def on_handle( self, record: logging.LogRecord ):
        # do something, e.g.
        logger = logging.getLogger( record.name )
        logger.handle( record )

这个内容改编自TCP示例,详细信息可以查看https://docs.python.org/3/howto/logging-cookbook.html#logging-cookbook

3

我想你第一次调用 recv(4) 的时候,会把数据包的前四个字节复制出来,然后把剩下的部分丢掉。接着你第二次调用 recv 时,就会发现没有东西可以读取了,因此返回一个 EOFError 错误。从我系统的 udp(7) 手册上可以看到:

   All receive operations return only one packet.  When the packet
   is smaller than the passed buffer, only that much data is
   returned; when it is bigger, the packet is truncated and the
   MSG_TRUNC flag is set.  MSG_WAITALL is not supported.

你可以尝试读取整个数据包,先从前四个字节中获取长度,然后再处理存储整个数据包的数组的一部分。

当然,如果你的数据包大小超过了连接的 最大传输单元(MTU),那可能就永远无法按你想的那样工作了。

撰写回答