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 = FingerProtocol 20 21 def __init__(self, users): 22 self.users = users 23 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 = FingerSetterProtocol 41 42 def __init__(self, fingerFactory): 43 self.fingerFactory = fingerFactory 44 45 def setUser(self, user, status): 46 self.fingerFactory.users[user] = status 47 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 = users 33 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] = status 39 40 def getFingerFactory(self): 41 f = protocol.ServerFactory() 42 f.protocol = FingerProtocol 43 f.getUser = self.getUser 44 return f 45 46 def getFingerSetterFactory(self): 47 f = protocol.ServerFactory() 48 f.protocol = FingerSetterProtocol 49 f.setUser = self.setUser 50 return f 51 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 = filename 23 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 = FingerProtocol 47 f.getUser = self.getUser 48 return f 49 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=users 23 #继承基类的__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="<h1>{}</h1><p>{}</p>".format(path,messageValue) 34 else: 35 text="<h1>{}</h1><p>no such path</p>".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=filename 41 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]=status 50 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=FingerProtocol 56 f.getUser=self.getUser 57 return f 58 def getResource(self): 59 f=FingerResource(self.users) 60 return f 61 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 <p/> usage: site/user") 70 path = path.decode("ascii") 71 user = user.decode("ascii") 72 text = '<h1>{}</h1><p>{}</p>'.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)