Components of computer systems often have dependencies
--
other components that must be
installed before they will function properly. These dependencies are frequently shared by
multiple components. For example, both the TELNET client program and the FTP cli
ent
program require that the TCP/IP networking software be installed before they can operate. If you
install TCP/IP and the TELNET client program, and later decide to add the FTP client program,
you do not need to reinstall TCP/IP.
For some components it
would not be a problem if the components on which they depended were
reinstalled; it would just waste some resources. But for others, like TCP/IP, some component
configuration may be destroyed if the component was reinstalled.
It is useful to be able to r
emove components that are no longer needed. When this is done,
components that only support the removed component may also be removed, freeing up disk
space, memory, and other resources. But a supporting component, not explicitly installed, may
be removed
only if all components which depend on it are also removed. For example, removing
the FTP client program and TCP/IP would mean the TELNET client program, which was not
removed, would no longer operate. Likewise, removing TCP/IP by itself would cause the fa
ilure
of both the TELNET and the FTP client programs. Also if we installed TCP/IP to support our
own development, then installed the TELNET client (which depends on TCP/IP) and then still
later removed the TELNET client, we would not want TCP/IP to be remo
ved.
We want a program to automate the process of adding and removing components. To do this we
will maintain a record of installed components and component dependencies. A component can
be installed explicitly in response to a command (unless it is alrea
dy installed), or implicitly if it
is needed for some other component being installed. Likewise, a component, not explicitly
installed, can be explicitly removed in response to a command (if it is not needed to support
other components) or implicitly remov
ed if it is no longer needed to support another component.
Input
The input will contain a sequence of commands (as described below), each on a separate line
containing no more than eighty characters. Item names are case sensitive, and each is no longer
th
an ten characters. The command names (
DEPEND
,
INSTALL
,
REMOVE
and
LIST
) always
appear in uppercase starting in column one, and item names are separated from the command
name and each other by one or more spaces. All appropriate
DEPEND
commands will appear
before the occurrence of any
INSTALL
dependencies. The end of the input is marked by a line
containing only the word
END
.
Command Syntax
Interpretation/Response
DEPEND item1 item2 [item3 ...]
item1
depends on
item2
(and
item3
...)
INSTALL item1
install
item1
and those on which it depends
REMOVE item1
remove
item1
, and those on whch it depends, if possible
LIST
list the names of all currently
-
installed components
Output
.
Components of computer systems often have dependencies--other co.pdf
1. Components of computer systems often have dependencies
--
other components that must be
installed before they will function properly. These dependencies are frequently shared by
multiple components. For example, both the TELNET client program and the FTP cli
ent
program require that the TCP/IP networking software be installed before they can operate. If you
install TCP/IP and the TELNET client program, and later decide to add the FTP client program,
you do not need to reinstall TCP/IP.
For some components it
would not be a problem if the components on which they depended were
reinstalled; it would just waste some resources. But for others, like TCP/IP, some component
configuration may be destroyed if the component was reinstalled.
It is useful to be able to r
emove components that are no longer needed. When this is done,
components that only support the removed component may also be removed, freeing up disk
space, memory, and other resources. But a supporting component, not explicitly installed, may
be removed
only if all components which depend on it are also removed. For example, removing
the FTP client program and TCP/IP would mean the TELNET client program, which was not
removed, would no longer operate. Likewise, removing TCP/IP by itself would cause the fa
ilure
of both the TELNET and the FTP client programs. Also if we installed TCP/IP to support our
own development, then installed the TELNET client (which depends on TCP/IP) and then still
later removed the TELNET client, we would not want TCP/IP to be remo
ved.
We want a program to automate the process of adding and removing components. To do this we
will maintain a record of installed components and component dependencies. A component can
be installed explicitly in response to a command (unless it is alrea
dy installed), or implicitly if it
is needed for some other component being installed. Likewise, a component, not explicitly
installed, can be explicitly removed in response to a command (if it is not needed to support
other components) or implicitly remov
ed if it is no longer needed to support another component.
Input
2. The input will contain a sequence of commands (as described below), each on a separate line
containing no more than eighty characters. Item names are case sensitive, and each is no longer
th
an ten characters. The command names (
DEPEND
,
INSTALL
,
REMOVE
and
LIST
) always
appear in uppercase starting in column one, and item names are separated from the command
name and each other by one or more spaces. All appropriate
DEPEND
commands will appear
before the occurrence of any
INSTALL
dependencies. The end of the input is marked by a line
containing only the word
END
.
Command Syntax
Interpretation/Response
DEPEND item1 item2 [item3 ...]
item1
depends on
item2
(and
item3
...)
INSTALL item1
install
item1
and those on which it depends
REMOVE item1
3. remove
item1
, and those on whch it depends, if possible
LIST
list the names of all currently
-
installed components
Output
Echo each line of input. Follow each echoed
INSTALL
or
REMOVE
line with the actions taken
in response, making certain that the actions are given in the proper order. Also identify
exceptional conditions (see
Expected Output
, below, for examples of all case
s). For the
LIST
command, display the names of the currently installed components. No output, except the echo,
is produced for a
DEPEND
command or the line containing
END
. There will be at most one
dependency list per item.
Sample Input
DEPEND TELNET TC
PIP NETCARD
DEPEND TCPIP NETCARD
DEPEND DNS TCPIP NETCARD
DEPEND BROWSER TCPIP HTML
INSTALL NETCARD
INSTALL TELNET
INSTALL foo
REMOVE NETCARD
4. INSTALL BROWSER
INSTALL DNS
LIST
REMOVE TELNET
REMOVE NETCARD
REMOVE DNS
REMOVE NETCARD
INSTALL NETCARD
REMOVE
TCPIP
REMOVE BROWSER
REMOVE TCPIP
LIST
END
Output for the Sample Output
DEPEND TELNET TCPIP NETCARD
DEPEND TCPIP NETCARD
DEPEND DNS TCPIP NETCARD
DEPEND BROWSER TCPIP HTML
INSTALL NETCARD
Installing NETCARD
INSTALL TELNET
Installing TCPIP
Installing TELNET
INSTALL foo
Installing foo
REMOVE NETCARD
NETCARD is still needed.
INSTALL BROWSER
Installing HTML
Installing BROWSER
INSTALL DNS
Installing DNS
LIST
HTML
BROWSER
5. DNS
NETCARD
foo
TCPIP
TELNET
REMOVE
TELNET
Removing TELNET
REMOVE NETCARD
NETCARD is still needed.
REMOVE DNS
Removing DNS
REMOVE NETCARD
NETCARD is still needed.
INSTALL NETCARD
NETCARD is already installed.
REMOVE TCPIP
TCPIP is still needed.
REMOVE BROWSER
Removing BROWSER
Removing HTML
Removing TCPIP
REMOVE TCPIP
TCPIP is not installed.
LIST
NETCARD
foo
END
Solution
TCPStream Class
Interface
The TCPStream class provides methods to send and receive data over a TCP/IP connection. It
contains a connected socket descriptor and information about the peer – either client or server –
in the form of the IP address and TCP port. TCPStream includes simple get methods that return
6. address and port, but not the socket descriptor which is kept private. One of the advantages of
programming with objects is the ability to logically group data members and methods to avoid
exposing data, in this case the socket descriptor, to the calling program that it does not need to
see. Each connection is completely encapsulated in each TCPStream object.
TCPStream objects are created by TCPConnector and TCPAcceptor objects only, so the
TCPStream constructors must be declared private to prevent them from being called directly by
any other objects. The TCPStream class grants friend privileges to the TCPConnector and
TCPAcceptor classes so they can access the TCPStream constructors to supply connected socket
descriptors.
#include
#include
#include
#include
using namespace std
class TCPStream
{
int m_sd;
string m_peerIP;
int m_peerPort;
public:
friend class TCPAcceptor;
friend class TCPConnector;
~TCPStream();
ssize_t send(char* buffer, size_t len);
ssize_t receive(char* buffer, size_t len);
string getPeerIP();
int getPeerPort();
private:
TCPStream(int sd, struct sockaddr_in* address);
TCPStream();
TCPStream(const TCPStream& stream);
};
Constructor
The constructor stores the connected socket descriptor then converts the socket information
structure fields to a peer IP address string and peer TCP port. These parameters can be inspected
with calls to TCPStream::getPeerIP() and TCPStream::getPeerPort().
7. #include
#include "tcpstream.h"
TCPStream::TCPStream(int sd, struct sockaddr_in* address) : msd(sd) {
char ip[50];
inet_ntop(PF_INET, (struct in_addr*)&(address->sin_addr.s_addr),
ip, sizeof(ip)-1);
m_peerIP = ip;
m_peerPort = ntohs(address->sin_port);
}
Destructor
The destructor simply closes the connection.
TCPStream::~TCPStream()
{
close(m_sd);
}
Network I/O Methods
TCPStream::send() and TCPStream::receive() simply wrap calls to read() and write(), returning
the number of bytes sent and bytes received, respectively. No additional buffering or other
capabilities are added.
Get Peer Information
TCPStream::getPeerIP() and TCPStream::getPeerPort() return the IP address and TCP port
information of the peer to which the network application, client or server, are connected. You
can get the same information from the sockets getpeername() function but it far easier to just
capture that information when the connections are established. Clients know in advance to where
they are connecting and the client’s socket address is returned the accept() function when the
server accepts a client connection – see the TCPAcceptor::accept() method definition. In both
cases the socket address information is passed to the TCPStream object when it is constructed.
TCPConnector Class
Interface
TCPConnector provides the connect() method to actively establish a connection with a server. It
accepts the server port and a string containing the server host name or IP address. If successful, a
pointer to a TCPStream object is returned to the caller.
#include
#include "tcpstream.h"
class TCPConnector
{
8. public:
TCPStream* connect(int port, const char* server);
private:
int resolveHost(const char* host, struct in_addr* addr);
};
Constructor/Destructor
The TCPConnector class does not use any member variables so the default constructor and
destructor generated by the C++ compiler are fine. No others are defined.
Connect to Server
[Lines 6-12] TCPConnector::connect() call takes a server host name or IP address string and the
server listening port as arguments. The server struct sockaddr_in sin_family is set to PF_INET
and the sin_port is set to the TCP port on which the server is listening for connections.
#include
#include
#include
#include "tcpconnector.h"
TCPStream* TCPConnector::connect(const char* server, int port)
{
struct sockaddr_in address;
memset (&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
if (resolveHostName(server, &(address.sin_addr)) != 0) {
inet_pton(PF_INET, server, &(address.sin_addr));
}
int sd = socket(AF_INET, SOCK_STREAM, 0);
if (::connect(sd, (struct sockaddr*)&address, sizeof(address)) != 0) {
return NULL;
}
return new TCPStream(sd, &address);
}
int TCPConnector::resolveHostName(const char* hostname, struct in_addr* addr)
{
struct addrinfo *res;
int result = getaddrinfo (hostname, NULL, NULL, &res);
if (result == 0) {
9. memcpy(addr, &((struct sockaddr_in *) res->ai_addr)->sin_addr,
sizeof(struct in_addr));
freeaddrinfo(res);
}
return result;
}
TCPAcceptor Class
Interface
TCPAcceptor includes member variables for the listening socket descriptor, the socket address
information – IP address and TCP port – and a flag that indicates whether or not the
TCPAcceptor has started listening for connections.
Two public methods are supported. One to start the listening and the other to accept connections.
#include
#include
#include "tcpstream.h"
using namespace std;
class TCPAcceptor
{
int m_lsd;
string m_address;
int m_port;
bool m_listening;
public:
TCPAcceptor(int port, const char* address="");
~TCPAcceptor();
int start();
TCPStream* accept();
private:
TCPAcceptor() {}
};
Constructor
The constructor sets the member variables to as shown here. Setting m_lsd indicates that the
lisetning socket has not been created.
#include
#include
#include
10. #include "tcpacceptor.h"
TCPAcceptor::TCPAcceptor(int port, const char* address)
: m_lsd(0), m_port(port), m_address(address), m_listening(false) {}
Destructor
If the listening socket has been created then it is closed in the destructor.
TCPAcceptor::~TCPAcceptor()
{
if (m_lsd > 0) {
close(m_lsd);
}
}
Start Listening for Connections
[Line 3-5] Creating a listening socket involves the most socket calls of any operation. Before
going through the series of calls, TCPAcceptor::start() checks to see if a listening socket already
exists. If so, the method just returns 0.
int TCPAcceptor::start()
{
if (m_listening == true) {
return 0;
}
m_lsd = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = PF_INET;
address.sin_port = htons(m_port);
if (m_address.size() > 0) {
inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
}
else {
address.sin_addr.s_addr = INADDR_ANY;
}
int optval = 1;
setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
int result = bind(m_lsd, (struct sockaddr*)&address, sizeof(address));
if (result != 0) {
perror("bind() failed");
11. return result;
}
result = listen(m_lsd, 5);
if (result != 0) {
perror("listen() failed");
return result;
}
m_listening = true;
return result;
}
TCPStream* TCPAcceptor::accept()
{
if (m_listening == false) {
return NULL;
}
struct sockaddr_in address;
socklen_t len = sizeof(address);
memset(&address, 0, sizeof(address));
int sd = ::accept(m_lsd, (struct sockaddr*)&address, &len);
if (sd < 0) {
perror("accept() failed");
return NULL;
}
return new TCPStream(sd, &address);
}
#include
#include
#include "tcpacceptor.h"
int main(int argc, char** argv)
{
if (argc < 2 || argc > 4) {
printf("usage: server [] ");
exit(1);
}
TCPStream* stream = NULL;
TCPAcceptor* acceptor = NULL;
12. if (argc == 3) {
acceptor = new TCPAcceptor(atoi(argv[1]), argv[2]);
}
else {
acceptor = new TCPAcceptor(atoi(argv[1]));
}
if (acceptor->start() == 0) {
while (1) {
stream = acceptor->accept();
if (stream != NULL) {
size_t len;
char line[256];
while ((len = stream->receive(line, sizeof(line))) > 0) {
line[len] = NULL;
printf("received - %s ", line);
stream->send(line, len);
}
delete stream;
}
}
}
perror("Could not start the server");
exit(-1);
}
Echo Client
The client application takes the server TCP port and IP address on the command line. For each
connection a string is displayed and sent to the server, the echoed string is received back and
displayed, then the connection is closed. The client will be defined in the file client.cpp
#include
#include
#include
#include "tcpconnector.h"
using namespace std;
int main(int argc, char** argv)
{
if (argc != 3) {
13. printf("usage: %s ", argv[0]);
exit(1);
}
int len;
string message;
char line[256];
TCPConnector* connector = new TCPConnector();
TCPStream* stream = connector->connect(argv[2], atoi(argv[1]));
if (stream) {
message = "Is there life on Mars?";
stream->send(message.c_str(), message.size());
printf("sent - %s ", message.c_str());
len = stream->receive(line, sizeof(line));
line[len] = NULL;
printf("received - %s ", line);
delete stream;
}
stream = connector->connect(argv[2], atoi(argv[1]));
if (stream) {
message = "Why is there air?";
stream->send(message.c_str(), message.size());
printf("sent - %s ", message.c_str());
len = stream->receive(line, sizeof(line));
line[len] = NULL;
printf("received - %s ", line);
delete stream;
}
exit(0);
}
#include
#include
#include
#include
using namespace std
class TCPStream
{
14. int m_sd;
string m_peerIP;
int m_peerPort;
public:
friend class TCPAcceptor;
friend class TCPConnector;
~TCPStream();
ssize_t send(char* buffer, size_t len);
ssize_t receive(char* buffer, size_t len);
string getPeerIP();
int getPeerPort();
private:
TCPStream(int sd, struct sockaddr_in* address);
TCPStream();
TCPStream(const TCPStream& stream);
};