python⽹络框架twisted基础学习及详细讲解
twisted⽹络框架的三个基础模块:Protocol, ProtocolFactory, Transport.这三个模块是构成twisted服务器端与客户端程序的基本。Protocol:Protocol对象实现协议内容,即通信的内容协议
ProtocolFactory: 是⼯⼚模式的体现,在这⾥⾯⽣成协议
Transport: 是⽤来收发数据,服务器端与客户端的数据收发与处理都是基于这个模块
在windows中安装twisted需要先安装pywin32,⾃⼰去下载下就⾏。随后pip install twisted就会帮我们安装twisted以及zope。
我们结合⼀张图,以及⼀段程序来理解下twisted的基础实现:
然后我们⾸先看看服务器端程序:
1# coding=utf-8
2from twisted.internet.protocol import Protocol
3from twisted.internet.protocol import Factory
4from dpoints import TCP4ServerEndpoint
5from twisted.internet import reactor
6
7
8 clients = []
9
10
11class Spreader(Protocol):
12def__init__(lf, factory):
13 lf.factory = factory
14
15def connectionMade(lf):
16 lf.factory.numProtocols = lf.factory.numProtocols + 1
17 lf.transport.write(
18"欢迎来到Spread Site, 你是第%s个客户端⽤户!\n" % (lf.factory.numProtocols)
19 )
20print"new connect: %d" % (lf.factory.numProtocols)
21 clients.append(lf)老年人吃西洋参的好处
22
23def connectionLost(lf, reason):
24 lf.factory.numProtocols = lf.factory.numProtocols - 1
25 ve(lf)
26print"lost connect: %d" % (lf.factory.numProtocols)
27
28def dataReceived(lf, data):
29if data == "clo":
30 lf.transport.loConnection()
31for client in clients:
32if client != lf:
33 ansport.write(data)
34el:
35print data
36
37
38class SpreadFactory(Factory):
39def__init__(lf):
40 lf.numProtocols = 0
41
42def buildProtocol(lf, addr):
43return Spreader(lf)
44
45
46 endpoint = TCP4ServerEndpoint(reactor, 8007)
47 endpoint.listen(SpreadFactory())
48 reactor.run()
创建⼀个TCP的IPv4版本的终结点,随后就开始监听listen, 在这⾥我们传⼊协议⼯⼚对象作为参数, 先看看我们⾃定义的⼯⼚类SpreadFactory, 它派⽣⾃Factory, 我们看看这个类的源码(此时你需要有道词典了:) ):
1 @implementer(interfaces.IProtocolFactory, interfaces.ILoggingContext)
2 @_oldStyle
3class Factory:
4"""
5 This is a factory which produces protocols.
6
7 By default, buildProtocol will create a protocol of the class given in
8 lf.protocol.
立体正方形怎么做
9"""
10
11# put a subclass of Protocol here:
12 protocol = None
13
14 numPorts = 0
15 noisy = True
16
17 @classmethod
18def forProtocol(cls, protocol, *args, **kwargs):
19"""
20 Create a factory for the given protocol.
21
22 It ts the C{protocol} attribute and returns the constructed factory
23 instance.
24
25 @param protocol: A L{Protocol} subclass
26
27 @param args: Positional arguments for the factory.
28
29 @param kwargs: Keyword arguments for the factory.
30
31 @return: A L{Factory} instance wired up to C{protocol}.
32"""
33 factory = cls(*args, **kwargs)
34 factory.protocol = protocol
35return factory
36
37
38def logPrefix(lf):
39"""
40 Describe this factory for log messages.
41"""
42return lf.__class__.__name__
43
44
45def doStart(lf):
46"""Make sure startFactory is called.
47
48 Urs should not call this function themlves!
49"""
50if not lf.numPorts:
isy:
52 _loggerFor(lf).info("Starting factory {factory!r}",
53 factory=lf)
54 lf.startFactory()
55 lf.numPorts = lf.numPorts + 1
56
57def doStop(lf):
58"""Make sure stopFactory is called.
59
60 Urs should not call this function themlves!
61"""
62if lf.numPorts == 0:
63# this shouldn't happen, but does sometimes and this is better
64# than blowing up in asrt as we did previously.
65return
66 lf.numPorts = lf.numPorts - 1
67if not lf.numPorts:
isy:
69 _loggerFor(lf).info("Stopping factory {factory!r}",
70 factory=lf)
71 lf.stopFactory()
72
73def startFactory(lf):
74"""This will be called before I begin listening on a Port or Connector.
75
76 It will only be called once, even if the factory is connected
77 to multiple ports.
78
79 This can be ud to perform 'unrialization' tasks that
80 are best put off until things are actually running, such
81 as connecting to a databa, opening files, etcetera.
82"""
83
84def stopFactory(lf):
85"""This will be called before I stop listening on all Ports/Connectors.
86
87 This can be overridden to perform 'shutdown' tasks such as disconnecting
88 databa connections, closing files, etc.
89
90 It will be called, for example, before an application shuts down,
91 if it was connected to a port. Ur code should not call this function
92 directly.
93"""
94
95
96def buildProtocol(lf, addr):
97"""
98 Create an instance of a subclass of Protocol.
99
100 The returned instance will handle input on an incoming rver
101 connection, and an attribute "factory" pointing to the creating
102 factory.
103
104 Alternatively, L{None} may be returned to immediately clo the
105 new connection.
106
107 Override this method to alter how Protocol instances get created.
108
109 @param addr: an object implementing L{twisted.internet.interfaces.IAddress}
蚂蚁图片简笔画110"""
111 p = lf.protocol()
112 p.factory = lf
113return p
在这⾥很重要的⼀个函数就是buildProtocol, 此函数就是在⼯⼚模式中创建协议的.我们是基于基类Factory来实现这个函数的, 下⾯我们看看派⽣⾃Protocol的协议类Spread,Spread的__Init__参数中,我们给它传⼊的是⾃定义的SpreadFactory, 然后我们看下基类Protocol的源代码
1 @implementer(interfaces.IProtocol, interfaces.ILoggingContext)
2class Protocol(BaProtocol):
3"""
4 This is the ba class for streaming connection-oriented protocols.
5
6 If you are going to write a new connection-oriented protocol for Twisted,
7 start here. Any protocol implementation, either client or rver, should
8 be a subclass of this class.
9幼儿园教师个人计划
10 The API is quite simple. Implement L{dataReceived} to handle both
11 event-bad and synchronous input; output can be nt through the
12 'transport' attribute, which is to be an instance that implements
13 L{twisted.internet.interfaces.ITransport}. Override C{connectionLost} to be
14 notified when the connection ends.
15
16 Some subclass exist already to help you write common types of protocols:
17 e the L{twisted.protocols.basic} module for a few of them.
18"""
19
20def logPrefix(lf):
21"""
22 Return a prefix matching the class name, to identify log messages
23 related to this protocol instance.
24"""
25return lf.__class__.__name__
26
27
28def dataReceived(lf, data):
29"""Called whenever data is received.
30
31 U this method to translate to a higher-level message. Usually, some
32 callback will be made upon the receipt of each complete protocol
33 message.
34
35 @param data: a string of indeterminate length. Plea keep in mind
36 that you will probably need to buffer some data, as partial
37 (or multiple) protocol messages may be received! I recommend
38 that unit tests for protocols call through to this method with
39 differing chunk sizes, down to one byte at a time.
40"""
41
42def connectionLost(lf, reason=connectionDone):
43"""Called when the connection is shut down.
44
45 Clear any circular references here, and any external references
46 to this Protocol. The connection has been clod.
47
48 @type reason: L{twisted.python.failure.Failure}
49"""
⽽Protocol⼜是派⽣⾃BaProtocol的,继续看这个类的源代码:
@_oldStyle
class BaProtocol:
"""
This is the abstract superclass of all protocols.
Some methods have helpful default implementations here so that they can
easily be shared, but otherwi the direct subclass of this class are more
interesting, L{Protocol} and L{ProcessProtocol}.
"""
connected = 0
transport = None
def makeConnection(lf, transport):
"""Make a connection to a transport and a rver.
This ts the 'transport' attribute of this Protocol, and calls the
connectionMade() callback.
"""
董瑜def connectionMade(lf):
"""Called when a connection is made.
This may be considered the initializer of the protocol, becau
it is called when the connection is completed. For clients,
this is called once the connection to the rver has been
established; for rvers, this is called after an accept() call
stops blocking and a socket has been received. If you need to
nd any greeting or initial message, do it here.
"""
connectionDone=failure.Failure(error.ConnectionDone())
connectionDone.cleanFailure()
可以看到,我们⾃定义的Spread不过是实现了基类的函数。接下来我们滚⼀边实现逻辑:
⾸先,我们定义⼀个列表clients,以便存储多个客户端的连接。当服务器端接收到了客户端的连接后,调⽤connectionMade函数,同时,我们给使⽤Transport客户端发送消息, 通知客户端我们已收到连接。当客户端连接失去的时候,我们调⽤ConnectionLost, 同时移除列表中的客户端连接, dataReceived函数来接受数据,当客户端传来"clo"命令时,我们就主动关闭连接, 否则,我们就把data输出来。
看看客户端的代码:
1# coding=utf-8
2from twisted.internet.protocol import Protocol, ClientFactory
3from twisted.internet import reactor
4import threading
5import time
6import sys
7import datetime
8
9
10class Echo(Protocol):
11def__init__(lf):
12 lf.connected = Fal
13
14def connectionMade(lf):
15 lf.connected = True
16
17def connectionLost(lf, reason):
18 lf.connected = Fal
19
20def dataReceived(lf, data):
21print data.decode("utf-8")
22
23
24class EchoClientFactory(ClientFactory):
25def__init__(lf):
直线英语26 lf.protocol = None
27
28def startedConnecting(lf, connector):
29print"Start "
30
31def buildProtocol(lf, addr):
"
33 lf.protocol = Echo()
34return lf.protocol
35
36def clientConnectionLost(lf, connector, reason):
37print"Lost connection. Reason: ", reason
38
39def clientConnectionFailed(lf, connector, reason):
40print"Connection is failed, Reason: ", reason
41
42
43 bStop = Fal
44
45
46def routine(factory):
47while not bStop:
48if factory.protocol and ted:
49 ansport.write("hello, I'm %s %s" % (
50 sys.argv[0], w()
51 ))
52print sys.argv[0], w()
53 time.sleep(5)
54
55
56 host = '127.0.0.1'
57 port = 8007
58 factory = EchoClientFactory()
tTCP(host, port, factory)
60 threading.Thread(target=routine, args=(factory,)).start()
61 reactor.run()
62 bStop = True
⼀开始我们建⽴TCP连接, 传⼊主机地址, 端⼝, 协议⼯⼚对象作为参数,随后reactor.run挂起运⾏。
下⾯我们看看ClientFactory基类,因为我们⾃定义的协议⼯⼚EchoClientFactory派⽣⾃它。源码:
1class ClientFactory(Factory):
2"""A Protocol factory for clients.
3
4 This can be ud together with the various connectXXX methods in
5 reactors.
6"""
7
8def startedConnecting(lf, connector):
作文家乡的风俗9"""Called when a connection has been started.
10
11 You can call connector.stopConnecting() to stop the connection attempt.
12
13 @param connector: a Connector object.
14"""
15
16def clientConnectionFailed(lf, connector, reason):
17"""Called when a connection has failed to connect.
18
19 It may be uful to t() - this will reconnect.
寻寻觅觅的意思20
21 @type reason: L{twisted.python.failure.Failure}
22"""
23
24def clientConnectionLost(lf, connector, reason):
25"""Called when an established connection is lost.
26
27 It may be uful to t() - this will reconnect.
28
29 @type reason: L{twisted.python.failure.Failure}
30"""
同样的,我们⾃定义的EchoClientFactory不过就是实现了基类中没有实现的函数,其中最重要的还是
buildProtocol, 它为我们⽣成⼀个协议,下⾯看下我们⾃定义的协议类Echo, 基类源代码与上⾯的是⼀样的.
客户端的协议函数与服务器端的协议函数是⼀样的,在这⾥就不多说。
客户端的twisted模块讲完了,随后我们创建了⼀个线程去和服务器端通信, 并且定时发送, 当然,在这⾥我们为了以防万⼀,需要判断是否已经与服务器取得了连接,随后才发送消息.
⼤概讲了下基础部分,所有的代码都是来⾃《python⾼效开发实战》⾥的代码,在这⾥也向⼤家推荐这本书,学习twisted还有两个不错的教程,在最后我会发百度⽹盘共享。之所以写这篇基础的,就是为了能够理解⾼效开发实战⾥的最后⼀个项⽬: ⽤Twisted开发跨平台物联⽹消息⽹关。因为第⼀次实习就接触到了物联⽹通信,在⼯作时,滚了⼀遍项⽬的源代码(java写的,但我毕竟也是学了C#, 两年的⼈, 看懂项⽬源码没压⼒, mvc orm都是与中的EF, MVC差不多, 不过就是语法有点不同),正好和书上的这个项⽬差不多,书上将服务器与客户端的通信协议指令都讲得很清楚。因此这是⼀本不容错过的好书, 也是学习, 精通twisted的最好途径
最后就是运⾏测试:
服务器端:
客户端: