如何使用 ctypes 多次 recv_into 一个 C 缓冲区?
How do I use ctypes to recv_into a C buffer multiple times?
我有一个 Python 库,它使用 ctypes
向 C 库注册回调。回调的签名是:
CFUNCTYPE(c_int_32, c_uint_32, POINTER(c_byte), POINTER(c_size_t))
在这种情况下,第三个参数是一个指向由C库分配的字节数组的指针,第四个参数是它的长度。
我想通过调用 socket.recv_into
用来自网络的数据填充字节数组。更重要的是,我想 完全填充 这个字节数组:也就是说,如果 recv_into
returns 比这个函数的第四个参数少那么我会想再打电话给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
的值以确保对 recv_into
的每次调用都附加到缓冲区,而不是覆盖以前写入的数据?
我认为诀窍是将其包装到内存视图中:
>>> buf = ctypes.create_string_buffer(b"fooxxx")
>>> sock.recv_into(memoryview(buf)[3:],3) # receiving b"bar"
3
>>> buf.value
b'foobar'
以评论为基础,不进行中间转换:
# 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 指向缓冲区的指针,因此可以使用 ctypes
提取其地址
这在一个简单的 C 代码中对我有用,该代码将 pre-allocated 缓冲区传递到回调中,该回调在 python 代码中注册。
这是一个代码示例,展示了如何将 recv_into
"append" 放入缓冲区。关于设置套接字的大量样板文件,但这允许您立即 运行 它并查看它的实际效果。
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()
这是否解决了您的问题?
我有一个 Python 库,它使用 ctypes
向 C 库注册回调。回调的签名是:
CFUNCTYPE(c_int_32, c_uint_32, POINTER(c_byte), POINTER(c_size_t))
在这种情况下,第三个参数是一个指向由C库分配的字节数组的指针,第四个参数是它的长度。
我想通过调用 socket.recv_into
用来自网络的数据填充字节数组。更重要的是,我想 完全填充 这个字节数组:也就是说,如果 recv_into
returns 比这个函数的第四个参数少那么我会想再打电话给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
的值以确保对 recv_into
的每次调用都附加到缓冲区,而不是覆盖以前写入的数据?
我认为诀窍是将其包装到内存视图中:
>>> buf = ctypes.create_string_buffer(b"fooxxx")
>>> sock.recv_into(memoryview(buf)[3:],3) # receiving b"bar"
3
>>> buf.value
b'foobar'
以评论为基础,不进行中间转换:
# 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 指向缓冲区的指针,因此可以使用 ctypes
这在一个简单的 C 代码中对我有用,该代码将 pre-allocated 缓冲区传递到回调中,该回调在 python 代码中注册。
这是一个代码示例,展示了如何将 recv_into
"append" 放入缓冲区。关于设置套接字的大量样板文件,但这允许您立即 运行 它并查看它的实际效果。
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()
这是否解决了您的问题?