Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

Question: Write a server that is able to: Receive input from a socket Give that input to a child process on its command line Get

Question: Write a server that is able to:

  • Receive input from a socket
  • Give that input to a child process on its command line
  • Get the child process's response from a pipe
  • Send the response back to the client via the socket
  1. Implementing doServer(int listenFd) (20 Points):

    doServer() should have a loop in which it waits for a client to connect to listenFd. When a client does, it should:

    1. malloc() or calloc() enough memory for 2 integers
    2. put the file descriptor from accept() in one of those spaces
    3. put the value of threadCount in the other space, and increment threadCount
    4. Make a detached thread to handle this new client. I called my function handleClient(), but you may call yours whatever. Pass the address of your malloc()-ed array.
    The loop should then go back for another accept().
  2. void* handleClient(void* vPtr) (30 Points):

    (Or whatever you call your function that runs a thread for the client.)
    1. The thread id and the file descriptor are passed, but they come in as a void* pointer.
      • Use another pointer to cast back to int*
      • Save the file descriptor and thread number in local vars
      • free() the memory
    2. Print the thread number and do a loop like this:
       // II.B. Read command: char buffer[BUFFER_LEN]; char command; char* textPtr; int shouldContinue = 1; while (shouldContinue) { read(fd,buffer,BUFFER_LEN); printf("Thread %d received: %s ",threadNum,buffer); command = buffer[0]; textPtr = buffer + 2; // YOUR CODE HERE }

      It read()s a line of text from the client into buffer[], and parses the line into a command character, and the rest of the text textPtr.

      Then do the following operations based upon the value of command. Except for QUIT_CMD_CHAR I strongly recommend using a different function for each!

      When the function ends just have it do:

       printf("Thread %d quitting. ",threadNum); return(NULL);
  3. command == STRING_LANG_CMD_CHAR (40 Points):

    This one applies the string language to textPtr by:

    1. Opens a pipe for the child process to communicate back to the parent. If opening the pipe fails then it sends STD_ERROR_MSG back to the client.
    2. fork()s a child process. This child process:
      • Sends its stdout to the output end of this pipe
      • Executes the program STRING_LANG_PROGNAME with the text in cPtr as its command line argument.
      • If the child process cannot execute the program, then it sends STD_ERROR_MSG back to the parent process using fd and does exit(EXIT_FAILURE).
    3. Meanwhile, the parent process does a read() from the input end of the pipe. It puts an ending '\0' at the end of the text it read, does a wait() to get the zombie child, and sends the text back to the client.
  4. command == QUIT_CMD_CHAR (10 Points):

    Just send STD_BYE_MSG back to the client and set shouldContinue to 0 to quit the loop.

server code here:

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

*--- ---*

*--- stringLangServer.c ---*

*--- ---*

*--- This file defines a C program that gets file-sys commands ---*

*--- from client via a socket, executes those commands in their own ---*

*--- threads, and returns the corresponding output back to the ---*

*--- client. ---*

*--- ---*

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

*--- ---*

*--- Version 1a ---*

*--- ---*

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

// Compile with:

// $ gcc stringLangServer.c -o stringLangServer -lpthread

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

#include "clientServer.h"

#include // For pthread_create()

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

#define STD_OKAY_MSG "Okay"

#define STD_ERROR_MSG "Error doing operation"

#define STD_BYE_MSG "Good bye!"

#define THIS_PROGRAM_NAME "stringLangServer"

const int ERROR_FD = -1;

//--- Definition of global vars: ---//

// PURPOSE: To be non-zero for as long as this program should run, or '0'

// otherwise.

//--- Definition of functions: ---//

// PURPOSE: To:

// (1) create a pipe

// (2) fork() a child process. This child process will:

// (2a) close() its file descriptor to stdout,

// (2b) send its output from the pipe to the close stdout file descriptor

// (2c) Run the program STRING_LANG_PROGNAME with cPtr on the cmd line

// (2d) Send an error message back to the client on file descriptor 'fd'

// if the execl() failed.

// (3) get input from the pipe, put the '\0' char at the end

// (4) wait() for the child process to end

// (5) send the input back to the client using file descriptor 'fd'

void stringLangFile (int socketFd,

const char* cPtr

)

{

// I. Application validity check:

// II. Apply string language file:

// YOUR CODE HERE

// III. Finished:

}

// PURPOSE: To cast 'vPtr' to the pointer type coming from 'doServer()'

// that points to two integers. Then, to use those two integers,

// one as a file descriptor, the other as a thread number, to fulfill

// requests coming from the client over the file descriptor socket.

// Returns 'NULL'.

void* handleClient (void* vPtr

)

{

// I. Application validity check:

// II. Handle client:

// YOUR CODE HERE

int fd = iPtr[0]; // <-- CHANGE THAT 0!

int threadNum = &iPtr[1]; // <-- CHANGE THAT 0!

// YOUR CODE HERE

// III. Finished:

printf("Thread %d quitting. ",threadNum);

return(NULL);

}

// PURPOSE: To run the server by 'accept()'-ing client requests from

// 'listenFd' and doing them.

void doServer (int listenFd

)

{

// I. Application validity check:

// II. Server clients:

pthread_t threadId;

pthread_attr_t threadAttr;

int threadCount = 0;

int* iPtr;

listen(listenFd,5);

pthread_attr_init(&threadAttr);

while (1) {

// YOUR CODE HERE

printf("descriptor ");

int fd = accept(listenFd,NULL,NULL);

printf("connectionDescriptor = %d ",fd);

if (fd < 0) {

perror("error on attempt ");

exit(EXIT_FAILURE);

}

iPtr = (int*)calloc(2,sizeof(int*));

iPtr[0] = fd;

threadId = getpid();

iPtr[1] = threadId;

printf("In doServer, iPtr[0] = %d, and iPtr[1] = %d ",iPtr[0], iPtr[1]);

threadCount++;

pthread_attr_setdetachstate(&threadAttr,PTHREAD_CREATE_DETACHED);

pthread_create(&threadId,&threadAttr,handleClient,(void*)iPtr);

// YOUR CODE HERE

// III. Finished:

}

}

// PURPOSE: To decide a port number, either from the command line arguments

// 'argc' and 'argv[]', or by asking the user. Returns port number.

int getPortNum (int argc,

char* argv[]

)

{

// I. Application validity check:

// II. Get listening socket:

int portNum;

if (argc >= 2)

portNum = strtol(argv[1],NULL,0);

else

{

char buffer[BUFFER_LEN];

printf("Port number to monopolize? ");

fgets(buffer,BUFFER_LEN,stdin);

portNum = strtol(buffer,NULL,0);

}

// III. Finished:

return(portNum);

}

// 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.

int getServerFileDescriptor

(int port

)

{

// I. Application validity check:

// 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 < 0)

{

perror(THIS_PROGRAM_NAME);

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 < 0)

{

perror(THIS_PROGRAM_NAME);

return(ERROR_FD);

}

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

listen(socketDescriptor,5);

// III. Finished:

return(socketDescriptor);

}

int main (int argc,

char* argv[]

)

{

// I. Application validity check:

// II. Do server:

int port = getPortNum(argc,argv);

int listenFd = getServerFileDescriptor(port);

int status = EXIT_FAILURE;

if (listenFd >= 0)

{

doServer(listenFd);

close(listenFd);

status = EXIT_SUCCESS;

}

// III. Finished:

return(status);

}

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Data Management Databases And Organizations

Authors: Richard T. Watson

6th Edition

1943153035, 978-1943153039

More Books

Students also viewed these Databases questions