python网络编程:Python 网络编程介绍说明

来源:互联网 发布:微云 mac 不能同步 编辑:程序博客网 时间:2024/06/14 05:54
一、网络知识些介绍
是网络连接端点例如当你Web浏览器请求www.jb51.net上主页时你Web浏览器创建个并命令它去连接
www.jb51.netWeb服务器主机Web服务器也对来自请求在个上进行监听两端使用各自来发送和 接收信息
在使用时候每个都被绑定到个特定IP地址和端口IP地址是个由4个成序列这4个数均是范围 0~255中值(例如
220,176,36,76);端口数值取值范围是0~65535端口数小于1024都是为众所周知网络服务所保留 (例如Web服
务使用80端口);最大保留数被存储在模块IPPORT_RESERVED变量中你也可以为你使用另外端口数 值
不是所有IP地址都对世界其它地方可见实际上些是专门为那些非公共地址所保留(比如形如192.168.y.z或
10.x.y.z)地址127.0.0.1是本机地址;它始终指向当前计算机可以使用这个地址来连接运行在同计算机上其它
IP地址不好记你可以花点钱为特定IP地址注册个主机名或域名(比如使用www.jb51.net代替222.76.216.16)域名
服务器(DNS)处理名字到IP地址映射每个计算机都可以有个主机名即使它没有在官方注册
多少信息通过个网络被传送基于许多原因其中的就是使用协议许多协议是基于简单、低级协议以形成个协议栈
例如HTTP协议它是用在Web浏览器和Web服务器的间通信协议它是基于TCP协议而TCP协议又基于IP协议
当 在你自己两个间传送信息时候你通常选择TCP或UDP协议TCP协议在两端间建立个持续连接并且你所发送信
息有保证按顺序到达它们 目地UDP不建立连接它速度快但不可靠你发送信息也可能到不了另端;或它们没有按

顺序到达有时候个信息多个复制到达接收端即使你 只发送了次

