Socket Programming in C++
===
Topics Covered: C++, Unix Socket API
## Introduction to Computer Networking
This course will cover layers #2-5 on the below diagram, working top to bottom. Will also touch base a bit on #1.
![image](https://hackmd.io/_uploads/ByDfDdleR.png)
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?).
### Why should I care?
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:
1. Cloud technology is here to stay. Cloud computing involves connected/networked devices at MASSIVE scale. With the job market lately performing very poorly for entry-level web development engineers, there is still plenty of hiring happening in backend/distributed systems/cloud, because not all universities demand students take CS 118 :)
2. With the widespread adoption of the Internet in almost all aspects of daily life, network security is extremely important. The UCLA CS department is working on addressing this in the curriculum, and there's plenty of opportunities post graduation in this field too!
## Socket API
### C++
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.
```cpp=
#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... :eyes:
#### Reading from a file
```cpp=
#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.
#### Misc.
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.
### Example
Finally, let's go through a simple case of a TCP client socket lifecycle in C/C++.
```cpp=
#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.