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
SocketAddrvalues. - Methods like
TcpStream::connect(addr: A)specify thatA: 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

Web

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::RequestAPI.
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.
Clientis 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_urlencodedfrom theurlcrate 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.
Serverlistens to multiple threads by default.- You must define a
HandlerforServerto 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
Handleris a trait that defines how requests will be handled by the server.- It only requires one method,
handle, which just takes aRequestand aResponseand does something to them. - (It actually defines 3 more methods, but they all have default implementations.)
