Insight into something.
////////////////////////////////////////////////////////////////////////////////
/**
* @file guthrn.c
* @brief A file storage server implemented in C.
*
* The server will be multithreaded using the pthreads library. In particular,
* clients connect via sockets. The server must hand off the incoming
* connections to dedicated threads. You can either create threads on-demand or
* manage a pool of available threads.
*
* @title guthrn
* @author Nicholas Guthrie
* @email guthrn@rpi.edu
* @web http//nickguthrie.com
* @created April 22, 2013
*
* Compile with: gcc -g -o file-server file-server.c -lpthread
*/
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------ :Libraries:
// _ _ _ _
// | | (_) |__ _ _ __ _ _ _(_)___ ___
// | |__| | '_ \ '_/ _` | '_| / -_|_-<
// |____|_|_.__/_| \__,_|_| |_\___/__/
//
// -----------------------------------------------------------------------------
#include <stdio.h> // for printf()
#include <stdlib.h> // for exit()
#include <string.h> // for strcpy(),strerror() and strlen()
#include <fcntl.h> // for file i/o constants
#include <sys/stat.h> // for file i/o constants
#include <errno.h>
// FOR BSD UNIX/LINUX
#include <sys/types.h> //
#include <netinet/in.h> //
#include <sys/socket.h> // for socket system calls
#include <arpa/inet.h> // for socket system calls (bind)
#include <sched.h>
#include <pthread.h> // P-thread implementation
#include <signal.h> // for signal
#include <semaphore.h> // for p-thread semaphores
//--------------------------------------------------------------------- :Global:
// ___ _ _ _
// / __| |___| |__ __ _| |
// | (_ | / _ \ '_ \/ _` | |
// \___|_\___/_.__/\__,_|_|
//
// -----------------------------------------------------------------------------
#define BUF_SIZE 1024 // buffer size in bytes
#define PEND_CONNECTIONS 100 // pending connections to hold
#define TRUE 1
#define FALSE 0
#define NTHREADS 5 /* Number of child threads */
#define NUM_LOOPS 10 /* Number of local loops */
#define SCHED_INTVL 5 /* thread scheduling interval */
#define HIGHPRIORITY 10
#define D_STORAGE ".storage"
// global variables ------------------------------------------------------------
sem_t thread_sem[NTHREADS];
int next_thread;
int can_run;
int i_stopped[NTHREADS];
unsigned int client_s; // Client socket descriptor
//----- HTTP response messages -------------------------------------------------
#define YESFILE "FILE EXISTS"
#define NOFILE "NO SUCH FILE"
#define ACK "ACK"
#define ERROR "ERROR"
//------------------------------------------------------------------ :Functions:
// ___ _ _
// | __| _ _ _ __| |_(_)___ _ _ ___
// | _| || | ' \/ _| _| / _ \ ' \(_-<
// |_| \_,_|_||_\__|\__|_\___/_||_/__/
//
//
// -----------------------------------------------------------------------------
void print_id()
{
fprintf(stdout, "\n[thread %d] ", getpid());
}
////////////////////////////////////////////////////////////////////////////////
/**
* @brief Child thread implementation
* @param[in] arg
* @return void*
*/
////////////////////////////////////////////////////////////////////////////////
void *my_thread(void * arg)
{
unsigned int myClient_s; //copy socket
// other local variables ---------------------------------------------------
char in_buf[BUF_SIZE]; // input buffer for GET resquest
char out_buf[BUF_SIZE]; // output buffer for HTML response
char fname[100]; // file name
char command[10]; // command given by user
unsigned int fh; // file handle (file descriptor)
unsigned int buf_len; // buffer length for file reads
unsigned int retcode; // return code
struct stat st; // stores file information
myClient_s = *(unsigned int *)arg; // copy the socket
// receive the command
int pos = 0;
while(1)
{
retcode = recv(client_s, in_buf, 1, 0);
if (retcode < 0)
{
printf("recv error detected ...\n");
return;
}
if(in_buf[0] == ' ')
break;
command[pos++] = in_buf[0];
}
command[pos] = '\0';
// receive the file name
pos = 0;
while(1)
{
retcode = recv(client_s, in_buf, 1, 0);
if (retcode < 0)
{
printf("recv error detected ...\n");
return;
}
if(in_buf[0] == '\n' || in_buf[0] == ' ')
break;
fname[pos++] = in_buf[0];
}
fname[pos] = '\0';
print_id();
fprintf(stdout, "Rcvd: %s %s", command, fname);
fflush(stdout);
char cor_fname[100] = D_STORAGE;
strcat(cor_fname, "/");
strcat(cor_fname, fname);
strcpy(fname, cor_fname);
//////////////////////////////////////////////////////////////////////
// ADD <filename> <bytes>\n<file-contents>
// - add <filename> to the storage server
// - IF the file already exists, return "FILE EXISTS" error
// - return "ACK" if successful, "ERROR" if not
//////////////////////////////////////////////////////////////////////
if(strcmp(command, "ADD") == 0)
{
// File Exists
if( access( fname, F_OK ) != -1 )
{
// Send Message Back
print_id();
fprintf(stdout, "Sent: %s", YESFILE);
fflush(stdout);
strcpy(out_buf, YESFILE);
write(client_s, out_buf, strlen(out_buf) );
}
// File doesn't exist
else
{
// Read the File Size
char sf_size[50];
pos = 0;
while(1)
{
retcode = recv(client_s, in_buf, 1, 0);
if ( retcode < 0 )
{
puts("recv error detected ...\n");
return;
}
if(in_buf[0] == '\n')
break;
sf_size[pos++] = in_buf[0];
}
sf_size[pos] = '\0';
int file_size = (int) atoi(sf_size);
fprintf(stdout, " %d", file_size);
fflush(stdout);
// Write File ------------------------------------------------------
FILE *wfile;
wfile = fopen(fname,"wb+");
// Recieve File Data and Write on Server
int BUFFSIZ = 100;
int remain_data = file_size;
int len;
char * buffer = (char*)malloc(BUFFSIZ * sizeof(char));
while (((len = recv(client_s, buffer, BUFSIZ, 0)) > 0) &&
(remain_data > 0))
{
fwrite(buffer, sizeof(char), len, wfile);
remain_data -= len;
//fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data);
}
fclose(wfile);
//free(buffer);
print_id();
fprintf(stdout, "Transferred file (%d bytes)", file_size);
fflush(stdout);
// Send Success Message to Client
print_id();
fprintf(stdout, "Sent: %s %d", ACK, file_size);
fflush(stdout);
strcpy(out_buf, ACK);
write(client_s, out_buf, strlen(out_buf) );
}
}
//////////////////////////////////////////////////////////////////////
// UPDATE <filename> <bytes>\n<file-contents>
// - update existing file <filename> on the storage server
// - the file's contents will be overwritten
// - IF the file does not already exist, return "NO SUCH FILE" error
// - return "ACK" if successful, "ERROR" if not
//////////////////////////////////////////////////////////////////////
else if(strcmp(command, "UPDATE") == 0)
{
// File Exists
if( access( fname, F_OK ) != -1 )
{
// Read the File Size
char sf_size[50];
pos = 0;
while(1)
{
retcode = recv(client_s, in_buf, 1, 0);
if ( retcode < 0 )
{
puts("recv error detected ...\n");
return;
}
if(in_buf[0] == '\n')
break;
sf_size[pos++] = in_buf[0];
}
sf_size[pos] = '\0';
int file_size = (int) atoi(sf_size);
fprintf(stdout, " %d", file_size);
// Write File ------------------------------------------------------
FILE *wfile;
wfile = fopen(fname,"wb");
// Recieve File Data and Write on Server
int BUFFSIZ = 100;
int remain_data = file_size;
int len;
char * buffer = (char*)malloc(BUFFSIZ * sizeof(char));
while (((len = recv(client_s, buffer, BUFSIZ, 0)) > 0) &&
(remain_data > 0))
{
fwrite(buffer, sizeof(char), len, wfile);
remain_data -= len;
}
print_id();
fprintf(stdout, "Transferred file (%d bytes)", file_size);
fflush(stdout);
// Send Message Back
print_id();
fprintf(stdout, "Sent: %s", ACK);
fflush(stdout);
strcpy(out_buf, ACK);
write(client_s, out_buf, strlen(out_buf) );
}
// File doesn't exist
else
{
print_id();
fprintf(stdout, "Sent: %s", NOFILE);
fflush(stdout);
strcpy(out_buf, NOFILE);
write(client_s, out_buf, strlen(out_buf) );
}
}
//////////////////////////////////////////////////////////////////////
// READ <filename>\n
// - server returns the length (in bytes) and content of <filename>
// - note that this does NOT remove the file on the server
// - IF the file does not exist, return "NO SUCH FILE" error
// - RETURN "ACK" if successful, "ERROR" if not
// - IF "ACK" is sent, follow it with file length and file as follows:
// ACK <bytes>\n<file-contents>
//////////////////////////////////////////////////////////////////////
else if(strcmp(command, "READ") == 0)
{
// File Exists
if( access( fname, F_OK ) != -1 )
{
FILE *xfile; //File to Read from Server
char buf[150]; //Temporary Storage Buffer
xfile = fopen(fname, "rb");
if (xfile == NULL)
{
perror ("The following error occurred");
fprintf( stdout, "Value of errno: %d\n", errno );
}
// Get File Size
stat(fname, &st);
int file_size = st.st_size; //Size of the file as sent
char str[100]; //File Size in a String
sprintf(str, "%d", file_size); //convert int to string
// Add to buf
strcat(buf, "ACK ");
strcat(buf, str);
strcat(buf, "\n");
// Read the File Into An Array
char *bdata = (char*)malloc(strlen(buf) * sizeof(char)
+ file_size * sizeof(char));
char *tdata = (char*)malloc(file_size * sizeof(char));
int pos = 0;
int c;
while ( (c = fgetc(xfile)) != EOF )
{
tdata[pos++] = (char) c;
}
strcat(bdata, buf); //copy command and size
strcat(bdata, tdata); //copy file contents
// Write to Server
write( client_s, bdata, strlen( bdata ) );
print_id();
fprintf(stdout, "Transferred file (%d bytes)", file_size);
fflush(stdout);
// Close Data
fclose(xfile);
free(bdata);
free(tdata);
}
else
{
print_id();
fprintf(stdout, "Sent: %s", ERROR);
fflush(stdout);
strcpy(out_buf, ERROR);
write(client_s, out_buf, strlen(out_buf) );
return;
}
}
////////////////////
// Exiting
////////////////////
close(client_s); // close the client connection
pthread_exit(NULL);
}
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------- :Main:
// __ __ _
// | \/ |__ _(_)_ _
// | |\/| / _` | | ' \
// |_| |_\__,_|_|_||_|
//
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
int main ( int argc, char* argv[] )
{
// Variables ---------------------------------------------------------------
unsigned int server_s; // server socket descriptor
struct sockaddr_in server_addr; // server internet address
struct sockaddr_in client_addr; // client internet address
struct in_addr client_ip_addr; // client IP address
int addr_len; // internet address length
unsigned int ids; // holds thread arguments
pthread_attr_t attr; // pthread attributes
pthread_t threads; // thread ID (for os)
int port_num;
// Create Storage Directory
struct stat info;
if(lstat(D_STORAGE,&info) != 0)
{
// doesn't exist
if(errno == ENOENT)
{
char new_dir[]= D_STORAGE;
if (mkdir(new_dir, S_IRWXU|S_IRGRP|S_IXGRP) != 0)
perror("mkdir() error");
}
}
// Start Server
printf("Started file-server\n");
if (argc == 2)
{
sscanf(argv[1], "%d", &port_num);
printf("Listening on port %d", port_num);
if(port_num < 8000 || port_num > 9000)
{
perror("ERROR: Port number must from 8000-9000.\n");
return -1;
}
}
else
{
perror("ERROR: Port number must from 8000-9000.\n");
return -1;
}
// create a new socket
server_s = socket(AF_INET, SOCK_STREAM, 0);
// fill-in address information, and then bind it
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port_num);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(server_s, (struct sockaddr *)&server_addr, sizeof(server_addr));
// listen for connections and then accept
listen( server_s, PEND_CONNECTIONS );
////////////////////////////////////////
// Main Server Loop
////////////////////////////////////////
pthread_attr_init(&attr);
while(TRUE)
{
// wait for the next client to arrive
addr_len = sizeof( client_addr );
client_s = accept(server_s, (struct sockaddr *)&client_addr, &addr_len);
// error handling when creating socket
if (client_s == FALSE)
{
printf("ERROR - Unable to create socket \n");
exit(FALSE);
}
// create child thread
else
{
printf("\nRecieved incoming connecint from <client-hostname>");
ids = client_s;
pthread_create (&threads, &attr,my_thread,&ids);
}
}
////////////////////
// Exiting
////////////////////
close (server_s); // close the primary socket
return (TRUE);
}