Principles of Computer Systems

Winter 2020

Stanford University

Computer Science Department

Instructors: Chris Gregg and 

                            Nick Troccoli

PDF of this presentation

Slide 1: Lecture 15: Introduction to Networking

Slide 2: Lecture 15: Assignment 6: ThreadPool

A sample thread pool (green boxes) with waiting tasks (blue) and completed tasks (yellow)

Source: https://en.wikipedia.org/wiki/Thread_pool#/media/File:Thread_pool.svg

cgregg@myth59: $ netstat -plnt
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:587           0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
tcp6       0      0 ::1:631                 :::*                    LISTEN      -

Slide 3: Lecture 15: Introduction to Networking

cgregg@myth59: $ netstat -plnt
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:587           0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
tcp6       0      0 ::1:631                 :::*                    LISTEN      -

Slide 4: Lecture 15: Introduction to Networking

int main(int argc, char *argv[]) {
  int server = createServerSocket(12345);
  while (true) {
    int client = accept(server, NULL, NULL); // the two NULLs could instead be used to
                                             // surface the IP address of the client
    publishTime(client);
  }
  return 0;
}

Slide 5: Lecture 15: Introduction to Networking

  • The first five lines here produce the full time string that should be published.
  • The remaining lines publish the time string to the client socket using the raw, low-level I/O we've seen before.
  • 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\n", ptm);
      size_t numBytesWritten = 0, numBytesToWrite = strlen(timestr);
      while (numBytesWritten < numBytesToWrite) {
        numBytesWritten += write(client,
                                 timestr + numBytesWritten,
                                 numBytesToWrite - numBytesWritten);
      }
      close(client);
    }

    Slide 6: Lecture 15: Introduction to Networking

    Slide 7: Lecture 15: Introduction to Networking

    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

    Slide 8: Lecture 15: Introduction to Networking

    int main(int argc, char *argv[]) {
        int server = createServerSocket(12345);
        ThreadPool pool(4);
        while (true) {
            int client = accept(server, NULL, NULL); // the two NULLs could instead be used
                                                     // to surface the IP address of the client
            pool.schedule([client] { publishTime(client); });
        }
        return 0;
    }

    Slide 9: Lecture 15: Introduction to Networking

    Slide 10: Lecture 15: Introduction to Networking

    static void publishTime(int client) {
        time_t rawtime;
        time(&rawtime);
        struct tm tm;
        gmtime_r(&rawtime, &tm);
        char timestr[128]; // more than big enough
        /* size_t len = */ strftime(timestr, sizeof(timestr), "%c", &tm);
        sockbuf sb(client); // destructor closes socket
        iosockstream ss(&sb);
        ss << timestr << endl;
    }

    Slide 11: Lecture 15: Introduction to Networking

    Slide 12: Lecture 15: Introduction to Networking

    int main(int argc, char *argv[]) {
        int clientSocket = createClientSocket("myth64.stanford.edu", 12345);
        assert(clientSocket >= 0);
        sockbuf sb(clientSocket);
        iosockstream ss(&sb);
        string timeline;
        getline(ss, timeline);
        cout << timeline << endl;
        return 0;
    }

    Slide 13: Lecture 15: Introduction to Networking

    static const string kProtocolPrefix = "http://";
    static const string kDefaultPath = "/";
    static pair<string, string> parseURL(string url) {
        if (startsWith(url, kProtocolPrefix))
            url = url.substr(kProtocolPrefix.size());
        size_t found = url.find('/');
        if (found == string::npos)
            return make_pair(url, kDefaultPath);
        string host = url.substr(0, found);
        string path = url.substr(found);
        return make_pair(host, path);
    }
    
    int main(int argc, char *argv[]) {
        pullContent(parseURL(argv[1]));
        return 0;
    }

    Slide 14: Lecture 15: Introduction to Networking

    static const unsigned short kDefaultHTTPPort = 80;
    static void pullContent(const pair<string, string>& components) {
        int client = createClientSocket(components.first, kDefaultHTTPPort);
        if (client == kClientSocketError) {
            cerr << "Could not connect to host named \"" << components.first << "\"." << endl;
            return;
        }
        sockbuf sb(client);
        iosockstream ss(&sb);
        issueRequest(ss, components.first, components.second);
        skipHeader(ss);
        savePayload(ss, getFileName(components.second));
    }
  • We've already used this createClientSocket function for our time-client. This time, we're connecting to real but arbitrary web servers that speak HTTP.
  • The implementations of issueRequest, skipHeader, and savePayload subdivide the client-server conversation into manageable chunks.
  • Slide 15: Lecture 15: Introduction to Networking

    Emulation of wget (continued)

    static void issueRequest(iosockstream& ss, const string& host, const string& path) {
        ss << "GET " << path << " HTTP/1.0\r\n";
        ss << "Host: " << host << "\r\n";
        ss << "\r\n";
        ss.flush();
    }
  • It's standard HTTP-protocol practice that each line, including the blank line that marks the end of the request, end in CRLF (short for carriage-return-line-feed), which is '\r' following by '\n'.
  • The flush is necessary to ensure all character data is pressed over the wire and consumable at the other end.
  • After the flush, the client transitions from supply to ingest mode. Remember, the iosockstream is read/write, because the socket descriptor backing it is bidirectional.
  • Slide 16: Lecture 15: Introduction to Networking

    static void skipHeader(iosockstream& ss) {
        string line;
        do {
            getline(ss, line);
        } while (!line.empty() && line != "\r");
    }
  • In practice, a true HTTP client—in particular, something as HTTP-compliant as the wget we're imitating—would ingest all of the lines of the response header into a data structure and allow it to influence how it treats payload.
  • Slide 17: Lecture 15: Introduction to Networking

  • Everything beyond the response header and that blank line is considered payload—that's the timestamp, the JSON, the HTML, the image, or the cat video.
  • static string getFileName(const string& path) {
        if (path.empty() || path[path.size() - 1] == '/') return "index.html";
        size_t found = path.rfind('/');
        return path.substr(found + 1);
    }
    
    static void savePayload(iosockstream& ss, const string& filename) {
        ofstream output(filename, ios::binary); // don't assume it's text
        size_t totalBytes = 0;
        while (!ss.fail()) {
            char buffer[2014] = {'\0'};
            ss.read(buffer, sizeof(buffer));
            totalBytes += ss.gcount();
            output.write(buffer, ss.gcount());
        }
        cout << "Total number of bytes fetched: " << totalBytes << endl;
    }
  • HTTP dictates that everything beyond that blank line is payload, and that once the server publishes that payload, it closes its end of the connection. That server-side close is the client-side's EOF, and we write everything we read.