Let's write a simple web server that can return a single line of text to any connected web browser.
The core idea is during the initialization of this web server, we should make a call to select.epoll() and register our server's file descriptor for event notifications. In the web server's executive code, the socket event is monitored as follows:
Listing 2.4 Simple web server using select.epoll #!/usr/bin/env python # Python Network Programming Cookbook, Second Edition -- Chapter - 2 # This program is optimized for Python 2.7.12 and Python 3.5.2. # It may run on any other version with/without modifications. import socket import select import argparse SERVER_HOST = 'localhost' EOL1 = b' ' EOL2 = b' ' SERVER_RESPONSE = b"""HTTP/1.1 200 OK Date: Mon, 1 Apr 2013 01:01:01 GMT Content-Type: text/plain Content-Length: 25 Hello from Epoll Server!""" class EpollServer(object): """ A socket server using Epoll""" def __init__(self, host=SERVER_HOST, port=0): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((host, port)) self.sock.listen(1) self.sock.setblocking(0) self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) print ("Started Epoll Server") self.epoll = select.epoll() self.epoll.register(self.sock.fileno(), select.EPOLLIN) def run(self): """Executes epoll server operation""" try: connections = {}; requests = {}; responses = {} while True: events = self.epoll.poll(1) for fileno, event in events: if fileno == self.sock.fileno(): connection, address = self.sock.accept() connection.setblocking(0) self.epoll.register(connection.fileno(),
select.EPOLLIN) connections[connection.fileno()] = connection requests[connection.fileno()] = b'' responses[connection.fileno()] = SERVER_RESPONSE elif event & select.EPOLLIN: requests[fileno] += connections[fileno].recv(1024) if EOL1 in requests[fileno] or EOL2
in requests[fileno]: self.epoll.modify(fileno, select.EPOLLOUT) print('-'*40 + ' ' + requests[fileno].decode()
[:-2]) elif event & select.EPOLLOUT: byteswritten = connections[fileno].
send(responses[fileno]) responses[fileno] = responses[fileno]
[byteswritten:] if len(responses[fileno]) == 0: self.epoll.modify(fileno, 0) connections[fileno].shutdown(socket.SHUT_RDWR) elif event & select.EPOLLHUP: self.epoll.unregister(fileno) connections[fileno].close() del connections[fileno] finally: self.epoll.unregister(self.sock.fileno()) self.epoll.close() self.sock.close() if __name__ == '__main__': parser = argparse.ArgumentParser(description='Socket Server
Example with Epoll') parser.add_argument('--port', action="store", dest="port",
type=int, required=True) given_args = parser.parse_args() port = given_args.port server = EpollServer(host=SERVER_HOST, port=port) server.run()
If you run this script and access the web server from your browsers, such as Google Chrome or Mozilla Firefox, by entering http://localhost:8800/, the following output will be shown in the console:
$ python 12_4_simple_web_server_with_epoll.py --port=8800 Started Epoll Server ---------------------------------------- GET / HTTP/1.1 Host: localhost:8800 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 DNT: 1 Accept-Encoding: gzip, deflate, sdch, br Accept-Language: en-US,en;q=0.8 ---------------------------------------- GET /favicon.ico HTTP/1.1 Host: localhost:8800 Connection: keep-alive User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Accept: image/webp,image/*,*/*;q=0.8 DNT: 1 Referer: http://localhost:8800/ Accept-Encoding: gzip, deflate, sdch, br Accept-Language: en-US,en;q=0.8
You will also be able to see the following line in your browser:
Hello from Epoll Server!
The following screenshot shows the scenario:
Simple Web Server: Terminal and Browser