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"));

4. // Try to receive data from the socket we've bound
5. let mut buf = [0; 10];
6. let (amt, src) = try!(socket.recv_from(&mut buf));

8. // Send a reply to the socket we just received data from
9. let buf = &mut buf[..amt];
10. buf.reverse();
11. try!(socket.send_to(buf, &src));

13. // Close the socket
14. 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();

4. // Uses std::io::{Read, Write}

6. // Try to write a byte to the stream
7. let write_result = stream.write(&[1]);

9. // Read from the stream into buf
10. let mut buf = [0; 128];
11. let read_result = stream.read(&mut buf);

13. // ...
14. // 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();

3. fn handle_client(stream: TcpStream) { /* ... */  }

5. // Accept connections and process them,
6. // spawning a new thread for each one.
7. for stream in listener.incoming() {
8. match stream {
9. Ok(stream) => {
10. thread::spawn(move|| {
11. // connection succeeded
12. handle_client(stream)
13. });
14. }
15. Err(e) => { /* connection failed */ }
16. }
17. }

19. // close the socket server
20. 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();

3. // GET
4. let res = client.get("http://cis.upenn.edu/~cis198")
5. .send().unwrap();
6. assert_eq!(res.status, hyper::Ok);

8. // POST
9. let res = client.post("http://cis.upenn.edu/~cis198")
10. .body("user=me")
11. .send().unwrap();
12. assert_eq!(res.status, hyper::Ok);

14. // 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;

3. use std::io::Read;
4. use hyper::client::Client;

6. // GET
7. fn get_contents(url: &str) -> hyper::Result<String> {
8. let client = Client::new();
9. let mut response = try!(client.get(url).send());
10. let mut buf = String::new();
11. try!(response.read_to_string(&mut buf));
12. Ok(buf)
13. }

15. println!("{}", get_contents("http://cis198-2016s.github.io/")
16. .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;

5. // POST
6. fn post_query(url: &str, query: Vec<(&str, &str)>)
7. -> hyper::Result<String> {
8. let body = form_urlencoded::serialize(query);

10. let client = Client::new();
11. let mut response = try!(client.post(url).body(&body[..]).send());
12. let mut buf = String::new();
13. try!(response.read_to_string(&mut buf));
14. Ok(buf)
15. }

17. let query = vec![("user", "me"), ("email", "me@email.email")];
18. println!("{}", post_query("http://httpbin.org/post", query)
19. .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};

4. fn post_json<T: Encodable>(url: &str, payload: &T)
5. -> hyper::Result<String> {
6. let body = json::encode(payload).unwrap();

8. // same as before from here
9. }

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};

3. fn hello(req: Request, res: Response) {
4. res.send(b"Hello World!").unwrap();
5. }

7. 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.)