I'm using an idTCPServer for processing data. For a new device I need to hand over the socket to a dll (stop the tcp server to read from that socket).
Is that possible with Indy or ICS?
[Edit] For test purposes I made a thread instead of using the dll, because the lack of hardware.
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;
In Indy, you can use the TIdPeerThread.Connection.Binding.Socket property (Indy 9 and earlier) or the TIdContext.Connection.Socket.Binding.Handle property (Indy 10 - shortcut as TIdContext.Binding.Handle) to access the underlying SOCKET handle.
Update: Indy maintains an InputBuffer of data that is read from the socket during Indy-based reading operations. That includes calls to TIdTCPConnection.Connected(), which TIdTCPServer calls in between successive triggers of the OnExecute event. So it is possible that you do not see all of the data that is on the socket because you are not looking at the InputBuffer for cached data. To avoid that, you would have to make sure the event only triggers once by running your own reading loop inside the event and avoid calling any of Indy's reading methods, eg:
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;
If possible, a better solution would be to change your logic to not need to access the socket directly anymore. Let Indy do all of the reading normally, and then pass any received data to the rest of your code for processing, eg:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With