2012-03-03 108 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数据。这包括呼叫TIdTCPConnection.Connected()TIdTCPServerOnExecute事件的连续触发器之间调用。因此,您可能看不到套接字上的所有数据,因为您没有查看InputBuffer的缓存数据。为了避免这种情况,你就必须确保事件仅通过运行事件中自己的阅读循环触发一次,避免调用任何Indy的阅读方法,如:

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; 

如果可能的话,一个更好的解决办法是改变你的逻辑,不需要直接访问套接字了。让印地做所有的读数正常,然后通过任何接收到的数据的处理代码的其余部分,例如:

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

雷米,我更新了我的问题。我有时会丢失数据.... – 2012-03-04 11:22:24

+0

感谢您的更新。也许我的问题不够清楚,但对于1种类型的设备,DLL正在处理套接字。在这种情况下,我希望indy停止阅读套接字(某种'双手'或'交给dll')。该DLL是第三方库需要一个插座。将我的测试代码移动到一个单独的线程中,并在OnExecute中创建一个无限循环,以便读取所有消息。或者AContext.Binding.Reset(false);不知道这是否是正确的方法。 – 2012-03-04 19:38:59