CS110: Principles of Computer Systems
Winter 2021-2022
Stanford University
Instructors: Nick Troccoli and Jerry Cain
Introduction to Networking
HTTP
Clients, Servers and APIs
Networking System Calls
assign6: implement an HTTP Proxy that sits between a client device and a web server to monitor, block or modify web traffic.
Your computer performs DNS lookups frequently on your behalf - e.g. when you want to visit a website in your browser.
For fun, we can view DNS servers using the dig command:
where are the edu nameservers? "dig -t NS +noall +answer edu"
the stanford.edu nameservers? "dig -t NS +noall +answer stanford.edu"
where is web.stanford.edu? "dig -t A +noall +answer web.stanford.edu"
// Opens a connection to a server (returns kClientSocketError on error)
int createClientSocket(const string& host, unsigned short port);
New helper function to connect to a server:
(Later on, we will learn how to implement createClientSocket!)
I am running a server on myth64.stanford.edu, port 12345 that can tell you the current time. Whenever a client connects to it, the server sends back the time as text.
int main(int argc, char *argv[]) {
// open a connection to the server
int client = createClientSocket("myth64.stanford.edu", 12345);
// Read the response from the server
char buf[1024];
size_t bytes_read = 0;
while (true) {
size_t read_this_time = read(client, buf + bytes_read, sizeof(buf) - bytes_read);
if (read_this_time == 0) break;
bytes_read += read_this_time;
}
close(client);
// print the response
printf("%s", buf);
return 0;
}
Client sockets work similarly to regular file descriptors - we open one, read from/write to it, and close it.
int main(int argc, char *argv[]) {
// open a connection to the server
int client = createClientSocket("myth64.stanford.edu", 12345);
// Read the response from the server
char buf[1024];
size_t bytes_read = 0;
while (true) {
size_t read_this_time = read(client, buf + bytes_read, sizeof(buf) - bytes_read);
if (read_this_time == 0) break;
bytes_read += read_this_time;
}
close(client);
// print the response
printf("%s", buf);
return 0;
}
Using read/write is cumbersome with socket descriptors. The socket++ library provides a type iosockstream that let us wrap a socket descriptor in a stream (so that we can read/write like we do with cout)
static string readLineFromSocket(int socketDescriptor) {
sockbuf sb(socketDescriptor);
iosockstream ss(&sb);
string line;
getline(ss, line);
return line;
} // sockbuf destructor closes client
// Creates a socket to listen for incoming requests (returns kServerSocketFailure on error)
int createServerSocket(unsigned short port, int backlog = kDefaultBacklog);
New helper function to create a socket descriptor to listen for incoming connections:
(Later on, we will learn how to implement createServerSocket!)
// Waits for an incoming connection and returns a descriptor for that connection
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
I am running a server on myth64.stanford.edu, port 12345 that can tell you the current time. Whenever a client connects to it, the server sends back the time as text.
int main(int argc, char *argv[]) {
int server = createServerSocket(12345);
while (true) {
int client = accept(server, NULL, NULL);
publishTime(client);
}
close(server);
return 0;
}
I am running a server on myth64.stanford.edu, port 12345 that can tell you the current time. Whenever a client connects to it, the server sends back the time as text.
static void publishTime(int client) {
time_t rawtime;
time(&rawtime);
struct tm *ptm = gmtime(&rawtime);
char timestr[128]; // more than big enough
/* size_t len = */ strftime(timestr, sizeof(timestr), "%c", ptm);
sockbuf sb(client);
iosockstream ss(&sb);
ss << timestr << endl;
} // sockbuf destructor closes client
I am running a server on myth64.stanford.edu, port 12345 that can tell you the current time. Whenever a client connects to it, the server sends back the time as text.
int main(int argc, char *argv[]) {
int server = createServerSocket(12345);
ThreadPool pool(4);
while (true) {
int client = accept(server, NULL, NULL);
pool.schedule([client] { publishTime(client); });
}
close(server)
return 0;
}
Next time: more about data formats and protocols