emacs.d/clones/ruslanspivak.com/lsbaws-part3/index.html

1216 lines
115 KiB
HTML
Raw Normal View History

2022-10-07 19:32:11 +02:00
<!DOCTYPE html>
<html lang="en"
xmlns:og="http://ogp.me/ns#"
xmlns:fb="https://www.facebook.com/2008/fbml">
<head>
<title>Lets Build A Web Server. Part 3. - Ruslan's Blog</title>
<!-- Using the latest rendering mode for IE -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="canonical" href="index.html">
<meta name="author" content="Ruslan Spivak" />
<meta name="description" content="“We learn most when we have to invent” —Piaget In Part 2 you created a minimalistic WSGI server that could handle basic HTTP GET requests. And I asked you a question, “How can you make your server handle more than one request at a time?” In this article you will …" />
<meta property="og:site_name" content="Ruslan's Blog" />
<meta property="og:type" content="article"/>
<meta property="og:title" content="Lets Build A Web Server. Part 3."/>
<meta property="og:url" content="https://ruslanspivak.com/lsbaws-part3/"/>
<meta property="og:description" content="“We learn most when we have to invent” —Piaget In Part 2 you created a minimalistic WSGI server that could handle basic HTTP GET requests. And I asked you a question, “How can you make your server handle more than one request at a time?” In this article you will …"/>
<meta property="article:published_time" content="2015-05-20" />
<meta property="article:section" content="blog" />
<meta property="article:author" content="Ruslan Spivak" />
<meta name="twitter:card" content="summary">
<meta name="twitter:domain" content="https://ruslanspivak.com">
<!-- Bootstrap -->
<link rel="stylesheet" href="../theme/css/bootstrap.min.css" type="text/css"/>
<link href="../theme/css/font-awesome.min.css" rel="stylesheet">
<link href="../theme/css/pygments/tango.css" rel="stylesheet">
<link href="../theme/css/typogrify.css" rel="stylesheet">
<link rel="stylesheet" href="../theme/css/style.css" type="text/css"/>
<link href="../static/custom.css" rel="stylesheet">
<link href="../feeds/all.atom.xml" type="application/atom+xml" rel="alternate"
title="Ruslan's Blog ATOM Feed"/>
</head>
<body>
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="../index.html" class="navbar-brand">
Ruslan's Blog </a>
</div>
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav">
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="../pages/about.html"><i class="fa fa-question"></i><span class="icon-label">About</span></a></li>
<li><a href="../archives.html"><i class="fa fa-th-list"></i><span class="icon-label">Archives</span></a></li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
</div> <!-- /.navbar -->
<!-- Banner -->
<!-- End Banner -->
<div class="container">
<div class="row">
<div class="col-sm-9">
<section id="content">
<article>
<header class="page-header">
<h1>
<a href="index.html"
rel="bookmark"
title="Permalink to Lets Build A Web Server. Part 3.">
Let&#8217;s Build A Web Server. Part&nbsp;3.
</a>
</h1>
</header>
<div class="entry-content">
<div class="panel">
<div class="panel-body">
<footer class="post-info">
<span class="label label-default">Date</span>
<span class="published">
<i class="fa fa-calendar"></i><time datetime="2015-05-20T06:00:00-04:00"> Wed, May 20, 2015</time>
</span>
</footer><!-- /.post-info --> </div>
</div>
<blockquote>
<p><em><span class="dquo">&#8220;</span>We learn most when we have to invent&#8221;&nbsp;&#8212;Piaget</em></p>
</blockquote>
<p>In <a href="../lsbaws-part2/index.html" title="Part 2">Part 2</a> you created a minimalistic <span class="caps">WSGI</span> server that could handle basic <span class="caps">HTTP</span> <span class="caps">GET</span> requests. And I asked you a question, &#8220;How can you make your server handle more than one request at a time?&#8221;
In this article you will find the answer. So, buckle up and shift into high gear. Youre about to have a really fast ride. Have your Linux, Mac <span class="caps">OS</span> X (or any *nix system) and Python ready. All source code from the article is available on <a href="https://github.com/rspivak/lsbaws/blob/master/part3/">GitHub</a>.</p>
<p>First lets remember what a very basic Web server looks like and what the server needs to do to service client requests. The server you created in <a href="../lsbaws-part1/index.html" title="Part 1">Part 1</a> and <a href="../lsbaws-part2/index.html" title="Part 2">Part 2</a> is an iterative server that handles one client request at a time. It cannot accept a new connection until after it has finished processing a current client request. Some clients might be unhappy with it because they will have to wait in line, and for busy servers the line might be too&nbsp;long.</p>
<p><img alt="" src="lsbaws_part3_it1.png" width="640"></p>
<p>Here is the code of the iterative server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3a.py">webserver3a.py</a>:</p>
<div class="highlight"><pre><span></span><span class="c1">#####################################################################</span>
<span class="c1"># Iterative server - webserver3a.py #</span>
<span class="c1"># #</span>
<span class="c1"># Tested with Python 2.7.9 &amp; Python 3.4 on Ubuntu 14.04 &amp; Mac OS X #</span>
<span class="c1">#####################################################################</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="n">SERVER_ADDRESS</span> <span class="o">=</span> <span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="mi">8888</span>
<span class="n">REQUEST_QUEUE_SIZE</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">def</span> <span class="nf">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">):</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">client_connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
<span class="n">http_response</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
<span class="s2">HTTP/1.1 200 OK</span>
<span class="s2">Hello, World!</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">http_response</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">serve_forever</span><span class="p">():</span>
<span class="n">listen_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">SERVER_ADDRESS</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">REQUEST_QUEUE_SIZE</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">&#39;Serving HTTP on port {port} ...&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">PORT</span><span class="p">))</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">client_connection</span><span class="p">,</span> <span class="n">client_address</span> <span class="o">=</span> <span class="n">listen_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="n">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">)</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">serve_forever</span><span class="p">()</span>
</pre></div>
<p>To observe your server handling only one client request at a time, modify the server a little bit and add a 60 second delay after sending a response to a client. The change is only one line to tell the server process to sleep for 60&nbsp;seconds.</p>
<p><img alt="" src="lsbaws_part3_it2.png" width="640"></p>
<p>And here is the code of the sleeping server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py">webserver3b.py</a>:</p>
<div class="highlight"><pre><span></span><span class="c1">#########################################################################</span>
<span class="c1"># Iterative server - webserver3b.py #</span>
<span class="c1"># #</span>
<span class="c1"># Tested with Python 2.7.9 &amp; Python 3.4 on Ubuntu 14.04 &amp; Mac OS X #</span>
<span class="c1"># #</span>
<span class="c1"># - Server sleeps for 60 seconds after sending a response to a client #</span>
<span class="c1">#########################################################################</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="n">SERVER_ADDRESS</span> <span class="o">=</span> <span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="mi">8888</span>
<span class="n">REQUEST_QUEUE_SIZE</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">def</span> <span class="nf">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">):</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">client_connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
<span class="n">http_response</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
<span class="s2">HTTP/1.1 200 OK</span>
<span class="s2">Hello, World!</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">http_response</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span> <span class="c1"># sleep and block the process for 60 seconds</span>
<span class="k">def</span> <span class="nf">serve_forever</span><span class="p">():</span>
<span class="n">listen_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">SERVER_ADDRESS</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">REQUEST_QUEUE_SIZE</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">&#39;Serving HTTP on port {port} ...&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">PORT</span><span class="p">))</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">client_connection</span><span class="p">,</span> <span class="n">client_address</span> <span class="o">=</span> <span class="n">listen_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="n">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">)</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">serve_forever</span><span class="p">()</span>
</pre></div>
<p>Start the server&nbsp;with:</p>
<div class="highlight"><pre><span></span>$ python webserver3b.py
</pre></div>
<p>Now open up a new terminal window and run the <em>curl</em> command. You should instantly see the <em>&#8220;Hello, World!&#8221;</em> string printed on the&nbsp;screen:</p>
<div class="highlight"><pre><span></span>$ curl http://localhost:8888/hello
Hello, World!
</pre></div>
<p>And without delay open up a second terminal window and run the same <em>curl</em>&nbsp;command:</p>
<div class="highlight"><pre><span></span>$ curl http://localhost:8888/hello
</pre></div>
<p>If youve done that within 60 seconds then the second <em>curl</em> should not produce any output right away and should just hang there. The server shouldnt print a new request body on its standard output either. Here is how it looks like on my Mac (the window at the bottom right corner highlighted in yellow shows the second <em>curl</em> command hanging, waiting for the connection to be accepted by the&nbsp;server):</p>
<p><img alt="" src="lsbaws_part3_it3.png"></p>
<p>After youve waited long enough (more than 60 seconds) you should see the first <em>curl</em> terminate and the second <em>curl</em> print <em>&#8220;Hello, World!&#8221;</em> on the screen, then hang for 60 seconds, and then&nbsp;terminate:</p>
<p><img alt="" src="lsbaws_part3_it4.png"></p>
<p>The way it works is that the server finishes servicing the first <em>curl</em> client request and then it starts handling the second request only after it sleeps for 60 seconds. It all happens sequentially, or iteratively, one step, or in our case one client request, at a&nbsp;time.</p>
<p>Lets talk about the communication between clients and servers for a bit. In order for two programs to communicate with each other over a network, they have to use sockets. And you saw sockets both in <a href="../lsbaws-part1/index.html" title="Part 1">Part 1</a> and <a href="../lsbaws-part2/index.html" title="Part 2">Part 2</a>. But what is a&nbsp;socket?</p>
<p><img alt="" src="lsbaws_part3_it_socket.png" width="480"></p>
<p>A <em>socket</em> is an abstraction of a communication endpoint and it allows your program to communicate with another program using file descriptors. In this article Ill be talking specifically about <span class="caps">TCP</span>/<span class="caps">IP</span> sockets on Linux/Mac <span class="caps">OS</span> X. An important notion to understand is the <span class="caps">TCP</span> socket&nbsp;pair.</p>
<blockquote>
<p>The <em>socket pair</em> for a <span class="caps">TCP</span> connection is a 4-tuple that identifies two endpoints of the <span class="caps">TCP</span> connection: the local <span class="caps">IP</span> address, local port, foreign <span class="caps">IP</span> address, and foreign port. A socket pair uniquely identifies every <span class="caps">TCP</span> connection on a network. The two values that identify each endpoint, an <span class="caps">IP</span> address and a port number, are often called a <em>socket</em>.<sup id="fnref2-1"><a class="footnote-ref" href="index.html#fn-1">1</a></sup></p>
</blockquote>
<p><img alt="" src="lsbaws_part3_it_socketpair.png"></p>
<p>So, the tuple {10.10.10.2:49152, 12.12.12.3:8888} is a socket pair that uniquely identifies two endpoints of the <span class="caps">TCP</span> connection on the client and the tuple {12.12.12.3:8888, 10.10.10.2:49152} is a socket pair that uniquely identifies the same two endpoints of the <span class="caps">TCP</span> connection on the server. The two values that identify the server endpoint of the <span class="caps">TCP</span> connection, the <span class="caps">IP</span> address 12.12.12.3 and the port 8888, are referred to as a socket in this case (the same applies to the client&nbsp;endpoint).</p>
<p>The standard sequence a server usually goes through to create a socket and start accepting client connections is the&nbsp;following:</p>
<p><img alt="" src="lsbaws_part3_it_server_socket_sequence.png" width="420"></p>
<ol>
<li>
<p>The server creates a <span class="caps">TCP</span>/<span class="caps">IP</span> socket.
This is done with the following statement in&nbsp;Python:</p>
<div class="highlight"><pre><span></span>listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
</pre></div>
</li>
<li>
<p>The server might set some socket options (this is optional, but you can see that the server code above does just that to be able to re-use the same address over and over again if you decide to kill and re-start the server right&nbsp;away).</p>
<div class="highlight"><pre><span></span>listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
</pre></div>
</li>
<li>
<p>Then, the server binds the address.
The <em>bind</em> function assigns a local protocol address to the socket. With <span class="caps">TCP</span>, calling <em>bind</em> lets you specify a port number, an <span class="caps">IP</span> address, both, or neither.<sup id="fnref-1"><a class="footnote-ref" href="index.html#fn-1">1</a></sup></p>
<div class="highlight"><pre><span></span>listen_socket.bind(SERVER_ADDRESS)
</pre></div>
</li>
<li>
<p>Then, the server makes the socket a listening&nbsp;socket</p>
<div class="highlight"><pre><span></span>listen_socket.listen(REQUEST_QUEUE_SIZE)
</pre></div>
</li>
</ol>
<p>The <em>listen</em> method is only called by <em>servers</em>. It tells the kernel that it should accept incoming connection requests for this&nbsp;socket.</p>
<p>After thats done, the server starts accepting client connections one connection at a time in a loop. When there is a connection available the <em>accept</em> call returns the connected client socket. Then, the server reads the request data from the connected client socket, prints the data on its standard output and sends a message back to the client. Then, the server closes the client connection and it is ready again to accept a new client&nbsp;connection.</p>
<p>Here is what a client needs to do to communicate with the server over <span class="caps">TCP</span>/<span class="caps">IP</span>:</p>
<p><img alt="" src="lsbaws_part3_it_client_socket_sequence.png" width="460"></p>
<p>Here is the sample code for a client to connect to your server, send a request and print the&nbsp;response:</p>
<div class="highlight"><pre><span></span> <span class="kn">import</span> <span class="nn">socket</span>
<span class="c1"># create a socket and connect to a server</span>
<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">sock</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="s1">&#39;localhost&#39;</span><span class="p">,</span> <span class="mi">8888</span><span class="p">))</span>
<span class="c1"># send and receive some data</span>
<span class="n">sock</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;test&#39;</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
</pre></div>
<p>After creating the socket, the client needs to connect to the server. This is done with the <em>connect</em>&nbsp;call:</p>
<div class="highlight"><pre><span></span><span class="n">sock</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="s1">&#39;localhost&#39;</span><span class="p">,</span> <span class="mi">8888</span><span class="p">))</span>
</pre></div>
<p>The client only needs to provide the remote <span class="caps">IP</span> address or host name and the remote port number of a server to connect&nbsp;to.</p>
<p>Youve probably noticed that the client doesnt call <em>bind</em> and <em>accept</em>. The client doesnt need to call <em>bind</em> because the client doesn&#8217;t care about the local <span class="caps">IP</span> address and the local port number. The <span class="caps">TCP</span>/<span class="caps">IP</span> stack within the kernel automatically assigns the local <span class="caps">IP</span> address and the local port when the client calls <em>connect</em>. The local port is called an <em>ephemeral port</em>, i.e. a short-lived&nbsp;port.</p>
<p><img alt="" src="lsbaws_part3_it_ephemeral_port.png"></p>
<p>A port on a server that identifies a well-known service that a client connects to is called a <em>well-known</em> port (for example, 80 for <span class="caps">HTTP</span> and 22 for <span class="caps">SSH</span>).
Fire up your Python shell and make a client connection to the server you run on localhost and see what ephemeral port the kernel assigns to the socket youve created (start the server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3a.py">webserver3a.py</a> or <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py">webserver3b.py</a> before trying the following&nbsp;example):</p>
<div class="highlight"><pre><span></span><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">socket</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sock</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="s1">&#39;localhost&#39;</span><span class="p">,</span> <span class="mi">8888</span><span class="p">))</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">host</span><span class="p">,</span> <span class="n">port</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">getsockname</span><span class="p">()[:</span><span class="mi">2</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">host</span><span class="p">,</span> <span class="n">port</span>
<span class="p">(</span><span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">60589</span><span class="p">)</span>
</pre></div>
<p>In the case above the kernel assigned the <em>ephemeral port</em> 60589 to the&nbsp;socket.</p>
<p>There are some other important concepts that I need to cover quickly before I get to answer the question from <a href="../lsbaws-part2/index.html" title="Part 2">Part 2</a>. You will see shortly why this is important. The two concepts are that of a <em>process</em> and a <em>file descriptor</em>.</p>
<p>What is a process? A <em>process</em> is just an instance of an executing program. When the server code is executed, for example, its loaded into memory and an instance of that executing program is called a process. The kernel records a bunch of information about the process - its process <span class="caps">ID</span> would be one example - to keep track of it. When you run your iterative server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3a.py">webserver3a.py</a> or <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py">webserver3b.py</a> you run just one&nbsp;process.</p>
<p><img alt="" src="lsbaws_part3_it_server_process.png"></p>
<p>Start the server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py">webserver3b.py</a> in a terminal&nbsp;window:</p>
<div class="highlight"><pre><span></span>$ python webserver3b.py
</pre></div>
<p>And in a different terminal window use the <em>ps</em> command to get the information about that&nbsp;process:</p>
<div class="highlight"><pre><span></span>$ ps <span class="p">|</span> grep webserver3b <span class="p">|</span> grep -v grep
<span class="m">7182</span> ttys003 <span class="m">0</span>:00.04 python webserver3b.py
</pre></div>
<p>The <em>ps</em> command shows you that you have indeed run just one Python process <em>webserver3b</em>. When a process gets created the kernel assigns a process <span class="caps">ID</span> to it, <span class="caps">PID</span>. In <span class="caps">UNIX</span>, every user process also has a parent that, in turn, has its own process <span class="caps">ID</span> called parent process <span class="caps">ID</span>, or <span class="caps">PPID</span> for short.
I assume that you run a <span class="caps">BASH</span> shell by default and when you start the server, a new process gets created with a <span class="caps">PID</span> and its parent <span class="caps">PID</span> is set to the <span class="caps">PID</span> of the <span class="caps">BASH</span>&nbsp;shell.</p>
<p><img alt="" src="lsbaws_part3_it_ppid_pid.png"></p>
<p>Try it out and see for yourself how it all works. Fire up your Python shell again, which will create a new process, and then get the <span class="caps">PID</span> of the Python shell process and the parent <span class="caps">PID</span> (the <span class="caps">PID</span> of your <span class="caps">BASH</span> shell) using <a href="https://docs.python.org/2.7/library/os.html#os.getpid">os.getpid()</a> and <a href="https://docs.python.org/2.7/library/os.html#os.getppid">os.getppid()</a> system calls. Then, in another terminal window run <em>ps</em> command and grep for the <span class="caps">PPID</span> (parent process <span class="caps">ID</span>, which in my case is 3148). In the screenshot below you can see an example of a parent-child relationship between my child Python shell process and the parent <span class="caps">BASH</span> shell process on my Mac <span class="caps">OS</span>&nbsp;X:</p>
<p><img alt="" src="lsbaws_part3_it_pid_ppid_screenshot.png"></p>
<p>Another important concept to know is that of a <em>file descriptor</em>.
So what is a file descriptor? A <em>file descriptor</em> is a non-negative integer that the kernel returns to a process when it opens an existing file, creates a new file or when it creates a new socket. Youve probably heard that in <span class="caps">UNIX</span> everything is a file. The kernel refers to the open files of a process by a file descriptor. When you need to read or write a file you identify it with the file descriptor. Python gives you high-level objects to deal with files (and sockets) and you dont have to use file descriptors directly to identify a file but, under the hood, thats how files and sockets are identified in <span class="caps">UNIX</span>: by their integer file&nbsp;descriptors.</p>
<p><img alt="" src="lsbaws_part3_it_process_descriptors.png"></p>
<p>By default, <span class="caps">UNIX</span> shells assign file descriptor 0 to the standard input of a process, file descriptor 1 to the standard output of the process and file descriptor 2 to the standard&nbsp;error.</p>
<p><img alt="" src="lsbaws_part3_it_default_descriptors.png" width="640"></p>
<p>As I mentioned before, even though Python gives you a high-level file or file-like object to work with, you can always use the <em>fileno()</em> method on the object to get the file descriptor associated with the file. Back to your Python shell to see how you can do&nbsp;that:</p>
<div class="highlight"><pre><span></span><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdin</span>
<span class="o">&lt;</span><span class="nb">open</span> <span class="nb">file</span> <span class="s1">&#39;&lt;stdin&gt;&#39;</span><span class="p">,</span> <span class="n">mode</span> <span class="s1">&#39;r&#39;</span> <span class="n">at</span> <span class="mh">0x102beb0c0</span><span class="o">&gt;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">fileno</span><span class="p">()</span>
<span class="mi">0</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">fileno</span><span class="p">()</span>
<span class="mi">1</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="o">.</span><span class="n">fileno</span><span class="p">()</span>
<span class="mi">2</span>
</pre></div>
<p>And while working with files and sockets in Python, youll usually be using a high-level file/socket object, but there may be times where you need to use a file descriptor directly. Here is an example of how you can write a string to the standard output using a <a href="https://docs.python.org/2.7/library/os.html#os.write">write</a> system call that takes a file descriptor integer as a&nbsp;parameter:</p>
<div class="highlight"><pre><span></span><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span>
<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">os</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">res</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">fileno</span><span class="p">(),</span> <span class="s1">&#39;hello</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
<span class="n">hello</span>
</pre></div>
<p>And here is an interesting part - which should not be surprising to you anymore because you already know that everything is a file in Unix - your socket also has a file descriptor associated with it. Again, when you create a socket in Python you get back an object and not a non-negative integer, but you can always get direct access to the integer file descriptor of the socket with the <em>fileno()</em> method that I mentioned&nbsp;earlier.</p>
<div class="highlight"><pre><span></span><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">socket</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sock</span><span class="o">.</span><span class="n">fileno</span><span class="p">()</span>
<span class="mi">3</span>
</pre></div>
<p>One more thing I wanted to mention: have you noticed that in the second example of the iterative server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py">webserver3b.py</a>, when the server process was sleeping for 60 seconds you could still connect to the server with the second <em>curl</em> command? Sure, the <em>curl</em> didnt output anything right away and it was just hanging out there but how come the server was not <em>accept</em> ing a connection at the time and the client was not rejected right away, but instead was able to connect to the server? The answer to that is the <em>listen</em> method of a socket object and its <span class="caps">BACKLOG</span> argument, which I called REQUEST_QUEUE_SIZE in the code. The <span class="caps">BACKLOG</span> argument determines the size of a queue within the kernel for incoming connection requests. When the server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py">webserver3b.py</a> was sleeping, the second <em>curl</em> command that you ran was able to connect to the server because the kernel had enough space available in the incoming connection request queue for the server&nbsp;socket.</p>
<p>While increasing the <span class="caps">BACKLOG</span> argument does not magically turn your server into a server that can handle multiple client requests at a time, it is important to have a fairly large backlog parameter for busy servers so that the <em>accept</em> call would not have to wait for a new connection to be established but could grab the new connection off the queue right away and start processing a client request without&nbsp;delay.</p>
<p>Whoo-hoo! Youve covered a lot of ground.
Lets quickly recap what youve learned (or refreshed if its all basics to you) so&nbsp;far.</p>
<p><img alt="" src="lsbaws_part3_checkpoint.png"></p>
<blockquote>
<ul>
<li>Iterative&nbsp;server</li>
<li>Server socket creation sequence (socket, bind, listen,&nbsp;accept)</li>
<li>Client connection creation sequence (socket,&nbsp;connect)</li>
<li>Socket&nbsp;pair</li>
<li>Socket</li>
<li>Ephemeral port and well-known&nbsp;port</li>
<li>Process</li>
<li>Process <span class="caps">ID</span> (<span class="caps">PID</span>), parent process <span class="caps">ID</span> (<span class="caps">PPID</span>), and the parent-child&nbsp;relationship.</li>
<li>File&nbsp;descriptors</li>
<li>The meaning of the <span class="caps">BACKLOG</span> argument of the <em>listen</em> socket&nbsp;method</li>
</ul>
</blockquote>
<p><br/>
Now I am ready to answer the question from <a href="../lsbaws-part2/index.html" title="Part 2">Part 2</a>: “How can you make your server handle more than one request at a time?” Or put another way, “How do you write a concurrent&nbsp;server?”</p>
<p><img alt="" src="lsbaws_part3_conc2_service_clients.png"></p>
<p>The simplest way to write a concurrent server under Unix is to use a <a href="https://docs.python.org/2.7/library/os.html#os.fork">fork()</a> system&nbsp;call.</p>
<p><img alt="" src="lsbaws_part3_fork.png"></p>
<p>Here is the code of your new shiny concurrent server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3c.py">webserver3c.py</a> that can handle multiple client requests at the same time (as in our iterative server example <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py">webserver3b.py</a>, every child process sleeps for 60&nbsp;secs):</p>
<p><img alt="" src="lsbaws_part3_it2.png" width="640"></p>
<div class="highlight"><pre><span></span><span class="c1">###########################################################################</span>
<span class="c1"># Concurrent server - webserver3c.py #</span>
<span class="c1"># #</span>
<span class="c1"># Tested with Python 2.7.9 &amp; Python 3.4 on Ubuntu 14.04 &amp; Mac OS X #</span>
<span class="c1"># #</span>
<span class="c1"># - Child process sleeps for 60 seconds after handling a client&#39;s request #</span>
<span class="c1"># - Parent and child processes close duplicate descriptors #</span>
<span class="c1"># #</span>
<span class="c1">###########################################################################</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="n">SERVER_ADDRESS</span> <span class="o">=</span> <span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="mi">8888</span>
<span class="n">REQUEST_QUEUE_SIZE</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">def</span> <span class="nf">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">):</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">client_connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span>
<span class="s1">&#39;Child PID: {pid}. Parent PID {ppid}&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
<span class="n">pid</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getpid</span><span class="p">(),</span>
<span class="n">ppid</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getppid</span><span class="p">(),</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
<span class="n">http_response</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
<span class="s2">HTTP/1.1 200 OK</span>
<span class="s2">Hello, World!</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">http_response</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">serve_forever</span><span class="p">():</span>
<span class="n">listen_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">SERVER_ADDRESS</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">REQUEST_QUEUE_SIZE</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">&#39;Serving HTTP on port {port} ...&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">PORT</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s1">&#39;Parent PID (PPID): {pid}</span><span class="se">\n</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">pid</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getpid</span><span class="p">()))</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">client_connection</span><span class="p">,</span> <span class="n">client_address</span> <span class="o">=</span> <span class="n">listen_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="n">pid</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fork</span><span class="p">()</span>
<span class="k">if</span> <span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># child</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close child copy</span>
<span class="n">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">)</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1"># child exits here</span>
<span class="k">else</span><span class="p">:</span> <span class="c1"># parent</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close parent copy and loop over</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">serve_forever</span><span class="p">()</span>
</pre></div>
<p>Before diving in and discussing how <em>fork</em> works, try it, and see for yourself that the server can indeed handle multiple client requests at the same time, unlike its iterative counterparts <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3a.py">webserver3a.py</a> and <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py">webserver3b.py</a>.
Start the server on the command line&nbsp;with:</p>
<div class="highlight"><pre><span></span>$ python webserver3c.py
</pre></div>
<p>And try the same two <em>curl</em> commands youve tried before with the iterative server and see for yourself that, now, even though the server child process sleeps for 60 seconds after serving a client request, it doesnt affect other clients because they are served by different and completely independent processes. You should see your <em>curl</em> commands output <em>&#8220;Hello, World!&#8221;</em> instantly and then hang for 60 secs. You can keep on running as many <em>curl</em> commands as you want (well, almost as many as you want :) and all of them will output the servers response <em>&#8220;Hello, World&#8221;</em> immediately and without any noticeable delay. Try&nbsp;it.</p>
<p>The most important point to understand about <a href="https://docs.python.org/2.7/library/os.html#os.fork">fork()</a> is that you call <em>fork</em> once but it returns twice: once in the parent process and once in the child process. When you fork a new process the process <span class="caps">ID</span> returned to the child process is 0. When the <em>fork</em> returns in the parent process it returns the childs <span class="caps">PID</span>.</p>
<p><img alt="" src="lsbaws_part3_conc2_how_fork_works.png" width="580"></p>
<p>I still remember how fascinated I was by <em>fork</em> when I first read about it and tried it. It looked like magic to me. Here I was reading a sequential code and then “boom!”: the code cloned itself and now there were two instances of the same code running concurrently. I thought it was nothing short of magic,&nbsp;seriously.</p>
<p>When a parent forks a new child, the child process gets a copy of the parents file&nbsp;descriptors:</p>
<p><img alt="" src="lsbaws_part3_conc2_shared_descriptors.png" width="580"></p>
<p>Youve probably noticed that the parent process in the code above closed the client&nbsp;connection:</p>
<div class="highlight"><pre><span></span><span class="k">else</span><span class="p">:</span> <span class="c1"># parent</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close parent copy and loop over</span>
</pre></div>
<p>So how come a child process is still able to read the data from a client socket if its parent closed the very same socket? The answer is in the picture above. The kernel uses descriptor reference counts to decide whether to close a socket or not. It closes the socket only when its descriptor reference count becomes 0. When your server creates a child process, the child gets the copy of the parents file descriptors and the kernel increments the reference counts for those descriptors. In the case of one parent and one child, the descriptor reference count would be 2 for the client socket and when the parent process in the code above closes the client connection socket, it merely decrements its reference count which becomes 1, not small enough to cause the kernel to close the socket.
The child process also closes the duplicate copy of the parents <em>listen_socket</em> because the child doesnt care about accepting new client connections, it cares only about processing requests from the established client&nbsp;connection:</p>
<div class="highlight"><pre><span></span><span class="n">listen_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close child copy</span>
</pre></div>
<p>Ill talk about what happens if you do not close duplicate descriptors later in the&nbsp;article.</p>
<p>As you can see from the source code of your concurrent server, the sole role of the server parent process now is to accept a new client connection, fork a new child process to handle that client request, and loop over to accept another client connection, and nothing more. The server parent process does not process client requests - its children&nbsp;do.</p>
<p>A little aside. What does it mean when we say that two events are&nbsp;concurrent?</p>
<p><img alt="" src="lsbaws_part3_conc2_concurrent_events.png" width="580"></p>
<p>When we say that two events are concurrent we usually mean that they happen at the same time. As a shorthand that definition is fine, but you should remember the strict&nbsp;definition:</p>
<blockquote>
<p>Two events are <em>concurrent</em> if you cannot tell by looking at the program which will happen first.<sup id="fnref-2"><a class="footnote-ref" href="index.html#fn-2">2</a></sup></p>
</blockquote>
<p>Again, its time to recap the main ideas and concepts youve covered so&nbsp;far.</p>
<p><img alt="" src="lsbaws_part3_checkpoint.png"></p>
<blockquote>
<ul>
<li>The simplest way to write a concurrent server in Unix is to use the <a href="https://docs.python.org/2.7/library/os.html#os.fork">fork()</a> system&nbsp;call</li>
<li>When a process forks a new process it becomes a parent process to that newly forked child&nbsp;process.</li>
<li>Parent and child share the same file descriptors after the call to <em>fork</em>.</li>
<li>The kernel uses descriptor reference counts to decide whether to close the file/socket or&nbsp;not</li>
<li>The role of a server parent process: all it does now is accept a new connection from a client, fork a child to handle the client request, and loop over to accept a new client&nbsp;connection.</li>
</ul>
</blockquote>
<p><br/>
Lets see what is going to happen if you dont close duplicate socket descriptors in the parent and child processes. Here is a modified version of the concurrent server where the server does not close duplicate descriptors, <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3d.py">webserver3d.py</a>:</p>
<div class="highlight"><pre><span></span><span class="c1">###########################################################################</span>
<span class="c1"># Concurrent server - webserver3d.py #</span>
<span class="c1"># #</span>
<span class="c1"># Tested with Python 2.7.9 &amp; Python 3.4 on Ubuntu 14.04 &amp; Mac OS X #</span>
<span class="c1">###########################################################################</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="n">SERVER_ADDRESS</span> <span class="o">=</span> <span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="mi">8888</span>
<span class="n">REQUEST_QUEUE_SIZE</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">def</span> <span class="nf">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">):</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">client_connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="n">http_response</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
<span class="s2">HTTP/1.1 200 OK</span>
<span class="s2">Hello, World!</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">http_response</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">serve_forever</span><span class="p">():</span>
<span class="n">listen_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">SERVER_ADDRESS</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">REQUEST_QUEUE_SIZE</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">&#39;Serving HTTP on port {port} ...&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">PORT</span><span class="p">))</span>
<span class="n">clients</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">client_connection</span><span class="p">,</span> <span class="n">client_address</span> <span class="o">=</span> <span class="n">listen_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="c1"># store the reference otherwise it&#39;s garbage collected</span>
<span class="c1"># on the next loop run</span>
<span class="n">clients</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">client_connection</span><span class="p">)</span>
<span class="n">pid</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fork</span><span class="p">()</span>
<span class="k">if</span> <span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># child</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close child copy</span>
<span class="n">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">)</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1"># child exits here</span>
<span class="k">else</span><span class="p">:</span> <span class="c1"># parent</span>
<span class="c1"># client_connection.close()</span>
<span class="k">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">clients</span><span class="p">))</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">serve_forever</span><span class="p">()</span>
</pre></div>
<p>Start the server&nbsp;with:</p>
<div class="highlight"><pre><span></span>$ python webserver3d.py
</pre></div>
<p>Use <em>curl</em> to connect to the&nbsp;server:</p>
<div class="highlight"><pre><span></span>$ curl http://localhost:8888/hello
Hello, World!
</pre></div>
<p>Okay, the <em>curl</em> printed the response from the concurrent server but it did not terminate and kept hanging.
What is happening here? The server no longer sleeps for 60 seconds: its child process actively handles a client request, closes the client connection and exits, but the client <em>curl</em> still does not&nbsp;terminate.</p>
<p><img alt="" src="lsbaws_part3_conc3_child_is_active.png" width="640"></p>
<p>So why does the <em>curl</em> not terminate? The reason is the duplicate file descriptors. When the child process closed the client connection, the kernel decremented the reference count of that client socket and the count became 1. The server child process exited, but the client socket was not closed by the kernel because the reference count for that socket descriptor was not 0, and, as a result, the termination packet (called <span class="caps">FIN</span> in <span class="caps">TCP</span>/<span class="caps">IP</span> parlance) was not sent to the client and the client stayed on the line, so to speak. There is also another problem. If your long-running server doesnt close duplicate file descriptors, it will eventually run out of available file&nbsp;descriptors:</p>
<p><img alt="" src="lsbaws_part3_conc3_out_of_descriptors.png"></p>
<p>Stop your server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3d.py">webserver3d.py</a> with <em>Control-C</em> and check out the default resources available to your server process set up by your shell with the shell built-in command <em>ulimit</em>:</p>
<div class="highlight"><pre><span></span>$ <span class="nb">ulimit</span> -a
core file size <span class="o">(</span>blocks, -c<span class="o">)</span> <span class="m">0</span>
data seg size <span class="o">(</span>kbytes, -d<span class="o">)</span> unlimited
scheduling priority <span class="o">(</span>-e<span class="o">)</span> <span class="m">0</span>
file size <span class="o">(</span>blocks, -f<span class="o">)</span> unlimited
pending signals <span class="o">(</span>-i<span class="o">)</span> <span class="m">3842</span>
max locked memory <span class="o">(</span>kbytes, -l<span class="o">)</span> <span class="m">64</span>
max memory size <span class="o">(</span>kbytes, -m<span class="o">)</span> unlimited
open files <span class="o">(</span>-n<span class="o">)</span> <span class="m">1024</span>
pipe size <span class="o">(</span><span class="m">512</span> bytes, -p<span class="o">)</span> <span class="m">8</span>
POSIX message queues <span class="o">(</span>bytes, -q<span class="o">)</span> <span class="m">819200</span>
real-time priority <span class="o">(</span>-r<span class="o">)</span> <span class="m">0</span>
stack size <span class="o">(</span>kbytes, -s<span class="o">)</span> <span class="m">8192</span>
cpu <span class="nb">time</span> <span class="o">(</span>seconds, -t<span class="o">)</span> unlimited
max user processes <span class="o">(</span>-u<span class="o">)</span> <span class="m">3842</span>
virtual memory <span class="o">(</span>kbytes, -v<span class="o">)</span> unlimited
file locks <span class="o">(</span>-x<span class="o">)</span> unlimited
</pre></div>
<p>As you can see above, the maximum number of open file descriptors (<em>open files</em>) available to the server process on my Ubuntu box is&nbsp;1024.</p>
<p>Now lets see how your server can run out of available file descriptors if it doesnt close duplicate descriptors. In an existing or new terminal window, set the maximum number of open file descriptors for your server to be&nbsp;256:</p>
<div class="highlight"><pre><span></span>$ <span class="nb">ulimit</span> -n <span class="m">256</span>
</pre></div>
<p>Start the server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3d.py">webserver3d.py</a> in the same terminal where youve just run the <em>$ ulimit -n 256</em>&nbsp;command:</p>
<div class="highlight"><pre><span></span>$ python webserver3d.py
</pre></div>
<p>and use the following client <a href="https://github.com/rspivak/lsbaws/blob/master/part3/client3.py">client3.py</a> to test the&nbsp;server.</p>
<div class="highlight"><pre><span></span><span class="c1">#####################################################################</span>
<span class="c1"># Test client - client3.py #</span>
<span class="c1"># #</span>
<span class="c1"># Tested with Python 2.7.9 &amp; Python 3.4 on Ubuntu 14.04 &amp; Mac OS X #</span>
<span class="c1">#####################################################################</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">errno</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="n">SERVER_ADDRESS</span> <span class="o">=</span> <span class="s1">&#39;localhost&#39;</span><span class="p">,</span> <span class="mi">8888</span>
<span class="n">REQUEST</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
<span class="s2">GET /hello HTTP/1.1</span>
<span class="s2">Host: localhost:8888</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">max_clients</span><span class="p">,</span> <span class="n">max_conns</span><span class="p">):</span>
<span class="n">socks</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">client_num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">max_clients</span><span class="p">):</span>
<span class="n">pid</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fork</span><span class="p">()</span>
<span class="k">if</span> <span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">for</span> <span class="n">connection_num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">max_conns</span><span class="p">):</span>
<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">sock</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">SERVER_ADDRESS</span><span class="p">)</span>
<span class="n">sock</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">REQUEST</span><span class="p">)</span>
<span class="n">socks</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">sock</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">connection_num</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentParser</span><span class="p">(</span>
<span class="n">description</span><span class="o">=</span><span class="s1">&#39;Test client for LSBAWS.&#39;</span><span class="p">,</span>
<span class="n">formatter_class</span><span class="o">=</span><span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentDefaultsHelpFormatter</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s1">&#39;--max-conns&#39;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="mi">1024</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s1">&#39;Maximum number of connections per client.&#39;</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s1">&#39;--max-clients&#39;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s1">&#39;Maximum number of clients.&#39;</span>
<span class="p">)</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
<span class="n">main</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">max_clients</span><span class="p">,</span> <span class="n">args</span><span class="o">.</span><span class="n">max_conns</span><span class="p">)</span>
</pre></div>
<p>In a new terminal window, start the <a href="https://github.com/rspivak/lsbaws/blob/master/part3/client3.py">client3.py</a> and tell it to create 300 simultaneous connections to the&nbsp;server:</p>
<div class="highlight"><pre><span></span>$ python client3.py --max-clients<span class="o">=</span><span class="m">300</span>
</pre></div>
<p>Soon enough your server will explode. Here is a screenshot of the exception on my&nbsp;box:</p>
<p><img alt="" src="lsbaws_part3_conc3_too_many_fds_exc.png"></p>
<p>The lesson is clear - your server should close duplicate descriptors. But even if you close duplicate descriptors, you are not out of the woods yet because there is another problem with your server, and that problem is&nbsp;zombies!</p>
<p><img alt="" src="lsbaws_part3_conc3_zombies.png" width="640"></p>
<p>Yes, your server code actually creates zombies. Lets see how. Start up your server&nbsp;again:</p>
<div class="highlight"><pre><span></span>$ python webserver3d.py
</pre></div>
<p>Run the following <em>curl</em> command in another terminal&nbsp;window:</p>
<div class="highlight"><pre><span></span>$ curl http://localhost:8888/hello
</pre></div>
<p>And now run the <em>ps</em> command to show running Python processes. This the example of <em>ps</em> output on my Ubuntu&nbsp;box:</p>
<div class="highlight"><pre><span></span>$ ps auxw <span class="p">|</span> grep -i python <span class="p">|</span> grep -v grep
vagrant <span class="m">9099</span> <span class="m">0</span>.0 <span class="m">1</span>.2 <span class="m">31804</span> <span class="m">6256</span> pts/0 S+ <span class="m">16</span>:33 <span class="m">0</span>:00 python webserver3d.py
vagrant <span class="m">9102</span> <span class="m">0</span>.0 <span class="m">0</span>.0 <span class="m">0</span> <span class="m">0</span> pts/0 Z+ <span class="m">16</span>:33 <span class="m">0</span>:00 <span class="o">[</span>python<span class="o">]</span> &lt;defunct&gt;
</pre></div>
<p>Do you see the second line above where it says the status of the process with <span class="caps">PID</span> 9102 is <strong>Z+</strong> and the name of the process is <strong>&lt;defunct&gt;</strong>? Thats our zombie there. The problem with zombies is that you cant kill&nbsp;them.</p>
<p><img alt="" src="lsbaws_part3_conc3_kill_zombie.png" width="640"></p>
<p>Even if you try to kill zombies with <em>$ kill -9 <PID></em>, they will survive. Try it and see for&nbsp;yourself.</p>
<p>What is a zombie anyway and why does our server create them? A <em>zombie</em> is a process that has terminated, but its parent has not <em>waited</em> for it and has not received its termination status yet. When a child process exits before its parent, the kernel turns the child process into a zombie and stores some information about the process for its parent process to retrieve later. The information stored is usually the process <span class="caps">ID</span>, the process termination status, and the resource usage by the process. Okay, so zombies serve a purpose, but if your server doesnt take care of these zombies your system will get clogged up. Lets see how that happens. First stop your running server and, in a new terminal window, use the <em>ulimit</em> command to set the <em>max user processess</em> to 400(make sure to set <em>open files</em> to a high number, lets say 500&nbsp;too):</p>
<div class="highlight"><pre><span></span>$ <span class="nb">ulimit</span> -u <span class="m">400</span>
$ <span class="nb">ulimit</span> -n <span class="m">500</span>
</pre></div>
<p>Start the server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3d.py">webserver3d.py</a> in the same terminal where youve just run the <em>$ ulimit -u 400</em>&nbsp;command:</p>
<div class="highlight"><pre><span></span>$ python webserver3d.py
</pre></div>
<p>In a new terminal window, start the <a href="https://github.com/rspivak/lsbaws/blob/master/part3/client3.py">client3.py</a> and tell it to create 500 simultaneous connections to the&nbsp;server:</p>
<div class="highlight"><pre><span></span>$ python client3.py --max-clients<span class="o">=</span><span class="m">500</span>
</pre></div>
<p>And, again, soon enough your server will blow up with an <strong>OSError: Resource temporarily unavailable</strong> exception when it tries to create a new child process, but it cant because it has reached the limit for the maximum number of child processes its allowed to create. Here is a screenshot of the exception on my&nbsp;box:</p>
<p><img alt="" src="lsbaws_part3_conc3_resource_unavailable.png"></p>
<p>As you can see, zombies create problems for your long-running server if it doesnt take care of them. I will discuss shortly how the server should deal with that zombie&nbsp;problem.</p>
<p>Lets recap the main points youve covered so&nbsp;far:</p>
<p><img alt="" src="lsbaws_part3_checkpoint.png"></p>
<blockquote>
<ul>
<li>If you dont close duplicate descriptors, the clients wont terminate because the client connections wont get&nbsp;closed.</li>
<li>If you dont close duplicate descriptors, your long-running server will eventually run out of available file descriptors (<em>max open files</em>).</li>
<li>When you fork a child process and it exits and the parent process doesnt <em>wait</em> for it and doesnt collect its termination status, it becomes a <em>zombie</em>.</li>
<li>Zombies need to eat something and, in our case, its memory. Your server will eventually run out of available processes (<em>max user processes</em>) if it doesnt take care of&nbsp;zombies.</li>
<li>You cant <em>kill</em> a zombie, you need to <em>wait</em> for&nbsp;it.</li>
</ul>
</blockquote>
<p><br/>
So what do you need to do to take care of zombies? You need to modify your server code to <em>wait</em> for zombies to get their termination status. You can do that by modifying your server to call a <a href="https://docs.python.org/2.7/library/os.html#os.wait">wait</a> system call. Unfortunately, thats far from ideal because if you call <em>wait</em> and there is no terminated child process the call to <em>wait</em> will block your server, effectively preventing your server from handling new client connection requests. Are there any other options? Yes, there are, and one of them is the combination of a <em>signal handler</em> with the <em>wait</em> system&nbsp;call.</p>
<p><img alt="" src="lsbaws_part3_conc4_signaling.png" width="240"></p>
<p>Here is how it works. When a child process exits, the kernel sends a <em><span class="caps">SIGCHLD</span></em> signal. The parent process can set up a signal handler to be asynchronously notified of that <em><span class="caps">SIGCHLD</span></em> event and then it can <em>wait</em> for the child to collect its termination status, thus preventing the zombie process from being left&nbsp;around.</p>
<p><img alt="" src="lsbaws_part_conc4_sigchld_async.png" width="640"></p>
<p>By the way, an asynchronous event means that the parent process doesnt know ahead of time that the event is going to&nbsp;happen.</p>
<p>Modify your server code to set up a <em><span class="caps">SIGCHLD</span></em> event handler and <em>wait</em> for a terminated child in the event handler. The code is available in <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3e.py">webserver3e.py</a>&nbsp;file:</p>
<div class="highlight"><pre><span></span><span class="c1">###########################################################################</span>
<span class="c1"># Concurrent server - webserver3e.py #</span>
<span class="c1"># #</span>
<span class="c1"># Tested with Python 2.7.9 &amp; Python 3.4 on Ubuntu 14.04 &amp; Mac OS X #</span>
<span class="c1">###########################################################################</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">signal</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="n">SERVER_ADDRESS</span> <span class="o">=</span> <span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="mi">8888</span>
<span class="n">REQUEST_QUEUE_SIZE</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">def</span> <span class="nf">grim_reaper</span><span class="p">(</span><span class="n">signum</span><span class="p">,</span> <span class="n">frame</span><span class="p">):</span>
<span class="n">pid</span><span class="p">,</span> <span class="n">status</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">wait</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span>
<span class="s1">&#39;Child {pid} terminated with status {status}&#39;</span>
<span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">pid</span><span class="o">=</span><span class="n">pid</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">):</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">client_connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
<span class="n">http_response</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
<span class="s2">HTTP/1.1 200 OK</span>
<span class="s2">Hello, World!</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">http_response</span><span class="p">)</span>
<span class="c1"># sleep to allow the parent to loop over to &#39;accept&#39; and block there</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">serve_forever</span><span class="p">():</span>
<span class="n">listen_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">SERVER_ADDRESS</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">REQUEST_QUEUE_SIZE</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">&#39;Serving HTTP on port {port} ...&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">PORT</span><span class="p">))</span>
<span class="n">signal</span><span class="o">.</span><span class="n">signal</span><span class="p">(</span><span class="n">signal</span><span class="o">.</span><span class="n">SIGCHLD</span><span class="p">,</span> <span class="n">grim_reaper</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">client_connection</span><span class="p">,</span> <span class="n">client_address</span> <span class="o">=</span> <span class="n">listen_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="n">pid</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fork</span><span class="p">()</span>
<span class="k">if</span> <span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># child</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close child copy</span>
<span class="n">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">)</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span> <span class="c1"># parent</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">serve_forever</span><span class="p">()</span>
</pre></div>
<p>Start the&nbsp;server:</p>
<div class="highlight"><pre><span></span>$ python webserver3e.py
</pre></div>
<p>Use your old friend <em>curl</em> to send a request to the modified concurrent&nbsp;server:</p>
<div class="highlight"><pre><span></span>$ curl http://localhost:8888/hello
</pre></div>
<p>Look at the&nbsp;server:</p>
<p><img alt="" src="lsbaws_part3_conc4_eintr.png"></p>
<p>What just happened? The call to <em>accept</em> failed with the error <em><span class="caps">EINTR</span></em>.</p>
<p><img alt="" src="lsbaws_part3_conc4_eintr_error.png" width="640"></p>
<p>The parent process was blocked in <em>accept</em> call when the child process exited which caused <em><span class="caps">SIGCHLD</span></em> event, which in turn activated the signal handler and when the signal handler finished the <em>accept</em> system call got&nbsp;interrupted:</p>
<p><img alt="" src="lsbaws_part3_conc4_eintr_accept.png" width="640"></p>
<p>Dont worry, its a pretty simple problem to solve, though. All you need to do is to re-start the <em>accept</em> system call. Here is the modified version of the server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3f.py">webserver3f.py</a> that handles that&nbsp;problem:</p>
<div class="highlight"><pre><span></span><span class="c1">###########################################################################</span>
<span class="c1"># Concurrent server - webserver3f.py #</span>
<span class="c1"># #</span>
<span class="c1"># Tested with Python 2.7.9 &amp; Python 3.4 on Ubuntu 14.04 &amp; Mac OS X #</span>
<span class="c1">###########################################################################</span>
<span class="kn">import</span> <span class="nn">errno</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">signal</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="n">SERVER_ADDRESS</span> <span class="o">=</span> <span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="mi">8888</span>
<span class="n">REQUEST_QUEUE_SIZE</span> <span class="o">=</span> <span class="mi">1024</span>
<span class="k">def</span> <span class="nf">grim_reaper</span><span class="p">(</span><span class="n">signum</span><span class="p">,</span> <span class="n">frame</span><span class="p">):</span>
<span class="n">pid</span><span class="p">,</span> <span class="n">status</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">wait</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">):</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">client_connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
<span class="n">http_response</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
<span class="s2">HTTP/1.1 200 OK</span>
<span class="s2">Hello, World!</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">http_response</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">serve_forever</span><span class="p">():</span>
<span class="n">listen_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">SERVER_ADDRESS</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">REQUEST_QUEUE_SIZE</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">&#39;Serving HTTP on port {port} ...&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">PORT</span><span class="p">))</span>
<span class="n">signal</span><span class="o">.</span><span class="n">signal</span><span class="p">(</span><span class="n">signal</span><span class="o">.</span><span class="n">SIGCHLD</span><span class="p">,</span> <span class="n">grim_reaper</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">client_connection</span><span class="p">,</span> <span class="n">client_address</span> <span class="o">=</span> <span class="n">listen_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">IOError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">code</span><span class="p">,</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">args</span>
<span class="c1"># restart &#39;accept&#39; if it was interrupted</span>
<span class="k">if</span> <span class="n">code</span> <span class="o">==</span> <span class="n">errno</span><span class="o">.</span><span class="n">EINTR</span><span class="p">:</span>
<span class="k">continue</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span>
<span class="n">pid</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fork</span><span class="p">()</span>
<span class="k">if</span> <span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># child</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close child copy</span>
<span class="n">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">)</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span> <span class="c1"># parent</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close parent copy and loop over</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">serve_forever</span><span class="p">()</span>
</pre></div>
<p>Start the updated server <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3f.py">webserver3f.py</a>:</p>
<div class="highlight"><pre><span></span>$ python webserver3f.py
</pre></div>
<p>Use <em>curl</em> to send a request to the modified concurrent&nbsp;server:</p>
<div class="highlight"><pre><span></span>$ curl http://localhost:8888/hello
</pre></div>
<p>See? No <em><span class="caps">EINTR</span></em> exceptions any more. Now, verify that there are no more zombies either and that your <em><span class="caps">SIGCHLD</span></em> event handler with <em>wait</em> call took care of terminated children. To do that, just run the <em>ps</em> command and see for yourself that there are no more Python processes with <strong>Z+</strong> status (no more <strong>&lt;defunct&gt;</strong> processes). Great! It feels safe without zombies running&nbsp;around.</p>
<p><img alt="" src="lsbaws_part3_checkpoint.png"></p>
<blockquote>
<ul>
<li>If you <em>fork</em> a child and dont wait for it, it becomes a <em>zombie</em>.</li>
<li>Use the <em><span class="caps">SIGCHLD</span></em> event handler to asynchronously <em>wait</em> for a terminated child to get its termination&nbsp;status</li>
<li>When using an event handler you need to keep in mind that system calls might get interrupted and you need to be prepared for that&nbsp;scenario</li>
</ul>
</blockquote>
<p><br/>
Okay, so far so good. No problems, right? Well, almost. Try your <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3f.py">webserver3f.py</a> again, but instead of making one request with <em>curl</em> use <a href="https://github.com/rspivak/lsbaws/blob/master/part3/client3.py">client3.py</a> to create 128 simultaneous&nbsp;connections:</p>
<div class="highlight"><pre><span></span>$ python client3.py --max-clients <span class="m">128</span>
</pre></div>
<p>Now run the <em>ps</em> command&nbsp;again</p>
<div class="highlight"><pre><span></span>$ ps auxw <span class="p">|</span> grep -i python <span class="p">|</span> grep -v grep
</pre></div>
<p>and see that, oh boy, zombies are back&nbsp;again!</p>
<p><img alt="" src="lsbaws_part3_conc5_zombies_again.png" width="640"></p>
<p>What went wrong this time? When you ran 128 simultaneous clients and established 128 connections, the child processes on the server handled the requests and exited almost at the same time causing a flood of <em><span class="caps">SIGCHLD</span></em> signals being sent to the parent process. The problem is that the signals are not queued and your server process missed several signals, which left several zombies running around&nbsp;unattended:</p>
<p><img alt="" src="lsbaws_part3_conc5_signals_not_queued.png" width="640"></p>
<p>The solution to the problem is to set up a <em><span class="caps">SIGCHLD</span></em> event handler but instead of <em>wait</em> use a <a href="https://docs.python.org/2.7/library/os.html#os.waitpid">waitpid</a> system call with a <em><span class="caps">WNOHANG</span></em> option in a loop to make sure that all terminated child processes are taken care of. Here is the modified server code, <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3g.py">webserver3g.py</a>:</p>
<div class="highlight"><pre><span></span><span class="c1">###########################################################################</span>
<span class="c1"># Concurrent server - webserver3g.py #</span>
<span class="c1"># #</span>
<span class="c1"># Tested with Python 2.7.9 &amp; Python 3.4 on Ubuntu 14.04 &amp; Mac OS X #</span>
<span class="c1">###########################################################################</span>
<span class="kn">import</span> <span class="nn">errno</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">signal</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="n">SERVER_ADDRESS</span> <span class="o">=</span> <span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="mi">8888</span>
<span class="n">REQUEST_QUEUE_SIZE</span> <span class="o">=</span> <span class="mi">1024</span>
<span class="k">def</span> <span class="nf">grim_reaper</span><span class="p">(</span><span class="n">signum</span><span class="p">,</span> <span class="n">frame</span><span class="p">):</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">pid</span><span class="p">,</span> <span class="n">status</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">waitpid</span><span class="p">(</span>
<span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="c1"># Wait for any child process</span>
<span class="n">os</span><span class="o">.</span><span class="n">WNOHANG</span> <span class="c1"># Do not block and return EWOULDBLOCK error</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">OSError</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">if</span> <span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># no more zombies</span>
<span class="k">return</span>
<span class="k">def</span> <span class="nf">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">):</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">client_connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
<span class="n">http_response</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
<span class="s2">HTTP/1.1 200 OK</span>
<span class="s2">Hello, World!</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">http_response</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">serve_forever</span><span class="p">():</span>
<span class="n">listen_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">SERVER_ADDRESS</span><span class="p">)</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">REQUEST_QUEUE_SIZE</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">&#39;Serving HTTP on port {port} ...&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">PORT</span><span class="p">))</span>
<span class="n">signal</span><span class="o">.</span><span class="n">signal</span><span class="p">(</span><span class="n">signal</span><span class="o">.</span><span class="n">SIGCHLD</span><span class="p">,</span> <span class="n">grim_reaper</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">client_connection</span><span class="p">,</span> <span class="n">client_address</span> <span class="o">=</span> <span class="n">listen_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">IOError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">code</span><span class="p">,</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">args</span>
<span class="c1"># restart &#39;accept&#39; if it was interrupted</span>
<span class="k">if</span> <span class="n">code</span> <span class="o">==</span> <span class="n">errno</span><span class="o">.</span><span class="n">EINTR</span><span class="p">:</span>
<span class="k">continue</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span>
<span class="n">pid</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fork</span><span class="p">()</span>
<span class="k">if</span> <span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># child</span>
<span class="n">listen_socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close child copy</span>
<span class="n">handle_request</span><span class="p">(</span><span class="n">client_connection</span><span class="p">)</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span> <span class="c1"># parent</span>
<span class="n">client_connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># close parent copy and loop over</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">serve_forever</span><span class="p">()</span>
</pre></div>
<p>Start the&nbsp;server:</p>
<div class="highlight"><pre><span></span>$ python webserver3g.py
</pre></div>
<p>Use the test client <a href="https://github.com/rspivak/lsbaws/blob/master/part3/client3.py">client3.py</a>:</p>
<div class="highlight"><pre><span></span>$ python client3.py --max-clients <span class="m">128</span>
</pre></div>
<p>And now verify that there are no more zombies. Yay! Life is good without zombies&nbsp;:)</p>
<p><img alt="" src="lsbaws_part3_conc5_no_zombies.png" width="640"></p>
<p>Congratulations! It&#8217;s been a pretty long journey but I hope you liked it. Now you have your own simple concurrent server and the code can serve as a foundation for your further work towards a production grade Web&nbsp;server.</p>
<p>Ill leave it as an exercise for you to update the <span class="caps">WSGI</span> server from <a href="../lsbaws-part2/index.html" title="Part 2">Part 2</a> and make it concurrent. You can find the modified version <a href="https://github.com/rspivak/lsbaws/blob/master/part3/webserver3h.py">here</a>. But look at my code only after youve implemented your own version. You have all the necessary information to do that. So go and just do it&nbsp;:)</p>
<p>Whats next? As Josh Billings&nbsp;said,</p>
<blockquote>
<p><em><span class="dquo">&#8220;</span>Be like a postage stamp — stick to one thing until you get&nbsp;there.&#8221;</em></p>
</blockquote>
<p>Start mastering the basics. Question what you already know. And always dig&nbsp;deeper.</p>
<p><img alt="" src="lsbaws_part3_dig_deeper.png" width="640"></p>
<blockquote>
<p><em><span class="dquo">&#8220;</span>If you learn only methods, youll be tied to your methods. But if you learn principles, you can devise your own methods.&#8221; &#8212;Ralph Waldo&nbsp;Emerson</em></p>
</blockquote>
<p>Below is a list of books that Ive drawn on for most of the material in this article. They will help you broaden and deepen your knowledge about the topics Ive covered. I highly recommend you to get those books somehow: borrow them from your friends, check them out from your local library, or just buy them on Amazon. They are the keepers(links are affiliate&nbsp;links):</p>
<ol>
<li>
<p><a href="http://www.amazon.com/gp/product/0131411551/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0131411551&linkCode=as2&tag=russblo0b-20&linkId=2F4NYRBND566JJQL">Unix Network Programming, Volume 1: The Sockets Networking <span class="caps">API</span> (3rd Edition)</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=0131411551" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
</li>
<li>
<p><a href="http://www.amazon.com/gp/product/0321637739/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321637739&linkCode=as2&tag=russblo0b-20&linkId=3ZYAKB537G6TM22J">Advanced Programming in the <span class="caps">UNIX</span> Environment, 3rd Edition</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=0321637739" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
</li>
<li>
<p><a href="http://www.amazon.com/gp/product/1593272200/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1593272200&linkCode=as2&tag=russblo0b-20&linkId=CHFOMNYXN35I2MON">The Linux Programming Interface: A Linux and <span class="caps">UNIX</span> System Programming Handbook</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=1593272200" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
</li>
<li>
<p><a href="http://www.amazon.com/gp/product/0321336313/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321336313&linkCode=as2&tag=russblo0b-20&linkId=K467DRFYMXJ5RWAY"><span class="caps">TCP</span>/<span class="caps">IP</span> Illustrated, Volume 1: The Protocols (2nd Edition) (Addison-Wesley Professional Computing Series)</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=0321336313" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
</li>
<li>
<p><a href="http://www.amazon.com/gp/product/1441418687/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441418687&linkCode=as2&tag=russblo0b-20&linkId=QFOAWARN62OWTWUG">The Little Book of <span class="caps">SEMAPHORES</span> (2nd Edition): The Ins and Outs of Concurrency Control and Common Mistakes</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=1441418687" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />. Also available for free on the authors site <a href="http://greenteapress.com/semaphores/">here</a>.</p>
</li>
</ol>
<p><br/></p>
<p><br/>
<p>If you want to get my newest articles in your inbox, then enter your email address below and click "Get Updates!"</p>
<!-- Begin MailChimp Signup Form -->
<link href="https://cdn-images.mailchimp.com/embedcode/classic-081711.css"
rel="stylesheet" type="text/css">
<style type="text/css">
#mc_embed_signup {
background: #f5f5f5;
clear: left;
font: 18px Helvetica,Arial,sans-serif;
}
#mc_embed_signup form {
text-align: center;
padding: 20px 0 10px 3%;
}
#mc_embed_signup .mc-field-group input {
display: inline;
width: 40%;
}
#mc_embed_signup div.response {
width: 100%;
}
</style>
<div id="mc_embed_signup">
<form
action="https://ruslanspivak.us4.list-manage.com/subscribe/post?u=7dde30eedc045f4670430c25f&amp;id=6f69f44e03"
method="post"
id="mc-embedded-subscribe-form"
name="mc-embedded-subscribe-form"
class="validate"
target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<div class="mc-field-group">
<label for="mce-NAME">Enter Your First Name *</label>
<input type="text" value="" name="NAME" class="required" id="mce-NAME">
</div>
<div class="mc-field-group">
<label for="mce-EMAIL">Enter Your Best Email *</label>
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
</div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div>
<!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
<div style="position: absolute; left: -5000px;"><input type="text" name="b_7dde30eedc045f4670430c25f_6f69f44e03" tabindex="-1" value=""></div>
<div class="clear"><input type="submit" value="Get Updates!" name="subscribe" id="mc-embedded-subscribe" class="button" style="background-color: rgb(63, 146, 236);"></div>
</div>
</form>
</div>
<!-- <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[1]='NAME';ftypes[1]='text';fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script> -->
<!--End mc_embed_signup-->
</p>
<p><br/>
<strong>All articles in this&nbsp;series:</strong></p>
<ul>
<li><a href="../lsbaws-part1/index.html">Let&#8217;s Build A Web Server. Part&nbsp;1.</a></li>
<li><a href="../lsbaws-part2/index.html">Let&#8217;s Build A Web Server. Part&nbsp;2.</a></li>
<li><a href="index.html">Let&#8217;s Build A Web Server. Part&nbsp;3.</a></li>
</ul>
<div class="footnote">
<hr>
<ol>
<li id="fn-1">
<p><a href="http://www.amazon.com/gp/product/0131411551/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0131411551&linkCode=as2&tag=russblo0b-20&linkId=2F4NYRBND566JJQL">Unix Network Programming, Volume 1: The Sockets Networking <span class="caps">API</span> (3rd Edition)</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=0131411551" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />&#160;<a class="footnote-backref" href="index.html#fnref-1" title="Jump back to footnote 1 in the text">&#8617;</a><a class="footnote-backref" href="index.html#fnref2-1" title="Jump back to footnote 1 in the text">&#8617;</a></p>
</li>
<li id="fn-2">
<p><a href="http://www.amazon.com/gp/product/1441418687/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441418687&linkCode=as2&tag=russblo0b-20&linkId=QFOAWARN62OWTWUG">The Little Book of <span class="caps">SEMAPHORES</span> (2nd Edition): The Ins and Outs of Concurrency Control and Common Mistakes</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=1441418687" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />.&#160;<a class="footnote-backref" href="index.html#fnref-2" title="Jump back to footnote 2 in the text">&#8617;</a></p>
</li>
</ol>
</div>
</div>
<!-- /.entry-content -->
<hr/>
<section class="comments" id="comments">
<h2>Comments</h2>
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'ruslanspivak'; // required: replace example with your forum shortname
var disqus_identifier = 'lets-build-a-web-server-part-3';
var disqus_url = 'https://ruslanspivak.com/lsbaws-part3/';
var disqus_config = function () {
this.language = "en";
};
/* * * DON'T EDIT BELOW THIS LINE * * */
(function () {
var dsq = document.createElement('script');
dsq.type = 'text/javascript';
dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by
Disqus.</a></noscript>
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
</section>
</article>
</section>
</div>
<div class="col-sm-3" id="sidebar">
<aside>
<section class="well well-sm">
<ul class="list-group list-group-flush">
<li class="list-group-item"><h4><i class="fa fa-home fa-lg"></i><span class="icon-label">Social</span></h4>
<ul class="list-group" id="social">
<li class="list-group-item"><a href="https://github.com/rspivak/"><i class="fa fa-github-square fa-lg"></i> github</a></li>
<li class="list-group-item"><a href="https://twitter.com/rspivak"><i class="fa fa-twitter-square fa-lg"></i> twitter</a></li>
<li class="list-group-item"><a href="https://linkedin.com/in/ruslanspivak/"><i class="fa fa-linkedin-square fa-lg"></i> linkedin</a></li>
</ul>
</li>
<li class="list-group-item"><h4><i class="fa fa-home fa-lg"></i><span class="icon-label">Popular posts</span></h4>
<ul class="list-group" id="popularposts">
<li class="list-group-item"
style="font-size: 15px; word-break: normal;">
<a href="../lsbaws-part1/index.html">
Let's Build A Web Server. Part 1.
</a>
</li>
<li class="list-group-item"
style="font-size: 15px; word-break: normal;">
<a href="../lsbasi-part1/index.html">
Let's Build A Simple Interpreter. Part 1.
</a>
</li>
<li class="list-group-item"
style="font-size: 15px; word-break: normal;">
<a href="../lsbaws-part2/index.html">
Let's Build A Web Server. Part 2.
</a>
</li>
<li class="list-group-item"
style="font-size: 15px; word-break: normal;">
<a href="index.html">
Let's Build A Web Server. Part 3.
</a>
</li>
<li class="list-group-item"
style="font-size: 15px; word-break: normal;">
<a href="../lsbasi-part2/index.html">
Let's Build A Simple Interpreter. Part 2.
</a>
</li>
</ul>
</li>
<li class="list-group-item">
<h4>
<span>Disclaimer</span>
</h4>
<p id="disclaimer-text"> Some of the links on this site
have my Amazon referral id, which provides me with a small
commission for each sale. Thank you for your support.
</p>
</li>
</ul>
</section>
</aside>
</div>
</div>
</div>
<footer>
<div class="container">
<hr>
<div class="row">
<div class="col-xs-10">&copy; 2020 Ruslan Spivak
<!-- &middot; Powered by <a href="https://github.com/DandyDev/pelican-bootstrap3" target="_blank">pelican-bootstrap3</a>, -->
<!-- <a href="http://docs.getpelican.com/" target="_blank">Pelican</a>, -->
<!-- <a href="http://getbootstrap.com" target="_blank">Bootstrap</a> -->
<!-- -->
</div>
<div class="col-xs-2"><p class="pull-right"><i class="fa fa-arrow-up"></i> <a href="index.html#">Back to top</a></p></div>
</div>
</div>
</footer>
<script src="../theme/js/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="../theme/js/bootstrap.min.js"></script>
<!-- Enable responsive features in IE8 with Respond.js (https://github.com/scottjehl/Respond) -->
<script src="../theme/js/respond.min.js"></script>
<!-- Disqus -->
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'ruslanspivak'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function () {
var s = document.createElement('script');
s.async = true;
s.type = 'text/javascript';
s.src = '//' + disqus_shortname + '.disqus.com/count.js';
(document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
}());
</script>
<!-- End Disqus Code -->
<!-- Google Analytics Universal -->
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-2572871-3', 'auto');
ga('send', 'pageview');
</script>
<!-- End Google Analytics Universal Code -->
</body>
</html>