#!/usr/bin/python import select import socket import errno import traceback import fcntl import os import sys import threading from sys import stdin, stdout, stderr from threading import Thread class Client: WAITING = 1 PARTNERED = 2 DEAD = 3 def __repr__(self): return "" % (hex(id(self)), self.sock.fileno(), self.state, self.evmask, len(self.outbuf)) def __init__(self, server, sock): self.outbuf = "" self.sock = sock self.server = server self.partner = None self.state = self.WAITING self.evmask = 0 def destroy(self): self.state = self.DEAD partner = self.partner self.partner = None if partner: self.server.del_client(partner) self.sock.close() def set_partner(self, partner): self.partner = partner self.state = self.PARTNERED self.register() def register(self): evmask = select.POLLHUP|select.POLLNVAL|select.POLLERR if self.state == self.PARTNERED: evmask = evmask | select.POLLIN if len(self.outbuf): evmask = evmask | select.POLLOUT if self.evmask != evmask: self.server.poller.register(self.sock.fileno(), evmask) self.evmask = evmask def write_val(self, val): self.outbuf += val self.register() def handle_write(self): try: n = self.sock.send(self.outbuf) except (IOError, OSError, socket.error): return self.server.del_client(self) if n <= 0: return self.server.del_client(self) self.outbuf = self.outbuf[n:] self.register() def handle_read(self): assert self.state == self.PARTNERED try: data = self.sock.recv(8192) except (IOError, OSError, socket.error): return self.server.del_client(self) if not len(data): return self.server.del_client(self) self.partner.write_val(data) class Server(Thread): def __init__(self, game_port): Thread.__init__(self) self.poller = select.poll() self.asock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) self.asock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.asock.bind(('', game_port)) self.asock.listen(10) print "Game service running on port" , game_port self.cnxlist = {} self.waitlist = [] self.ccount = 0 self.poller.register(self.asock.fileno(), select.POLLIN) def run(self): while 1: try: evs = self.poller.poll() except select.error, e: if e[0] == errno.EINTR: continue for I in evs: if I[0] == self.asock.fileno(): csock, addr = self.asock.accept() fcntl.fcntl(csock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) print "\nNew connection from %s, waitlist has %d items" % (addr, len(self.waitlist)) cli = Client(self, csock) self.cnxlist[csock.fileno()] = cli self.waitlist.append(cli) self.ccount += 1 cli.register() else: if not self.cnxlist.has_key(I[0]): assert 0, "Maybe something is broken with FD %s" % I[0] continue cnx = self.cnxlist[I[0]] if (I[1] & (select.POLLHUP|select.POLLNVAL|select.POLLERR)): self.del_client(cnx) else: if (I[1] & select.POLLIN): cnx.handle_read() if cnx.state != cnx.DEAD and (I[1] & select.POLLOUT): cnx.handle_write() while len(self.waitlist) >= 2: c1 = self.waitlist[0] c2 = self.waitlist[1] self.waitlist = self.waitlist[2:] c1.write_val("set_peer 1\n") # First in line gets to move first :) c2.write_val("set_peer 31\n") c1.set_partner(c2) c2.set_partner(c1) def del_client(self, cli): if cli.state == cli.DEAD: return del self.cnxlist[cli.sock.fileno()] self.poller.unregister(cli.sock.fileno()) cli.destroy() if cli in self.waitlist: self.waitlist.remove(cli) self.ccount -= 1 # This is minimal code to make the process a daemon in the Unix # environment. See W. R. Stevens "Advanced Programming in the Unix # Environment". See chapter 13, esp. section 13.3 pid = os.fork () if (pid != 0): os._exit (0) os.setsid () os.chdir ("/") for fd in range (0, os.sysconf ("SC_OPEN_MAX")): try: os.close (fd) except: pass open ("/dev/null", "r") # new stdin open ("/dev/null", "rw") # new stdout open ("/dev/null", "rw") # new stderr # Create the servers - one for each game iagno1 = Server(26478) gnibbles1 = Server(26479) # Start the servers iagno1.start() gnibbles1.start()