Client-Server Model

93 views 10:27 am 0 Comments March 28, 2023

 

5004CEM Lab 17

Client-Server Model

In this lab, you will create a program that uses the client-server model to create a Reverse Polish Notation Calculator.

You could create the system from scratch using the language of your or by adapt the C++ code (tcp_server.cc, tcp_client.cc and Makefile) or python (client.py and server.py) provided in Session17 on the server or on AULA.

See the ‘Getting Started with Lab 17’ video on AULA to give you additional information on completing this lab.

If you have any additional questions on this lab or any other labs please do not hesitate to ask your tutor and/or email me on [email protected].

Basic Task – Simple RPN

Simple RPN (Reverse Polish Notation) calculator server. For example, if the client sends three inputs (3, 5 and +), then the server will return 8. You should allow addition, subtraction, multiplication and division

Advanced Task – Extended RPN

Extend the program to allow more general calculations by implementing a stack for values and operators that apply to the top pair. Example, if the client sends the input (3 5 6 + *) 5 and 6 should be added together and the result multiplied by 3. See the Wikipedia article on RPN for a good description.

Solution

#include <arpa/inet.h>

#include <netdb.h>

#include <netinet/in.h>

#include <unistd.h>

#include <iostream>

#include <cstring>

#include <stack>

#include <stdlib.h>

#define MAX_MSG 100

#define LINE_ARRAY_SIZE (MAX_MSG+1)

using namespace std;

int main()

{

int listenSocket, connectSocket, i;

unsigned short int listenPort;

socklen_t clientAddressLength;

struct sockaddr_in clientAddress, serverAddress;

char line[LINE_ARRAY_SIZE]; // Declare a char variable line

int a = 0; // Declare an integer variable a

int b = 0; // Declare an integer variable b

int c = 0; // Declare an integer variable c

int result = 0; // Declare an integer variable to store the result

cout << “Enter port number to listen on (between 1500 and 65000): “;

cin >> listenPort; // Enter the port number

// Create socket for listening for client connection

// requests.

listenSocket = socket(AF_INET, SOCK_STREAM, 0);

if (listenSocket < 0) {

cerr << “cannot create listen socket”; // Print error

exit(1); // Exit with return value 1

}

// Bind listen socket to listen port. First set various

// fields in the serverAddress structure, then call

// bind().

// htonl() and htons() convert long integers and short

// integers (respectively) from host byte order (on x86

// this is Least Significant Byte first) to network byte

// order (Most Significant Byte first).

serverAddress.sin_family = AF_INET;

serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);

serverAddress.sin_port = htons(listenPort);

if (bind(listenSocket,(struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) {

cerr << “cannot bind socket”; // Print error

exit(1); // Exit with return value 1

}

// Wait for connections from clients. This is a

// non-blocking call; i.e., it registers this program with

// the system as expecting connections on this socket, and

// then this thread of execution continues on.

listen(listenSocket, 5);

while (1) {

cout << “Waiting for TCP connection on port ” << listenPort << ” …n”;

// Accept a connection with a client that is requesting

// one. The accept() call is a blocking call; i.e., this

// thread of execution stops until a connection comes

// in. connectSocket is a new socket that the system

// provides, separate from listenSocket. We *could*

// accept more connections on listenSocket, before

// connectSocket is closed, but this program doesn’t do

// that.

clientAddressLength = sizeof(clientAddress);

connectSocket = accept(listenSocket,

(struct sockaddr *) &clientAddress,

&clientAddressLength);

if (connectSocket < 0) {

cerr << “cannot accept connection “; // Print error

exit(1); // Exit with return value 1

}

// Show the IP address of the client.

// inet_ntoa() converts an IP address from binary form to the

// standard “numbers and dots” notation.

cout << ” connected to ” << inet_ntoa(clientAddress.sin_addr);

// Show the client’s port number.

// ntohs() converts a short int from network byte order (which is

// Most Significant Byte first) to host byte order (which on x86,

// for example, is Least Significant Byte first).

cout << “:” << ntohs(clientAddress.sin_port) << “n”;

// Read lines from socket, using recv(), storing them in the line

// array. If no messages are currently available, recv() blocks

// until one arrives.

// First set line to all zeroes, so we’ll know where the end of

// the string is.

memset(line, 0x0, LINE_ARRAY_SIZE);

while (recv(connectSocket, line, MAX_MSG, 0) > 0) {

stack <int> num; // Declare a stack called num

for (int i = 0; line[i] != ‘’; i++)

{

if ((line[i] >= ‘0’) && (line[i] <= ‘9’))

{

c = line[i] – 48; // Set c as an int converted from an ASCII value

num.push(c); // Push c onto the stack

}

if (line[i] == ‘+’)

{

a = num.top(); // Assign a as the top of the stack

num.pop(); // Take a off the stack

b = num.top(); // Assign b as the top of the stack

num.pop(); // Take b off the stack

result = a + b; // Calculate result

num.push(result); // Push result onto the stack

}

if (line[i] == ‘-‘)

{

a = num.top(); // Assign a as the top of the stack

num.pop(); // Take a off the stack

b = num.top(); // Assign b as the top of the stack

num.pop(); // Take b off the stack

result = a – b; // Calculate result

num.push(result); // Push result onto the stack

}

if (line[i] == ‘*’)

{

a = num.top(); // Assign a as the top of the stack

num.pop(); // Take a off the stack

b = num.top(); // Assign b as the top of the stack

num.pop(); // Take b off the stack

result = a * b; // Calculate result

num.push(result); // Push result onto the stack

}

if (line[3] == ‘/’)

{

a = num.top(); // Assign a as the top of the stack

num.pop(); // Take a off the stack

b = num.top(); // Assign b as the top of the stack

num.pop(); // Take b off the stack

result = a / b; // Calculate result

num.push(result); // Push result onto the stack

}

}

sprintf(line, “%i”, result); // Print the result

// Send converted line back to client.

if (send(connectSocket, line, strlen(line) + 1, 0) < 0)

cerr << “Error: cannot send modified data”; // Print error

memset(line, 0x0, LINE_ARRAY_SIZE); // set line to all zeroes

}

}

}

Lab Evidence

The commented code and examples of the code running to show that the client-server model can deal with three inputs and unlimited numbers.