在Python中限制套接字上的readline长度

时间:2012-07-01 04:50:57

标签: python sockets readline

我正在使用服务器,并且所有数据都是基于行的。我希望能够在一行超过给定长度时引发异常而不读取任何更多的数据。例如,即使行长度限制为1024字节,客户端 X 也会发送长度为16KB的行。读取超过1024个字节后,我想停止读取其他数据,关闭套接字并引发异常。我查看了文档和一些源代码,如果不重写_readline方法,我看不到这样做的方法。我有一种更容易忽视的方式吗?

编辑:评论让我意识到我需要添加更多信息。我知道我可以在没有太多工作的情况下编写逻辑来执行此操作,但我希望使用内置函数来利用内存视图的高效缓冲,而不是自己再次实现它,或者采用天真的方法来读取块,根据需要进行拼接和拆分没有记忆观点。

2 个答案:

答案 0 :(得分:1)

我意识到您的编辑正在澄清您想要的是实现目标的内置方法。但我不知道有任何现有的东西可以帮助你对readline方法进行细粒度的控制。但我想我可能只是包含一个例子,它使用生成器和分割进行编码方法......只是为了好玩。

引用另一个问题/答案,找出一个读取线条的好发生器:
https://stackoverflow.com/a/822788/496445

基于该读者:

<强> server.py

import socket

MAXLINE = 100

def linesplit(sock, maxline=0):
    buf = sock.recv(16)
    done = False
    while not done:
        # mid line check        
        if maxline and len(buf) > maxline:
            yield buf, True

        if "\n" in buf:
            (line, buf) = buf.split("\n", 1)
            err = maxline and len(line) > maxline
            yield line+"\n", err
        else:
            more = sock.recv(16)
            if not more:
                done = True
            else:
                buf = buf+more
    if buf:
        err = maxline and len(buf) > maxline
        yield buf, err


HOST = ''                
PORT = 50007             
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
for line, err in linesplit(conn, MAXLINE):
    if err:
        print "Error: Line greater than allowed length %d (got %d)" \
                % (MAXLINE, len(line))
        break
    else:
        print "Received data:", line.strip()
conn.close()

<强> client.py

import socket
import time
import random

HOST = ''    
PORT = 50007             
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    val = 'x'*random.randint(1, 50)
    if random.random() > .5:
        val += "\n"
    s.sendall(val)
    time.sleep(.1)
s.close()

<强>输出

Connected by ('127.0.0.1', 57912)
Received data: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Received data: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Received data: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
...
Received data: xxxxxxxxxxx
Received data: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Error: Line greater than allowed length 100 (got 102)

服务器读取它收到的数据,并在组装一行后不断检查线路的长度。如果该行超过指定的数量,则返回错误代码。我把它放在一起很快,所以我确信可以清理更多的检查,并且可以更改读取缓冲区的数量,以解决在消耗过多数据之前检测长行的速度。在上面的输出示例中,我只有比允许的多2个字节,并且它停止了。

客户端只发送随机长度数据,换行更换50/50。

答案 1 :(得分:1)

我真的不喜欢接受那些没有真正回答问题的答案,所以这就是我实际上最终采用的方法,如果没有人有更好的解决方案,我只会将其标记为社区维基或稍后无人接听:

#!/usr/bin/env python3
class TheThing(object):
    def __init__(self, connection, maxlinelen=8192):
        self.connection = connection
        self.lines = self._iterlines()
        self.maxlinelen = maxlinelen

    def _iterlines(self):
        """
        Yield lines from class member socket object.
        """
        buffered = b''
        while True:
            received = self.connection.recv(4096)
            if not received:
                if buffered:
                    raise Exception("Unexpected EOF.")
                yield received
                continue

            elif buffered:
                received = buffered + received

            if b'\n' in received:
                for line in received.splitlines(True):
                    if line.endswith(b'\n'):
                        if len(line) > self.maxlinelen:
                            raise LineTooLong("Line size: %i" % len(line))
                        yield line
                    else:
                        buffered = line
            else:
                buffered += received

            if len(buffered) > self.maxlinelen:
                raise LineTooLong("Too much data in internal buffer.")

    def _readline(self):
        """
        Return next available line from member socket object.
        """
        return next(self.lines)

我没有费心去比较代码是肯定的,但我做的连接和分割更少,所以我认为我的效率更高。