Programming Project, C++ Programming
Programming Project, C++ Programming
2 System Requirements
2.1 General Requirements
The system consists of a server that handles a database containing newsgroups and articles and a
client that accepts commands from the user and communicates with the server. Several clients
may be connected to the server simultaneously.
The following tasks can be performed:
• List newsgroups.
• Create and delete newsgroups.
• List articles in a newsgroup.
• Read, write and delete articles in a newsgroup.
The system keeps track of the title and the author of each article. It cannot handle subject threads,
follow-up articles or similar advanced concepts that are available in standard NNTP-based news
implementations.
The communication between the server and the client follows the messaging protocol that is
defined in section 6.
There are no security requirements on the system. For instance, any user can delete news-
groups and articles, even if he or she is not the creator.
• There are two versions of the server: the first version uses a database stored in primary
memory, the second a database stored on disk.
• The in-memory version of the server starts from scratch each time it is invoked and builds
the database in primary memory.
• The disk version of the database remembers the database between invocations. Changes to
the database are immediately reflected on disk. See section 3.2.
• Each newsgroup has a unique name. A newsgroup also has a unique identification number,
greater than zero. Identification numbers may not be reused.
1 See http://www.faqs.org/rfcs/rfc3977.html.
4
• Each article has a title, an author and an article text. The article names need not be unique.
An article also has an identification number, which must be unique in the newsgroup.
Identification numbers may not be reused.
• Listings of newsgroups and articles are in chronological order, with the oldest item first.
• There are no limitations on the number of newsgroups or the number of articles in a
newsgroup.
• There are no limitations on the length of newsgroup titles, article titles, author names or
article texts.
• If a client misbehaves, for example by not following the messaging protocol, it is immediately
disconnected by the server.
• The server tries to handle all errors. If it cannot recover from an error, it terminates with an
informative error message.
• The clients reads commands from the keyboard, communicates with the server and presents
the replies from the server on the terminal.
• The client is easy to use and contains informative help texts. No manual is necessary to use
the client program.
• The client tries to handle all errors. If it cannot recover from an error, it terminates with an
informative error message.
3 Development Procedure
3.1 General Advice
• Start by writing the in-memory version of the server. Use the test clients described in
section 7.1 during development.
• Then write the client, using your own server.
• Finally, write the disk version of the server. Your server design should be such that you
only have to change the part of the server that deals with the database.
• Use the classes described in section 5.2 for the low-level communication between the server
and the clients.
• You must use the messaging protocol described in section 6 for communication.
• It is a good idea to define a class MessageHandler to handle the communication on “low
protocol level”. It should be possible to use this class also in the client. See the file
MessageHandler.java in /usr/ local/ cs/ cpp/ serverTests/ src/ common for ideas on how
to write such a class.
• Write a class that functions as an interface to the database, so it is easy to switch between
the in-memory version and the disk version of the server.
5
• A production version of the server would most probably be multithreaded with one thread
for each client. In this project, the server should be singlethreaded; the communication
delays that are caused by this are acceptable.
• Separate the communication and the database parts of the server as far as possible. For
instance, you should not have to modify the database in any way if the messaging protocol
is changed.
• You may choose any method to implement the disk version of the database, as long as
the implementation fulfills the requirements. One method is to dedicate a root directory
to the database. A newsgroup is represented as a directory in the root directory, and the
articles as regular files in the group directories. If you use this method you should study
the system calls mkdir (chapter 2 of the man pages) and remove, opendir, readdir, and
closedir (chapter 3C).
If you wish to use a relational database you can get a username on the department’s MySQL
installation. But after that you are on your own — you will have to download and learn to
use a MySQL connector for C++.
4 Submission of Results
You shall hand in a written report that describes your system, and also all files necessary to build
your system. Don’t hand in anything before everything (for example, the tests in TestServer2)
works. The latest date for submission is in the course plan.
The report must be well structured, complete, easy to understand, well formulated, etc. It
should (minimally) contain:
• A cover sheet with the title of the project, the course name, your names and e-mail addresses.
• A detailed description of your system design, both for the server and the clients. Preferably
use UML diagrams to give an overview of the design. (It is not necessary that you list
attributes and methods in these diagrams.) You must also describe the classes, at least as
far as stating the responsibilities of each class.
Also give an overview of the dynamics of the server, i.e., trace an interaction between a
client and the server from the point that the server receives a command until it sends the
reply. UML sequence diagrams are good for this purpose.
• Conclusions: requirements that you fulfill, problems that you haven’t succeeded in solving,
etc. If you have found that the system ought to have more features you should elaborate on
this. Any suggestions for improvements to the project are also welcome.
When you are ready to submit your report and your programs, do the following:
by is a delimiter. id1 and id2 are your StiL id’s (the ones that you used when you signed
up for the labs). Write your full names in the letter.
5 Low-Level Communication
5.1 Communication Between Unix Computers
Unix computers communicate via numbered ports. Some ports are dedicated to special purposes,
but all ports with numbers above 1024 are freely available for application programs. Two
programs that wish to communicate must have agreed on which port to use. The server listens
for traffic on the specified port, and clients may connect to this port on the server’s computer.
Once a connection has been established, messages are exchanged via sockets. A program
writes to a socket and another program reads from another socket.
You shall use two classes in your system: Connection, which handles a connection to another
program (in principle, a socket), and Server, which handles several simultaneous connections.
Each client creates a Connection object for communication with the server. The server creates a
Server object that keeps track of the clients with the help of a Connection object for each client.
Class specifications (only the public interface):
/* Deregisters a connection */
void deregisterConnection(const std::shared_ptr<Connection>& conn);
In the following programs, the client reads 32-bit integers from the terminal and sends them to
the server. The server reads an integer and responds with a string, Positive/Zero/Negative,
depending on the sign of the number. Notice that there are rudiments of a protocol here: integers
are transmitted as four bytes with the high-order byte first and the end of a variable-length string
is marked with a special character.
#include <iostream>
#include <string>
#include <stdexcept>
#include <cstdlib>
/*
* Send an integer to the server as four bytes.
*/
void writeNumber(const Connection& conn, int value) {
conn.write((value >> 24) & 0xFF);
conn.write((value >> 16) & 0xFF);
conn.write((value >> 8) & 0xFF);
conn.write(value & 0xFF);
}
/*
* Read a string from the server.
*/
string readString(const Connection& conn) {
string s;
char ch;
while ((ch = conn.read()) != ’$’) {
s += ch;
}
return s;
}
8
--------------------------------------------------------------------------
#include <memory>
#include <iostream>
#include <string>
#include <stdexcept>
#include <cstdlib>
/*
* Read an integer from a client.
*/
int readNumber(const shared_ptr<Connection>& conn) {
unsigned char byte1 = conn->read();
unsigned char byte2 = conn->read();
unsigned char byte3 = conn->read();
unsigned char byte4 = conn->read();
return (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
}
9
/*
* Send a string to a client.
*/
void writeString(const shared_ptr<Connection>& conn, const string& s) {
for (char c : s) {
conn->write(c);
}
conn->write(’$’);
}
Server server(port);
if (!server.isReady()) {
cerr << "Server initialization error." << endl;
exit(1);
}
while (true) {
auto conn = server.waitForActivity();
if (conn != nullptr) {
try {
int nbr = readNumber(conn);
string result;
if (nbr > 0) {
result = "positive";
} else if (nbr == 0) {
result = "zero";
} else {
result = "negative";
}
writeString(conn, result);
} catch (ConnectionClosedException&) {
server.deregisterConnection(conn);
cout << "Client closed connection" << endl;
}
} else {
conn = make_shared<Connection>();
server.registerConnection(conn);
cout << "New client connects" << endl;
}
}
}
10
6 Message Protocol
A message consist of a number of characters (bytes). A message has the following format:
PAR STRING and PAR NUM are one-byte constants. A number N is transmitted as four bytes with
the most significant byte first.
The list below shows the format of all messages. Messages from the server to a client contain
a status indication: ANS ACK if the command was executed successfully, ANS NAK followed by an
error code otherwise. The names of the error codes are hopefully self explanatory.
[x | y] means x or y, x* means x zero or more times.
1. List newsgroups. The reply contains the number of newsgroups followed by the identifica-
tion numbers and titles of the groups.
COM_LIST_NG COM_END
ANS_LIST_NG num_p [num_p string_p]* ANS_END
4. List articles in a newsgroup. The identification number of the group is sent as a parameter.
The reply contains the number of articles, followed by the identification numbers and titles
of the articles.
5. Create an article. The identification number of the group is sent as a parameter, followed by
the article title, author and text.
6. Delete an article. The group and article identification numbers are sent as parameters.
7. Get an article. The group and article identification numbers are sent as parameters. The
reply contains the title, author, and text of the article.
All symbolic constants are defined in the class Protocol (see section 7.2).
Two test programs are available: TestServer1 and TestServer2. Both programs are written in
Java, and .jar-files may be downloaded from the course homepage (the files are also available in
the directory /usr/ local/ cs/ cpp). Execution:
The programs open a connection to the server running on hostname (which may be localhost)
on the port port. TestServer1 is intended to be used during development of the server. It
contains buttons to execute the commands that the server should be able to handle.
TestServer2 is a more complete program that systematically tests all commands, including
erroneous commands (creating a newsgroup with a duplicate name, deleting a non-existing
newsgroup, and so on).
struct Protocol {
enum {
/* Command codes, client -> server */
COM_LIST_NG = 1, // list newsgroups
...
/* Parameters */
PAR_STRING = 40, // string
...
/* Error codes */
ERR_NG_ALREADY_EXISTS = 50, // newsgroup already exists
...
};
};
The test programs rely on the same constant definitions, so the definitions must not be changed.