Skip to content

Instantly share code, notes, and snippets.

@ariyike
Last active May 8, 2019 15:51
Show Gist options
  • Save ariyike/ba991c12a0eab3b40a4e1fc784f4c8d5 to your computer and use it in GitHub Desktop.
Save ariyike/ba991c12a0eab3b40a4e1fc784f4c8d5 to your computer and use it in GitHub Desktop.
A client coded in C to communicate with a server and send messages
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/utsname.h>
#include "rdwrn.h"
#define INPUTSIZ 10
//***********************************************************
// INITIALISING ALL THE FUNCTIONS TO BE CALLED
//***********************************************************
void get_student_details(int);
void get_random_numbers(int);
void get_utname(int);
void get_filenames(int);
void send_options(int, char);
void send_file_name(int);
//***********************************************************
// FUNCTION WHICH GETS THE SERVER UTSNAME DETAILS
//***********************************************************
//this function is based on the starter code provided for sending the employee struct, it gets the machine's details from the utsname struct
void get_utname(int socket)
{
struct utsname send_this; //initialise the struct which will contain the received struct
size_t payload_length;//initialise the size of the payload
size_t n;//do the same for the size of the struct received in bytes
readn(socket, (unsigned char *) &payload_length, sizeof(size_t));//reads the size of the information being sent by the server
n = readn(socket, (unsigned char *) &send_this, payload_length);//reads the information being sent by the server
//print out details of received & altered struct
//printf("%d", (int)sizeof(send_this));
printf("OS name %s\n", send_this.sysname);//print the string to the client terminal
printf("version %s\n", send_this.release);//print the string to the client terminal
printf("release %s\n", send_this.version);//print the string to the client terminal
printf("machine %s\n", send_this.machine);//print the string to the client terminal
printf("nodename %s\n", send_this.nodename);//print the string to the client terminal
printf("(%zu bytes)\n", n);//print the size to the client terminal
}
//***********************************************************
// FUNCTION WHICH SENDS THE INPUT OPTION
//***********************************************************
//this function is based on the starter code provided for the hello_string, it sends the option selected by the user to the server
void send_options(int socket, char option)//it takes an additional argument, different from all the other functions in the form of a char
{
size_t n = sizeof(option) + 1;//derives the size of the message to be sent
writen(socket, (unsigned char *) &n, sizeof(size_t));//write the size of the string being sent
writen(socket, (unsigned char *) &option, n);//write the char being sent
}
//***********************************************************
// FUNCTION WHICH GETS STUDENT DETAILS FROM SERVER
//***********************************************************
//this function is based on the starter code provided for the hello_string, it receives the student details from the server
void get_student_details(int socket)
{
char std_details[200]; //empty string that will contain the output string
size_t k; //size variable that will be used to store the size of the string
readn(socket, (unsigned char *) &k, sizeof(size_t));//reads the size of the information being sent by the server
readn(socket, (unsigned char *) std_details, k);//reads the information being sent
printf("Code written by: %s\n", std_details);//print the string to the client terminal
printf("Received: %zu bytes\n\n", k);//print the size to the client terminal
}
//***********************************************************
// FUNCTION WHICH GETS THE RANDOM NUMBERS FROM THE SERVER
//***********************************************************
//this function is based on the starter code provided for the hello_string, it receives the student details from the server
void get_random_numbers(int socket)
{
size_t g; //size variable that will be used to store the size of the string
readn(socket, (unsigned char *) &g, sizeof(size_t));//reads the size of the information being sent by the server
char random_num[40];//empty string that will contain the output string
readn(socket, (unsigned char *) random_num, g);//reads the information being sent
printf("Your random numbers are: %s\n", random_num);//print the string to the client terminal
printf("Received: %zu bytes\n\n", g);//print the size to the client terminal
}
//***********************************************************
// FUNCTION WHICH GETS THE FILENAMES IN THE SERVER UPLOAD DIRECTORY
//***********************************************************
//this function is based on the starter code provided for the hello_string, it receives the student details from the server
void get_filenames(int socket)
{
char std_details[10000]; //empty string that will contain the output string
size_t k; //size variable that will be used to store the size of the string
readn(socket, (unsigned char *) &k, sizeof(size_t));//reads the size of the information being sent by the server
readn(socket, (unsigned char *) std_details, k);//reads the information being sent
const char s[2] = "*";//sets the delimeter for identification by strtok()
char *file;//sets the pointer to the first file name
file = strtok(std_details, s);//get the first file
printf("Files in upload\n");//prints the heading for the output
while( file != NULL ) { //this walks through the other files
printf("File: %s\n", file);
file = strtok(NULL, s);
}
printf("Received: %zu bytes\n\n", k);//prints the number of bytes received
}
//***********************************************************
// FUNCTION WHICH GETS THE FILE THE USER WANTS TO CHANGE
//***********************************************************
//this function is based on the starter code provided for the hello_string, it sends the filename that the user would like to copy from the server
//it then receives a copy of the file content and makes a duplicate file in the local directory
void send_file_name(int socket)
{
size_t l;
FILE *file_write;//create a pointer
char file_name[50];//filename
printf("what file would you like to copy from server upload directory\n");//print instructions
scanf("%s", file_name);//specify the users outut and pass it to the file
//file_name[strcspn(file_name, "\n")]=0; //sets filename
size_t n = strlen(file_name) + 1;//gets the size
writen(socket, (unsigned char *) &n, sizeof(size_t));//print the string to the client terminal
writen(socket, (unsigned char *) file_name, n);//writes the information being sent
readn(socket, (unsigned char *) &l, sizeof(size_t));//reads the size of the information being sent by the client
char newfile[l];//create the variable to store the content being returned from the server, the file content
readn(socket, (unsigned char *) newfile, l);//reads the information being sent
file_write = fopen(file_name, "w");//use fopen to write to the file signified by file_name. If it doesn't exist it creates a new file
if (file_write == NULL)//error check, if the file contains nothing
{
perror("This file does not exist in the server\n");
}
else{
fputs(newfile, file_write);
fclose(file_write);
}
}
//***********************************************************
// MAIN METHOD
//***********************************************************
int main(void)
{
int sockfd = 0;
struct sockaddr_in serv_addr;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Error - could not create socket");
exit(EXIT_FAILURE);
}
serv_addr.sin_family = AF_INET;
// IP address and port of server we want to connect to
serv_addr.sin_port = htons(50031);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// try to connect...
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) {
perror("Error - connect failed");
exit(1);
} else
printf("Connected to server...\n");
//***********************************************************
// THE MENU WITHIN THE MAIN FUNCTION
//***********************************************************
char input; //create a new var to use in the switch
char name[INPUTSIZ];
printf("0. Get student details\n");
printf("1. Get random numbers\n");
printf("2. Get utname\n");
printf("3. Get filenames\n");
printf("4. Copy a file from the server\n");
printf("5. Quit\n");
do {
printf("option> ");
fgets(name, INPUTSIZ, stdin); // get the value from input
name[strcspn(name, "\n")] = 0;
input = name[0];
if (strlen(name) > 1)
input = 'x'; // set invalid if input more 1 char
send_options(sockfd, input);
switch (input) {
case '0':
get_student_details(sockfd);
break;
case '1':
get_random_numbers(sockfd);
break;
case '2':
get_utname(sockfd);
break;
case '3':
get_filenames(sockfd);
break;
case '4':
send_file_name(sockfd);
break;
case '5':
printf("Goodbye!\n");
break;
default:
printf("Invalid choice!\n");
break;
}
} while (input != '5');
//***********************************************************
// THE END OF THE MAIN MENU
//***********************************************************
// make sure sockets are cleaned up
close(sockfd);
exit(EXIT_SUCCESS);
} // end main()
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/utsname.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
#include <signal.h>
#include "rdwrn.h"
//***********************************************************
// INITIALISING ALL THE FUNCTIONS TO BE CALLED
//***********************************************************
// thread function
void *client_handler(void *);
void send_utname(int);
void send_student_details(int);
void send_random_numbers(int);
void stat_file(char *);
void send_filenames(int);
char get_options(int);
void get_file_to_copy(int);
//these two global variables will be used to compute runtime
//written with help from https://www.mkyong.com/c/calculate-program-execute-time-time-elapsed-in-c/
time_t start, stop;
//***********************************************************
// SIGNAL HANDLER METHOD TO BE CALLED ON RECEIPT OF SIGINT
//***********************************************************
// written with help from the code from lab 7
static void handler(int sig, siginfo_t *siginfo, void *context)
{
printf("exiting the server\n");
//record the time at the end of the process
time(&stop);
printf("Finished in about %.0f seconds. \n", difftime(stop, start));//difftime calculates the amount of time that has passed between two supplied times
exit(130);//exit code to end the process
}
//***********************************************************
// MAIN METHOD
//***********************************************************
int main(void)
{
//record the time at the start of the process
time(&start);
/*
the following block of code which works with the
handler to intercept ctrl+c events is from lab 7
*/
struct sigaction act;
memset(&act, '\0', sizeof(act));
// this is a pointer to a function
act.sa_sigaction = &handler;
// the SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGINT, &act, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}//end of code from lab 7
//begin socket setup
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
struct sockaddr_in client_addr;
socklen_t socksize = sizeof(struct sockaddr_in);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(50031);
bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (listen(listenfd, 10) == -1) {
perror("Failed to listen");
exit(EXIT_FAILURE);
}
// end socket setup
//Accept and incoming connection
puts("Waiting for incoming connections...");
while (1) {
printf("Waiting for a client to connect...\n");
connfd =
accept(listenfd, (struct sockaddr *) &client_addr, &socksize);
printf("Connection accepted...\n");
pthread_t sniffer_thread;
// third parameter is a pointer to the thread function, fourth is its actual parameter
if (pthread_create
(&sniffer_thread, NULL, client_handler,
(void *) &connfd) < 0) {
perror("could not create thread");
exit(EXIT_FAILURE);
}
//Now join the thread , so that we dont terminate before the thread
pthread_join( sniffer_thread , NULL);
printf("Handler assigned\n");
}
// never reached...
exit(EXIT_SUCCESS);
} // end main()
//***********************************************************
// CLIENT HANDLER FUNCTION
//***********************************************************
// thread function - one instance of each for each connected client
void *client_handler(void *socket_desc)
{
//Get the socket descriptor
int connfd = *(int *) socket_desc;
/*first initialise the variable that will be used to switch the cases,
it is used to store the output from the get_options function which receives the options from the client
*/
char option;
do {
option = get_options(connfd);//run get_options and store the return value
switch (option) {
case '0':
send_student_details(connfd);
break;
case '1':
send_random_numbers(connfd);
break;
case '2':
send_utname(connfd);
break;
case '3':
send_filenames(connfd);
break;
case '4':
get_file_to_copy(connfd);
break;
case '5':
printf("Goodbye!\n");
break;
default:
printf("Invalid choice!\n");
break;
}
} while (option != '5');//*********might need to change back to 4
shutdown(connfd, SHUT_RDWR);
close(connfd);
printf("Thread %lu exiting\n", (unsigned long) pthread_self());
// always clean up sockets gracefully
shutdown(connfd, SHUT_RDWR);
close(connfd);
return 0;
} // end client_handler()
//***********************************************************
// FUNCTION WHICH GETS THE USER OPTIONS
//***********************************************************
//this function is based on the starter code provided for the hello_string, it receives the option selected by the user
char get_options(int socket){
char optn;//initialise variable to store the option
size_t k;//initialise size of the variable
readn(socket, (unsigned char *) &k, sizeof(size_t));//reads the size of the information being sent by the client
readn(socket, (unsigned char *) &optn, k);//reads the information being sent
return optn;//return the now non-empty character
}
//***********************************************************
// FUNCTION WHICH RECEIVES THE FILE TO BE COPIED FROM THE CLIENT
// THEN SENDS THE COPIED TEXT BACK TO THE CLIENT TO CREATE A NEW FILE
//***********************************************************
void get_file_to_copy(int socket)
{
char direct[] = "./upload/";//preparing the path of the file name to be used
size_t l;//the size of the file name
readn(socket, (unsigned char *) &l, sizeof(size_t));//reads the size of the information being sent by the client
char filename[l];//creates a string using the size of the file name to receive the file name
readn(socket, (unsigned char *) filename, l);//reads the information being sent
strcat(direct, filename);//concatenate the path to the file name to search the upload directory
FILE *file_open; //file pointer that will be used to read the file the contents
char content[200];
//the following parts were completed with assistance from: https://www.geeksforgeeks.org/basics-file-handling-c/
file_open = fopen(direct, "r"); // read mode, pass the file to fopen to be read
if (file_open == NULL)//error check, if the file contains nothing
{
perror("Error while opening the file.\n");
}
else{
while( fgets ( content, sizeof(content), file_open ) != NULL )
{
printf( "%s" , content );//print the content of the file to the server terminal
}
fclose(file_open);//close the file pointer
}
size_t n = strlen(content) + 1;//get the size of the string being sent
writen(socket, (unsigned char *) &n, sizeof(size_t));//write the size of the string being sent
writen(socket, (unsigned char *) content, n);//write the string being sent
}
//***********************************************************
// FUNCTION WHICH SENDS THE STUDENT DETAILS
//***********************************************************
//this function is based on the starter code provided for the hello_string
void send_student_details(int socket)
{
char fName[] = " Anjolaoluwa Ariyike ";
char lName[] = "Adetimehin ";
char sID[] = "S1719003";
char terminator[] = "\0"; //the terminator that will indicate the end of the string
//will first create the string containing the IPadress of the server
//step 1: set up the buffer that will house the string
char buffer[INET_ADDRSTRLEN];
//the next three lines are written with help from:
//https://stackoverflow.com/questions/5401942/how-to-get-the-ip-address-of-the-accepted-in-bound-socket
struct sockaddr_in addr;//create a new buffer address
socklen_t addr_size = sizeof(struct sockaddr_in);//get the size of the buffer address
getsockname(socket, (struct sockaddr*)&addr, &addr_size);//using sockname, get the current address that the socket has and returns it to the buffer
//then create a variable to store the ipaddress
const char* result=inet_ntop(AF_INET,(struct sockaddr_in *)&addr.sin_addr.s_addr,buffer,sizeof(buffer));
if (result==0) {
printf("failed to convert address to string (errno=%d)",errno);//ensure that the action was successful
}
//end of IP string set up
char dest[200];//set the new string that will be used for the concatenation
strcat(dest, result);
strcat(dest, fName);
strcat(dest, lName);
strcat(dest, sID);
strcat(dest, terminator);
size_t n = strlen(dest) + 1;//get the size of the string being sent
writen(socket, (unsigned char *) &n, sizeof(size_t));//write the size of the string being sent
writen(socket, (unsigned char *) dest, n);//write the string being sent
}
//***********************************************************
// FUNCTION WHICH SENDS THE RANDOM NUMBERS
//***********************************************************
//this function is based on the starter code provided for the hello_string
void send_random_numbers(int socket){
int random_array[5];//set an array for the random numbers
int i;//initialise my iterator
for(i = 0; i<5; i++){
int num = (rand() % 1000) + 1;//the use of rand() will help the generation of random numbers to fill the array
random_array[i] = num;//add the numbers to the array
}
char output_string[40];//initialise output string
int a = random_array[0], b = random_array[1], c = random_array[2], d = random_array[3], e = random_array[4];//set the values to be passed in the string
snprintf(output_string, sizeof(random_array), "%d %d %d %d %d", a, b, c, d, e);//sprintf helps with redirecting variables on type int into a string
//see http://joequery.me/code/snprintf-c/
size_t n = strlen(output_string) + 1;//get the size of the string being sent
writen(socket, (unsigned char *) &n, sizeof(size_t));//write the size of the string being sent
writen(socket, (unsigned char *) output_string, n);//write the string being sent
}
//***********************************************************
// FUNCTION WHICH SENDS THE UTSNAME
//***********************************************************
void send_utname(int socket)
{
struct utsname xyz;//create a new struct to pass into uname
uname(&xyz);//pass the struct into uname to be populated with system information
size_t payload_length = sizeof(xyz);//set size of the payload
writen(socket, (unsigned char *) &payload_length, sizeof(size_t));//write the size of the string being sent
writen(socket, (unsigned char *) &xyz, payload_length);//write the string being sent
}
//***********************************************************
// FUNCTION WHICH GETS THE FILENAMES FROM THE UPLOADS DIRECTORY
//***********************************************************
/*
the following block of code which sends the filenames from the
upload directory is written with assistance from lab 9 activities
*/
void send_filenames(int socket)
{
struct dirent **namelist;//create a struct to be passed into scandir
int n;
char output_string[10000];//initialise the output string
if ((n = scandir("./upload", &namelist, NULL, alphasort)) == -1)//scans the directory specified and adds files it finds to the buffer struct, sorting them as it goes
perror("scandir");//error check
else {
while (n--) {
strcat(output_string, namelist[n]->d_name);//concatenates each filename to the output string
strcat(output_string, "*");//concatenates a delimeter after each filename
free(namelist[n]);//free up memory resources
}
free(namelist);//free up memory resources
}
size_t y = strlen(output_string) + 1;//get the size of the string being sent
writen(socket, (unsigned char *) &y, sizeof(size_t));//write the size of the string being sent
writen(socket, (unsigned char *) output_string, y);//write the string being sent
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment