| Safe from bugs | Easy to understand | Ready for change |
|---|---|---|
| Correct today and correct in the unknown future. | Communicating clearly with future programmers, including future you. | Designed to accommodate change without rewriting. |
$ dig +short web.mit.edu 18.9.22.69+ `dmz-mailsec-scanner-4.mit.edu` is the name for one of MIT's spam filter machines responsible for handling incoming email. + `google.com` is exactly what you think it is. Try using one of the commands above to find `google.com`'s IP address. What do you see? + `localhost` is a name for `127.0.0.1`. When you want to talk to a server running on your own machine, talk to `localhost`. Translation from hostnames to IP addresses is the job of the [Domain Name System (DNS)](http://en.wikipedia.org/wiki/Domain_Name_System). It's super cool, but not part of our discussion today. ### Port numbers A single machine might have multiple server applications that clients wish to connect to, so we need a way to direct traffic on the same network interface to different processes. Network interfaces have multiple [ports](http://en.wikipedia.org/wiki/Port_(computer_networking)) identified by a 16-bit number from 0 (which is reserved, so we effectively start at 1) to 65535. A server process binds to a particular port --- it is now **listening** on that port. Clients have to know which port number the server is listening on. There are some [well-known ports](http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports) which are reserved for system-level processes and provide standard ports for certain services. For example: + Port 22 is the standard SSH port. When you connection to `athena.dialup.mit.edu` using SSH, the software automatically uses port 22. + Port 25 is the standard email server port. + Port 80 is the standard web server port. When you connect to the URL `http://web.mit.edu` in your web browser, it connects to `18.9.22.69` on port 80. When the port is not a standard port, it is specified as part of the address. For example, the URL `http://128.2.39.10:9000` refers to port 9000 on the machine at `128.2.39.10`. When a client connects to a server, that outgoing connection also uses a port number on the client's network interface, usually chosen at random from the available *non*-well-known ports. ### Network sockets A [**socket**](http://en.wikipedia.org/wiki/Network_socket) represents one end of the connection between client and server. + A **listening socket** is used by a server process to wait for connections from remote clients. In Java, use [`ServerSocket`](java:java/net/ServerSocket) to make a listening socket, and use its [`accept`](http://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html#accept--) method to listen to it. + An **connected socket** can send and receive messages to and from the process on the other end of the connection. It is identified by both the local IP address and port number plus the remote address and port, which allows a server to differentiate between concurrent connections from different IPs, or from the same IP on different remote ports. In Java, clients use a [`Socket`](java:java/net/Socket) constructor to establish a socket connection to a server. Servers obtain an connected socket as a `Socket` object returned from `ServerSocket.accept`. ## Buffers The data that clients and servers exchange over the network is sent in chunks. These are rarely just byte-sized chunks, although they might be. The sending side (the client sending a request or the server sending a response) typically writes a large chunk (maybe a whole string like "HELLO, WORLD!" or maybe 20 megabytes of video data). The network chops that chunk up into packets, and each packet is routed separately over the network. At the other end, the receiver reassembles the packets together into a stream of bytes. The result is a bursty kind of data transmission --- the data may already be there when you want to read them, or you may have to wait for them to arrive and be reassembled. When data arrive, they go into a **buffer**, an array in memory that holds the data until you read it. mitx:c10af5c2ab514a99ad0b201de353f117 Client server socket buffer* \* see [***What if Dr. Seuss Did Technical Writing?***](http://web.mit.edu/adorai/www/seuss-technical-writing.html), although the issue described in the first stanza is no longer relevant with the obsolescence of floppy disk drives ## Streams The data going into or coming out of a socket is a [**stream**](http://en.wikipedia.org/wiki/Stream_(computing)) of bytes. In Java, [`InputStream`](java:java/io/InputStream) objects represent sources of data flowing into your program: reading from a file on disk with a [`FileInputStream`](java:java/io/FileInputStream), taking user input from [`System.in`](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#in), or input from a network socket. [`OutputStream`](java:java/io/OutputStream) objects represent data sinks, places we can write data to: [`FileOutputStream`s](java:java/io/FileOutputStream) for saving to files, [`System.out`](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#out) is a [`PrintStream`](java/io/InputStream), or the output to a network socket.
You'll be using Telnet on the problem set, so try these out now. User input is shown in green, and for input to the telnet connection, newlines (pressing enter) are shown with ↵:
$ telnet www.eecs.mit.edu 80 Trying 18.62.0.96... Connected to eecsweb.mit.edu. Escape character is '^]'. GET /↵ <!DOCTYPE html> ... lots of output ... <title>Homepage | MIT EECS</title> ... lots more output ...The `GET` command gets a web page. The `/` is the path of the page you want on the site. So this command fetches the page at `http://www.eecs.mit.edu:80/`. Since 80 is the default port for HTTP, this is equivalent to visiting [http://www.eecs.mit.edu/](http://www.eecs.mit.edu/) in your web browser. The result is HTML code that your browser renders to display the EECS homepage. Internet protocols are defined by [RFC specifications](http://en.wikipedia.org/wiki/Request_for_Comments) (RFC stands for "request for comment", and some RFCs are eventually adopted as standards). [RFC 1945](http://tools.ietf.org/html/rfc1945) defined HTTP version 1.0, and was superseded by HTTP 1.1 in [RFC 2616](http://tools.ietf.org/html/rfc2616). So for many web sites, you might need to speak HTTP 1.1 if you want to talk to them. For example:
$ telnet web.mit.edu 80 Trying 18.9.22.69... Connected to web.mit.edu. Escape character is '^]'. GET /aboutmit/ HTTP/1.1↵ Host: web.mit.edu↵ ↵ HTTP/1.1 200 OK Date: Wed, 22 Oct 2014 21:26:38 GMT ... more headers ... <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ... more HTML ... <title>MIT — About</title> ... lots more HTML ...This time, your request must end with a blank line. HTTP version 1.1 requires the client to specify some extra information (called headers) with the request, and the blank line signals the end of the headers. You will also more than likely find that telnet does not exit after making this request --- this time, the server keeps the connection open so you can make another request right away. To quit Telnet manually, type the escape character (probably `Ctrl`-`]`) to bring up the `telnet>` prompt, and type `quit`. ### SMTP [Simple Mail Transfer Protocol (SMTP)](http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol) is the protocol for sending email (different protocols are used for client programs that retrieve email from your inbox). Because the email system was designed in a time before spam, modern email communication is fraught with traps and heuristics designed to prevent abuse. But we can still try to speak SMTP. Recall that the well-known SMTP port is 25, and `dmz-mailsec-scanner-4.mit.edu` was the name of an MIT email handler. You'll need to fill in *your-IP-address-here* and *your-username-here*, and the ↵ indicate newlines for clarity. This will only work if you're on MITnet, and even then your mail might be rejected for looking suspicious:
$ telnet dmz-mailsec-scanner-4.mit.edu 25 Trying 18.9.25.15... Connected to dmz-mailsec-scanner-4.mit.edu. Escape character is '^]'. 220 dmz-mailsec-scanner-4.mit.edu ESMTP Symantec Messaging Gateway HELO your-IP-address-here↵ 250 2.0.0 dmz-mailsec-scanner-4.mit.edu says HELO to your-ip-address:port MAIL FROM: <your-username-here@mit.edu>↵ 250 2.0.0 MAIL FROM accepted RCPT TO: <your-username-here@mit.edu>↵ 250 2.0.0 RCPT TO accepted DATA↵ 354 3.0.0 continue. finished with "\r\n.\r\n" From: <your-username-here@mit.edu>↵ To: <your-username-here@mit.edu>↵ Subject: testing↵ This is a hand-crafted artisanal email.↵ .↵ 250 2.0.0 OK 99/00-11111-22222222 QUIT↵ 221 2.3.0 dmz-mailsec-scanner-4.mit.edu closing connection Connection closed by foreign host.SMTP is quite chatty in comparison to HTTP, providing some human-readable instructions like `continue. finished with "\r\n.\r\n"` to tell us how to terminate our message content. ## Designing a wire protocol When designing a wire protocol, apply the same rules of thumb you use for designing the operations of an abstract data type: + Keep the number of different messages **small**. It's better to have a few commands and responses that can be combined rather than many complex messages. + Each message should have a well-defined purpose and **coherent** behavior. + The set of messages must be **adequate** for clients to make the requests they need to make and for servers to deliver the results. Just as we demand representation independence from our types, we should aim for **platform-independence** in our protocols. HTTP can be spoken by any web server and any web browser on any operating system. The protocol doesn't say anything about how web pages are stored on disk, how they are prepared or generated by the server, what algorithms the client will use to render them, etc. We can also apply the three big ideas in this class: + **Safe from bugs** + The protocol should be easy for clients and servers to generate and parse. Simpler code for reading and writing the protocol (whether written with a parser generator like ANTLR, with regular expressions, etc.) will have fewer opportunities for bugs. + Consider the ways a broken or malicious client or server could stuff garbage data into the protocol to break the process on the other end. Email spam is one example: when we spoke SMTP above, the mail server asked *us* to say who was sending the email, and there's nothing in SMTP to prevent us from lying outright. We've had to build systems on top of SMTP to try to stop spammers who lie about `From:` addresses. Security vulnerabilities are a more serious example. E.g. protocols that allow a client to send requests with arbitrary amounts of data require careful handling on the server to avoid running out of buffer space, [or worse](http://en.wikipedia.org/wiki/Buffer_overflow). + **Easy to understand**: for example, choosing a text-based protocol means that we can debug communication errors by reading the text of the client/server exchange. It even allows us to speak the protocol "by hand" as we saw above. + **Ready for change**: for example, HTTP includes the ability to specify a version number, so clients and servers can agree with one another which version of the protocol they will use. If we need to make changes to the protocol in the future, older clients or servers can continue to work by announcing the version they will use. [**Serialization**](http://en.wikipedia.org/wiki/Serialization) is the process of transforming data structures in memory into a format that can be easily stored or transmitted. Rather than invent a new format for serializing your data between clients and servers, use an existing one. For example, [JSON (JavaScript Object Notation)](http://en.wikipedia.org/wiki/JSON) is a simple, widely-used format for serializing basic values, arrays, and maps with string keys. ### Specifying a wire protocol In order to precisely define for clients & servers what messages are allowed by a protocol, use a grammar. For example, here is a very small part of the HTTP 1.1 request grammar from [RFC 2616 section 5](http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html): request ::= request-line ((general-header | request-header | entity-header) CRLF)* CRLF message-body? request-line ::= method SPACE request-uri SPACE http-version CRLF method ::= "OPTIONS" | "GET" | "HEAD" | "POST" | ... ... Using the grammar, we can see that in this example request from earlier: GET /aboutmit/ HTTP/1.1 Host: web.mit.edu + `GET` is the `method`: we're asking the server to get a page for us. + `/aboutmit/` is the `request-uri`: the description of what we want to get. + `HTTP/1.1` is the `http-version`. + `Host: web.mit.edu` is some kind of header --- we would have to examine the rules for each of the `...-header` options to discover which one. + And we can see why we had to end the request with a blank line: since a single `request` can have multiple headers that end in CRLF (newline), we have another CRLF at the end to finish the `request`. + We don't have any `message-body` --- and since the server didn't wait to see if we would send one, presumably that only applies for other kinds of requests. The grammar is not enough: it fills a similar role to method signatures when defining an ADT. We still need the specifications: + **What are the preconditions of a message?** For example, if a particular field in a message is a string of digits, is any number valid? Or must it be the ID number of a record known to the server? Under what circumstances can a message be sent? Are certain messages only valid when sent in a certain sequence? + **What are the postconditions?** What action will the server take based on a message? What server-side data will be mutated? What reply will the server send back to the client? mitx:88c67dd152e243d694c11b97d5ea04bc Wire protocols ## Summary In the *client/server design pattern*, concurrency is inevitable: multiple clients and multiple servers are connected on the network, sending and receiving messages simultaneously, and expecting timely replies. A server that *blocks* waiting for one slow client when there are other clients waiting to connect to it or to receive replies will not make those clients happy. At the same time, a server that performs incorrect computations or returns bogus results because of concurrent modification to shared mutable data by different clients will not make anyone happy. All the challenges of making our multi-threaded code **safe from bugs**, **easy to understand**, and **ready for change** apply when we design network clients and servers. These processes run concurrently with one another (if on different machines), and any server that wants to talk to multiple clients concurrently (or a client that wants to talk to multiple servers) must manage that multi-threaded communication.