CSC 374: Computer Systems II: 2022 Fall, Assignment #4Last Modified 2022 November 7Purpose:To practice:SocketsLow-level I/O in CDetached threadsAssignment:Please finish the server of a client-server...

1 answer below ยป


CSC 374: Computer Systems II: 2022 Fall, Assignment #4


Last Modified 2022 November 7



Purpose:


To practice:

  • Sockets

  • Low-level I/O in C

  • Detached threads


Assignment:


Please finish the server of a client-server application. The client asks the user for a direction (forward or reverse) and a string. The client sends the string length of the string plus 2 (for the Forward/Reverse/Quit command char, followed by the string itself, followed by the NUL char of the string) to the server. This length is an integer in network endian. Then the server sends the result back to the client (not including the nul char).


The server makes a new thread for each client.
In a loop, that client-handling server thread gets the length of the command char and string from the client. It converts it to its own endian, then it gets the command char and string. Then it makes a pipe and a chile process.


The child process




  • close()s the writing-end of the pipe

  • redirects itsSTDIN_FILENOto read from the reading-end of the pipe

  • redirects itsSTDOUT_FILENOto the write to the socket used to communicate with the client

  • Runs the program namedAPP_PATH. If it isnotrunning in forward mode then it should add the argument"-d"on the command line



The parent process:




  • close()s the input end of the pipe.

  • Sends the stringcPtrto the output end of the pipe


  • close()s the output end of the pipe.

  • makes sure the child does not remain a zombie.



When the client sends the command char'Q'to the server that tells the client-handling thread on the server to end.




  1. doServer():



    1. Before the loop initializeattrand then sets it for detached threads.

    2. Inside the loop itaccept()s a connect from the client. Then, itcalloc()s space for two integers, and puts the file descriptor for talking to the client and the thread-counting integeriin that space. It creates a detached thread to dohandleClient(), and passes the memory to it. Finally, it incrementsi.

    3. After the loop it gets rid ofattr.





  2. handleClient():



    1. Get the file descriptor from the passed memory. (You will have to cast thevoid*pointer toint*, and then get the passed integers.) After getting the file descriptor and thread number,free()the memory.

    2. It has a loop that:

      1. Gets a string length (in network endian) from the client

      2. Converts this length to its own endian

      3. Gets the string (including the command char, which is at the beginning) from the client.

      4. If the command char is not'Q'then runscomputeAndSendResult()



    3. After the loop itclose()s the file descriptor for talking with the client, and quits.





  3. computeAndSendResult():



    1. Makes a pipe and a child process.

    2. The child process:


      • close()s the writing-end of the pipe

      • redirects itsSTDIN_FILENOto read from the reading-end of the pipe

      • redirects itsSTDOUT_FILENOto the write to the socket used to communicate with the client

      • Runs the program namedAPP_PATH. If it isnotrunning in forward mode then it should add the argument"-d"on the command line



    3. The parent process:


      • close()s the input end of the pipe.

      • Sends the stringcPtrto the output end of the pipe


      • close()s the output end of the pipe.

      • makes sure the child does not remain a zombie.







Code:


/*-------------------------------------------------------------------------*

*------*

*---base64App.h---*

*------*

*--- This file declares constants common to both the client and---*

*---server of an application where the client asks the user for a---*

*---direction (forward or reverse) and a string. It sends both to---*

*---the server. If the direction is forward then the server---*

*---computes the base-64 representation of the string, and sends it---*

*---back to the client. If the direction is reverse then the---*

*---server assumes the input is the base-64 representation, and it---*

*---computes the original string, which it sends back to the---*

*---client.---*

*------*

*--------------------------------------*

*------*

*---Version 1.0 ---*

*------*

*-------------------------------------------------------------------------*/




/*---Header file inclusion---*/


#include
#include
#include
#include// read(), write()
#include// For socket()
#include// For sockaddr_in and htons()
#include// For getaddrinfo()
#include// For errno var
#include// For open(), read(),write(), stat()
#include// and close()
#include// For wait()




/*---Definition of constants:---*/


const intBUFFER_LEN= 1024;
#defineDEFAULT_HOSTNAME"localhost"
#defineAPP_PATH"/usr/bin/base64"






/*-------------------------------------------------------------------------*

*------*

*---base64Client.c---*

*------*

*--- This file defines a C program that asks the user for a---*

*---direction (forward or reverse) and a string, which it sends to---*

*---the server. If the direction is forward then the server ---*

*---computes and returns the base-64 representation of the string.---*

*---If the direction is reverse then the server assumes that the---*

*---string is the base-64 representation, and it computes and---*

*---returns the original string, which it sends back to the client.---*

*------*

*--------------------------------------*

*------*

*---Version 1.0 ---*

*------*

*-------------------------------------------------------------------------*/


