Networking & Web

CIS 198 Lecture 9


Networking


Sockets

  • Most of this section is preface.
  • We’re not actually going to cover most of what’s in it directly.

Sockets

  • A basic way to send data over the network.
    • Not to be confused with IPC sockets, which are a Unix thing.
  • Abstractly, a socket is just a channel that can send and/or receive data over some network.
  • Many layers of socket-programming providers:
    • Operating system-provided system calls.
    • Low-level/low-abstraction programming language standard library.
    • Higher-level networking libraries or libraries handling a specific protocol (e.g. HTTP).
  • Usually, you won’t use sockets directly unless you want to do some low-level networking.
  • Two general types: datagram & stream.

Datagram Sockets (UDP)

  • User Datagram Protocol sockets
  • Stateless: no connection to establish with another network device.
    • Simply send data to a destination IP and port, and assume they’re listening.
  • “At least once” delivery.
    • Packets are not guaranteed to be delivered in order.
    • Packets may be received more than once.
  • Traditionally implement two methods:
    • send_to(addr) — sends data over the socket to the specified address
    • recv_from() — listens for data being sent to the socket

std::net::UdpSocket

  1. // Try to bind a UDP socket
  2. let mut socket = try!(UdpSocket::bind("127.0.0.1:34254"));
  3. // Try to receive data from the socket we've bound
  4. let mut buf = [0; 10];
  5. let (amt, src) = try!(socket.recv_from(&mut buf));
  6. // Send a reply to the socket we just received data from
  7. let buf = &mut buf[..amt];
  8. buf.reverse();
  9. try!(socket.send_to(buf, &src));
  10. // Close the socket
  11. drop(socket);

¹Taken from the Rust docs.


Stream Sockets (TCP)

  • “This is where the drugs kick in” - Matt Blaze on TCP sockets
  • Transmission Control Protocol sockets
  • Stateful: require a connection to be established and acknowledged between two clients (using SYN packet).

    • Connection must also be explicitly closed.
  • Packets are delivered in-order, exactly once.
    • Achieved via packet sequence numbers.
  • Packets have delivery acknowledgement (ACK packet).
  • Generally two types of TCP socket:
    • TCP listeners: listen for data
    • TCP streams: send data

std::net::TcpStream

  • A TCP stream between a local socket and a remote socket.
  1. // Create a TCP connection
  2. let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
  3. // Uses std::io::{Read, Write}
  4. // Try to write a byte to the stream
  5. let write_result = stream.write(&[1]);
  6. // Read from the stream into buf
  7. let mut buf = [0; 128];
  8. let read_result = stream.read(&mut buf);
  9. // ...
  10. // Socket gets automatically closed when it goes out of scope

std::net::TcpListener

  • A TCP socket server.
  1. let listener = TcpListener::bind("127.0.0.1:80").unwrap();
  2. fn handle_client(stream: TcpStream) { /* ... */ }
  3. // Accept connections and process them,
  4. // spawning a new thread for each one.
  5. for stream in listener.incoming() {
  6. match stream {
  7. Ok(stream) => {
  8. thread::spawn(move|| {
  9. // connection succeeded
  10. handle_client(stream)
  11. });
  12. }
  13. Err(e) => { /* connection failed */ }
  14. }
  15. }
  16. // close the socket server
  17. drop(listener);

SocketAddr

  • A socket address representation.
  • May be either IPv4 or IPv6.
  • Easily created using…

ToSocketAddrs

  1. pub trait ToSocketAddrs {
  2. type Iter: Iterator<Item=SocketAddr>;
  3. fn to_socket_addrs(&self) -> Result<Self::Iter>;
  4. }
  • A trait for objects which can be converted into SocketAddr values.
  • Methods like TcpStream::connect(addr: A) specify that A: ToSocketAddr.
    • This makes it easier to specify what may be converted to a socket address-like object.
  • See the docs for the full specification.

Web

09. Networking & Web - 图1


Web

09. Networking & Web - 图2


HTTP - Preface

  • HTTP defines several common methods for interacting with servers & clients over the Internet
  • Common HTTP verbs are:
    • GET: retrieve data (e.g. access a web page)
    • POST: send data (e.g. submit a login form)
    • PATCH: modify existing data (e.g. modify a user profile)
    • DELETE: delete data (e.g. delete a user)
  • Others exist, but are less common

