Code:
/* server.c
Simple server, receive connections, checks for 'hello' and responds,
closing the connection.
Compile with:
gcc -O2 -pthread -o server server.c -lpthread
*/
// Needed because we're using strerror_r, GNU style.
#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// Max, 16 simultaneous connections requests.
#define QUEUE_SIZE 16
static void *thread_routine( void * );
int main( void )
{
// setup an efemeral port 8080 at ANY address.
struct sockaddr_in sin = { .sin_port = htons(8080) };
int fd; // socket file descriptor.
// Create an stream TCP/IP socket.
if ( ( fd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) < 0 )
{
perror( "socket" );
return EXIT_FAILURE;
}
// before binding, set reuse addr flag for the socket.
{
// for solaris change this to char and set to '1' (0x31).
int n = 1;
setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof n );
}
// bind the address of this server socket to the descriptor.
if ( bind( fd, (struct sockaddr *)&sin, sizeof sin ) )
{
perror( "bind" );
close( fd );
return EXIT_FAILURE;
}
// Puts the socket in listening mode.
if ( listen( fd, QUEUE_SIZE ) )
{
perror( "listen" );
close( fd );
return EXIT_FAILURE;
}
fputs( "Waiting connections...\n", stderr );
// Accept connections and spawn threads...
while ( 1 )
{
struct sockaddr_in sin_remote;
socklen_t size = sizeof sin_remote;
int conn_fd; // connection socket descriptor.
// accept will block if there is no incomming connection.
if ( ( conn_fd = accept( fd, &sin_remote, &size ) ) > 0 )
{
pthread_t tid;
pthread_attr_t attr;
char buffer[INET_ADDRSTRLEN];
// Show accepted connection.
inet_ntop( AF_INET, &sin_remote.sin_addr, buffer, sizeof buffer );
fprintf( stderr, "Connection from %s:%hu\n", buffer, ntohs(sin_remote.sin_port) );
// Spawn a new detached thread.
pthread_attr_init( &attr );
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
/* NOTE: setup the thread stack size if you want to limit them. */
//pthread_attr_setstacksize( &attr, _SC_THREAD_STACK_MIN );
pthread_create( &tid, &attr, thread_routine, &conn_fd );
pthread_attr_destroy( &attr );
}
else
{
// 256 bytes should be enough!
char buffer[256];
// strerror_r is thread safe!
fprintf( stderr, "Error accepting connection: %s\n", strerror_r(errno, buffer, sizeof buffer) );
}
}
// never gets here!
return EXIT_SUCCESS;
}
// Out thread routine.
void *thread_routine( void *paramp )
{
static char *data[] = { "Hello, professor Falken.\n"
"I don't want to play a nice game of chess right now.\n\n",
"What?!\n"
"Cannot understand you. Bye!\n\n" };
char *p;
char buffer[33] = { 0 };
ssize_t size;
int fd;
fd = *(int *)paramp;
// SIMPLE server... I'm not testing for disconnections (size == 0)
// or insufficient data...
if ( ( size = recv( fd, buffer, sizeof buffer - 1, 0 ) ) > 0 )
{
// The client will send "GET" followed or not by \r or \n
p = buffer + size - 1;
while ( p >= buffer && ( *p == '\r' || *p == '\n' ) )
*p-- = '\0';
// shortcut to:
// if ( ! strcasecmp( buffer, "hello" )
// p = data[0];
// else
// p = data[1];
p = data[ !! strcasecmp( buffer, "hello" ) ];
send( fd, p, strlen( p ) - 1, 0 );
}
close( fd );
// fputs() is MT-Safe!
fputs( "Connection closed.\n", stderr );
return NULL;
}