Thursday, January 3, 2008

Writing a server with Python's asyncore module

The Python asyncore and aynchat modules

The Python standard library provides two modules---asyncore and
asynchat---to help in writing concurrent network servers using
event-based designs. The documentation does not give good examples,
so I am making some notes.

Overview

The basic idea behind the asyncore module is that:

  • there is a function, asyncore.loop() that does select() on a bunch of 'channels'. Channels are thin wrappers around sockets.
  • when select reports an event on any socket, loop() examines the event and the socket's state to create a higher level event;
  • it then calls a method on the channel corresponding to the higher level event.
asyncore provides a low-level, but flexible API to build network
servers. asynchat builds upon asyncore and provides an API that is
more suitable for request/response type of protocols.

aysncore

The asyncore module's API consists of:

  • the loop method, to be called by a driver program;
  • the dispatcher class, to be subclassed to do useful stuff. The dispatcher class is what is called 'channel' elsewhere.


+-------------+ +--------+
| driver code |---------> | loop() |
+-------------+ +--------+
| |
| | loop-dispatcher API (a)
| |
| +--------------+
| | dispatcher |
+----------------->| subclass |
+--------------+
|
| dispatcher-logic API (b)
|
+--------------+
| server logic |
+--------------+


This is all packaged nicely in an object oriented way. So, we have
the dispatcher class, that extends/wraps around the socket class (from
the socket module in the Python standard library). It provides all
the socket class' methods, as well as methods to handle the higher
level events. You are supposed to subclass dispatcher and implement
the event handling methods to do something useful.


The loop-dispatcher API

The loop function looks like this:

loop( [timeout[, use_poll[, map[,count]]]])

What is the map? It is a dictionary whose keys are the
file-descriptors, or fds, of the socket (i.e., socket.fileno()), and
whose values are the dispatcher objects.

When we create a dispatcher object, it automatically gets added to a
global list of sockets. The loop() function does a select() on this
list unless we provide an explicit map. (Hmm... we might always want
to use explicit maps; then our loop calls will be thread safe and we
will be able to launch multiple threads, each calling loop on
different maps.)

Methods a dispatcher subclass should implement

loop() needs some methods from the dispatcher object:

  • readable(): should return True, if you want the fd to be observed for read events;
  • writable(): should return True, if you want the fd to be observed for write events;

If either read or write is true, the corresponding fd will be examined
for errors also. Obviously, it makes no sense to have a dispatcher
which returns False for both readable() and writable().

  • handle_read: socket is readable; dispatcher.recv() can be used to actually get the data
  • handle_write: socket is writable; dispatcher.send(data) can be used to actually send the data
  • handle_error: socket encountered an error
  • handle_expt: socket received OOB data (not really used in practice)
  • handle_close: socket was closed remotely or locally
Server sockets get one more event.

  • handle_accept: a new incoming connection can be accept()ed. Call the accept() method really accept the connection. To create a server socket, call the bind() and listen() methods on it first.
Client sockets get this event:

  • handle_connect: connection to remote endpoint has been made. To initiate the connection, first call the connect() method on it.

Other socket methods are available in dispatch: create_socket(),
close(), set_resue_addr().


How to write a server using asyncore

The standard library documentation gives a client example, but not a
server example. Here are some notes on the latter.

  1. Subclass dispatched to create a listening socket
  2. In its handle_accept method, create new dispatchers. They'll get added to the global socket map.

Note: the handlers must not block or take too much time... or the
server won't be concurrent.

These socket-like functions that dispatcher extends should not be bypassed. They do funky things to detect higher level events. For e.g., how does asyncore figure out that the socket is closed? If I remember correctly, there are two ways to detect whether a non-blocking socket is closed:

  • select() returns a read event, but when you call recv()/read() you get zero bytes;
  • you call send()/write() and it fails with an error (sending zero bytes is not an error).

(I wish I had a copy of Unix Network Programming by Stevens handy
right now.)

Will look at asynchat in another post.

The code for the server is below:


asyncore_echo_server.py

import logging
import asyncore
import socket

logging.basicConfig(level=logging.DEBUG, format="%(created)-15s %(msecs)d %(levelname)8s %(thread)d %(name)s %(message)s")
log = logging.getLogger(__name__)

BACKLOG = 5
SIZE = 1024

class EchoHandler(asyncore.dispatcher):


def __init__(self, conn_sock, client_address, server):
self.server = server
self.client_address = client_address
self.buffer = ""

# We dont have anything to write, to start with
self.is_writable = False

# Create ourselves, but with an already provided socket
asyncore.dispatcher.__init__(self, conn_sock)
log.debug("created handler; waiting for loop")

def readable(self):
return True # We are always happy to read


def writable(self):
return self.is_writable # But we might not have
# anything to send all the time


