如何使用ctypes多次recv_into C缓冲区?

时间:2017-02-15 16:45:54

标签: python ctypes

我有一个使用ctypes来注册C库回调的Python库。回调的签名是:

CFUNCTYPE(c_int_32, c_uint_32, POINTER(c_byte), POINTER(c_size_t))

在这种情况下,第三个参数是指向C库分配的字节数组的指针,第四个参数是其长度。

我想通过调用socket.recv_into来填充来自网络的数据的字节数组。更重要的是,我想完全填充这个字节数组:也就是说,如果recv_into返回的字节少于此函数的第四个参数,那么我想调用{{1}再次。

假设我的函数如下所示:

recv_into

我的问题是:如何操纵def callback(first, second, buffer, size): total_read = 0 while total_read < size[0]: total_read += some_socket.recv_into(buffer, size[0] - total_read) # XXX: What happens here??? 的值以确保每次调用buffer都附加到缓冲区,而不是覆盖以前写入的数据?

3 个答案:

答案 0 :(得分:2)

在评论的基础上,没有中间演员:

# assuming we know OFFSET (where to start copying in the buffer)
# and AMOUNT (how many bytes we will read)
tmp_buf = (ctypes.c_char * size).from_address(ctypes.addressof(buffer.contents))
mview = memoryview(tmp_buf)[OFFSET:AMOUNT]
_ = sock.recv_into(mview, AMOUNT)

buffer.contents返回指向缓冲区的指针,因此可以使用ctypes

提取其地址

这对我来说是一个简单的C代码,它将预先分配的缓冲区传递给一个在python代码中注册的回调。

答案 1 :(得分:0)

我相信诀窍是把它包装成一个记忆视图:

>>> buf = ctypes.create_string_buffer(b"fooxxx")
>>> sock.recv_into(memoryview(buf)[3:],3)  # receiving b"bar"
3
>>> buf.value
b'foobar'

答案 2 :(得分:0)

这是一个代码示例,展示如何recv_into&#34;追加&#34;进入缓冲区。设置套接字周围有很多样板,但是这样可以让你立即运行它并看到它正在运行。

import socket
import ctypes


_BUFFER_SIZE = 20
BufferType = ctypes.c_byte * _BUFFER_SIZE


if __name__ == '__main__':

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('127.0.0.1', 10000))
    s.listen(1)
    c, _ = s.accept()

    buffer = BufferType()
    offset = 0
    available = _BUFFER_SIZE
    memview = memoryview(buffer)

    while offset < _BUFFER_SIZE:
        print('recv_into: offset=%r, available=%r' % (offset, available))
        count = c.recv_into(memview[offset:], available)
        offset += count
        available -= count

    print('done: buffer=%r' % (memview.tobytes(),))

    c.close()
    s.close()

这会解决您的问题吗?