//Compile with:
//$ gcc base64Client.c -o base64Client


/*---Header file inclusion---*/


#include"base64App.h"
#include// open, close, read, write






// PURPOSE: To ask the user for the name and the port of the server. The
//server name is returned in 'url' up to length 'urlLen'. The port
//number is returned in '*portPtr'. No return value.
voidobtainUrlAndPort(inturlLen,

char*url,

int*portPtr

)
{

// I. Application validity check:

if ( (url == NULL) || (portPtr == NULL) )

{

fprintf(stderr,"BUG: NULL ptr to obtainUrlAndPort()\n");

exit(EXIT_FAILURE);

}



if (urlLen <=>

{

fprintf(stderr,"BUG: Bad string length to obtainUrlAndPort()\n");

exit(EXIT_FAILURE);

}



// II. Get server name and port number:

// II.A. Get server name:

printf("Machine name [%s]? ",DEFAULT_HOSTNAME);

fgets(url,urlLen,stdin);



char*cPtr= strchr(url,'\n');



if (cPtr != NULL)

*cPtr = '\0';



if (url[0] == '\0')

strncpy(url,DEFAULT_HOSTNAME,urlLen);



// II.B. Get port numbe:

charbuffer[BUFFER_LEN];



printf("Port number? ");

fgets(buffer,BUFFER_LEN,stdin);



*portPtr = strtol(buffer,NULL,10);



// III. Finished:
}




// PURPOSE: To attempt to connect to the server named 'url' at port 'port'.
//Returns file-descriptor of socket if successful, or '-1' otherwise.
intattemptToConnectToServer(const char*url,

intport

)
{

// I. Application validity check:

if (url == NULL)

{

fprintf(stderr,"BUG: NULL ptr to attemptToConnectToServer()\n");

exit(EXIT_FAILURE);

}





// II. Attempt to connect to server:

// II.A. Create a socket:

int socketDescriptor = socket(AF_INET, // AF_INET domain

SOCK_STREAM, // Reliable TCP

0);



// II.B. Ask DNS about 'url':

struct addrinfo* hostPtr;

int status = getaddrinfo(url,NULL,NULL,&hostPtr);



if (status != 0)

{

fprintf(stderr,"%s\n",gai_strerror(status));

return(-1);

}



// II.C. Attempt to connect to server:

struct sockaddr_in server;

// Clear server datastruct

memset(&server, 0, sizeof(server));



// Use TCP/IP

server.sin_family = AF_INET;



// Tell port # in proper network byte order

server.sin_port = htons(port);



// Copy connectivity info from info on server ("hostPtr")

server.sin_addr.s_addr =

((struct sockaddr_in*)hostPtr->ai_addr)->sin_addr.s_addr;



status = connect(socketDescriptor,(struct sockaddr*)&server,sizeof(server));



if (status <>

{

fprintf(stderr,"Could not connect %s:%d\n",url,port);

return(-1);

}



freeaddrinfo(hostPtr);



// III. Finished:

return(socketDescriptor);
}




// PURPOSE: To do the work of the application. Gets letter from user, sends
//it to server over file-descriptor 'fd', and either prints text of
//returned error code, or prints returned file. No return value.
voidcommunicateWithServer(intfd

)
{

// I. Application validity check:



// II. Do work of application:

// II.A. Continually ask for filenames, send them and get hashes:

charbuffer[BUFFER_LEN+1];

char*cPtr;

intlength;

intlengthInNetEndian;



do

{

// II.A.1. Ask for and get direction:

do

{

printf("Action: (F)orward, (R)everse or (Q)uit: ");

fgets(buffer,BUFFER_LEN-1,stdin);

buffer[0]= toupper(buffer[0]);

}

while ( (buffer[0] != 'F') && (buffer[0] != 'R') && (buffer[0] != 'Q') );



// II.A.2. Take action:

if (buffer[0] == 'Q')

{

// II.A.2.a. Handle quit:

lengthInNetEndian= htonl(1);

write(fd,&lengthInNetEndian,sizeof(int));

write(fd,buffer,1);

}

else

{

// II.A.2.b. Handle forward and reverse:

// II.A.2.b.I. Get string:

printf("Please enter the string: ");

fgets(buffer+1,BUFFER_LEN-2,stdin);



// II.A.2.b.II. Remove ending newline:

cPtr= strchr(buffer+1,'\n');



if (cPtr != NULL)

*cPtr= '\0';



// II.A.2.b.III. Send length, command and string:

length= strlen(buffer) + 1;

lengthInNetEndian= htonl(length);

write(fd,&lengthInNetEndian,sizeof(int));

write(fd,buffer,length);



// II.A.2.b.IV. Receive and print result:

length= read(fd,buffer,BUFFER_LEN-1);

buffer[length]= '\0';

printf("%s\n",buffer);

}

}

while (buffer[0] != 'Q');



// III. Finished:
}




// PURPOSE: To do the work of the client. Ignores command line parameters.
//Returns 'EXIT_SUCCESS' to OS on success or 'EXIT_FAILURE' otherwise.
intmain()
{

charurl[BUFFER_LEN];

intport;

intfd;



obtainUrlAndPort(BUFFER_LEN,url,&port);

fd= attemptToConnectToServer(url,port);



if (fd <>

exit(EXIT_FAILURE);



communicateWithServer(fd);

close(fd);

return(EXIT_SUCCESS);
}




/*-------------------------------------------------------------------------*

*------*

*---base64Server.c---*

*------*

*--- This file defines a C program that waits for a client to---*

*---connect, and gets a direction (forward or reverse) and a ---*

*---If the direction is forward then this server computes the---*

*---base-64 representation of the string, and sends it back to the---*

*---client. If the direction is reverse then this server assumes---*

*---the input is the base-64 representation, and it computes the---*

*---original string, which it sends back to the client.---*

*------*

*--------------------------------------*

*------*

*---Version 1a ---*

*------*

*-------------------------------------------------------------------------*/


//Compile with:
//$ gcc base64Server.c -o base64Server -lpthread -g


/*---Header file inclusion---*/


#include"base64App.h"
#include


const intLO_LEGAL_PORT= 1025;
const intHI_LEGAL_PORT= 65535;
const intERROR_FD= -1;
const intNUM_CLIENTS_TO_SERVE

= 4;


// PURPOSE: To attempt to create and return a file-descriptor for listening
//to the OS telling this server when a client process has connect()-ed
//to 'port'. Returns that file-descriptor, or 'ERROR_FD' on failure.
intgetServerFileDescriptor

(intport,

const char*progName

)
{

// I. Application validity check:

if (progName == NULL)

{

fprintf(stderr,"BUG: NULL ptr to getServerFileDescriptor().\n");

exit(EXIT_FAILURE);

}



// II. Attempt to get socket file descriptor and bind it to 'port':

// II.A. Create a socket

int socketDescriptor = socket(AF_INET, // AF_INET domain

SOCK_STREAM, // Reliable TCP

0);



if (socketDescriptor <>

{

perror(progName);

return(ERROR_FD);

}



// II.B. Attempt to bind 'socketDescriptor' to 'port':

// II.B.1. We'll fill in this datastruct

struct sockaddr_in socketInfo;



// II.B.2. Fill socketInfo with 0's

memset(&socketInfo,'\0',sizeof(socketInfo));



// II.B.3. Use TCP/IP:

socketInfo.sin_family = AF_INET;



// II.B.4. Tell port in network endian with htons()

socketInfo.sin_port = htons(port);



// II.B.5. Allow machine to connect to this service

socketInfo.sin_addr.s_addr = INADDR_ANY;



// II.B.6. Try to bind socket with port and other specifications

int status = bind(socketDescriptor, // from socket()

(struct sockaddr*)&socketInfo,

sizeof(socketInfo)

);



if (status <>

{

perror(progName);

return(ERROR_FD);

}



// II.B.6. Set OS queue length:

listen(socketDescriptor,5);



// III. Finished:

return(socketDescriptor);
}




// PURPOSE: To ask the user which port to attempt to monopolize, and to return
//entered port number.
intgetPort()
{

// I. Application validity check:



// II. Get port number

intport;



do

{

charbuffer[BUFFER_LEN];



printf("Please enter port number to monopolize [%d-%d]: ",

LO_LEGAL_PORT,HI_LEGAL_PORT

);

fgets(buffer,BUFFER_LEN,stdin);

port = strtol(buffer,NULL,10);

}

while ( (port < lo_legal_port)="" ||="" (port=""> HI_LEGAL_PORT) );



// III. Finished:

return(port);
}






// PURPOSE: To make a process that will compute either the base-64
//representation of 'cPtr' (if 'isForward' is true), or will use 'cPtr'
//as a base-64 representation and will compute the original string
//(if 'isForward' is false). Either way, the result is returned back to
//the client via 'toClient'. No return value.
voidcomputeAndSendResult(inttoClient,

intisForward,

const char*cPtr

)
{

// I. Application validity check:



// II. Compute and send hash:

// II.A. Create pipe and child process:

inttoChild[2];

pid_tchildId;



// YOUR CODE HERE FOR C.




// III. Finished:
}




// PURPOSE: To do the work of handling the client. Communication with the
//client take place using file-descriptor obtained from '(int*)vPtr'.
//Returns 'NULL'.
void*handleClient(void*vPtr

)
{

// I. Application validity check:

// YOUR CODE HERE FOR B.1.


int*argArray= NULL;
// CHANGE THAT NULL!


intfd= 0;
// CHANGE THAT 0!


intthreadNum= 0;
// CHANGE THAT 0!




if (fd <>

{

fprintf(stderr,"BUG: Illegal file descriptor to handleClient()\n");

return(NULL);

}



// II. Handle the client:

printf("Thread %d beginning.\n",threadNum);



// II.A. Each iteration handles one more file:

while (1)

{

// II.A.1. Get length and then command and string from client:

intlength;

charstring[BUFFER_LEN];



// YOUR CODE HERE FOR B.2.




if (string[0] == 'Q')

break;



printf(" %c %s\n",string[0],string+1);



// II.A.2. Handle request:

computeAndSendResult(fd,string[0]=='F',string+1);

}



// III. Finished:

printf("Thread %d ending.\n",threadNum);

// YOUR CODE HERE FOR B.3.


return(NULL);
}




// PURPOSE: To serve the clients using file-descriptor 'listenFd' to tell
//when a client has connected. Each client is handled by its own child
//thread. No return value.
void doServer (int listenFd

)
{

// I. Application validity check:

if (listenFd <>

{

fprintf(stderr,"Illegal file-descriptor to doServer()\n");

exit(EXIT_FAILURE);

}



// II. Do the work of the server:

inti= 0;

pthread_tthread;

pthread_attr_tattr;



// YOUR CODE HERE FOR A.1.




while (1)

{

// YOUR CODE HERE FOR A.2.


// Accept connection to client

intclientFd= -1;
// CHANGE THAT -1!


int*argArray= NULL;
// CHANGE THAT NULL!




i++;

}



// YOUR CODE HERE FOR A.3.




// III. Finished:
}




// PURPOSE: To oversee the main work of the server. Ignores 'argc' but
//uses 'argv[0]' as the name of the program. Returns 'EXIT_SUCCESS' to
//OS on success or 'EXIT_FAILURE' otherwise.
intmain(intargc,

char*argv[]

)
{

// I. Application validity check:



// II. Do server:

intport= getPort();

intsocketFd= getServerFileDescriptor(port,argv[0]);



doServer(socketFd);

close(socketFd);



// III. Finished:

return(EXIT_SUCCESS);
}


Sample output:


I made a file namedhello.txtthat just says:


Hello


There is no file named
no file
.

















































Client output:Server output:




$
./server


Please enter port number to monopolize [1025-65535]:
1026

Thread 0 beginning.



$
./base64Client


Machine name [localhost]?
(I just pressed enter)


Port number?
1026


Thread 0 beginning.


Action: (F)orward, (R)everse or (Q)uit:
f

Please enter the string:
hello



aGVsbG8=




F hello


Action: (F)orward, (R)everse or (Q)uit:
r

Please enter the string:
aGVsbG8=



hello




R aGVsbG8=


Action: (F)orward, (R)everse or (Q)uit:
f

Please enter the string:
aGVsbG8=



YUdWc2JHOD0=




F aGVsbG8=


Action: (F)orward, (R)everse or (Q)uit:
r

Please enter the string:
YUdWc2JHOD0=



aGVsbG8=




R YUdWc2JHOD0=


Action: (F)orward, (R)everse or (Q)uit:
r

Please enter the string:
aGVsbG8=



hello




R aGVsbG8=


Action: (F)orward, (R)everse or (Q)uit:
f

Please enter the string:
Good bye!



R29vZCBieWUh




F Good bye!


Action: (F)orward, (R)everse or (Q)uit:
r

Please enter the string:
R29vZCBieWUh



Good bye!




R R29vZCBieWUh


Action: (F)orward, (R)everse or (Q)uit:
q


Thread 0 ending.
(The thread ended but the server is still running.)
Answered 7 days AfterNov 10, 2022

Answer To: CSC 374: Computer Systems II: 2022 Fall, Assignment #4Last Modified 2022 November 7Purpose:To...

Nidhi answered on Nov 18 2022
39 Votes
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions ยป

Submit New Assignment

Copy and Paste Your Assignment Here