2012-03-03 15 views
3

私はデータを処理するためにidTCPServerを使用しています。 新しいデバイスの場合、ソケットをdll(そのソケットから読み取るためにtcpサーバーを停止する)に引き渡す必要があります。TCPサーバー:ハンドオーバーソケット接続

IndyまたはICSで可能ですか?

[編集] ハードウェアの不足のため、テスト目的のために、DLLを使用する代わりにスレッドを作成しました。

procedure TfrmIndyMain.IdTCPServer1Connect(AContext: TIdContext); 
    begin 
    FMyThread := TMyThread.Create(true); 
    FMyThread.FSocket := AContext.Binding.Handle; 
    FMyThread.Resume; 
    end; 

    procedure TfrmTest.IdTCPServer1Execute(AContext: TIdContext); 
    begin 
    // Its necessary that indy stop reading from the socket, without reset a lot of messages is lost 
    AContext.Binding.Reset(false); //not sure if this is correct 
    end; 

    procedure TMyThread.Execute; 
    var 
    len: integer; 
    data: string; 
    begin 
    ioctlsocket(FSocket, FIONREAD, len); 
    if len>0 then 
    begin 
     Setlength(data, len); 
     if recv(FSocket, pointer(data)^, len, 0) <> SOCKET_ERROR then 
     Log('Data: ' + data) 
     else 
     Log('Error'); 
    end; 
    end; 

答えて

4

はインディでは、あなたがTIdPeerThread.Connection.Binding.Socket財産(インディ9およびそれ以前)またはTIdContext.Connection.Socket.Binding.Handleプロパティを使用することができます(インディ10 - TIdContext.Binding.Handleなどのショートカット)が基礎となるSOCKETハンドルにアクセスします。

更新:Indyは、Indyベースの読み取り操作中にソケットから読み取られるデータのInputBufferを維持します。これには、への呼び出しが含まれ、イベントの連続するトリガー間でTIdTCPServerが呼び出されます。したがって、キャッシュされたデータについてはInputBufferを見ていないため、ソケットにあるすべてのデータが表示されない可能性があります。

procedure TfrmTest.IdTCPServer1Execute(AContext: TIdContext); 
var 
    ret: Integer; 
    data: AnsiString; 
    tv: timeval; 
    fd: fd_set; 
begin 
    repeat 
    FD_ZERO(@fd); 
    FD_SET(AContext.Binding.Handle, @fd); 
    tv.tv_sec := 1; 
    tv.tv_usec := 0; 
    ret := select(0, @fd, nil, nil, @tv); 
    if ret = SOCKET_ERROR then 
    begin 
     Log('Error on select()'); 
     Break; 
    end; 
    if ret = 0 then Continue; 
    ret := ioctlsocket(AContext.Binding.Handle, FIONREAD, len); 
    if ret = SOCKET_ERROR then 
    begin 
     Log('Error on ioctlsocket()'); 
     Break; 
    end; 
    if len < 1 then Break; 
    SetLength(data, len); 
    len := recv(AContext.Binding.Handle, Pointer(data)^, len, 0); 
    if len = SOCKET_ERROR then 
    begin 
     Log('Error on recv()'); 
     Break; 
    end; 
    SetLength(data, len); 
    Log('Data: ' + data); 
    until (some stop condition); 
    AContext.Connection.Disconnect; 
end; 

可能な場合は、よりよい解決策は次のようになります。それを回避するには、例えば、イベントが唯一のイベント中にあなた自身の読書のループを実行することにより、一度トリガーを確認し、インディの読み取りメソッドのいずれかの呼び出しを回避しなければなりませんロジックを変更してもはやソケットに直接アクセスする必要はありません。 Indyがすべての読み取りを正常に行い、受け取ったデータを残りのコードに渡して処理させます。例:

procedure TfrmTest.IdTCPServer1Execute(AContext: TIdContext); 
var 
    data: TMemoryStream; 
begin 
    with AContext.Connection.IOHandler do 
    begin 
    if InputBufferIsEmpty then 
    begin 
     CheckForDataOnSource(1000); 
     CheckForDisconnect(True); 
     if InputBufferIsEmpty then Exit; 
    end; 
    end; 
    data := TMemoryStream.Create; 
    try 
    with AContext.Connection.IOHandler do 
     ReadStream(data, InputBuffer.Size, False); 
    // use data.Memory up to data.Size bytes as needed... 
    finally 
    data.Free; 
    end; 
end; 
+0

Remy、私は質問を更新しました。時々データがありません.... –

+0

アップデートありがとうございます。おそらく私の質問は十分に明確ではなかったが、1つのタイプのデバイスではdllがソケットを処理しています。その場合、私はindyにソケットの読み込みを止めさせたいと思っています(何らかの「手を離してください」または「dllに渡す」)。 dllはソケットが必要な第三者のライブラリです。私のテストコードを別のスレッドに移動し、OnExecuteの無限ループの結果、すべてのメッセージが読み込まれます。またはAContext.Binding.Reset(false);それが正しい方法であるかどうかはわかりません。 –