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

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

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
×