Working With TCP Sockets
Working With TCP Sockets
Format: PDF / Kindle (mobi) / ePub
Do you know how your web server opens a socket, binds to an address, and accepts a connection?
I did a lot of web programming before I had enough knowledge to dig in and figure this stuff out.
I knew that other developers had a better grasp on the full stack than I did, but diving deep under the hood is one of the things that really made me a better developer all around.
I recently read a great thread that asked "What did the really successful programmers do differently?". This response really caught my eye:
> Be ready, willing, & able to deep dive multiple levels at any time. You must know what's going on under the hood. There is a strong correlation between "number of levels of deepness understood" and "programming prowess".
In this book I'll teach you these fundamentals using Ruby. I'll start with the fundamentals that are portable to any environment. Then I'll show you the beautiful abstractions that Ruby has layered on top of them.
Learning this stuff doesn't just apply to Ruby, or any other language. Every modern programming language has support for networking. Every language has their own way of doing things. But all modern languages support the Berkeley Sockets API. Ruby is no exception. There's certainly plenty of syntactic sugar, but below the sugar you can use the same Sockets API that you would in C, Java, Python, whatever. This is portable knowledge that will serve you for many years to come.
What you'll learn:
* The steps in the lifecycle of servers and clients.
* The various ways that we can read and write data in Ruby, and when they're appropriate.
* All the things you were never quite sure about: EOF, listen queues, TCPNODELAY, and tons more.
* The low level methods required for constructing sockets, as well as the syntactic sugar that Ruby provides.
* Known methods that will help you improve socket performance.
* Basics of SSL sockets.
* Should you write a lot of data at once or chunk it into smaller writes?
* Get comfortable with the socket programming API that's available in any modern programming language.
* More example code than you shake a stick at!
* A look at 6 different architecture patterns for building concurrency into your network programs.
* A closer look at a few different protocols: FTP and Redis.
* Multiplexing connections, non-blocking IO, socket timeouts, socket options, and more...
the connection until EOF. request = connection.read # Write back the result of the hash operation. connection.write process(request) end # Supported commands: # SET key value # GET key def process(request) command, key, value = request.split case command.upcase when 'GET' @storage[key] when 'SET' @storage[key] = value end end end end server = CloudHash::Server.new(4481) server.start The Client And here's a simple client: # ./code/cloud_hash/client.rbrequire 'socket' module CloudHash class
of IO objects which you want to read from. The second argument is an Array of IO objects which you want to write to. The third argument is an Array of IO objects for which you are interested in exceptional conditions. The vast majority of applications can ignore the third argument unless you're interested in out-of-band data (more on that in the Urgent Data chapter). Note that even if you're interested in reading from a single IO object you still must put it in an Array to pass to IO.select. It
wasn't ideal. Opening a new connection for each command adds unnecessary overhead. It's certainly possible to send multiple messages over the same TCP connection, but if you're going to leave the connection open, you need some way of signaling that one message is ending and another is beginning. This idea of reusing connections across multiple messages is the same concept behind the familiar keep-alive feature of HTTP. By leaving the connection open for multiple requests (and having an
described in the last few chapters. Puma The puma rubygem provides "a Ruby web server built for concurrency" http://puma.io. Puma is designed as the go-to HTTP server for Ruby implementations without a GIL (Rubinius or JRuby) because it leans heavily on threads. The Puma README https://github.com/puma/puma#description provides a good overview of where it's applicable and reminds us about the effect of the GIL on threading. So how does Puma achieve its concurrency? At a high level Puma uses a
Socket.accept_loop, but you can't write it any more succinctly than that! System Calls From This Chapter Socket#bind -> bind(2) Socket#listen -> listen(2) Socket#accept -> accept(2) Socket#local_address -> getsockname(2) Socket#remote_address -> getpeername(2) Socket#close -> close(2) Socket#close_write -> shutdown(2) Socket#shutdown -> shutdown(2) Client Lifecycle I mentioned that there are two critical roles that make up a network connection. The server takes the listening role, listening