def handle_read(self):
log.debug("handle_read")
data = self.recv(SIZE)
log.debug("after recv")
if data:
log.debug("got data")
self.buffer += data
self.is_writable = True # sth to send back now
else:
log.debug("got null data")

def handle_write(self):
log.debug("handle_write")
if self.buffer:
sent = self.send(self.buffer)
log.debug("sent data")
self.buffer = self.buffer[sent:]
else:
log.debug("nothing to send")
if len(self.buffer) == 0:
self.is_writable = False


# Will this ever get called? Does loop() call
# handle_close() if we called close, to start with?
def handle_close(self):
log.debug("handle_close")
log.info("conn_closed: client_address=%s:%s" % \
(self.client_address[0],
self.client_address[1]))
self.close()
#pass


class EchoServer(asyncore.dispatcher):

allow_reuse_address = False
request_queue_size = 5
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM


def __init__(self, address, handlerClass=EchoHandler):
self.address = address
self.handlerClass = handlerClass

asyncore.dispatcher.__init__(self)
self.create_socket(self.address_family,
self.socket_type)

if self.allow_reuse_address:
self.set_resue_addr()

self.server_bind()
self.server_activate()


def server_bind(self):
self.bind(self.address)
log.debug("bind: address=%s:%s" % (self.address[0], self.address[1]))


def server_activate(self):
self.listen(self.request_queue_size)
log.debug("listen: backlog=%d" % self.request_queue_size)


def fileno(self):
return self.socket.fileno()


def serve_forever(self):
asyncore.loop()


# TODO: try to implement handle_request()

# Internal use
def handle_accept(self):
(conn_sock, client_address) = self.accept()
if self.verify_request(conn_sock, client_address):
self.process_request(conn_sock, client_address)


def verify_request(self, conn_sock, client_address):
return True


def process_request(self, conn_sock, client_address):
log.info("conn_made: client_address=%s:%s" % \
(client_address[0],
client_address[1]))
self.handlerClass(conn_sock, client_address, self)


def handle_close(self):
self.close()


and to use it:

server = asyncore_echo_server.EchoServer((interface, port))
server.serve_forever()

23 comments:

Diodotus Reserva said...

Thank you.

xtealc said...

Thanks a lot.

Tom@sQo said...

