Re: [info-performer] Non-blocking UDP in Performer

Date view Thread view Subject view Author view

From: Dan Johnston (dan.johnston++at++nrc.ca)
Date: 08/23/2004 12:13:45


"Kevin C. Chao" wrote:

> Hi,
>
> I am trying to use non-blocking UDP sockets in my performer application but
> could not get it to work.
> I am able to make the blocking UDP working. When I change it to non-blocking
> mode with "ioctl(sid, FIONBIO, ...);", my recvfrom() returns immediately
> with -1 (i.e. no data is read to the buffer).
>
> How do I fix this? Thanks.
>
> Kevin
>

Well... you can't. When the socket is non-blocking then recvfrom will

always return immediately (unless you set a time delay). You must

check the error code from recvfrom.

Here is some code pieces for how I do it...

I create my socket(s) like this....

    /* X input done, now initialize the datagram sockets */
    if( chan == 0 )
    {
        /* create control socket */
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if ( sock < 0)
        {
            pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
                "openXSocketInput() Can't open datagram socket");
            return;
        }

        if( blocking_mode == NONBLOCKING )
            /* set the socket to be non-blocking */
            if( fcntl( sock, F_SETFL, FNDELAY ) < 0 )
            {
                pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
                "openXSocketInput() Can't set datagram socket to non-blocking");
                return;
            }

        /*just in case, clear the address struct */
        bzero( (void *)&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        /* The constant INADDR_ANY tells the system
         * that we will accept a connection from any
         * Internet interface on the system.
         */

        /* Assign server port number */
        server.sin_port = socketData->controlPortNumber;

        /* name the socket - I don't know why this is required, but it is */
        if (bind(sock, (struct sockaddr *)&server, sizeof server) < 0 )
        {
            pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
                "openXSocketInput() Can't bind the datagram socket");
            return;
        }
        /* pass on the socket for communications */
        socketData->controlSocket = sock;

        /* all done sockets - set the flags to say we are ready */

    } /* end of if chan == 0 */

Then I set the socket to 'interupt' when it is ready...

            /* we only do this (check for sockets) if input is default */
            if( chan == 0 )
            {
                /* look for any socket events or X11 events */
                /* The old method was to block waiting for an X11 event, then
                 * go get it. Now we have to check for (one of several)
                 * network/socket events OR an X11 event. The selected way
                 * (pun intended) is an interrupt-like 'select' method to
                 * do a blocking wait from any source.
                 */
                /* set up the file descriptors */
                FD_ZERO( &inputFDSet );
                /* the X11 input stream */
                fdX11 = ConnectionNumber( DeviceInput->ichan[chan].dsp );
                FD_SET( fdX11, &inputFDSet );
                /* the standard keyboard/mouse emulating socket stream */
                FD_SET( socketData->controlSocket, &inputFDSet );
                /* if set, the optional user socket stream */
                if( socketData->userDefinedSocket > 0 )
                    FD_SET( socketData->userDefinedSocket, &inputFDSet );

                /* now use 'switch' function to wait (forever) until some
                 * input is ready
                 */
                switch (select(FD_SETSIZE, &inputFDSet, (fd_set *)NULL,
                              (fd_set *)NULL, NULL))
                {
                case 0: /* timeout should never happen! */
                    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
                          "mpXInput() unexpected timeout!");
                    perror("select error");
                    break;
                case -1: /* Error, should call select again */
                    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
                          "mpXInput() select error!");
                    perror("select error");
                    break;
                default: /* Data is ready */
                    /* find out which file descriptor is ready */
                    if (FD_ISSET(socketData->controlSocket, &inputFDSet))
                    {
                        /* control socket is ready to be read from. */
                        /* read (should be ready!) control input events */
                        i = recvfrom( socketData->controlSocket, data_buffer,
                                    MAX_MESSAGE_SIZE, 0, 0, 0 );
                        if( i < 0 )
                        {
                            pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
                                "mpXInput() Can't read control datagram socket");
                            perror("recvfrom error");
                            exit(1);
                        }
                        /* process the socket message (terminate string) */
                        data_buffer[i] = '\0';
                        collectXSocketInput(chan, data_buffer);
                    }
                    else if (FD_ISSET(socketData->userDefinedSocket, &inputFDSet))
                    {
                        /* user defined socket is ready to be read from. */
                        /* read the (should be ready!) user socket input events */
                        i = recvfrom( socketData->userDefinedSocket,
                                    socketData->dataBufferUser,
                                    MAX_USER_MESSAGE_SIZE, 0, 0, 0 );
                        if( i < 0 )
                        {
                            pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
                                "mpXInput() Can't read user datagram socket");
                            perror("recvfrom error");
                            exit(1);
                        }
                        /* we have a user message - store it */
                        socketData->dataBufferUser[i] = '\0';
                        socketData->dataLengthUser = i;
                    }
                    else /*(FD_ISSET(fdX11, &read_template)) */
                    {
                        /* do a blocking check for X11 events and process
                         * them if any
                         */
                        XPeekEvent(DeviceInput->ichan[chan].dsp, &event);
                        collectXSocketInput(chan, NULL);
                    }
                    break;

                } /* end switch */
            }
            else /* if chan = 0 */
            {
                /* for non-default channels do the normal X11 input */
                XPeekEvent(DeviceInput->ichan[chan].dsp, &event);
                collectXSocketInput(chan,NULL);
            }

Notice that when the 'select' function indicates that the datagram

socket is ready, only then do I call recvfrom to grab the message

and call another routine to process it.

On Win32 you have to do even more. I had to create a 'pseudo'

window to receive the socket events.

Good luck!!

--
      ___|__    |
      /  |  \   ||\      Daniel (Dan) Johnston
     /___|___\  || \     Dan.Johnston++at++nrc.gc.ca
    _____|____  ||  \    National Research Council of Canada, London, ON
    |    |    | ||   \   Integrated Manufacturing Technologies Institute
\___|    |    | ||____\  Tel: (519) 430-7081   Fax: (519) 430-7090
 \_o_\___|____|_|______\_    Inst: http://www.nrc.gc.ca/imti
  \                o /    These opinions are my own! Not those of NRC.
   \________________/    Virtual Reality:
                        http://www.nrc.ca/imti/vetc/home.html
 More Tall Ships - Fewer Computers!


Date view Thread view Subject view Author view

This archive was generated by hypermail 2b29 : Mon Aug 23 2004 - 12:19:00 PDT