Lab Handout 6: Networking
The lab checkoff sheet for all students can be found right here.
Get Started
Before starting, go ahead and clone the lab6
folder, which contains the code for the file-server
program discussed in Problem 3.
git clone /usr/class/cs110/repos/lab6/shared lab6
Problem 1: Hello Server Short Answers
Est. 25min.
Consider the following server called hello-server
:
static const size_t kNumWorkers = 3;
static pid_t workerPIDs[kNumWorkers];
static void shutdownAllServers(int unused) {
for (size_t i = 0; i < kNumWorkers; i++) {
cout << "Shutting down server with pid " << workerPIDs[i] << "." << endl;
kill(workerPIDs[i], SIGINT);
waitpid(workerPIDs[i], NULL, 0);
}
cout << "Shutting down orchestrator." << endl;
exit(0);
}
static void handleRequest(int client) {
sockbuf sb(client);
iosockstream ss(&sb);
ss << "Hello from server (pid: " << getpid() << ")" << endl;
}
static void runServer(int server) {
cout << "Firing up hello server inside process with pid " << getpid() << "." << endl;
while (true) {
int client = accept(server, NULL, NULL);
cout << "Request handled by server (pid " << getpid() << ")." << endl;
handleRequest(client);
}
}
int main(int argc, char *argv[]) {
int server = createServerSocket(33334);
for (size_t i = 0; i < kNumWorkers; i++) {
if ((workerPIDs[i] = fork()) == 0) runServer(server);
}
signal(SIGINT, shutdownAllServers);
runServer(server); // let orchestrator process be consumed by same server
return 0;
}
If we launch the above executable on myth64
, hit it 12 times by repeatedly typing telnet myth64 33334
at the prompt from a myth65
shell, and then press ctrl-c
on myth64
, we get the following:
myth64$ ./hello-server
Firing up hello server inside process with pid 19941.
Firing up hello server inside process with pid 19940.
Firing up hello server inside process with pid 19942.
Firing up hello server inside process with pid 19943.
Request handled by server (pid 19940).
Request handled by server (pid 19942).
Request handled by server (pid 19941).
Request handled by server (pid 19943).
Request handled by server (pid 19940).
Request handled by server (pid 19942).
Request handled by server (pid 19941).
Request handled by server (pid 19943).
Request handled by server (pid 19940).
Request handled by server (pid 19942).
Request handled by server (pid 19941).
Request handled by server (pid 19943).
^CShutting down server with pid 19941.
Shutting down server with pid 19942.
Shutting down server with pid 19943.
Shutting down orchestrator.
Based on the code and test run you see above, answer each of the following short answer questions.
- Note the very first line of the server’s
main
function creates a server socket that listens to 33334 for incoming network activity. Why, after thefor
loop withinmain
, are all of the child processes also listening to port 33334 through the same server socket descriptor? - One way to describe port numbers is as "virtual process IDs". What might be meant by that, and why are virtual process IDs needed with networked applications?
- What happens if the call to
exit(0)
is removed from the implementation ofshutdownServers
and we send aSIGINT
to the orchestrator process in the suite of 4hello-server
servers? - If the call to the
signal
function is moved to reside above thefor
loop inmain
instead of below it, does that impact our ability to close down the full suite of 4hello-server
servers? - The above program doesn’t close the server sockets in
shutdownServers
. Describe a simple way to ensure a server socket is properly closed withclose()
before the surrounding server executable exits.
Problem 2: Networking, Client/Server, Request/Response
(Est. 15min)
- Explain the differences between a pipe and a socket.
- Explain how system calls are a form of client/server and request/response.
- Describe how networking is just another form of function call and return. What “function” is being called? What are the parameters? And what's the return value? Assume HTTP is the operative protocol.
- Describe the network architecture needed for:
- Email servers and clients
- Peer-to-Peer Text Messaging via cell phone numbers
- Skype
- As it turns out, each of the three network applications above all make use of custom protocols. Which ones could have relied on HTTP and/or HTTPS instead of custom protocols?
Problem 3: File Server
(Est. 40min)
In class, we built a server that used our subprocess function to run an external program. Using most of the same code (minus the subprocess, timing, caching, and JSON functionality), we can write a file server that sends files from our file system to a client, and also produces directory listings if the client requests a directory. Many World Wide Web servers have this ability built-in, and it is a quick way to access files on your web server. If you cloned the repository, you have all of the code for the file server.
In order to request a file, the client requests the server name and port number, followed by the file path, which has its root wherever the server is running. e.g. http://myth57:13133/filename
.
The code for getFilename
is shown below:
static string getFilename(iosockstream& ss) {
string method, path, protocol;
ss >> method >> path >> protocol;
string rest;
getline(ss, rest);
cout << "\tPath requested: " << path << endl;
if (path == "/") {
// serve current directory
return (".");
}
size_t pos = path.find("/");
return pos == string::npos ? path : path.substr(pos + 1);
}
- The function populates the
method
,path
, andprotocol
variables, but only uses thepath
variable. What is the purpose of reading in data we won't use? - What is the purpose of the
getline(ss, rest)
statement? - Can you think of any security vulnerabilities in the code above? Hint: what if you ran the server in a directory located at
~/yourHome/SecureServer
. Is there any way for the client to be malicious?
The three functions shown below determine whether the file requested by the client is a file or a directory, and format the fileContents
appropriately. If the name requested is a directory, the opendir
and readdir
functions are used to populate an html listing, complete with hyperlinks. For actual files, the file is sent in plain text without any extra formatting.
/**
* Function: listDir
* ------------------------------
* Populates fileContents with a directory listing in HTML
*/
static void listDir(string& fileContents, const string& directoryPath, bool& isHTML) {
isHTML = true;
/* Create a list of all entries in this directory, in (filename, path) pairs
* e.g. (myfile.txt, samples/mydir/files/myfile.txt).
*/
vector<pair<string, string>> containedFiles;
DIR *dir = opendir(directoryPath.c_str());
if (dir != NULL) {
struct dirent *ent = readdir(dir);
// Loop through every entry in this directory
while (ent != NULL) {
string entryFileName = string(ent->d_name);
string pathToFile;
if (entryFileName == ".") {
pathToFile = ".";
} else if (entryFileName == "..") {
// remove up to the final slash
size_t lastSlashIndex = directoryPath.rfind("/");
if (lastSlashIndex != string::npos) {
pathToFile = directoryPath.substr(0, lastSlashIndex);
} else {
pathToFile = "";
}
} else {
pathToFile = directoryPath + "/" + entryFileName;
}
// Add a new (filename, path) pair
containedFiles.push_back(make_pair(entryFileName, pathToFile));
ent = readdir(dir);
}
closedir(dir);
// Fill fileContents with HTML for these pairs (omitted)
makeHTMLForDirectoryContents(fileContents, directoryPath, containedFiles);
} else {
/* could not open directory */
fileContents = "<h1>Could not open directory.</h1>\r\n";
}
}
/**
* Function: showFile
* ------------------------------
* Populates fileContents with the file contents
*/
static void showFile(string& fileContents, const string& fileName, bool& isHtml) {
ifstream t(fileName);
stringstream buffer;
buffer << t.rdbuf();
fileContents = buffer.str();
isHtml = false;
}
/**
* Function: getFileContents
* ------------------------------
* If fileName is a file, populate fileContents with
* the file's data. If fileName is a directory,
* populate fileContents with a directory listing
*/
static void getFileContents(string& fileContents, const string& fileName, bool& isHTML) {
struct stat s;
if (stat(fileName.c_str(), &s) == 0) {
if (s.st_mode & S_IFDIR) {
//it's a directory
listDir(fileContents, fileName, isHTML);
}
else if (s.st_mode & S_IFREG) {
//it's a file
showFile(fileContents, fileName, isHTML);
} else {
fileContents = "<h1>Requested name was not a file or directory.</h1>\r\n";
isHTML = true;
return;
}
} else {
fileContents = "<h1>File \"" + fileName + "\" was not found.</h1>\r\n";
isHTML = true;
return;
}
}
- Explain how the path to the file is generated for the “..” file. Why does it need to be handled as a special case?
- Assuming that the client is using a web browser, some files that the client might request could actually be html files, yet we are displaying them as raw text files. How might you modify
showFile
to set theisHTML
flag appropriately for html files?
The sendResponse
function is shown below:
static void sendResponse(iosockstream& ss, const string& payload, bool isHTML) {
ss << "HTTP/1.1 200 OK\r\n";
if (isHTML) {
ss << "Content-Type: text/html; charset=UTF-8\r\n";
} else {
ss << "Content-Type: text/plain; charset=UTF-8\r\n";
}
ss << "Content-Length: " << payload.size() << "\r\n";
ss << "\r\n";
ss << payload << flush;
}
- Notice that we use the
isHTML
bool we populated in the file-handling functions to tell the client's browser what type of data we are sending back. The “Content-Type” string, known as a “media type” or “MIME type” is an important identifier for the Internet. We have already used theapplication/javascript
see here type for the scrabble solver server, but there are many other types. You can find an overwhelming list here. - Why must we declare the character set along with the content type?
- What is the purpose of the
flush
on the last line?
You can try out the server by running it and then requesting files using your web browser (if you aren't on the Stanford campus, you should use the Stanford VPN so you can access the myth
machines with your browser). There are two directories (subdir
and anotherdir
) in samples
with files in them to test going up and down directories.
Icons by Piotr Kwiatkowski