1 from twisted.application import service, strports 2 from twisted.internet import protocol, reactor, defer 3 from twisted.protocols import basic 4 5 class FingerProtocol(basic.LineReceiver): 6 def lineReceived(self, user): 7 d = self.factory.getUser(user) 8 9 def onError(err):10 return b'Internal error in server'11 d.addErrback(onError)12 13 def writeResponse(message):14 self.transport.write(message + b'\r\n')15 self.transport.loseConnection()16 d.addCallback(writeResponse)17 18 class FingerFactory(protocol.ServerFactory):19 protocol = FingerProtocol20 21 def __init__(self, users):22 self.users = users23 24 def getUser(self, user):25 return defer.succeed(self.users.get(user, b"No such user"))26 27 class FingerSetterProtocol(basic.LineReceiver):28 def connectionMade(self):29 self.lines = []30 31 def lineReceived(self, line):32 self.lines.append(line)33 34 def connectionLost(self, reason):35 user = self.lines[0]36 status = self.lines[1]37 self.factory.setUser(user, status)38 39 class FingerSetterFactory(protocol.ServerFactory):40 protocol = FingerSetterProtocol41 42 def __init__(self, fingerFactory):43 self.fingerFactory = fingerFactory44 45 def setUser(self, user, status):46 self.fingerFactory.users[user] = status47 48 ff = FingerFactory({b'moshez': b'Happy and well'})49 fsf = FingerSetterFactory(ff)50 51 application = service.Application('finger', uid=1, gid=1)52 serviceCollection = service.IServiceCollection(application)53 strports.service("tcp:79", ff).setServiceParent(serviceCollection)54 strports.service("tcp:1079", fsf).setServiceParent(serviceCollection)
这个项目有两个协议-工厂类,每一个都是application的子程序。更具体地说,setServiceParents方法把两个服务定义为application,实现了IServiceCollections。这两个服务都由应用程序启动。
使用Services让依赖更加合理
使用服务基类,实现泛型行为
1 from twisted.application import service, strports 2 from twisted.internet import protocol, reactor, defer 3 from twisted.protocols import basic 4 5 class FingerProtocol(basic.LineReceiver): 6 def lineReceived(self, user): 7 d = self.factory.getUser(user) 8 9 def onError(err):10 return b'Internal error in server'11 d.addErrback(onError)12 13 def writeResponse(message):14 self.transport.write(message + b'\r\n')15 self.transport.loseConnection()16 d.addCallback(writeResponse)17 18 class FingerSetterProtocol(basic.LineReceiver):19 def connectionMade(self):20 self.lines = []21 22 def lineReceived(self, line):23 self.lines.append(line)24 25 def connectionLost(self,reason):26 user = self.lines[0]27 status = self.lines[1]28 self.factory.setUser(user, status)29 30 class FingerService(service.Service):31 def __init__(self, users):32 self.users = users33 34 def getUser(self, user):35 return defer.succeed(self.users.get(user, b"No such user"))36 37 def setUser(self, user, status):38 self.users[user] = status39 40 def getFingerFactory(self):41 f = protocol.ServerFactory()42 f.protocol = FingerProtocol43 f.getUser = self.getUser44 return f45 46 def getFingerSetterFactory(self):47 f = protocol.ServerFactory()48 f.protocol = FingerSetterProtocol49 f.setUser = self.setUser50 return f51 52 application = service.Application('finger', uid=1, gid=1)53 f = FingerService({b'moshez': b'Happy and well'})54 serviceCollection = service.IServiceCollection(application)55 strports.service("tcp:79", f.getFingerFactory()56 ).setServiceParent(serviceCollection)57 strports.service("tcp:1079", f.getFingerSetterFactory()58 ).setServiceParent(serviceCollection)
简化了代码,在一个服务中使用两个协议 1 from twisted.application import service, strports 2 from twisted.internet import protocol, reactor, defer
3 from twisted.protocols import basic 4 5 class FingerProtocol(basic.LineReceiver): 6 def lineReceived(self, user): 7 d = self.factory.getUser(user) 8 9 def onError(err):10 return b'Internal error in server'11 d.addErrback(onError)12 13 def writeResponse(message):14 self.transport.write(message + b'\r\n')15 self.transport.loseConnection()16 d.addCallback(writeResponse)17 18 19 class FingerService(service.Service):20 def __init__(self, filename):21 self.users = {}22 self.filename = filename23 24 def _read(self):25 with open(self.filename, "rb") as f:26 for line in f:27 user, status = line.split(b':', 1)28 user = user.strip()29 status = status.strip()30 self.users[user] = status #这里每30秒刷新一次31 self.call = reactor.callLater(30, self._read)32 33 def startService(self):34 self._read()35 service.Service.startService(self)36 37 def stopService(self):38 service.Service.stopService(self)39 self.call.cancel()40 41 def getUser(self, user):42 return defer.succeed(self.users.get(user, b"No such user"))43 44 def getFingerFactory(self):45 f = protocol.ServerFactory()46 f.protocol = FingerProtocol47 f.getUser = self.getUser48 return f49 50 51 application = service.Application('finger', uid=1, gid=1)52 f = FingerService('/etc/users')53 finger = strports.service("tcp:79", f.getFingerFactory())54 55 finger.setServiceParent(service.IServiceCollection(application))56 f.setServiceParent(service.IServiceCollection(application))
这个版本在一个集中管理的文件中读取消息,而且进行缓存,每30秒刷新一次
在网上进行宣布
也有其他的服务可以产生这种有效的通信。举个例子,在twisted.web里,这个类本身不做基类,而是被给一个资源,表示通过URL可用的树形资源。这个层级结构由Site动态地覆盖,通过getChild。
为了把它集合到Finger应用程序里,我们设置一个新的TCPservice,这个服务器调用Site的心函数来获取资源。
1 from twisted.application import service, strports 2 from twisted.internet import protocol, reactor, defer 3 from twisted.protocols import basic 4 from twisted.web import resource,server,static 5 import cgi 6 7 class FingerProtocol(basic.LineReceiver): 8 def lineReceived(self, user): 9 d = self.factory.getUser(user)10 11 def onError(err):12 return b'Internal error in server'13 d.addErrback(onError)14 15 def writeResponse(message):16 self.transport.write(message + b'\r\n')17 self.transport.loseConnection()18 d.addCallback(writeResponse)19 20 class FingerResource(resource.Resource):21 def __init__(self,users):22 self.users=users23 #继承基类的__init__24 super(FingerResource, self).__init__()25 def getChild(self, path, request):26 messageValue=self.users.get(path)27 if messageValue:28 messageValue=messageValue.decode('ascii')29 if path:30 path=path.decode('ascii')31 if messageValue is not None:32 messageValue=cgi.escape(messageValue)33 text="{}
{}
".format(path,messageValue)34 else:35 text="{}
no such path
".format(path)36 text=text.encode('ascii')37 return static.Data(text,'text/html')38 class FingerService(service.Service):39 def __init__(self,filename):40 self.filename=filename41 self.users={}42 def _read(self):43 self.users.clear()44 with open(self.filename,'rb')as f:45 for line in f:46 user,status=line.split(b":",1)47 user=user.strip()48 status=status.strip()49 self.users[user]=status50 self.call=reactor.callLater(30,self._reaf)51 def getUser(self,user):52 return defer.succeed(self.users.get(user,b'no such user'))53 def getFingerFactory(self):54 f=protocol.ServerFactory()55 f.protocol=FingerProtocol56 f.getUser=self.getUser57 return f58 def getResource(self):59 f=FingerResource(self.users)60 return f61 def startService(self):62 self._read()63 service.Service.stopService(self)64 def stopService(self):65 service.Service.stopService(self)66 self.call.cancel()67 application=service.Application('finger',uid=1,gid=1)68 f=FingerService('/finger')69 serviceCollection=service.IServiceCollection(application)70 f.setServiceParent(serviceCollection)71 strports.service("tcp:79",f.getFingerFactory()72 ).setServiceParent(serviceCollection)73 strports.service("tcp:8000",server.Site(f.getResource())74 ).setServiceParent(serviceCollection)
在IRC上宣布
IRC客户端通常很像服务器,相应来自网咯的时间。客户服务奖确保切断的连接重新建立,用了很常见的指数退避。
1 from twisted.application import internet, service, strports 2 from twisted.internet import protocol, reactor, defer, endpoints 3 from twisted.words.protocols import irc 4 from twisted.protocols import basic 5 from twisted.web import resource, server, static 6 7 import cgi 8 9 class FingerProtocol(basic.LineReceiver): 10 def lineReceived(self, user): 11 d = self.factory.getUser(user) 12 13 def onError(err): 14 return b'Internal error in server' 15 d.addErrback(onError) 16 17 def writeResponse(message): 18 self.transport.write(message + b'\r\n') 19 self.transport.loseConnection() 20 d.addCallback(writeResponse) 21 22 23 class IRCReplyBot(irc.IRCClient): 24 def connectionMade(self): 25 self.nickname = self.factory.nickname 26 irc.IRCClient.connectionMade(self) 27 28 def privmsg(self, user, channel, msg): 29 user = user.split('!')[0] 30 if self.nickname.lower() == channel.lower(): 31 d = self.factory.getUser(msg.encode("ascii")) 32 33 def onError(err): 34 return b'Internal error in server' 35 d.addErrback(onError) 36 37 def writeResponse(message): 38 message = message.decode("ascii") 39 irc.IRCClient.msg(self, user, msg + ': ' + message) 40 d.addCallback(writeResponse) 41 42 43 class FingerService(service.Service): 44 def __init__(self, filename): 45 self.filename = filename 46 self.users = {} 47 48 def _read(self): 49 self.users.clear() 50 with open(self.filename, "rb") as f: 51 for line in f: 52 user, status = line.split(b':', 1) 53 user = user.strip() 54 status = status.strip() 55 self.users[user] = status 56 self.call = reactor.callLater(30, self._read) 57 58 def getUser(self, user): 59 return defer.succeed(self.users.get(user, b"No such user")) 60 61 def getFingerFactory(self): 62 f = protocol.ServerFactory() 63 f.protocol = FingerProtocol 64 f.getUser = self.getUser 65 return f 66 67 def getResource(self): 68 def getData(path, request): 69 user = self.users.get(path, b"No such users usage: site/user") 70 path = path.decode("ascii") 71 user = user.decode("ascii") 72 text = '{}
{}
'.format(path, user) 73 text = text.encode("ascii") 74 return static.Data(text, 'text/html') 75 76 r = resource.Resource() 77 r.getChild = getData 78 return r 79 80 def getIRCBot(self, nickname): 81 f = protocol.ClientFactory() 82 f.protocol = IRCReplyBot 83 f.nickname = nickname 84 f.getUser = self.getUser 85 return f 86 87 def startService(self): 88 self._read() 89 service.Service.startService(self) 90 91 def stopService(self): 92 service.Service.stopService(self) 93 self.call.cancel() 94 95 96 application = service.Application('finger', uid=1, gid=1) 97 f = FingerService('/etc/users') 98 serviceCollection = service.IServiceCollection(application) 99 f.setServiceParent(serviceCollection)100 strports.service("tcp:79", f.getFingerFactory()101 ).setServiceParent(serviceCollection)102 strports.service("tcp:8000", server.Site(f.getResource())103 ).setServiceParent(serviceCollection)104 internet.ClientService(105 endpoints.clientFromString(reactor, "tcp:irc.freenode.org:6667"),106 f.getIRCBot('fingerbot')).setServiceParent(serviceCollection)