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
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
-
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:
- malloc() or calloc() enough memory for 2 integers
- put the file descriptor from accept() in one of those spaces
- put the value of threadCount in the other space, and increment threadCount
- 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.
-
void* handleClient(void* vPtr) (30 Points):
(Or whatever you call your function that runs a thread for the client.)- 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
- 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);
- The thread id and the file descriptor are passed, but they come in as a void* pointer.
-
command == STRING_LANG_CMD_CHAR (40 Points):
This one applies the string language to textPtr by:
- 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.
- 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).
- 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.
-
command == QUIT_CMD_CHAR (10 Points):
Just send STD_BYE_MSG back to the client and set shouldContinue to 0 to quit the loop.
part of the 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 Joseph Phillips ---*
*--- ---*
*-------------------------------------------------------------------------*/
// 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 = 0; // <-- CHANGE THAT 0!
int threadNum = 0; // <-- 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;
while (1)
{
// YOUR CODE HERE
}
// 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
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started