Topics Covered: C++, Unix Socket API
This course will cover layers #2-5 on the below diagram, working top to bottom. Will also touch base a bit on #1.
The abstractions are quite clever. Each layer i + 1
does not care how layer i
is implemented. For example, HTTP can add its own headers on top of TCP or UDP, whichever is chosen (but until recently used TCP, why?).
As a software engineer or whichever career you choose, you will likely need to use the Internet. So, it is important to understand how the Internet works. That's what makes someone a good engineer.
My opinions:
I highly recommend using C++ for the projects in this course. The runtime performance is pretty much identical to C, and you get more data structures (STL) which can be VERY useful. Additionally, C++ and low level languages enjoyed a surge in popularity in 2023, so it's a great skill to have. Let's do a review of some concepts that can help with the project(s). Note: make sure you are only using C++ 17 libraries. Grader uses g++-11
which does not support C++ 20.
std::string
HTTP responses are variable length, so C strings are absolute pain to work with. Don't do that to yourself. std::stringstream
is also great.
#include <string>
#include <sstream>
std::stringstream response_builder;
response_builder << "Content-Type: " <<
get_content_type() <<
"; charset=UTF-8\r\n";
// add more necessary things to the response_builder...
std::string response = response_builder.str();
send(client_socket, response.c_str(), response.length(), 0);
Here's an example where I'm using it to set the Content-Type HTTP header, and then sending the built response. The string methods .find_last_of(pattern)
and .substr(idx)
are also pretty useful for certain tasks…
#include <iostream>
#include <fstream>
#include <sstream>
std::string path = /* some file path */;
std::ifstream f(path.c_str());
std::ostringstream file_reader;
if (f.good()) {
file_reader << f.rdbuf();
std::string contents = file_reader.str();
std::cout << contents << std::endl;
} else {
// file does not exist
}
Way easier than dealing with C strings, and the code is actually readable.
Some other useful things to look into are std::vector<T>
, using class
abstraction for components of your web server, enum
to represent MIME types, and <regex>
library for string pattern substitutions. There are many ways to go about your project, and these are just suggestions. Use whatever you're most comfortable using.
Finally, let's go through a simple case of a TCP client socket lifecycle in C/C++.
#include <iostream>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
// create the socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
std::cerr << "Failed to create socket" << std::endl;
return 1;
}
// create address representing the server
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080); // remember big endian here
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // localhost
// connect to the server
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
std::cerr << "Failed to connect to server" << std::endl;
close(sockfd);
return 1;
}
std::cout << "Connected to server" << std::endl;
// send data
const char *message = "I love CS 118!";
if (send(sockfd, message, strlen(message), 0) == -1) {
std::cerr << "Failed to send data to server" << std::endl;
close(sockfd);
return 1;
}
// close connection
close(sockfd);
std::cout << "Connection closed" << std::endl;
return 0;
}
Using what we have covered so far, try writing a simple TCP server running on localhost:8080
and use this client code to receive the message.
Putting this all together, you should get started on your first project ASAP! Remember that HTTP is just a set of additional "rules" you will implement in your C/C++ code to interpret the message sent to the server.