Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-05

fpselect does not work on linux

13 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Rainer Stratmann

To find out if a nonblocking socket has connected I use the following piece of code:

With windows that works, but with linux I get alwas the result that the socket is writable.

function is_writable_socket( sck : integer ) : boolean;
var
  fds : tfdset;
  tv  : timeval;
begin
  {$ifdef linux}   fpfd_zero( fds ); fpfd_set( sck , fds ); {$endif}
  {$ifdef windows}   fd_zero( fds );   fd_set( sck , fds ); {$endif}
  tv.tv_sec  := 0;
  tv.tv_usec := 0;
  //                 socket+1 , read , write , except , timeout
  {$ifdef linux}
  result := fpselect( sck + 1 , nil  , @fds  , nil    , @tv ) > 0;
  {$else}
  result :=   select( sck + 1 , nil  , @fds  , nil    , @tv ) > 0;
  {$endif}
end;

Reply by email

Rainer Stratmann
to Michael Van Canneyt: And why do you think that this is a bug ? Michael.
Michael Van Canneyt wrote 25 lines
On Sat, 9 May 2009, Rainer Stratmann wrote: > To find out if a nonblocking socket has connected I use the following > piece of code: > > With windows that works, but with linux I get alwas the result that the > socket is writable. > > function is_writable_socket( sck : integer ) : boolean; > var > fds : tfdset; > tv : timeval; > begin > {$ifdef linux} fpfd_zero( fds ); fpfd_set( sck , fds ); {$endif} > {$ifdef windows} fd_zero( fds ); fd_set( sck , fds ); {$endif} > tv.tv_sec := 0; > tv.tv_usec := 0; > // socket+1 , read , write , except , timeout > {$ifdef linux} > result := fpselect( sck + 1 , nil , @fds , nil , @tv ) > 0; > {$else} > result := select( sck + 1 , nil , @fds , nil , @tv ) > 0; > {$endif} > end; And why do you think that this is a bug ?

I know that the socket I tested is not writable, but the function returns writable... always with linux.

It is not working on linux operating system. With windows os that works.

Someone else have (had?) exactly the same problem: http://community.freepascal.org:10000/bboards/message?message_id=270583&forum_id=24083 Rainer

Michael. _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Michael Van Canneyt
to Rainer Stratmann: To find out if a nonblocking socket has connected I use the following piece of code: With…
Rainer Stratmann wrote 22 lines
To find out if a nonblocking socket has connected I use the following piece of code: With windows that works, but with linux I get alwas the result that the socket is writable. function is_writable_socket( sck : integer ) : boolean; var fds : tfdset; tv : timeval; begin {$ifdef linux} fpfd_zero( fds ); fpfd_set( sck , fds ); {$endif} {$ifdef windows} fd_zero( fds ); fd_set( sck , fds ); {$endif} tv.tv_sec := 0; tv.tv_usec := 0; // socket+1 , read , write , except , timeout {$ifdef linux} result := fpselect( sck + 1 , nil , @fds , nil , @tv ) > 0; {$else} result := select( sck + 1 , nil , @fds , nil , @tv ) > 0; {$endif} end;

And why do you think that this is a bug ?

Michael.

Reply by email

Jonas Maebe
to Rainer Stratmann: To find out if a nonblocking socket has connected I use the following piece of code: With…
Rainer Stratmann wrote 20 lines
To find out if a nonblocking socket has connected I use the following piece of code: With windows that works, but with linux I get alwas the result that the socket is writable. function is_writable_socket( sck : integer ) : boolean; var fds : tfdset; tv : timeval; begin {$ifdef linux} fpfd_zero( fds ); fpfd_set( sck , fds ); {$endif} {$ifdef windows} fd_zero( fds ); fd_set( sck , fds ); {$endif} tv.tv_sec := 0; tv.tv_usec := 0; // socket+1 , read , write , except , timeout {$ifdef linux} result := fpselect( sck + 1 ,

Where does the "+ 1" come from?

nil , @fds , nil , @tv ) > 0;

