TCP服务器:切换套接字连接

时间:2012-03-03 17:37:10

标签: delphi

我正在使用idTCPServer来处理数据。 对于新设备,我需要将套接字移交给dll(停止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;

1 个答案:

答案 0 :(得分:4)

在Indy中,您可以使用TIdPeerThread.Connection.Binding.Socket属性(Indy 9及更早版本)或TIdContext.Connection.Socket.Binding.Handle属性(Indy 10 - 快捷方式为TIdContext.Binding.Handle)来访问基础{{1}处理。

更新:Indy维护在基于Indy的读取操作期间从套接字读取的SOCKET数据。这包括对InputBuffer的调用,TIdTCPConnection.Connected()TIdTCPServer事件的连续触发器之间调用OnExecute。因此,有可能您没有看到套接字上的所有数据,因为您没有查看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;

如果可能,更好的解决方案是将逻辑更改为不再需要直接访问套接字。让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;