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:
-
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 -
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. -
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. -
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. -
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;
}