HTTP - Preface

  • An HTTP request is made by sending some data to a server over HTTP containing some data, such as:
    • the URL of the server
    • the method you want to invoke
    • data the server needs to look at (like in a POST)
    • names of data you want back from the server
    • etc.

HTTP - Preface

  • Once the server processes your request, you get a response
  • Responses usually contain:
    • a status code (200, 404, 502, etc.)
    • some information pertaining to your request:
      • HTML content
      • JSON-formatted data
      • Error messages
      • etc.

Hyper

  • “A Modern HTTP library for Rust”
  • Provides a relatively low-level wrapper over raw HTTP.
    • (Examples below won’t run on the Rust Playpen since they require extern crates)
  • Because you never want to implement the HTTP protocol yourself.

hyper::client

  • An HTTP client.
  • Designed for most people to make HTTP requests, using the client::Request API.
  1. let client = Client::new();
  2. // GET
  3. let res = client.get("http://cis.upenn.edu/~cis198")
  4. .send().unwrap();
  5. assert_eq!(res.status, hyper::Ok);
  6. // POST
  7. let res = client.post("http://cis.upenn.edu/~cis198")
  8. .body("user=me")
  9. .send().unwrap();
  10. assert_eq!(res.status, hyper::Ok);
  11. // PUT, PATCH, DELETE, etc. are all legal verbs too.
  • Client is shareable between threads, so you can make requests in parallel by default!

Client Requests

  • Let’s see some full client examples with proper error handling.
  • A GET request that reads out the body of a web page:
  1. extern crate hyper;
  2. use std::io::Read;
  3. use hyper::client::Client;
  4. // GET
  5. fn get_contents(url: &str) -> hyper::Result<String> {
  6. let client = Client::new();
  7. let mut response = try!(client.get(url).send());
  8. let mut buf = String::new();
  9. try!(response.read_to_string(&mut buf));
  10. Ok(buf)
  11. }
  12. println!("{}", get_contents("http://cis198-2016s.github.io/")
  13. .unwrap()); // A whole mess of HTML

¹Adapted from Zbigniew Siciarz’s 24 Days of Rust


Client Requests

  • A POST request, using form_urlencoded from the url crate to do URL encoding:
  1. extern crate hyper;
  2. extern crate url;
  3. use url::form_urlencoded;
  4. // POST
  5. fn post_query(url: &str, query: Vec<(&str, &str)>)
  6. -> hyper::Result<String> {
  7. let body = form_urlencoded::serialize(query);
  8. let client = Client::new();
  9. let mut response = try!(client.post(url).body(&body[..]).send());
  10. let mut buf = String::new();
  11. try!(response.read_to_string(&mut buf));
  12. Ok(buf)
  13. }
  14. let query = vec![("user", "me"), ("email", "me@email.email")];
  15. println!("{}", post_query("http://httpbin.org/post", query)
  16. .unwrap());

Client Requests

  • Using rustc_serialize, we can generalize our POST request encoding to allow any data that’s encodable to JSON!
  1. extern crate rustc_serialize;
  2. use rustc_serialize::{Encodable, json};
  3. fn post_json<T: Encodable>(url: &str, payload: &T)
  4. -> hyper::Result<String> {
  5. let body = json::encode(payload).unwrap();
  6. // same as before from here
  7. }

hyper::server

  • An HTTP server that listens on a port, parses HTTP requests, and passes them to a handler.
  • Server listens to multiple threads by default.
  • You must define a Handler for Server to define how it handles requests.
  1. use hyper::{Server, Request, Response};
  2. fn hello(req: Request, res: Response) {
  3. res.send(b"Hello World!").unwrap();
  4. }
  5. Server::http("127.0.0.1:3000").unwrap().handle(hello).unwrap();

hyper::server::Handler

  • Handler is a trait that defines how requests will be handled by the server.
  • It only requires one method, handle, which just takes a Request and a Response and does something to them.
    • (It actually defines 3 more methods, but they all have default implementations.)