处理以换行符结束的套接字数据

时间:2017-03-23 21:49:49

标签: python python-2.7 python-3.x sockets tcp

处理套接字连接的最佳方法是什么,我需要var data以换行符结束\n? 我使用下面的代码,但有时tcp数据包会 chunked ,并且匹配data.endswith("\n")需要很长时间。 我还尝试了其他方法,例如,如果它不以\n结尾,则保存最后一行,并在下一个循环中将其附加到data。但这也不起作用,因为多个数据包被分块,第一和第二部分不匹配。 我无法控制另一端,它基本上会发送以\r\n结尾的多行。

任何建议都会受到欢迎,因为我对套接字连接知之甚少。

def receive_bar_updates(s):
    global all_bars
    data = ''
    buffer_size = 4096
    while True:
        data += s.recv(buffer_size)
        if not data.endswith("\n"):
            continue
        lines = data.split("\n")
        lines = filter(None, lines)
        for line in lines:
            if line.startswith("BH") or line.startswith("BC"):
                symbol = str(line.split(",")[1])
                all_bars[symbol].append(line)
                y = Thread(target=proccess_bars, kwargs={'symbol': symbol})
                y.start()
        data = ""

" 正常" data

line1\r\n
line2\r\n
line3\r\n

分块 data的示例:

line1\r\n
line2\r\n
lin

5 个答案:

答案 0 :(得分:4)

如果您要将原始输入作为行处理,则io模块是您的朋友,因为它将在行中进行低级别的数据包组合。

您可以使用:

class SocketIO(io.RawIOBase):
    def __init__(self, sock):
        self.sock = sock
    def read(self, sz=-1):
        if (sz == -1): sz=0x7FFFFFFF
        return self.sock.recv(sz)
    def seekable(self):
        return False

它比endswith('\n')更强大,因为如果一个数据包包含嵌入的换行符('ab\ncd'),io模块将正确处理它。您的代码可能会变成:

def receive_bar_updates(s):
    global all_bars
    data = ''
    buffer_size = 4096
    fd = SocketIO(s)  # fd can be used as an input file object

    for line in fd:
        if should_be_rejected_by_filter(line): continue # do not know what filter does...
        if line.startswith("BH") or line.startswith("BC"):
            symbol = str(line.split(",")[1])
            all_bars[symbol].append(line)
            y = Thread(target=proccess_bars, kwargs={'symbol': symbol})
            y.start()

答案 1 :(得分:1)

使用 socket.socket.makefile() 将套接字包装在实现 Text I/O 的类中。它处理缓冲、字节和字符串之间的转换,并允许您遍历行。记得刷新任何写入。

示例:

#!/usr/bin/env python3
import socket, threading, time


def client(addr):
    with socket.create_connection(addr) as conn:
        conn.sendall(b'aaa')
        time.sleep(1)
        conn.sendall(b'bbb\n')
        time.sleep(1)
        conn.sendall(b'cccddd\n')
        time.sleep(1)
        conn.sendall(b'eeefff')
        time.sleep(1)
        conn.sendall(b'\n')
        conn.shutdown(socket.SHUT_WR)
        response = conn.recv(1024)
        print('client got %r' % (response,))


def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as listen_socket:
        listen_socket.bind(('localhost', 0))
        listen_socket.listen(1)
        addr = listen_socket.getsockname()
        threading.Thread(target=client, args=(addr,)).start()
        conn, _addr = listen_socket.accept()
        conn_file = conn.makefile(mode='rw', encoding='utf-8')
        for request in conn_file:
            print('server got %r' % (request,))
        conn_file.write('response1\n')
        conn_file.flush()


if __name__ == '__main__':
    main()
$ ./example.py
server got 'aaabbb\n'
server got 'cccddd\n'
server got 'eeefff\n'
client got b'response1\n'
$

答案 2 :(得分:0)

我没有测试过这段代码,但它应该可以运行:

def receive_bar_updates(s):
    global all_bars
    data = ''
    buf = ''
    buffer_size = 4096
    while True:
        if not "\r\n" in data:  # skip recv if we already have another line buffered.
            data += s.recv(buffer_size)
        if not "\r\n" in data:
            continue
        i = data.rfind("\r\n")
        data, buf = data[:i+2], data[i+2:]
        lines = data.split("\r\n")
        lines = filter(None, lines)
        for line in lines:
            if line.startswith("BH") or line.startswith("BC"):
                symbol = str(line.split(",")[1])
                all_bars[symbol].append(line)
                y = Thread(target=proccess_bars, kwargs={'symbol': symbol})
                y.start()
        data = buf

编辑:忘记提及,我只修改了接收数据的代码,我不知道函数的其余部分(以lines = data.split("\n")开头)是什么。

编辑2:现在使用" \ r \ n"对于换行而不是" \ n"。

编辑3:修复了问题。

答案 3 :(得分:0)

您是否接受不同的联系?或者它是一个数据流,由\r\n分开?

接受多个连接时,您需要等待与s.accept()的连接,然后处理其所有数据。获得所有数据包后,处理其数据,然后等待下一次连接。 那你做什么取决于每个数据包的结构。 (例如:https://wiki.python.org/moin/TcpCommunication

如果您正在使用数据流,那么您应该处理每一行'你在一个单独的线程中找到了,而你继续在另一个线程中消费。

修改 所以,如果我的情况正确的话;一个连接,数据是由\r\n分解的字符串,以\n结尾。然而,数据与您期望的不一致,而是无限循环等待\n

根据我的理解,套接字接口以空数据结果结束。因此,最后一个缓冲区可能以\n结束,但之后只是继续获取​​None个对象,尝试查找另一个\n

相反,请尝试添加:

if not data:
    break

完整代码:

def receive_bar_updates(s):
    global all_bars
    data = ''
    buffer_size = 4096
    while True:
        data += s.recv(buffer_size)
        if not data:
            break
        if not data.endswith("\n"):
            continue
        lines = data.split("\n")
        lines = filter(None, lines)
        for line in lines:
            if line.startswith("BH") or line.startswith("BC"):
                symbol = str(line.split(",")[1])
                all_bars[symbol].append(line)
                y = Thread(target=proccess_bars, kwargs={'symbol': symbol})
                y.start()
        data = ""

编辑2:糟糕,代码错误

答案 4 :(得分:-1)

你似乎基本上想从套接字读取行。也许您最好不要使用低级recv来电,只需使用sock.makefile()并将结果视为常规文件,您可以在其中读取以下行:from line in sfile: ...

留下延迟/块问题。这可能是由发送方Nagle's algorithm引起的。尝试禁用:

sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)