This is the mail archive of the cygwin-developers@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Interruptable connect



On Tue, 25 Feb 2003, Corinna Vinschen wrote:

> On Tue, Feb 25, 2003 at 03:32:08PM +0100, Corinna Vinschen wrote:
>
> Btw. Thomas, do you have the Stevens book "UNIX Network Programming"?
> The end of chapter 15.5 contains the sub chapter "Interrupted connect"
> which states
>
>   What happens if our call to connect on a normal blocking socket is
>   interrupted, say, by a caught signal, before TCP's three-way handshake
>   completes?  Assuming the connect is not automatically restarted, it
>   returns EINTR.  But we cannot call connect again to wait for the
>   connect to complete.  Doing so will return EADDRINUSE.
>
>   What we must do in this scenario is call select, just as we have done
>   in this section for a nonblocking connect.  Then select returns when
>   the connection completes successfully (making the socket writable) or
>   when the connection fails (making the socket readable and writable).
>
> So, IIUC, connect isn't supposed to stop the TCP layer further trying
> to connect!  Which means, I hope, appropriately screwing your accept()
> code into connect() in fhandler_socket.cc should be completely sufficient
> to solve our problem.
>

This is in accordance with

http://www.opengroup.org/onlinepubs/007904975/functions/connect.html :

If connect() is interrupted by a signal that is caught while blocked
waiting to establish a connection, connect() shall fail and set errno to
[EINTR], but the connection request shall not be aborted, and the
connection shall be established asynchronously.
[...]
When the connection has been established asynchronously, select() and
poll() shall indicate that the file descriptor for the socket is ready for
writing.


The connect can be changed to look like this :

int fhandler_socket::connect (const struct sockaddr *name, int namelen)
{
  WSAEVENT ev[2] = { WSA_INVALID_EVENT, signal_arrived };
  int res = -1;
  BOOL secret_check_failed = FALSE;
  BOOL in_progress = FALSE;
  sockaddr_in sin;
  int secret [4];


  if (!get_inet_addr (name, namelen, &sin, &namelen, secret))  {
    return -1;

  if (!is_nonblocking () && !is_connect_pending ())
    {
      ev[0] = WSACreateEvent ();
      WSAEventSelect (sock, ev[0], FD_CONNECT);
    }

  res = ::connect (get_socket (), (sockaddr *) &sin, namelen);

  if (res && !is_nonblocking () && !is_connect_pending () &&
    WSAGetLastError () == WSAEWOULDBLOCK)
  {

    wait_result = WSAWaitForMultipleEvents (2, ev, FALSE, 0, FALSE);

    if (wait_result == WSA_WAIT_EVENT_0)
      WSAEnumNetworkEvents (sock, ev[0], &sock_event);

    /* Unset events for connecting socket and
       switch back to blocking mode */
    WSAEventSelect (get_socket (), ev[0], 0);
    unsigned long nonblocking = 0;
    ioctlsocket ( get_socket (), FIONBIO, &nonblocking);

    switch (wait_result)
      {
        case WSA_WAIT_EVENT_0:
          if (sock_event.lNetworkEvents & FD_CONNECT)
            {
              if (sock_event.iErrorCode[FD_CONNECT_BIT])
                {
                  WSASetLastError (sock_event.iErrorCode[FD_CONNECT_BIT]);
                  set_winsock_errno ();
                }
                else
                  res = 0;
            }

         /* else; : Should never happen since FD_CONNECT is the only
            event that has been selected */
	 break;

         case WSA_WAIT_EVENT_0 + 1:
           debug_printf ("signal received during connect");
           WSASetLastError (WSAEINPROGESS);
           set_errno (EINTR);
           break;

         case WSA_WAIT_FAILED:
	 default: /* Should never happen */
	   WSASetLastError (WSAEFAULT);
	   set_winsock_errno ();
           break;
      }
  }

  if (res)
    {
     /* Special handling for connect to return the correct error code
	 when called on a non-blocking socket. */
      if (is_nonblocking ())

[...]

  if (ev[0] != WSA_INVALID_EVENT)
    WSACloseEvent (ev[0]);

  return res;
}

A second attempt to connect will return WSAEALREADY.

To avoid a second call to WSAEventSelect WSAEINPROGESS is set when a
signal arrived, this will set the connect state to CONNECT_PENDING.

I have not tested this code yet. I will generate a patch if you agree.


BTW, i see a really strange behaviour with select and getsockopt, it seems
that SO_ERROR is set some time after the select is signaled. Here is a
WIN32 test case :

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>

#include <stdio.h>

int main(void)
{
  fd_set wrfds;
  fd_set excfds;

  WSADATA wsaData;
  SOCKET sock;

  int res;
  int sock_error;
  int sock_error_len = sizeof(sock_error);

  struct sockaddr_in s_server;

  unsigned long nonblocking;

  WSAStartup( 0x202, &wsaData );

  sock = socket(AF_INET,SOCK_STREAM,0);

  s_server.sin_family = AF_INET;
  s_server.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
  s_server.sin_port = htons( 5678 );

  nonblocking = 1;
  res = ioctlsocket (sock, FIONBIO, &nonblocking);

  if (connect (sock, (struct sockaddr*) &s_server, sizeof (s_server)) !=
0)
    {
      if (WSAGetLastError () != WSAEWOULDBLOCK)
        {
          printf( "%d\n", WSAGetLastError());
          return 0;
        }
    }

  nonblocking = 0;
  res = ioctlsocket (sock, FIONBIO, &nonblocking);

  FD_ZERO(&wrfds);
  FD_ZERO(&excfds);
  FD_SET(sock, &wrfds);
  FD_SET(sock, &excfds);

  select (FD_SETSIZE, NULL, &wrfds, &excfds, NULL);

  if (FD_ISSET(sock, &wrfds))
    {
      res = getsockopt (sock, SOL_SOCKET, SO_ERROR, &sock_error,
&sock_error_len);
      printf( "wr: %d %d\n", res, sock_error);
    }
  else if (FD_ISSET(sock, &excfds))
    {
      do
        {
          res = getsockopt (sock, SOL_SOCKET, SO_ERROR, &sock_error,
&sock_error_len);
          printf( "ex: %d %d\n", res, sock_error);
          Sleep (0);
        } while (sock_error == 0);
    }

  closesocket(sock);

  return 0;
}

The first call to getsockopt returns 0 in sock_error, the second will
return 10061 as expected.

This is on NT4 German, SP6

Can you duplicate this ?

Thomas


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]