2017-02-15 3 views
4

У меня есть библиотека Python, которая использует ctypes для регистрации обратного вызова с помощью библиотеки C. Подпись обратного вызова:Как использовать ctypes для recv_into в буфере C несколько раз?

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

В этом случае третий аргумент является указателем на массив байт, выделенный библиотекой C, и четвертый аргумент его длина.

Я хотел бы заполнить массив байтов данными из сети, позвонив по телефону socket.recv_into. Что еще более важно, я хотел бы, чтобы полностью заполнил этот массив байтов: то есть, если recv_into возвращает меньше байтов, чем четвертый аргумент этой функции, то я бы снова позвонил 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 дописывает в буфер, а не перезаписывать ранее записанные данные?

+0

После вашего комментария к другому ответу: работает ли он, если вы наложили кусок буфера и затем выполнили 'recv_into'? Как 'ctypes.cast (buffer [start: end], ctypes.POINTER (ctypes.c_byte))'. Недостатком является то, что вы должны предоставить конец срезу, иначе интерпретатор жалуется на слишком большую строку. – alexpeits

+0

Я так не думаю. Кусочек буфера дает мне список целых чисел, который, как представляется, был скопирован из базового буфера. Это не бросается. – Lukasa

ответ

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-коде, который передает предварительно выделенный буфер в обратный вызов, что i зарегистрированных в коде python.

+0

Это довольно близко к ответу, который мне нужен. Я закончил тем, что уменьшил количество кода, изменив обратный вызов, чтобы он получал 'c_void_p' вместо' POINTER (c_byte) ', что означает, что первая строка может стать' (ctypes.c_char * size) .from_address (buffer) ' , Однако остальная часть этого кода точно верна и работает отлично. – Lukasa

0

Я считаю, что хитрость заключается в том, чтобы обернуть его в memoryview:

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

Боюсь, что это не сработает. Чтобы получить указатель, подобный тому, который у меня есть, вы должны сделать это: 'buf = ctypes.create_string_buffer (b'fooxxx '); ptr = ctypes.cast (buffer, ctypes.POINTER (c_byte)) 'и затем работать с' ptr'. Если вы попробуете это, вы обнаружите, что получаете «IndexError», когда вы это делаете. – Lukasa

+0

Мы решили проблему @ Lukasa, изменив «POINTER (c_byte)» на «POINTER (c_void_p)», а затем используя '(c_char * 1) .from_address()'. Для «нормальных» буферов ctypes приведенный выше ответ верен. –

0

Вот пример кода, который показывает, как добавить recv_into «добавить» в буфер. Множество шаблонов вокруг настройки сокета, но это позволяет сразу же запустить его и увидеть в действии.

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

Это решение проблемы?

+1

Нет. Я не хочу создавать новый буферный объект и выделять для него память.Я хотел бы сообщить ctypes, чтобы создать новый объект буфера из указателя и размера, который у меня уже есть, так что он не выделяет новый буфер, а использует тот, который мне предоставил код C. – Lukasa

Смежные вопросы