Jump to content
FearDC

[Need help] Linux recv() timeout

Recommended Posts

I need to perform simple TCP socket peek during Indy server event. To do this I use recv(), but it stays blocking forever if I don't receive required data, not allowing any further server <> client communication.

procedure OnServerBeforeConnect(AContext: TIdContext);
var
  mPeek: Array [0..1] of AnsiChar;
  mTime, mRes: Integer;
begin
{
  // returns error -1
  mTime := 5;
  mRes := SetSockOpt(AContext.Binding.Handle, SOL_SOCKET, SO_RCVTIMEO, mTime, SizeOf(mTime));
}

{
  // nothing happens
  AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_RCVTIMEO, 5);
}

  // hangs forever
  if (Recv(AContext.Binding.Handle, mPeek, 2, MSG_PEEK) = 2) and (mPeek[0] = #22) and (mPeek[1] = #03) then
    TIdSSLIOHandlerSocketOpenSSL(AContext.Connection.IOHandler).PassThrough := False;
end;

On Windows this works just fine, setting SO_RCVTIMEO helps to exit recv() even if needed data is not received.

 

Please help.

 

Regards.

Share this post


Link to post

What SetSockOpt are you using?

In Linux SO_RCVTIMEO parameter is supposed to be a pointer to timeval record. Not Int.

Share this post


Link to post

SetSockOpt from Posix.SysSocket/SysSocketAPI.inc

 

The second one is Indys built-in.

 

I know that Posix wants a time struct, but how to do that in Delphi?

 

Regards.

Share this post


Link to post

Unfortunately I do not have Delphi version new enough. But maybe there is an overloaded version, that allows required parameters?

Indy own TIdSocketHandle.SetScokOpot also has AOptVal:Integer... But GStack.SetSocketOption seems to have required version.

Share this post


Link to post

Finally got it working.

 

type
  TTimeVal = record
    tv_sec, tv_usec: LongInt;
  end;

 

var
  mTime: TTimeVal;
  mRes: Integer;
begin
  mTime.tv_sec := 5;
  mTime.tv_usec := 0;
  // returns 0 - no error
  mRes := Posix.SysSocket.SetSockOpt(AContext.Binding.Handle, SOL_SOCKET, SO_RCVTIMEO, &mTime, SizeOf(TTimeVal));

 

Thank you for hints @Virgo.

Edited by FearDC

Share this post


Link to post
2 hours ago, FearDC said:

var
  mTime: TTimeVal;
  mRes: Integer;
begin
  mTime.tv_sec := 5;
  mTime.tv_usec := 0;
  // returns 0 - no error
  mRes := Posix.SysSocket.SetSockOpt(AContext.Binding.Handle, SOL_SOCKET, SO_RCVTIMEO, &mTime, SizeOf(TTimeVal));

 

Indy's TIdSocketHandle class does not have a SetSockOpt() overload for non-integer inputs, but its TIdStackBSDBase class does (for BSD-based socket stacks, like Linux and Windows):

uses
  ..., IdStackBSDBase;
  
GBSDStack.SetSocketOption(AContext.Binding.Handle, Id_SOL_SOCKET, Id_SO_RCVTIMEO, mTime, SizeOf(TTimeVal));

I probably need to update Indy to expose this overload in TIdSocketHandle and TIdStack for easier use.

Edited by Remy Lebeau

Share this post


Link to post
9 hours ago, FearDC said:

Oh, cool. Does IdStackBSDBase have recv() too?

It has the public Receive() method inherited from TIdStack, but that takes only a socket handle and a TIdBytes as parameters. I assume you want access to the flags parameter of recv()? For that, you can use the protected TIdStackBSDBase.WSRecv() method.

type TIdStackBSDBaseAccess = class(TIdStackBSDBase)
end;

TIdStackBSDBaseAccess(GBSDStack).WSRecv(AContext.Binding.Handle, buffer, length, MSG_PEEK);

 

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×