thanks a lot. This is an article I`ve been looking for ages ;)

FrogFace said...

Nice post.

There is one typo I've noticed, self.set_resue_addr() should be self.set_reuse_addr()

lalakis said...

great post, thanx

Neil said...

Many thanks for the code.

wholesales said...

ugg bootsTHE SNOW wholesale ugg bootsSTARTED TO FALLWholesale handbags SEVERAL HOURS wholesale clothingBEFORE HER laborWholesale jewelry began.wholesale clothing A few flakes wholesale handbagsfirst, wholesale furniturein the dull Furniture Wholesalegray late-afternoon Wholesale jewelrysky,Ceramic tileand then Micro sd cardswind-driven swirls Wholesale clothingand eddies Wholesale Jewelryaround the edges Wholesale fashion jewelryof their wide Wholesale costume jewelryfront porch. ugg bootsHe stood by her wholesaleside at the windowwholesale electronics, watching sharp gusts of snow billow,skin care products
then swirl and drift to the ground. All around the neighborhood, lights came on, and the naked branches of the trees turned white.

wholesales said...

ugg bootsOutside,uggs snow continuedHigh pressure blower to fall quietly throughIndustrial fan the darkness, Industrial bloweras bright and thick Commercial bloweras static in the wholesalecones of light castchina wholesale by the streetlights.wholesale shoes By the time he rosewholesale clothing and looked out watchesthe window, pressure blowertheir car had become a soft white hill on the edge pressure blowersof the street.fans Already his footprints blowers
in the driveway had filled and disappeared.

wholesales said...

wholesaleShe looked up, wholesale atvsmiling, wedding dresseswhen he tuckedMP3 player
the blanketwholesale mp3 around her legs.Wholesale Mp4
"You know,digital camera wholesale I've been wondering consumer electronicswhat it's like,"mp5 player she said. memory cards"Before we're born,Car video players I mean. It's too bad we can't remember." gps devicesShe opened her robe and pulled up the bluetooth headsetsweater she wore underneath, revealing a Mp3 Wholesalebelly as round and hard asMp3 Wholesale a melon. Mp4 WholesaleShe ran her hand across its smooth surface, Mp4 Wholesalefirelight playing across her skin, casting reddish gold onto her hair.

thugliffe said...

Thanks a lot.
probably just the thing i was searching for

Gre7g said...

Thanks, man!

Now why couldn't someone have put something like that in the help file? Sheesh. Usually the docs are great, but I couldn't figure out how to use that module for the life of me!

amanda said...

Yiwu City is famous in the world for its biggest commodity markets and tremendous goods. 200, 000 foreign traders and wholesalers come to Yiwu market for purchasing varieties of China wholesale products. Nearly all leading China manufacturers and factories in Mainland China have showrooms in Yiwu wholesale market. All the prices in Yiwu market is the factory price. Because of good quality and LOW price, Yiwu commodities are exported to over 200 countries and regions. AmandaIEC is your ideal China sourcing partner and professional Yiwu Agent. You one stop sourcing agent, export agent, trade agent, purchasing agent, shipping agent in Yiwu China. promotional gifts, dollar items, lingerie wholesale, shoes wholesale, keychains wholesale and more. Wholesale shoes, Wholesale boots, Wholesale sneakers, Wholesale casual shoes, Wholesale dress shoes, Wholesale sandals, Wholesale slippers, from Yiwu, good quality, for all your cheap wholesale needs, your one stop China suppliers. Start your one stop China discount wholesale sourcing here today and experience top notch service and fast shipping. Welcome to Yiwu China to buy China wholesale products all at lowest wholesale price on www.AmandaIEC.com.

wowgoldme said...

cheep wow gold,buy wow goldworld of warcrft gold.

Affordable Luxurious Wedding Dress Blog said...

cheap wedding gownsdiscount bridal gownsChina wedding dressesChina wedding online store

筱娅 said...

Have you heared about 9Dragons which you need use Anarchy credits to play, and you can also borrow Anarchy Online credits from other players? But you can buy AO credits, or you will lose the choice if you do not have Anarchy online gold. If you get Anarchy gold, you can continue this game.
Have you heared about 9Dragons which you need use 9Dragons gold to play, and you can also borrow 9 Dragons gold from other players? But you can buy 9 Dragons gold, or you will lose the choice if you do not have cheap 9Dragons gold. If you get 9Dragons money, you can continue this game.

Affordable Luxurious Wedding Dress Blog said...

cheap wedding gownsdiscount bridal gownsChina wedding dressesChina wedding online storediscount designer wedding dresses

game gold said...

After separate for one year, I can not leave you, leave Scions Of Fate gold . Now I buy SOF gold again, I do not want to leave you, but at that time, I have no idea. Buy Scions Of Fate money is the thing I want to do for long time. I know that cheap SOF gold is your life. So I will try my best and do not let you pass away. In my mind, I think I buy sof gold is the fate.

Without hesitate, I bought second life linden , in the game I can find myself. I feel lonely, but I do not want to talk with anyone, so I buy lindens . At present, think the happy day I spend in knight, I am eager to enter it, and cheap linden . Own linden dollars , it means that you own the life of happiness. So I will not leave secondlife money . It is the origin of the happiness.

jislsnhd said...

It is the maple mesos which make me very happy these days, my brother says mesos is his favorite games gold he likes, he usually buy some cheap mesos to start his game and most of the time he will win the maplestory mesos back and give me some maple story mesos to play the game.
I always heard something from my neighbor that he sometimes goes to the internet bar to play the game which will use him some maple mesos,he usually can win a lot of mesos,then he let his friends all have some cheap mesos,his friends thank him very much for introducing them the maplestory mesos,they usually maple story mesos together.

feilin said...

My friends and I like to buy habbo credits, because the habbo gold is very useful to upgrade equipment. Only your equipment becomes better, then you can win this game. In habbo coins, you can buy everything you want in this game. Tomorrow will be my birthday, so my friends promise to buy habbo gold as gifts. I am so happy. They understand me so well, cheap habbo credits is my favorite.
I like hero gold very much because it is very useful. In fact at first sight I have fallen in love with hero online gold. So no matter how much I have spent to buy hero gold, I never regret. Because of hero online money, I meet a lot of friends. So I never hesitate to buy hero money.

woow said...

Do you play any internet game? Do you know WOW? The biggest and the most famous mmorpg. What is your hero's level? Do you want to make them more charmful, more powerful? If you use yes to answer my questions, then I guess you must want to get much more gold to make your hero more charmful and more powerful. How do you get the gold? farm by yourself or just buy the gold from some internet store? If you have the habit to get the gold from internet store, I strongly recommand you get wow gold from Masswowgold.
Masswowgold is a professional site designed specifically for tradingWorldofWarcraft gold from gamer to gamer. Not only is the price cheaper than other sites, but also the service is instant, secure and professional. Masswowgold dedicate themselves to offer WOW players with great prices and quality services. They do instant delivery through customer service 24 hours a day, 7days a week and the prices are updated regularly according to current market rates. You can buy cheap wow gold from masswowgold, fast delivery and good service. Masswowgold is a booming provider of MMORPG virtual currency and assets including buying & selling service. It has started its online selling since 2004. It supports payment by Paypal, Credit Card, Moneybookers ,Western Union and Bank Transfer. With its advanced internal ERP system, extensive supplier network, enthusiastic employees and 24/7 live chat; Masswowgold has helped about 150,000 WOW players. Its concept is: Honesty, Enthusiasm, Innovation and Cheap. New chapters will be written by its unceasing innovation.
Hope what i said just now is useful for you and for your WOW life. Hope you can enjoy your WOW life better with WOW gold from masswowgold.

Aion also is a very interesing game, new game. But i dont know if it has been started in USA. It is very popular in Korea and China. 永恒之塔代练 is a very hot word in China. Many people want to level their hero become high level, but they have not enough time to do that. So they just let others to help them. Beuwant provides this kind of service. They do the powerleveling with hands but not bots, so aion代练 is very safe.

Jerry said...

cheap wedding gowns,
discount bridal gowns,
China wedding dresses,
discount designer wedding dresses,
China wedding online store,
plus size wedding dresses,
cheap informal wedding dresses,
junior bridesmaid dresses,
cheap bridesmaid dresses,
maternity bridesmaid dresses,
discount flower girl gowns,
cheap prom dresses,
party dresses,
evening dresses,
mother of the bride dresses,
special occasion dresses,
cheap quinceanera dresses,
hot red wedding dresses

asdfsadf said...

nike shoes, jordan shoes, nike dunks, nike air force 1, nike shox, nike shoes, nike shox, nike dunks, nike air force 1, nike shox, nike shoes, nike shox oz, nike shox nz, nike shox r3, nike shox r4, nike shox r5, nike shox tl
nike air force 1










nike Air Force 1, nike shoes, Jordan shoes, nike sb dunks, cheap nike Air Force 1, cheap nike shoes, cheap Jordan shoes, cheap nike sb dunks, discount nike Air Force 1, discount nike shoes, discount Jordan shoes, discount nike sb dunks, nike shoes, nike shox, nike dunks, nike air force 1, jordan shoes, nike shoes, nike shox, nike dunks, nike air force 1, jordan shoes












nike shoes, nike shox, jordan shoes, puma shoes, nike dunks, nike air max, nike air force one, timberland boots, ugg boots, nike shoes, nike shox, nike air force 1, nike sb dunks, puma shoes, nike air max, jordan shoes, ugg boot, jordan sneakers, timberland shoes, bape shoes, nike shoes, nike shox, nike dunks, nike air force 1, jordan shoes

nike said...

http://www.shoesbuying.com/nike-shox-shoes-mens-shox-c-163_127.htm : men's shox R4
http://www.shoesbuying.com/nike-shox-shoes-womens-shox-c-163_128.htm : women's shox R4
http://www.shoesbuying.com/nike-shox-shoes-c-164.htm : nike shox R5
http://www.shoesbuying.com/nike-shox-shoes-mens-shox-c-164_144.htm : nike shox
http://www.shoesbuying.com/nike-shox-shoes-womens-shox-c-164_145.htm : womens shox
http://www.shoesbuying.com/nike-shox-c-165.htm : nike shox
http://www.shoesbuying.com/nike-shox-mens-shox-c-165_129.htm : nike shox
http://www.shoesbuying.com/nike-shox-womens-shox-c-165_130.htm : womens shox
http://www.shoesbuying.com/nike-shox-c-171.htm : nike tl3
http://www.shoesbuying.com/nike-shox-mens-shox-c-171_172.htm : mens nike tl3
http://www.shoesbuying.com/nike-shox-womens-shox-c-171_173.htm : women's tl3
http://www.shoesbuying.com/nike-trainers-lovers-c-118.htm : nike trainers
http://www.shoesbuying.com/other-nike-shoes-c-193.htm : nike shoes
http://www.polosales.com/ : cheap polo shirts
http://www.polosales.com/ : polo shirt
http://www.polosales.com/ : lacoste polo shirts
http://www.polosales.com/ : ralph lauren polo shirts
http://www.polosales.com/ : wholesale polo shirts
http://www.superpolos.com/mens-lacoste-solid-classicfit-polo-shirt-light-yellow-p-146/ : Yellow Polo Shirt
http://www.superpolos.com/wholesale-mens-short-sleeve-lacoste-polo-shirts-p-132/ : Men's Polo Shirts
http://www.superpolos.com/wholesale-mens-short-sleeve-lacoste-polo-shirts-p-133/ : Wholesale Polo Shirts
http://www.superpolos.com/ : polo shirts
http://www.superpolos.com/ : Lacoste polo shirts
http://www.pumachaussure.com/ : chaussure puma
http://www.pumachaussure.com/ : chaussures puma