我正在使用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;
答案 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;