#1
  1. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    6
    Rep Power
    0

    fgets not blocking on stdin?


    Hello. I have a program that is using fgets() to block program execution on stdin and wait for user input. My problem is that fgets is not blocking program flow, and the lack of input causes my program to crash.

    I'm playing around with C sockets and using SIGIO.

    My program:

    1. Takes the port to listen to as an argument to the program name on the command line. ( main() )

    2. Creates and binds a socket. ( connect_sock() )

    3. Creates a sigaction structure and sets SIGIO's default action using sigaction(). ( setSIGIO() )

    4. Sets the socket attributes F_SETOWN to the programs pid and F_SETFL to O_NONBLOCK | FASYNC using fcntl(). ( set_sock() )

    5. Then in a while loop my program prompts the user for an IP address and then blocks with the mysterious fgets(). Ideally, the user enters the IP address of a remote host, then is prompted for and enters a port number, and then a message to send to a remote host. After all this, the program sends the message to the port number of the IP address. ( UseIdleTime() )

    6. All this while the same program sits there on the remote host, and if a message is received, a SIGIO signal is thrown, the socket is read, and the received message is printed for the user to read. ( SIGIOHandler() )

    Ideally...

    Now my problem is that in step five ( UseIdleTime() ), the fgets() will not block, and the program rushes past my request for input, tries to send() a message with no IP address, port number or message, and crashes.

    Why is fgets() not blocking? fgets() reads it's input from stdin. Is the problem with fgets() or have I done something to stdin? I've tried calling fflush(stdin); but it didn't help.

    The strange thing is, the first version of this program ran fine. It had steps 1 through 4 in the main(), step 5 was a function called in the main(), and 6 is the SIGIO signal handler.

    My second version put steps 2, 3 and 4 all in seperate functions, all called from main(). Now the fgets() will not block on stdin.

    Can anybody help me with this problem?

    For the intrepid, the code of the second version is below.

    #include <stdio.h>
    #include <netinet/in.h> /* to define `struct in_addr' and IPPROTO_UDP */
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/file.h>
    #include <signal.h>
    #include <errno.h>

    void UseIdleTime();
    void SIGIOHandler(int signalType);
    int connect_sock(int port);
    void setSIGIO();
    void set_sock(int sock);

    int sock; /* GLOBAL -- for signal handler */
    int tries = 0; /* Global - for timer */

    int main(int argc, char *argv[])
    {
    unsigned short echoServPort;

    if(argc != 2)
    {
    fprintf(stderr, "Usage: %s<UDP SERVER PORT>\n", argv[0]);
    exit(0);
    }

    echoServPort = atoi(argv[1]);

    sock = connect_sock(echoServPort);

    setSIGIO();

    set_sock( sock );

    while(1)
    UseIdleTime();

    return 0;
    }

    /***************************************
    * connect_sock
    *
    * Usage: to create a socket, set the address structure
    * and bind the socket. Then return an integer descriptor.
    *
    * Parameters : none.
    *
    * Returns : An integer file descriptor.
    **************************************/
    int connect_sock(int port)
    {
    int the_sock;
    int test_result;
    struct sockaddr_in echoServAddr;

    the_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sock < 0)
    {
    perror("socket() failed");
    exit(0);
    }

    memset(&echoServAddr, 0, sizeof(echoServAddr));
    echoServAddr.sin_family = AF_INET;
    echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    echoServAddr.sin_port = htons(port);

    test_result = bind(the_sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr));
    if(test_result < 0)
    {
    perror("bind() failed");
    exit(0);
    }

    return sock;
    }

    /***************************************
    * setSIGIO
    *
    * Usage : To create a sigaction struct and
    * set SIGIO's default action using
    * sigaction.
    *
    * Parameters : none.
    *
    * Returns : nothing.
    ****************************************/
    void setSIGIO()
    {
    int test_result;
    struct sigaction handler;

    /*************/
    handler.sa_handler = SIGIOHandler;
    test_result = sigfillset(&handler.sa_mask);
    if(test_result < 0)
    {
    perror("sigfillset() failed");
    exit(0);
    }
    handler.sa_flags = 0;
    /*************/

    /************/
    test_result = sigaction(SIGIO, &handler, 0);
    if(test_result < 0)
    {
    perror("sigaction() failed");
    exit(0);
    }
    /************/
    }

    /*****************************************
    * set_sock
    *
    * Usage : Set the ownership of SIGIO to this
    * process, and nonblocking and asynchronous
    * I/O.
    *
    * Parameters : Integer socket descriptor
    *
    * Returns : Void.
    *****************************************/
    void set_sock(int the_sock)
    {
    int test_result;

    /*************/
    test_result = fcntl(the_sock, F_SETOWN, getpid());
    if(test_result < 0)
    {
    perror("first call to fcntl failed");
    exit(0);
    }

    /****************/
    test_result = fcntl(the_sock, F_SETFL, O_NONBLOCK | FASYNC);
    if(test_result < 0)
    {
    perror("second call to fcntl() failed");
    exit(0);
    }
    /***************/
    }

    /*****************************************
    * UseIdleTime
    *
    * Usage : Blocks on stdin waiting for a
    * IP address, port number, and a
    * message to send.
    *
    * Parameters : None.
    *
    * Returns : Void.
    *****************************************/
    void UseIdleTime()
    {
    char buffer[80];
    int bufferLen;
    int test_result;
    struct sockaddr_in remoteAddr;

    test_buffer = (char *)malloc(80*sizeof(char));

    printf("\nEnter remote IP address: ");
    fgets(buffer, sizeof(buffer), stdin);

    memset(&remoteAddr, 0, sizeof(remoteAddr));
    remoteAddr.sin_family = AF_INET;
    remoteAddr.sin_addr.s_addr = inet_addr(buffer);

    printf("\nEnter Port: ");
    fgets(buffer, sizeof(buffer), stdin);

    remoteAddr.sin_port = htons(atoi(buffer));


    printf("\nEnter message: ");
    fgets(buffer,sizeof(buffer), stdin);

    bufferLen = strlen(buffer);

    test_result = sendto(sock, buffer, bufferLen, 0, (struct sockaddr *)&remoteAddr, sizeof(remoteAddr));
    if(test_result != bufferLen)
    {
    perror("sendto() failed");
    exit(0);
    }
    }

    /******************************************
    * SIGIOHandler
    *
    * Usage : When a SIGIO signal is thrown, this
    * program catches it, and this is the handler.
    * It receives a message, sends the message, and then
    * calls UseIdleTime to wait for user input.
    *
    * Parameters : An integer denoting signal type
    *
    * Returns : Void.
    *******************************************/
    void SIGIOHandler(int signalType)
    {
    int test_result;
    struct sockaddr_in echoClntAddr;
    unsigned int clntLen;
    int recvMsgSize;
    char echoBuffer[255];

    do
    {
    clntLen = sizeof(echoClntAddr);

    recvMsgSize = recvfrom(sock, echoBuffer, 255, 0, (struct sockaddr *) &echoClntAddr, &clntLen);
    if(recvMsgSize < 0)
    {
    if(errno != EWOULDBLOCK)
    {
    perror("recvfrom() failed");
    exit(0);
    }
    }
    else
    {
    echoBuffer[recvMsgSize] = '\0';
    printf("Message from %s \n", inet_ntoa(echoClntAddr.sin_addr));
    printf("sock num: %i\n", sock);
    printf("buffer contents: %s\n", echoBuffer);
    printf("size of message: %i\n", recvMsgSize);

    /* Used to send response message
    * to do - fix echo problem
    strcpy(echoBuffer, "Message Received");
    test_result = sendto(sock, echoBuffer, recvMsgSize, 0, (struct sockaddr *)&echoClntAddr, clntLen);
    if(test_result != recvMsgSize)
    {
    perror("sendto() failed");
    exit(0);
    }*/
    }

    }while(recvMsgSize >= 0);
    UseIdleTime();
    }
  2. #2
  3. not a fan of fascism (n00b)
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Feb 2003
    Location
    ct
    Posts
    2,756
    Rep Power
    95
    my guess is that this line:
    Code:
    fcntl(the_sock, F_SETFL, O_NONBLOCK | FASYNC);
    is actually setting STDIN(0) to non-blocking. check for socket errors. use strace to verify what's happening.

    edit: yes im correct in the connect() function you create the sock with:
    the_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    and then bind with
    test_result = bind(the_sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr));

    but then you test and return the variable 'sock' instead.
    Last edited by infamous41md; August 21st, 2003 at 10:06 AM.
  4. #3
  5. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    6
    Rep Power
    0
    Never mind, found the error. Sloppy use of a global variable.

    I named the socket file descriptor 'sock', and made it global. I then used a local variable in my socket manipulation functions named 'the_sock'. The thing was, a few times I forgot, and used 'sock' (the global) instead of 'the_sock' in the functions that create and manipulate the socket.

    Thanks to all who had a look.
  6. #4
  7. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    6
    Rep Power
    0
    infamous41md:

    Yes, you're completely right.
    Thank you for the help.

IMN logo majestic logo threadwatch logo seochat tools logo