This is probably unrelated to your problem, but you have a bug here. (fp)select can also return EINTR (possibly under Windows as well, I don't know), in which case you have to retry. You need something like this:

repeat
   res:=fpselect( sck + 1 , nil  , @fds  , nil    , @tv );
until (res-1) or (fpgeterrnoESysEIntr);

Note that you have to use this construct for many low level unix calls. Check the man pages to see which routines can return EINTR and may need restarting.

Jonas

Reply by email

Michael Van Canneyt
to Rainer Stratmann: I know that the socket I tested is not writable, but the function returns writable...…
Rainer Stratmann wrote 33 lines
Am Samstag, 9. Mai 2009 12:17 schrieb Michael Van Canneyt: > On Sat, 9 May 2009, Rainer Stratmann wrote: > > To find out if a nonblocking socket has connected I use the following > > piece of code: > > > > With windows that works, but with linux I get alwas the result that the > > socket is writable. > > > > function is_writable_socket( sck : integer ) : boolean; > > var > > fds : tfdset; > > tv : timeval; > > begin > > {$ifdef linux} fpfd_zero( fds ); fpfd_set( sck , fds ); {$endif} > > {$ifdef windows} fd_zero( fds ); fd_set( sck , fds ); {$endif} > > tv.tv_sec := 0; > > tv.tv_usec := 0; > > // socket+1 , read , write , except , timeout > > {$ifdef linux} > > result := fpselect( sck + 1 , nil , @fds , nil , @tv ) > 0; > > {$else} > > result := select( sck + 1 , nil , @fds , nil , @tv ) > 0; > > {$endif} > > end; > > And why do you think that this is a bug ? I know that the socket I tested is not writable, but the function returns writable... always with linux. It is not working on linux operating system. With windows os that works. Someone else have (had?) exactly the same problem: http://community.freepascal.org:10000/bboards/message?message_id=270583&forum_id=24083

The call functions correctly, IMHO, but the behaviour of the call is not as you expect.

Select does *not* tell you if a file descriptor is writeable or readable. It tells you if the read/write operation will block. This is something subtly different.

For instance, it reports file descriptor 0 as writable, which is rather strange, since it is read-only, but correct, because the write call will not block. Indeed the write call will return at once with an error condition.

This is what the kernel sees (I used strace to test your code with descriptor 0):

select(1, NULL, [0], NULL, {0, 0}) = 1 (out [0], left {0, 0})

Which is exactly what I sent.

In short, is_writable_socket should be implemented differently, not using select.

Michael.

Reply by email

Unknown author
to Michael Van Canneyt

According to the FPC rtl.pdf version 2.2.2 Doc version 2.1 of June 2008, page 165, states, and I quote: "Description:FPSelect checks one of the file descriptors in the FDSets to see if its status changed."

//-------------------------------------------------------------------------
Michael Van Canneyt wrote 63 lines
On Sat, 9 May 2009, Rainer Stratmann wrote: > Am Samstag, 9. Mai 2009 12:17 schrieb Michael Van Canneyt: > > On Sat, 9 May 2009, Rainer Stratmann wrote: > > > To find out if a nonblocking socket has connected I use the following > > > piece of code: > > > > > > With windows that works, but with linux I get alwas the result that > > > the socket is writable. > > > > > > function is_writable_socket( sck : integer ) : boolean; > > > var > > > fds : tfdset; > > > tv : timeval; > > > begin > > > {$ifdef linux} fpfd_zero( fds ); fpfd_set( sck , fds ); {$endif} > > > {$ifdef windows} fd_zero( fds ); fd_set( sck , fds ); {$endif} > > > tv.tv_sec := 0; > > > tv.tv_usec := 0; > > > // socket+1 , read , write , except , timeout > > > {$ifdef linux} > > > result := fpselect( sck + 1 , nil , @fds , nil , @tv ) > 0; > > > {$else} > > > result := select( sck + 1 , nil , @fds , nil , @tv ) > 0; > > > {$endif} > > > end; > > > > And why do you think that this is a bug ? > > I know that the socket I tested is not writable, but the function returns > writable... always with linux. > > It is not working on linux operating system. With windows os that works. > > Someone else have (had?) exactly the same problem: > http://community.freepascal.org:10000/bboards/message?message_id=270583&f >orum_id=24083 The call functions correctly, IMHO, but the behaviour of the call is not as you expect. Select does *not* tell you if a file descriptor is writeable or readable. It tells you if the read/write operation will block. This is something subtly different. For instance, it reports file descriptor 0 as writable, which is rather strange, since it is read-only, but correct, because the write call will not block. Indeed the write call will return at once with an error condition. This is what the kernel sees (I used strace to test your code with descriptor 0): select(1, NULL, [0], NULL, {0, 0}) = 1 (out [0], left {0, 0}) Which is exactly what I sent. In short, is_writable_socket should be implemented differently, not using select. Michael. _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Michael Van Canneyt
to Jonas Maebe: Where does the "+ 1" come from? This is probably unrelated to your problem, but you have…
Jonas Maebe wrote 25 lines
On 09 May 2009, at 09:38, Rainer Stratmann wrote: >To find out if a nonblocking socket has connected I use the following piece >of >code: > >With windows that works, but with linux I get alwas the result that the >socket >is writable. > >function is_writable_socket( sck : integer ) : boolean; >var > fds : tfdset; > tv : timeval; >begin > {$ifdef linux} fpfd_zero( fds ); fpfd_set( sck , fds ); {$endif} > {$ifdef windows} fd_zero( fds ); fd_set( sck , fds ); {$endif} > tv.tv_sec := 0; > tv.tv_usec := 0; > // socket+1 , read , write , except , timeout > {$ifdef linux} > result := fpselect( sck + 1 , Where does the "+ 1" come from?

Man select:
nfds is the highest-numbered file descriptor in any of the three sets, plus 1.

Michael.

Reply by email

Rainer Stratmann
to dmitry boyarintsev: do you check the socket's writability by fpFD_ISSET(sck, fds) ? dmitry
Select does *not* tell you if a file descriptor is writeable or readable. It tells you if the read/write operation will block. This is something subtly different.

This is interesting for blocking mode.
But since you can switch to nonblocking mode every operation will return immediately.

Here are some descriptions I found about select:
http://publib.boulder.ibm.com/infocenter/tpfhelp/current/index.jsp?topic=/com.ibm.ztpf-ztpfdf.doc_put.cur/gtpc2/cpp_select.html http://docs.sun.com/app/docs/doc/816-5168/select-3c?a=view http://www.developerweb.net/forum/showthread.php?p=13486

"...The select socket function monitors a list of file descriptors for readability, readiness for writing, and exception pending conditions."

Of course if you are in a blocking mode then a call does not block. ...if the select call returns that what we expect.

Show quoted text 10 lines
For instance, it reports file descriptor 0 as writable, which is rather strange, since it is read-only, but correct, because the write call will not block. Indeed the write call will return at once with an error condition. This is what the kernel sees (I used strace to test your code with descriptor 0): select(1, NULL, [0], NULL, {0, 0})      = 1 (out [0], left {0, 0})

May be it is better to test it with sockets descriptors. If I get a result that the call will not block (as you say) and then I write data to the descriptor then the program crashes with exitcode 13...

Which is exactly what I sent. In short, is_writable_socket should be implemented differently, not using select.

How can I find out if a socket connection in a nonblocking mode is established?

Michael.
dmitry boyarintsev wrote
hello Rainer, do you check the socket's writability by fpFD_ISSET(sck, fds) ?

Yes, before I did it this way, but it was the same behaviour. Windows ok. Linux failed.

function is_writable_socket2( s : integer ; var error : longint ) : boolean;
var
  fds : tfdset;
  tv : timeval;
  valopt : longint = 1;
  vallen : {$ifdef linux} longword {$else} longint {$endif};
begin
  result := false;
  {$ifdef linux}   fpfd_zero( fds ); fpfd_set( s , fds ); {$endif}
  {$ifdef windows}   fd_zero( fds );   fd_set( s , fds ); {$endif}
  tv.tv_sec  := 0;
  tv.tv_usec := 0;
  //               socket+1 , read , write , except , timeout
  {$ifdef linux}
  if fpselect( s + 1 , nil , @fds , nil , @tv ) > 0 then begin
    if fpfd_isset( s , fds ) = 1 then begin
      vallen := sizeof( valopt );
      if fpgetsockopt( s , sol_socket , so_error , @valopt , @vallen ) = 0
then begin
        error := valopt;
        result := valopt = 0;
      end;
    end;
  end;
  {$else}
  if select( s + 1 , nil , @fds , nil , @tv ) > 0 then begin
    if fd_isset( s , fds ) then begin
      vallen := sizeof( valopt );
      if getsockopt( s , sol_socket , so_error , valopt , vallen ) = 0 then
begin
        error := valopt;
        result := valopt = 0;
      end;
    end;
  end;
  {$endif}
end;
Show quoted text 5 lines
thanks, dmitry _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Rainer

Reply by email

Michael Van Canneyt
to Rainer Stratmann
Rainer Stratmann wrote 6 lines
>Select does *not* tell you if a file descriptor is writeable or readable. >It tells you if the read/write operation will block. >This is something subtly different. This is interesting for blocking mode. But since you can switch to nonblocking mode every operation will return immediately.

And therefor every select() call will indicate that all is OK.

Show quoted text 8 lines
Here are some descriptions I found about select: http://publib.boulder.ibm.com/infocenter/tpfhelp/current/index.jsp?topic=/com.ibm.ztpf-ztpfdf.doc_put.cur/gtpc2/cpp_select.html http://docs.sun.com/app/docs/doc/816-5168/select-3c?a=view http://www.developerweb.net/forum/showthread.php?p=13486 "...The select socket function monitors a list of file descriptors for readability, readiness for writing, and exception pending conditions."

You should check what the linux manpage says. It describes the exact behaviour.

Show quoted text 17 lines
Of course if you are in a blocking mode then a call does not block. ...if the select call returns that what we expect. >For instance, it reports file descriptor 0 as writable, which is rather >strange, since it >is read-only, but correct, because the write call will not block. Indeed the >write call >will return at once with an error condition. > >This is what the kernel sees (I used strace to test your code with descriptor >0): > >select(1, NULL, [0], NULL, {0, 0})      = 1 (out [0], left {0, 0}) May be it is better to test it with sockets descriptors. If I get a result that the call will not block (as you say) and then I write data to the descriptor then the program crashes with exitcode 13...

Yes, and this is normal: if the socket is not open, you get an error when attempting to write to it -> program exits. select() does not say anything about that.

In each case, the call functions correctly, because it's simply passed on to the kernel. I verified several cases.

Show quoted text 8 lines
>Which is exactly what I sent. > >In short, is_writable_socket should be implemented differently, not using >select. How can I find out if a socket connection in a nonblocking mode is established?

This, I don't know.

Michael.

Reply by email

dmitry boyarintsev
to Rainer Stratmann: This is interesting for blocking mode. But since you can switch to nonblocking mode every…
How can I find out if a socket connection in a nonblocking mode is established?

according to the man page, you're doing correct path: http://linux.die.net/man/2/connect
i did use non-blocking connection for both Linux and OSX, and it did work as described: by "selecting" sockets for writting. Thought i used infinite wait time.

Anyway, could you provide more code, on how do you actually establish a server address (sockaddr structure). I remeber there were some differences in Windows sockaddr structure and Linux sockaddr structure (IIRC, IP byte order is messed-up).

thanks,
dmitry

Reply by email

Rainer Stratmann
to dmitry boyarintsev
dmitry boyarintsev wrote 13 lines
> How can I find out if a socket connection in a nonblocking mode is > established? according to the man page, you're doing correct path: http://linux.die.net/man/2/connect i did use non-blocking connection for both Linux and OSX, and it did work as described: by "selecting" sockets for writting. Thought i used infinite wait time. Anyway, could you provide more code, on how do you actually establish a server address (sockaddr structure). I remeber there were some differences in Windows sockaddr structure and Linux sockaddr structure (IIRC, IP byte order is messed-up).

With nonblocking sockets it works.
Also it works with linux if I call connect again and again (nonblocking) until the result from connect says ok, connected.
But I don't know exactly if there is more traffic on the network or internet lines when calling connect often.
Windows may produces traffic every time by calling connect I think. But I am not sure. This was the reason why I tried with select to find out if a connection is ok.
I tested with longer timeouts, but for me there is the same behaviour. Another way to find out can be to program select in c, put it in a library and then call it from fpc.

regards, Rainer

var
 c_connected   : boolean;
 c_socket      : integer;
 inetaddr_client_remote : tsockaddr;
implementation
const port_program = 12021;
procedure memclr( p : pointer ; len : longint );
var x : longint;
begin
 for x := 1 to len do begin
  byte( p^ ) := 0;
  inc( p );
 end;
end;
procedure setsockaddr( var sa : tsockaddr ; adr : string ; port : longint );
begin
 memclr( @sa , sizeof( sa ) );
 sa.sin_family := AF_INET;
 sa.sin_addr   := strtonetaddr( adr );
 sa.sin_port   := htons( port );
end;
function my_connect( s : integer ; var ina : tsockaddr ) : boolean;
var c : longint;
begin
  c := fpconnect( s , @ina , sizeof( ina ) );
  {$ifdef windows}
  if c = SOCKET_ERROR then begin
    if wsagetlasterror = WSAEISCONN then c := 0; // !
  end;
  {$endif}
  result := ( c = 0 );
end;
procedure setnonblockingsocket( s : integer );
var
  nb : dword;
  n : integer;
  arg : longint;
begin
  // nonblocking
  {$ifdef linux}
  arg := fpfcntl( s , F_GETFL );
  if arg >= 0 then begin
   arg := arg or O_NONBLOCK;
   fpfcntl( s , F_SETFL , arg );
  end;
  {$endif}
  {$ifdef windows}
  nb := 1; // 1 = nonblocking, 0 = blocking
  n := winsock2.ioctlsocket( s , FIONBIO , @nb );
  {$endif}
end;
function is_writable_socket( s : integer ; var error : longint ) : boolean;
var
  fds : tfdset;
  tv : timeval;
  valopt : longint = 1;
  vallen : {$ifdef linux} longword {$else} longint {$endif};
begin
  result := false;
  {$ifdef linux}   fpfd_zero( fds ); fpfd_set( s , fds ); {$endif}
  {$ifdef windows}   fd_zero( fds );   fd_set( s , fds ); {$endif}
  tv.tv_sec  := 0;
  tv.tv_usec := 0;
  //               socket+1 , read , write , except , timeout
  {$ifdef linux}
  if fpselect( s + 1 , nil , @fds , nil , @tv ) > 0 then begin
    if fpfd_isset( s , fds ) = 1 then begin
      vallen := sizeof( valopt );
      if fpgetsockopt( s , sol_socket , so_error , @valopt , @vallen ) = 0
then begin
        error := valopt;
        result := valopt = 0;
      end;
    end;
  end;
  {$else}
  if select( s + 1 , nil , @fds , nil , @tv ) > 0 then begin
    if fd_isset( s , fds ) then begin
      vallen := sizeof( valopt );
      if getsockopt( s , sol_socket , so_error , valopt , vallen ) = 0 then
begin
        error := valopt;
        result := valopt = 0;
      end;
    end;
  end;
  {$endif}
end;
function ip_client_start_socket( s : string ) : boolean;
begin
 result := false;
 setsockaddr( inetaddr_client_remote , s , port_program );
 c_socket := fpsocket( PF_INET , SOCK_STREAM , 0 );
 if c_socket >= 0 then begin
   setnonblockingsocket( c_socket ); // nonblocking connect call
   result := my_connect( c_socket , inetaddr_client_remote );
 end;
// setnonblockingsocket( c_socket ); // blocking connect call, if this is
executed later
 c_connected := false;
end;
function ip_client_is_connected( var error : longint ) : boolean;
begin
 if not( c_connected ) then c_connected := is_writable_socket( c_socket ,
error );
 result := c_connected;
end;
procedure ip_client_close_socket;
begin
 if c_socket >= 0 then closesocket( c_socket );
 c_socket := -1;
 c_connected := false;
end;
Show quoted text 5 lines
thanks, dmitry _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email