2017-02-15 16 views
4

私は​​を使ってコールバックをCライブラリに登録するPythonライブラリを持っています。コールバックの署名である:この場合ctypesを使ってCバッファにrecv_intoを何度も繰り返しますか?

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

、第3引数はCライブラリによって割り当てられたバイト配列へのポインタであり、第4引数は、その長さです。

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

私の質問は:どのように私はrecv_intoへの各呼び出しではなく、以前に書き込まれたデータを上書きするよりも、バッファに追加することを確実にするためにbufferの値を操作することができますか?

+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ので、そのアドレス、バッファへのポインタを返します。これは、コールバックに予め割り当てられたバッファを通過する単純なCコードを、その私に私のために働い​​

を用いて抽出することができますPythonコードに登録されています。ここで

+0

これは私が必要としていた答えにかなり近いです。 'POINTER(c_byte)'の代わりに 'c_void_p'を受け取るようにコールバックを変更することでコードの量を減らしました。つまり、最初の行は'(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(バッファ、ctypes.POINTER(c_byte)) 'を呼び出し、' ptr'で操作します。これを試してみると、これを行うときに 'IndexError'が得られるでしょう。 – Lukasa

+0

'POINTER(c_byte)'を 'POINTER(c_void_p)'に変更し、 '(c_char * 1).from_address()'を使用して、@ Lukasaの特有の問題を解決しました。 「通常の」ctypesバッファの場合、上記の答えが当てはまります。 –

0

はバッファに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() 

これで問題は解決しますか?

+1

いいえ:私は新しいバッファオブジェクトを作成し、そのためのメモリを割り当てたくありません。私はctypesにポインタとサイズから新しいバッファオブジェクトを作成するように指示したいので、新しいバッファを割り当てずにCコードを使ってください。 – Lukasa

関連する問題