Thursday, November 25, 2010

Linux sockets example


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>


#define EPOLL_ARRAY_SIZE   64


void sprint_buffer(const char *buffer, int size)
{
   
int i;
   
for (i = 0; i < size; i++)
   
{
     
if (isprint(buffer[i]))
         printf
("%c", buffer[i]);
     
else
         printf
("\\0x%02X", buffer[i]);
   
}
}

int main(int argc, char *argv[])
{
   
int sd, efd, clientsd, fd;
   
struct sockaddr_in bindaddr, peeraddr;
   socklen_t salen
= sizeof(peeraddr);
   
int pollsize = 1;
   
struct epoll_event ev;
   
struct epoll_event epoll_events[EPOLL_ARRAY_SIZE];
   uint32_t events
;
   
unsigned short port = 20000;
   
int i, rval, on = 1;
   ssize_t rc
;
   
char buffer[1024];


   efd
= epoll_create(pollsize);

   
if (efd < 0)
   
{
      printf
("Could not create the epoll fd: %m");
     
return 1;
   
}

   sd
= socket(AF_INET, SOCK_STREAM, 0);
   
if (sd < 0)
   
{
      printf
("Could not create new socket: %m\n");
     
return 2;
   
}

   printf
("New socket created with sd %d\n", sd);

   
if (fcntl(sd, F_SETFL, O_NONBLOCK))
   
{
      printf
("Could not make the socket non-blocking: %m\n");
      close
(sd);
     
return 3;
   
}

   
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
   
{
      printf
("Could not set socket %d option for reusability: %m\n", sd);
      close
(sd);
     
return 4;
   
}

   bindaddr
.sin_addr.s_addr = htonl(INADDR_ANY);
   bindaddr
.sin_family= AF_INET;
   bindaddr
.sin_port = htons(port);

   
if (bind(sd, (struct sockaddr *) &bindaddr, sizeof(struct sockaddr_in)) < 0)
   
{
      printf
("Could not bind socket %d to address 'INADDR_ANY' and port %u: %m", sd, port);
      close
(sd);
     
return 5;
   
}
   
else
   
{
      printf
("Bound socket %d to address 'INADDR_ANY' and port %u\n", sd, port);
   
}

   
if (listen(sd, SOMAXCONN))
   
{
      printf
("Could not start listening on server socket %d: %m\n", sd);
     
goto cleanup;
   
}
   
else
   
{
      printf
("Server socket %d started listening to address 'INADDR_ANY' and port %u\n", sd, port);
   
}

   ev
.events = EPOLLIN;
   ev
.data.u64 = 0LL;
   ev
.data.fd = sd;

   
if (epoll_ctl(efd, EPOLL_CTL_ADD, sd, &ev) < 0)
   
{
      printf
("Couldn't add server socket %d to epoll set: %m\n", sd);
     
goto cleanup;      
   
}

   
for (;;)
   
{
      printf
("Starting epoll_wait on %d fds\n", pollsize);

     
while ((rval = epoll_wait(efd, epoll_events, EPOLL_ARRAY_SIZE, -1)) < 0)
     
{
         
if ((rval < 0) && (errno != EINTR))
         
{
            printf
("EPoll on %d fds failed: %m\n", pollsize);
           
goto cleanup;
         
}
     
}

     
for (i = 0; i < rval; i++)
     
{
         events
= epoll_events[i].events;
         fd
= epoll_events[i].data.fd;

         
if (events & EPOLLERR)
         
{
           
if (fd == sd)
           
{
               printf
("EPoll on %d fds failed: %m\n", pollsize);
               
goto cleanup;
           
} else
           
{
               printf
("Closing socket with sd %d\n", fd);
               shutdown
(fd, SHUT_RDWR);
               close
(fd);
               
continue;
           
}
         
}

         
if (events & EPOLLHUP)
         
{
           
if (fd == sd)
           
{
               printf
("EPoll on %d fds failed: %m\n", pollsize);
               
goto cleanup;
           
} else
           
{
               printf
("Closing socket with sd %d\n", fd);
               shutdown
(fd, SHUT_RDWR);
               close
(fd);
               
continue;
           
}
         
}

         
if (events & EPOLLRDHUP)
         
{
           
if (fd == sd)
           
{
               printf
("EPoll on %d fds failed: %m\n", pollsize);
               
goto cleanup;
           
} else
           
{
               printf
("Closing socket with sd %d\n", fd);
               shutdown
(fd, SHUT_RDWR);
               close
(fd);
               
continue;
           
}
         
}

         
if (events & EPOLLOUT)
         
{
           
if (fd != sd)
           
{
               rc
= snprintf(buffer, sizeof(buffer), "Hello socket %d from server socket %d!\n", fd, sd);
               
while ((rc = send(fd, buffer, rc, 0)) < 0)
               
{
                 
if ((fd < 0) && (errno != EINTR))
                 
{
                     printf
("Send to socket %d failed: %m\n", fd);
                     pollsize
--;
                     shutdown
(fd, SHUT_RDWR);
                     close
(fd);
                     
continue;
                 
}
               
}

               
if (rc == 0)
               
{
                  printf
("Closing socket with sd %d\n", fd);
                  pollsize
--;
                  shutdown
(fd, SHUT_RDWR);
                  close
(fd);
                 
continue;
               
} else if (rc > 0)
               
{
                  printf
("Sent '");
                  sprint_buffer
(buffer, rc);
                  printf
("' to socket with sd %d\n", fd);

                  ev
.events = EPOLLIN;
                  ev
.data.u64 = 0LL;
                  ev
.data.fd = fd;

                 
if (epoll_ctl(efd, EPOLL_CTL_MOD, fd, &ev) < 0)
                 
{
                     printf
("Couldn't modify client socket %d in epoll set: %m\n", fd);
                     
goto cleanup;      
                 
}
               
}
           
}
         
}

         
if (events & EPOLLIN)
         
{
           
if (fd == sd)
           
{
               
while ((clientsd = accept(sd, (struct sockaddr *) &peeraddr, &salen)) < 0)
               
{
                 
if ((clientsd < 0) && (errno != EINTR))
                 
{
                     printf
("Accept on socket %d failed: %m\n", sd);
                     
goto cleanup;
                 
}
               
}

               
if (inet_ntop(AF_INET, &peeraddr.sin_addr.s_addr, buffer, sizeof(buffer)) != NULL)
               
{
                  printf
("Accepted connection from %s:%u, assigned new sd %d\n", buffer, ntohs(peeraddr.sin_port), clientsd);
               
} else
               
{
                  printf
("Failed to convert address from binary to text form: %m\n");
               
}

               ev
.events = EPOLLIN;
               ev
.data.u64 = 0LL;
               ev
.data.fd = clientsd;    

               
if (epoll_ctl(efd, EPOLL_CTL_ADD, clientsd, &ev) < 0)
               
{
                  printf
("Couldn't add client socket %d to epoll set: %m\n", clientsd);
                 
goto cleanup;      
               
}

               pollsize
++;
           
} else
           
{
               
while ((rc = recv(fd, buffer, sizeof(buffer), 0)) < 0)
               
{
                 
if ((fd < 0) && (errno != EINTR))
                 
{
                     printf
("Receive from socket %d failed: %m\n", fd);
                     pollsize
--;
                     shutdown
(fd, SHUT_RDWR);
                     close
(fd);
                     
continue;
                 
}
               
}

               
if (rc == 0)
               
{
                  printf
("Closing socket with sd %d\n", fd);
                  pollsize
--;
                  shutdown
(fd, SHUT_RDWR);
                  close
(fd);
                 
continue;
               
} else if (rc > 0)
               
{
                  printf
("Received '");
                  sprint_buffer
(buffer, rc);
                  printf
("' from socket with sd %d\n", fd);

                  ev
.events = EPOLLIN | EPOLLOUT;
                  ev
.data.u64 = 0LL;
                  ev
.data.fd = fd;

                 
if (epoll_ctl(efd, EPOLL_CTL_MOD, fd, &ev) < 0)
                 
{
                     printf
("Couldn't modify client socket %d in epoll set: %m\n", fd);
                     
goto cleanup;      
                 
}
               
}
           
}
         
}
     
}
   
}

cleanup
:
   shutdown
(sd, SHUT_RDWR);
   close
(sd);

   
return 0;
}

Wednesday, November 24, 2010

Hello World

I will post mostly technical, programming related stuff, that at some point later I could use as reference.