第14章 网络编程

14.1 几个网络模块

1. 模块socket

套接字(socket)分为客户端套接字和服务器套接字;套接字是模块socket中socket类的实例。

实例化套接字:

socket.socket([family[, type[, proto]]])

参数

  • family: 地址族可以使 AF_UNIX (本地通信)或者 AF_INET(默认,TCP/IPv4)。
  • type: 套接字类型可以根据是面向连接的还是非连接分为 SOCK_STREAM(默认,流套接字) 或 SOCK_DGRAM(数据报套接字)
  • protocol: 协议,一般不填默认为 0。
服务器端套接字
s.bind()绑定地址(host,port)到套接字, 在 AF_INET下,以元组(host,port)的形式表示地址。
s.listen()开始 TCP 监听。backlog 指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为 1。
s.accept()被动接受TCP客户端连接,(阻塞式)等待连接的到来
客户端套接字
s.connect()主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
公共用途的套接字函数
s.recv()接收 TCP 数据,数据以字符串形式返回,bufsize 指定要接收的最大数据量。flag 提供有关消息的其他信息,通常可以忽略。
s.send()发送 TCP 数据,将 string 中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于 string 的字节大小。
#最简单的服务器,
import socket

s = socket.socket()

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

s.listen(5)
while True:
    c, addr = s.accept()
    print('Got connection from', addr)
    c.send('Thank you for connecting')
    c.close()
#最简单的客户端
import socket

s = socket.socket()

host = socket.gethostname()
port = 1234

s.connect((host, port))
print(s.recv(1024))

2. 模块urllib和urllib2

#打开远程文件
#urlopen返回类似于文件的对象webpage,其支持方法close、read、readline、readlines等,且支持迭代
from urllib.request import urlopen
webpage = urlopen('http://www.python.org')

#获取远程文件,将其副本存储在一个本地文件中
#urlretrieve返回一个元组(filename, headers),其中filename为本地文件的名称有urllib指定,
#或通过urlretrieve的第2个参数自行指定
urlretrieve('http://www.python.org', 'C:\\python_webpage.html')
#如果没有自行指定文件名,文件将被放在某个临时位置,可使用函数open来打开,
#使用完毕后,可选择使用urlcleanup来清理临时文件。

14.2 SocketServer及相关的类

模块SocketServer是标准库提供的服务器框架的基石,包含4个基本的服务器: TCPServer(支持TCP套接字流)、UDPServer(支持UDP数据报套接字)以及更难懂的UnixStreamServer和UnixDatagramServer。

使用模块SocketServer编写服务器时,大部分代码都位于请求处理器中。每当服务器收到客户端的连接请求时,都将实例化一个请求处理程序,并对其调用各种处理方法来处理请求。具体调用哪些方法取决于使用的服务器类和请求处理程序类;还可从这些请求处理器类派生出子类,从而让服务器调用一组自定义的处理方法。基本请求处理程序类BaseRequestHandler将所有操作都放在一个方法中——服务器调用的方法handle。这个方法可通过属性self.request来访问客户端套接字。如果处理的是流(使用TCPServer时很可能如此),可使用StreamRequestHandler类,它包含另外两个属性: self.rfile(用于读取)和self.wfile(用于写入)。

#基于SocketServer的极简服务器
from socketserver import TCPServer, StreamRequestHandler

class Handler(StreamRequestHandler):

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

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

14.3 多个连接

处理多个连接的主要方式有3种:分叉(forking)、线程化和异步I/O。

使用框架SocketServer创建分叉或线程化服务器。

#分叉服务器
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()
#线程化服务器
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和poll实现异步I/O

select和poll两个函数都位于模块select中,其中poll的可伸缩性更高,但只有UNIX系统支持它( Windows不支持)。

select:接受三个必选参数和一个可选参数(超时时间,单位为秒)。前3个参数均为文件描述符序列,3个序列中的元素分别为等待输入、输出和发生异常的连接的文件描述符。如果没有指定超时时间, select将阻断(即等待)到有文件描述符准备就绪;如果指定了超时时间, select将最多阻断指定的秒数;如果超时时间为零, select将不断轮询(即不阻断)。 select返回三个序列(即一个长度为3的元组),其中每个序列都包含相应参数中处于活动状态的文件描述符。例如,返回的第一个序列包含有数据需要读取的所有输入文件描述符。

#使用select的简单服务器
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

方法poll使用起来比select容易。调用poll时,将返回一个轮询对象。你可使用方法register向这个对象注册文件描述符(或包含方法fileno的对象)。注册后可使用方法unregister将它们删除。注册对象(如套接字)后,可调用其方法poll(它接受一个可选的超时时间参数)。这将返回一个包含(fd, event)元组的列表(可能为空),其中fd为文件描述符,而event是发生的事件。event是一个位掩码,这意味着它是一个整数,其各个位对应于不同的事件。各种事件是用select模块中的常量表示的,如下表所示。要检查指定的位是否为1(即是否发生了相应的事件),可下面这样使用按位与运算符( &):
if event & select.POLLIN:...

select模块中的轮询事件常量
事件名描述
POLLIN文件描述符中有需要读取的数据
POLLPRI文件描述符中有需要读取的紧急数据
POLLOUT文件描述符为写入数据做好了准备
POLLERR文件描述符出现了错误状态
POLLHUP挂起。连接已断开。
POLLNVAL无效请求。连接未打开
#使用poll的简单服务器
import socket, select

s = socket.socket()

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

fdmap = {s.fileno(): s}


s.listen(5)
p = select.poll()
p.register(s)
while True:
    events = p.poll()
    for fd, event in events:
        if fd in fdmap:
            c, addr = s.accept()
            print 'Got connection from', addr
            p.register(c)
            fdmap[c.fileno()] = c
        elif event & select.POLLIN:
            data = fdmap[fd].recv(1024)
            if not data: # No data -- connection closed
                print fdmap[fd].getpeername(), 'disconnected'
                p.unregister(fd)
                del fdmap[fd]
            else:
                print data

14.4 Twisted

Twisted是一个事件驱动的Python网络框架。在Twisted中,能实现事件处理程序,就像在GUI工具包中一样。实际上, Twisted与多个常用的GUI工具包( Tk、 GTK、 Qt和wxWidgets)配合得天衣无缝。

#使用Twisted创建的简单服务器
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory

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.protocol = SimpleLogger

reactor.listenTCP(1234, factory)
reactor.run()
#使用协议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()


 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MallocLu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值