标签:14 self 编程 网络 print 服务器 接字 select socket
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:...
事件名 | 描述 |
---|---|
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()
标签:14,self,编程,网络,print,服务器,接字,select,socket 来源: https://blog.csdn.net/qq_42283621/article/details/112108867
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。