2、使用地址和主机名
模块提供了几个用于使用主机名和地址来工作
gethostname返回运行所在计算机主机名:
>>> import
>>> .gethostname
'lenovo'
gethostbyname(name) 尝试将给定主机名解释为个IP地址首先将检查当前计算机是否能够解释如果不能个解
释请求将发送给个远程DNS服务器(远程DNS服务器 还可能将解释请求转发给另个DNS服务器直到该请求可以
被处理)gethostbyname返回这个IP地址或在查找失败后引发个异常
>>> .gethostbyname('lenovo')
'192.168.1.4'
>>> .gethostbyname('www.jb51.net')
'222.76.216.16'
个扩展形式是gethostbyname_ex(name)它返回个包含 3个元素元组分别是给定地址主要主机名、同IP地址可
选主机名个列表、有关同主机同接口其它IP地址个列表(列表可能都是空)
>>> .gethostbyname('www.163.com')
'60.191.81.49'
>>> .gethostbyname_ex('www.163.com')
('www.cache.split.netease.com', ['www.163.com'], ['60.191.81.48', '60.191.81.49
, '60.191.81.50', '60.191.81.51', '60.191.81.52', '60.191.81.53', '60.191.81.54
, '220.181.28.50', '220.181.28.51', '220.181.28.52', '220.181.28.53', '220.181.
8.54', '220.181.31.182', '220.181.31.183', '220.181.31.184'])
gethostbyaddr(address)作用和gethostbyname_ex相同只是你提供给它参数是个IP地址串:
>>> .gethostbyaddr('202.165.102.205')
('homepage.vip.cnb.yahoo.com', ['www.yahoo.com.cn'], ['202.165.102.205'])
getservbyname(service,protocol)要求个服务名(如'telnet'或'ftp')和个协议(如'tcp'或'udp')返回服务所使用端
口号:
>>>.getservbyname('http','tcp')
80
>>>.getservbyname('telnet','tcp)
23
通常非Python以32位字节包形式存储和使用IP地址inet_aton(ip_addr)和inet_ntoa(packed)在这个形式和IP地
址间作转换:
>>> .inet_aton('222.76.216.16')
'\xdeL\xd8\x10'
>>> .inet_ntoa('\xdeL\xd8\x10')
'222.76.216.16'
也定义了些变量来代表保留IP地址INADDR_ANY和INADDR_BROADCAST是被保留IP地址分别代表任意IP地
址和广播地 址;INADDR_LOOPBACK代表loopback设备总是地址127.0.0.1这些变量是32位字节数字形式
getfqdn([name])返回有关给定主机名全域名(如果省略则返回本机全域名)

3、使用低级通信
尽管Python提供了些封装使得使用更容易但是你也可以直接使用来工作
1、创建和销毁
模块中(family,type[,proto])创建个新对象family取值通常是AF_INETtype 取值通常是SOCK_STREAM(用于定
向连接可靠TCP连接)或SOCK_DGRAM(用于UDP):
>>> from import *
>>> s=(AF_INET,SOCK_STREAM)
family和type参数暗指了个协议但是你可以使用第 3个可选参数(proto取值如IPPROTO_TCP或
IPPROTO_RAW)来指定所使用协议代替使用IPPROTO_XX变量你可以使用getprotobyname:
>>> getprotobyname('tcp')
6
>>> IPPROTO_TCP
6
fromfd(fd,type[,proto]) 是个很少被使用它用来从打开个文件描述符创建个对象(文件描述符由文件fileno思路
方法返回)文件描述符和个真实 连接而非个文件对象fileno思路方法返回有关这个文件描述符
当你使用完工 对象时你应close思路方法显式关闭以尽快释放资源(尽管被垃圾回收器回收时将自动被关闭)另外
你也 可以使用shutdown(how)思路方法来关闭连接边或两边参数0阻止接收数据1阻止发送2阻止接收和发送
2、连接
当 两个连接时(例如使用TCP)端监听和接收进来连接而另端发起连接临听端创建个bind(address) 去绑定个特定
地址和端口listen(backlog)来临听进来连接最后accept来接收这个新进来连接,下面是在 服务器端代码:
>>> s=(AF_INET,SOCK_STREAM)
>>> s.bind(('127.0.0.1',44444))
>>> s.listen(1)
>>> q,v=s.accept #返回 q和地址v
注意:上面代码将直处于等待直到连接被建立下面我们再打开另个Python解释器用作客户端;然后键入如下代码
:
>>> from import *
>>> s=(AF_INET,SOCK_STREAM)
>>> s.connect(('127.0.0.1',44444) #发起连接
好了我们验证下连接是否建立了我们在服务器端键入以下代码来发送条信息:
>>> q.send('hello,i come from pythontik.com') 注:有时可能出现send argument 1 must be or
buffer,not str 原因可能是您机器不支持UTF-8集临时解决方案是q.send(b' hello...')
31 #发送字节数
在客户端键入以下代码来接收信息:
>>> s.recv(1024)
'hello,i come from pythontik.com'
你 传递给bind和connect地址是个有关AF_INET元组(ipAddress,port)代替connect你也可以调 用
connect_ex(address)思路方法如果背后对Cconnect返回个那么connect_ex也将返回个(否则返回 0代表成功
)代替引发个异常
当你listen时你给了它个参数这个数值表示在等待队列中允许放置进来连接总数当等待队列已满时如果有更多连
接到达那么远程端将被告知连接被拒绝在模块中SOMAXCONN变量表明了等待队列所能容纳最大量
accept思路方法返回形如bind和connect个地址代表远程地址下面显示变量v值:
>>> v
('127.0.0.1', 1334)
UDP是不定向连接但是你仍然可以使用给定目地址和端口来connect去关联个
3、发送和接收数据
函 数send([,flags])发送给定串到远程sendto([,flags],address)发送给 定串到个特定地址通常send思路方法用
于可靠连接sendto思路方法用于不可靠连接但是如果你在个 UDP 上connect来使它和个特定目标建立联系那
么这时你也可以使用send思路方法来代替sendto
send和sendto都返回实际发送字节数当你快速发送大量数据时候你可能想去确保全部信息已被发送那么你可以
使用如下个:
def safeSend(sock,msg):
sent=0
while msg:
i=sock.send(msg)
i-1: #发生了
-1
senti
msg=msg[i:]
time.sleep(25)
sent
recv(bufsize[,flags]) 思路方法接收个进来消息如果有大量数据在等待它只返回前面bufsize字节数数据
recvfrom(bufsize[,flags])做同 样事除了它使用AF_INET 返回值是(data,(ipAddress,port)),这便于你知道消息
来自哪儿(这对于非连接 是有用)
send,sendto,recv和recvfrom思路方法都有个可选参数flags默认值为0你可以通过对.MSG_*变量进行组合(按
位或)来建立flags值这些值因平台而有所区别但是最通用值如下所示:
MSG_OOB:处理带外数据(既TCP紧急数据)
MSG_DONTROUTE:不使用路由表;直接发送到接口
MSG_PEEK:返回等待数据且不把它们从队列中删除
例如如果你有个打开它有个消息等待被接收你可以接收这个消息后并不把它从进来数据队列中删除:
>>> q.recv(1024,MSG_PEEK)
'hello'
>>> q.recv(1024,MSG_PEEK) #没有删除所以你可以再得到它
'hello'
makefile([mode[,bufsize]]) 思路方法返回个文件类对象其中封装了以便于你以后将它传递给要求参数为个文
件代码(或许你喜欢使用文件思路方法来代替send和 recv)这个可选mode和bufsize参数取值和内建open样
4、使用选项
对象getpeername和 getsockname思路方法都返回包含个IP地址和端口 2元组(这个 2元组形式就像你传递给
connect和bind) getpeername返回所连接远程地址和端口getsockname返回有关本地相同信息
在默认 情况下是阻塞式意思就是思路方法在任务完成的前是不会返回例如如果存储向外发送数据缓存Cache已
满你又企图发送 更多数据那么你对send将被阻塞直到它能够将更多数据放入缓存Cache你可以通过
blocking(flag)思路方法(其中flag取值 是0blocking(0))来改变这个默认行为以使为非阻塞式当为非阻塞式时候
如果所做动作将导致阻塞将 会引起error异常下面段代码将试图不断地接受新连接并使用processRequest来处
理如果个新连接无效它将间隔半秒再试另 思路方法是在你监听上select或poll来检测个新连接到达
别选项可以使用 sockopt(level,name,value)和getsockopt(level,name[,buflen])思路方法来设置和获取 代表
了个协议栈区别层level参数指定了选项应用于哪层level取值以SOL_开头(SOL_SOCKET,SOL_TCP 等等
)name表明你涉及是哪个选项对于value如果该选项要求数值值value只能传入数字值你也可以传递入个缓存
Cache(个串) 但你必须使用正确格式对getsockopt不指定buflen参数意味你要求个数字值并返回这个值如果你
提供了 buflengetsockopt返回代表个缓存Cache串它最大长度是buflen字节数下面例子设置了个用于发送缓存
Cache 尺寸为64KB:
>>> s=(AF_INET,SOCK_STREAM)
>>> s.sockopt(SOL_SOCKET,SO_SNDBUF,65535)
要得到个包在被路由丢弃前所能有生命周期(TTL)和跳数你可以使用如下代码:
>>> s.getsockopt(SOL_IP,IP_TTL)
32
5、数值转换
由于区别平台字节顺序不样所以当在网络中传输数据时我们使用标准网络字节顺序nthol(x)和ntohs(x)要求个网
络字节顺序数值并把它转换为当前主机字节顺序相同数值而htonl(x)和htons(x)则相反:
>>> import.
>>> .htons(20000) #转换为个16位值
8270
>>> .htonl(20000) #转换为个32位值
541982720
>>> .ntohl(541982720)
20000
使用SocketServers
SocketServers模块为组服务类定义了个基类这组类压缩和隐藏了监听、接受和处理进入连接细节
1、SocketServers家族
TCPServer和UDPServer都是SocketServer子类它们分别处理TCP和UDP信息
注意:SocketServer也提供UnixStreamServer(TCPServer子类)和UNIXdatagramServer(UDPServer子类)它们
都如同其父类样除了在创建监听时使用AF_UNIX代替了AF_INET
默 认情况下服务次处理个连接但是你可以使用ThreadingMixIN和ForkingMixIn类来创建任 SocketServer线
程和子进程实际上SocketServer模块提供了些对些有用类来解决你麻烦它们 是:ForkingUDPServer、
ForkingTCPServer、ThreadingUDPServer、 ThreadingTCPServer、ThreadingUnixStreamServer和
ThreadingUnixDatagramServer
SocketServer以通常思路方法处理进入连接;要使它更有用你应该 提供你自己请求处理器类给它以便它传递个
去处理SocketServer模块中BaseRequestHandler类是所有请求处 理器父类假设例如你需要写个多线程电子邮
件服务器首先你要创建个MailRequestHandler它是 BaseRequestHandler子类然后把它传递给个新创建
SocketServer:
import SocketServer
...#创建你MailRequestHandler
addr=('220.172.20.6',25) #监听地址和端口
server=SocketServer.ThreadingTCPServer(addr,MailRequestHandler)
server.serve_forever
每 次个新连接到来时这个server创建个新MailRequestHandler例子并它handle思路方法来处理这个新请求
server继承自ThreadingTCPServer对于每个新请求它都启动个单独线程来处理这个请求以便于多个请求能够被
同时处理如果 用handle_request代替server_forever它将个个处理连接请求server_forever 只是反复
handle_request而已
般来说你只需使用服务的但是如果你需要创建你自己子类话你可以覆盖我们下面提到思路方法来定制它
当 服务被第次创建时候__init__server_bind思路方法来绑定监听(self.)到正确地址 (self.server_address)然后
server_activate来激活这个服务(默认情况下listen 思路方法)
这个服务不做任何事情直到了handle_request或serve_forever思路方法 handle_requestget_request去等待
和接收个新连接然后 very_request(request,client_address)去看服务是否会处理这个连接(你可以在访问控制
中使用这个默认情况下 very_request总是返回true)如果会处理这个请求handle_request然后
process_request(request,client_address)如果 process_request(request,client_address)导致个异常话将
handle_error(request,client_address)默认情况下process_request简单地
finish_request(request,client_address);子进程和线程类覆盖了这个行为去开始新进程或线程然后
finish_requestfinish_request例子化个新请求处理器请求处理器轮流它们handle思路方法
当SocketServer创建个新请求处理器时它传递给这个处理器__init__self变量以便于这个处理器能够访问有关这
个服务信息
SocketServer fileno思路方法返回监听文件描述符address_family成员变量指定了监听族(如
AF_INET)server_address包含了监听被绑定到地址变量包含监听自身
2、请求处理器
请 求处理器有up、handle和finish思路方法你可以覆盖它们来定制你自己行为般情况下你只需要覆盖handle思
路方法 BaseRequestHandler__init__up思路方法来做化工作handle服务于请求finish用 于执行清理工作如果
handle或up导致个异常finish不会被记住你请求处理器会为每个请求创建个新例子
request 成员变量有有关流(TCP)服务最近接受;对于数据报服务它是个包含进入消息和监听元组
client_address包含发送者地址server有对SocketServer个引用(通过这你可以访问它成员如 server_address)
下面例子实现了个EchoRequestHandler这作为个服务端它将客户端所发送数据再发送回客户端:
>>> import SocketServer
>>> EchoRequestHandler(SocketServer.BaseRequestHandler):
... def handle(self):
... pr 'Got connection!'
... while 1:
... mesg=self.request.recv(1024)
... not msg:
...
... pr 'Received:',msg
... self.request.send(msg)
... pr 'Done with connection'
>>> server=SocketServer.ThreadingTCPServer(('127.0.0.1',12321),EchoReuestHandler)
>>> server.handle_request #执行后将等待连接
Got connection!
Received: Hello!
Received: I like Tuesdays!
Done with connection
打开另个Python解释器作为客户端然后执行如下代码:
>>> from import *
>>> s=(AF_INET,SOCK_STREAM)
>>> s.connect(('120.0.0.1',12321))
>>> s.send('Hello!')
6
>>> pr s.recv(1024)
Hello!
>>> s.send('I like Tuesdays!')
16
>>> pr s.recv(1024)
I like Tuesdays!
>>> s.close
SocketServer 模块也定义了BaseRequestHandler两个子类:StreamRequestHandler和
DatagramRequestHandler它们覆盖了up和finish思路方法并创建了两个文件对象rfile和wfile你可以用这两个
文 件对象来向客户端读写数据从而代替使用思路方法
阻塞或同步编程
、使用
网 络编程中最基本部分就是(套接字)有两种:服务端和客户端 在你创建了个服务端的 后你告诉它去等待连接然
后它将监听某个网络地址(形如:xxx.xxx.xxx.xxx:xxx) 直到客户端连接然后这两端就可以通信了
处理客户端通常比处理服务端要容易点服务端必须时刻准备处理来自客户端连接并且它必须处理多个连接而客
户端只需要简单连接然后做点什么然后断开连接
实 例化个时可以指定 3个参数:地址系列(默认为.AF_INET)、流(这是个默认 值: .SOCK_STREAM)或数据报
(.SOCK_DGRAM)、协议(默认值是0)对于简单 你可以不指定任何参数而全部使用默认值
服务端在使用bind思路方法的后listen思路方法去监听个给定 地址然后客户端就可以通过使用connect思路方
法(connect思路方法所使用地址参数和bind相同)去连接服务端listen思路方法 要求个参数这个参数就是等待连
接队列中所能包含连接数
旦服务端了listen思路方法就进入了临听状态然后通 常使用个无限循环:1、开始接受客房端连接这通过
accept思路方法来实现了这个思路方法后将处于阻塞状态(等待客户端发起连接)直到个客 户端连接连接后
accept返回形如(client,address)个元组其中client是个用于和客户端通信 address是客户端形如
xxx.xxx.xxx.xxx:xxx地址;2、然后服务端处理客户端请求;3、处理完成的后又 1
有关传输数据有两个思路方法:send和recvsend使用串参数发送数据;recv参数是字节数表示次接受数据量如
果你不确定次该接受数据量话最好使用1024
下面给出个最小服务器/客户机例子:
服务端:
import
s = .
host = .gethostname
port = 1234
s.bind((host, port))
s.listen(5)
while True:
c, addr = s.accept
pr 'Got connection from', addr
c.send('Thank you for connecting')
c.close
客户端:
import
s = .
host = .gethostname
port = 1234
s.connect((host, port))
pr s.recv(1024)
注意:如果你使用Ctrl-C来停止服务端话如果再次使用相同端口可能需要等待会儿
2、使用SocketServer
SocketServer模块简单化了编写网络服务器工作
它提供了 4个基本服务类:TCPServer(使用TCP协议)、UDPServer(使用数据报)、UnixStreamServer、
UnixDatagramServerUnixStreamServer和UnixDatagramServer用于类Unix平台
这 4个类处理请求都使用同步思路方法也就是说在下个请求处理开始的前当前请求处理必须已完成
用SocketServer创建个服务器需要 4步:
1、通过子类化BaseRequestHandler类和覆盖它handle思路方法来创建个请求处理器类用于处理进来
请求;
2、例子化服务类如TCPServer并传递给它参数:服务器地址和请求处理器类;
3、服务例子对象handle_request或serve_forever思路方法去处理请求
下面使用SocketServer用同步思路方法写个最简单服务器:
from SocketServer import TCPServer, StreamRequestHandler
#第步其中StreamRequestHandler类是BaseRequestHandler类子类它为流定义了
#rfile和wfile思路方法
Handler(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername
pr 'Got connection from', addr
self.wfile.write('Thank you for connecting')
#第 2步其中''代表运行服务器主机
server = TCPServer(('', 1234), Handler)
#第 3步serve_forever导致进入循环状态
server.serve_forever
注意:使用阻塞或同步思路方法次只能连接个客户端处理完成后才能连接下个客户端
非阻塞或异步编程

================================

例如,对于一个聊天室来说,因为有多个连接需要同时被处理,所以很显然,阻塞或同步的方法是不合适的,这就像买票只开了一个窗口,佷多人排队等一样。那么我们如何解决这个问题呢?主要有三种方法:forking、threading、异步I/O。

Forking和threading的方法非常简单,通过使用SocketServer服务类的min-in类就可以实现。forking只适用于类Unix平台;threading需要注意内存共享的问题。
异步I/O如果底层的方法来实现是有点困难的。要简单点,我们可以考虑使用标准库中的框架或Twisted(Twisted是一个非常强大的异步网络编程的框架)。

一、用ScoketServer实现Forking和threading

下面我们使用两个例子来分别创建forking服务器和threading服务器。

Forking 服务器

from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler

class Server(ForkingMixIn, TCPServer): pass

class Handler(StreamRequestHandler):
    def handle(self):
        addr = self.request.getpeername()
        print 'Got connection from', addr
        self.wfile.write('Thank you for connecting')

server = Server(('', 1234), Handler)
server.serve_forever()


threading服务器

from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler

class Server(ThreadingMixIn, TCPServer): pass

class Handler(StreamRequestHandler):
    def handle(self):
        addr = self.request.getpeername()
        print 'Got connection from', addr
        self.wfile.write('Thank you for connecting')

server = Server(('', 1234), Handler)
server.serve_forever()


二、使用select实现异步I/O

所谓异步I/O,打个比方,就是如果一大群人都想你听他说话,那么你就给他们每人一分钟的时间说,大家轮流说,没说完的待会儿轮到时再继续说。也就是一个时间片的方法。

要实现异步I/O,我们可以通过使用框架asyncore/asynchat或Twisted,它们都是基于select函数或poll函数(poll只适于类Unix系统)的。select和poll函数都来自select模块。

select 函数要求三个必须序列作为参数和一个可选的以秒为单位的超时值。序列中是表示文件描述符的整数值,它们是我们要等待的连接。这三个序列是关于输入、输出和 异常条件的。如果超时值没有给出的话,select将处于阻塞状态(也就是等待)直到有文件描述符准备动作。如果超时值给出了,那么select只阻塞给 定的时间。如果超时值是0的话,那么将不阻塞。select返回的值是一个由三个序列组成的元组,它们分别代表相应参数的活动的子集。例如,第一个序列返 回的是用于读的输入文件描述符构成的序列。

序列可以包含文件对象(不适于Windows)或socket。下面这个例子创建一个使用 select去服务几个连接的服务器(注意:服务端的socket自身也提供给了select,以便于它能够在有新的连接准备接受时发出信号通知)。这个 服务器只是简单地打印接受自客户端的数据。你可以使用telnet(或写一个基于socket的简单的客户端)来连接测试它。

select server

import socket, select

s = socket.socket()
host = socket.gethostname()
port = 1234
s.bind((host, port))

s.listen(5)
inputs = [s]
while True:
    rs, ws, es = select.select(inputs, [], [])
    for r in rs:
        if r is s:
            c, addr = s.accept()
            print 'Got connection from', addr
            inputs.append(c)
        else:
            try:
                data = r.recv(1024)
                disconnected = not data
            except socket.error:
                disconnected = True

            if disconnected:
                print r.getpeername(), 'disconnected'
                inputs.remove(r)
            else:
                print data





三、Twisted

Twisted 是针对Python的一个事件驱动的网络框架,最初是为了网络游戏而开发的,但是现在被应用于各类网络软件。用Twisted,你可以实现事件处理器,非 常类似用GUI工具包(Tk, GTK, Qt, wxWidgets)。这部分我将介绍一些基本的概念和演示如何使用Twisted来做一些相对简单的 网络编程。Twisted是非常强大的框架并提供了大量的支持,如:Web服务器和客户端、 SSH2, SMTP, POP3, IMAP4, AIM, ICQ, IRC, MSN,Jabber, NNTP, DNS等等。

早先我们所写的基于socket的服务器,它们都有一个显示的事件循环:寻找新的连接和新的数据;基于SocketServer的服务器有一个隐含的循环:寻找连接和为连接创建处理器。但时处理器仍然时显示的读数据。

而 Twisted使用了更多的基于事件的方式。要写一个基本的服务器,你要实现事件处理器,它处理诸如一个新的客户端连接、新的数据到达和客户端连接中断等 情况。在Twisted中,你的事件处理器定义在一个protocol中;你也需要一个factory,当一个新的连接到达时它能够构造这个 protocol对象,但是如果你仅仅想创建一个自定义的Protocol类的实例的话,你可以使用来自Twisted的factory,Factory 类在模块twisted.internet.protocol中。当你写你的protocol时,使用 twisted.internet.protocol模块中的Protocol作为你的父类。当你得到一个连接时,事件处理器 connectionMade被调用;当你丢失了一个连接时,connectionLost被调用。从客户端接受数据使用处理器 dataReceived。但是你不能使用事件处理策略向客户端发送数据;要向客户端发送数据,你可以使用self.transport,它有一个 write方法。它也有一个client属性,其中包含了客户端的地址(主机名和端口)。

下面这个例子是一个Twisted版的服务器。 其中实例化了Factory并设置了它的protocol属性以便它知道使用哪个protocol与客户端通信(这就是所谓的你的自定义 protocol)。然后你使用factory开始监听指定的端口,factory通过实例化的protocol对象处理连接。监听使用reactor模 块中的listenTCP函数。最后,你通过调用reactor模块中的run函数来开始服务器。

from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory

# 定义你Protocol类
class SimpleLogger(Protocol):

    def connectionMade(self):
        print 'Got connection from', self.transport.client
    def connectionLost(self, reason):
        print self.transport.client, 'disconnected'
    def dataReceived(self, data):
        print data


# 实例化Factory

factory = Factory()

# 设置factory的protocol属性以便它知道使用哪个protocol与客户端通信(这就是所谓的你的自定义
# protocol)

factory.protocol = SimpleLogger

# 监听指定的端口

reactor.listenTCP(1234, factory)

# 开始运行主程序
reactor.run()


为 你的处理目的而写一个自定义的protocol是很容易的。模块twisted.protocols.basic中包含了几个有用的已存在的 protocol,其中的LineReceiver执行dataReceived并在接受到了一个完整的行时调用事件处理器lineReceived。如 果当你在接受数据时除了使用lineReceived,还要做些别的,那么你可以使用LineReceiver定义的名为rawDataReceived 事件处理器。下面是一使用LineReceiver的服务器例子:

from twisted.internet import reactor
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver

class SimpleLogger(LineReceiver):

    def connectionMade(self):
        print 'Got connection from', self.transport.client
    def connectionLost(self, reason):
        print self.transport.client, 'disconnected'
    def lineReceived(self, line):
        print line

factory = Factory()
factory.protocol = SimpleLogger
reactor.listenTCP(1234, factory)
reactor.run()

原创粉丝点击