Socket programming

Hi everyone,

I'm new to this forum. I'm working on new project for last few days and this forum already helped me on
couple of occasions. I don't have any prior experience with network programming so I'll appreciate any advise given.

I'm trying to do the following:

  1. open user specified port for TCP connection and set LISTEN mode. I must be able to respond to client request but
    I must also be able to send data to clientin case of an event

  2. I'm using interval timer in my application (SIGALRM signal every 1 s). I found article
    describing connect() and interrupted system calls. I solved this problem using sigaction flag SA_RESTART.

  3. I haven't figure out a way to resume communication in case of broken pipe (caused by restart of client application).
    I solved some of problem with SO_REUSEADDR option.

  4. I'm not sure will I be able to send data to client in case of event. Response time is not the issue. The easiest way is
    to send data in timer interrupt but that is not the best practice.

  5. I must resume application in case on unexpected events (signals, exception)

Any suggestion, advise, example etc. will be greatly appreciated.

Best regards,
Tomislav

//   -----------------------------------------------------------------------------
///                               Headers
//  -----------------------------------------------------------------------------

#include  <stdio.h>
#include <stdlib.h>
#include  <sys/ioctl.h>
#include <sys/poll.h>
#include  <sys/socket.h>
#include <sys/time.h>
#include  <netinet/in.h>
#include <errno.h>
#include  <unistd.h>
#include <string.h>
#include  <sys/socket.h>
#include <sys/types.h>
#include  <arpa/inet.h>
#include <time.h>
#include  <signal.h>
#include <fcntl.h>


//  -----------------------------------------------------------------------------
///  Print a system error message.
//  -----------------------------------------------------------------------------
void  error(const char *err, int fd)
{
    perror(err); 
    if(fd  != -1)
        close(fd);

    exit(EXIT_FAILURE);
}

void  timer_handler (int signum)
{
    // Code
}

void  start_timer(void)
{

    struct sigaction sa;
     struct  itimerval timer;

    memset (&sa, 0, sizeof (sa));
    
      sa.sa_handler = &timer_handler;
    sa.sa_flags = SA_RESTART;

     sigaction(SIGALRM, &sa, NULL);

    timer.it_value.tv_sec =  1;
     timer.it_value.tv_usec = 0;
    
      timer.it_interval.tv_sec = 1;
     timer.it_interval.tv_usec = 0;

     setitimer(ITIMER_REAL, &timer, NULL);
}

//  -----------------------------------------------------------------------------
///  Main program.
//  -----------------------------------------------------------------------------
int  main ()
{

    struct sockaddr_in   addr;
    struct pollfd  fds[200];

    int    len, rc, on = 1, flags;
      int     socketFD = -1, new_sd = -1;
      int    end_server = 0,  compress_array = 0;
      int    nfds = 1, current_size = 0, i, j;
       int    close_conn;
      int    timeout;
    unsigned char    buffer[80];

    start_timer();
    
      // Create an  AF_INET stream socket to receive incoming connections  on                                           
      socketFD =  socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(socketFD < 0)
            error("socket() failed", -1);

      // Allow socket  descriptor to be reuseable                     
      rc =  setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR,(char *)&on,  sizeof(on));
      if (rc < 0)
        error("setsockopt()  failed", socketFD);

      // Set socket to be nonblocking
       rc = ioctl(socketFD, FIONBIO, (char *)&on);
      if (rc <  0)
        error("ioctl() failed", socketFD);
                               
      memset(&addr, 0, sizeof(addr));
       addr.sin_family      = AF_INET;
      addr.sin_addr.s_addr =  htonl(INADDR_ANY);
      addr.sin_port        = htons(2404);

     // Bind the socket 
    rc = bind(socketFD,(struct sockaddr  *)&addr, sizeof(addr));
    if (rc < 0)
         error("bind() failed", socketFD);

      // Set listen to 32  completely established sockets waiting to be  accepted                                   
      rc =  listen(socketFD, 32);
      if (rc < 0)
        error("listen()  failed", socketFD);

      // Initialize the pollfd  structure                           
      memset(fds, 0 ,  sizeof(fds));

      // Set up the initial listening  socket                        
      fds[0].fd = socketFD;
       fds[0].events = POLLIN;
    
      // Initialize the timesocout to  3 minutes
      timeout = (20);

      do 
      {
         // Call poll() and wait 3 minutes for it to complete.   
         printf("Waiting on poll()...\n");        
        if ((rc = poll(fds,  nfds, timeout)) < 0)
        {
              perror("  poll()  failed");
              break;
        }

        // Check  to see if the 3 minute time out expired.    
        if (rc == 0)
         {
              printf("  poll() timed out.  End program.\n");
               //break;
            continue;
        }
        //  One or more descriptors are readable. Need to determine which  ones.           
        current_size = nfds;
        for (i = 0; i  < current_size; i++)
        {
              // Loop through  to find the descriptors that returned POLLIN and 
            //  determine whether it's the listening  or the active connection. 
             if(fds.revents == 0)
                continue;

               // If revents is not POLLIN, it's an unexpected result
               if(fds.revents != POLLIN)
              {
                 printf("  Error! revents = %d\n", fds.revents);
                 end_server = 1;
                break;
              }
             
            // Listening descriptor is readable
             if (fds.fd == socketFD)
              {
                 printf("  Listening socket is readable\n");

                //  Accept all incoming connections that are queued up on the listening 
                 // socket before we loop back and call poll  again.                  
                do
                {
                       // Accept each incoming connection. If accept fails with  EWOULDBLOCK, 
                    // then we have accepted all of  them. Any other  failure on accept will 
                    // cause  us to end the server.                                 
                     new_sd = accept(socketFD, NULL, NULL);

                     if (new_sd < 0)
                      {
                         if (errno != EWOULDBLOCK)
                        {
                               perror("  accept() failed");
                               end_server = 1;
                        }
                         break;
                      }

                       // Add the new incoming connection to the pollfd  structure                                
                       printf("  New incoming connection - %d\n", new_sd);
                       fds[nfds].fd = new_sd;
                      fds[nfds].events =  POLLIN;
                      nfds++;

                      //  Loop back up and accept another incoming  connection                                      
                }  while (new_sd != -1);
              }

              // This is  not the listening socket, therefore an existing connection must be  readable     
              else
              {
                 printf("  Descriptor %d is readable\n", fds.fd);
                 close_conn = 0;
                  
                // Receive  all incoming data on this socket before we loop back and call poll  again.  
                while(1)
                {
                       // Receive data on this connection until the recv fails with  EWOULDBLOCK. 
                    // If any other failure occurs, we  will close the connection.                                
                     rc = recv(fds.fd, buffer, sizeof(buffer), 0);
                       if (rc < 0)
                      {
                         if (errno != EWOULDBLOCK)
                        {
                               perror("  recv() failed");
                               // close_conn = 1;
                        }
                            break;
                      }

                       // Check to see if the connection has been  closed by the client      
                      if (rc == 0)
                      {
                         printf("  Connection closed\n");
                         close_conn = 1;
                        break;
                       }

                      // Data was  received                                
                    // SEND  response back code goes here 
    
                }

                 // If the close_conn flag was turned on, we need to clean up  this 
                // active connection. This clean up process  includes removing the       
                //  descriptor.                                         
                 if (close_conn)
                {
                       close(fds.fd);
                      fds.fd = -1;
                       compress_array = 1;
                }
                }              
        }                                     
         // If the compress_array flag was turned on, we need to squeeze  together the 
        // array and decrement the number of file  descriptors. We do not need to move 
        // back the events and  revents fields because the events will always be POLLIN 
        //  in this case, and revents is output.         
        if  (compress_array)
        {
              compress_array = 0;
               for (i = 0; i < nfds; i++)
              {
                 if (fds.fd == -1)
                {
                       for(j = i; j < nfds; j++)
                      {
                         fds[j].fd = fds[j+1].fd;
                      }
                       nfds--;
                }
              }
        }
     } while (end_server == 0); /* End of serving running.    */

       // Clean up all of the sockets that are open                  
     for (i = 0; i < nfds; i++)
      {
        if(fds.fd >=  0)
              close(fds.fd);
      }
    
    return  0;
    
}



Is the lcient listening on a known port. If not you cannot call connect() and get a connection to your client. It has nothing to do with the listening port, necessarily, unless you have a simplex connection (both sides have a listener).

I personally believe select() is a better choice for what you are doing than is poll().
Of course that is opinion. Some implementaions support pselect() = even better.

Consider some reading. Normally what socket apps do:
loop thru their collection of listeners looking for a connect request. Take a nanosleep() or usleep() and loop again.

select() is good for this. When you get a connection request create a thread to play with it. The thread exits when the socket is closed on the remote side. Or the connection errors out.

I think beej still has this site going, it is a very good place to get some basic ideas. I just connected it still works. Amazing.
Beej's Guide to Network Programming

1 Like

Yes, client is on known port. I don't have a problem with this part of code, I managed to send and receive in both ways.

Why do you think select is a better choice? I
Thank you for the advice and link. I will go back to reading.