简体   繁体   English

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

[英]How do I use ctypes to recv_into a C buffer multiple times?

I have a Python library that is using ctypes to register a callback with a C library. 我有一个Python库,它使用ctypes向C库注册回调。 The signature of the callback is: 回调的签名是:

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

In this case, the third argument is a pointer to a byte array allocated by the C library, and the fourth argument is its length. 在这种情况下,第三个参数是指向C库分配的字节数组的指针,第四个参数是其长度。

I would like to populate the byte array with data from the network by calling socket.recv_into . 我想通过调用socket.recv_into来填充来自网络数据的字节数组。 More importantly, I would like to completely fill this byte array: that is, if recv_into returns fewer bytes than the fourth argument of this function then I'd like to call recv_into again. 更重要的是,我想完全填充这个字节数组:也就是说,如果recv_into返回的字节数少于此函数的第四个参数,那么我想再次调用recv_into

Suppose my function looks like this: 假设我的函数看起来像这样:

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???

My question is: how can I manipulate the value of buffer to ensure that each call to recv_into appends to the buffer, rather than overwriting previously-written data? 我的问题是:如何操作buffer的值以确保每次调用recv_into附加到缓冲区,而不是覆盖以前写入的数据?

Building on the comments, without intermediate casting: 在评论的基础上,没有中间铸造:

# 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 returns the pointer to the buffer, so its address can be extracted using ctypes buffer.contents返回指向缓冲区的指针,因此可以使用ctypes提取其地址

This worked for me in a simple C code that passes a pre-allocated buffer into a callback, that is registered in the python code. 这对我来说是一个简单的C代码,它将预先分配的缓冲区传递给一个在python代码中注册的回调。

I believe the trick is to wrap it into a memoryview: 我相信诀窍是把它包装成一个内存视图:

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

Here's a code sample that shows how to have recv_into "append" into the buffer. 这是一个代码示例,演示如何将recv_into “追加”到缓冲区中。 Lots of boilerplate around setting up a socket but this allows you to run it immediatelly and see it in action. 设置套接字的大量样板文件,但这允许您立即运行它并查看它的运行情况。

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()

Does this solve your problem? 这会解决您的问题吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM