12. 网络编程的奇淫巧技

  1. 简单的网络交互

通过HTTP协议以客户端的方式访问多种服务,通常使用 urllib.request 模块就够了

# Base URL being accessed

url = ‘http://httpbin.org/get’

# Dictionary of query parameters (if any)

parms = {

‘name1’ : ‘value1’,

‘name2’ : ‘value2′

}

# Encode the query string

querystring = parse.urlencode(parms)

# Make a GET request and read the response

u = request.urlopen(url+’?’ + querystring)

resp = u.read()

如果需要使用POST方法

from urllib import request, parse

# Base URL being accessed

url = ‘http://httpbin.org/post’

# Dictionary of query parameters (if any)

parms = {

‘name1’ : ‘value1’,

‘name2’ : ‘value2’

}

# Encode the query string

querystring = parse.urlencode(parms)

# Make a POST request and read the response

u = request.urlopen(url, querystring.encode(‘ascii’))

resp = u.read()

如果需要提供一些自定义的HTTP头

from urllib import request, parse

# Extra headers

headers = {

‘User-agent’ : ‘none/ofyourbusiness’,

‘Spam’ : ‘Eggs’

}

req = request.Request(url, querystring.encode(‘ascii’), headers=headers)

# Make a request and read the response

u = request.urlopen(req)

resp = u.read()

当然如果希望使用更为复杂的操作,可以使用requests 库

import requests

# Base URL being accessed

url = ‘http://httpbin.org/post’

# Dictionary of query parameters (if any)

parms = {

‘name1’ : ‘value1’,

‘name2’ : ‘value2’

}

# Extra headers

headers = {

‘User-agent’ : ‘none/ofyourbusiness’,

‘Spam’ : ‘Eggs’

}

resp = requests.post(url, data=parms, headers=headers)

# Decoded text returned by the request

text = resp.text

其中resp.text 带给我们的是以Unicode解码的响应文本。但是,如果去访问 resp.content ,就会得到原始的二进制数据。另一方面,如果访问 resp.json ,那么就会得到JSON格式的响应内容。

比如requests 库发起一个HEAD请求

import requests

resp = requests.head(‘http://www.python.org/index.html’)

status = resp.status_code

last_modified = resp.headers[‘last-modified’]

content_type = resp.headers[‘content-type’]

content_length = resp.headers[‘content-length’]

基本认证登录

import requests

resp = requests.get(‘http://pypi.python.org/pypi?:action=login’,

auth=(‘user’,’password’))

用requests上传内容

import requests

url = ‘http://httpbin.org/post’

files = { ‘file’: (‘data.csv’, open(‘data.csv’, ‘rb’)) }

r = requests.post(url, files=files)

Request还支持了OAuth这些认证

http://docs.python-requests.org/

  1. 创建简单的服务器

这里看下简单的服务器,利用TCP协议和客户端进行通信

from socketserver import BaseRequestHandler, TCPServer

class EchoHandler(BaseRequestHandler):

def handle(self):

print(‘Got connection from’, self.client_address)

while True:

msg = self.request.recv(8192)

if not msg:

break

self.request.send(msg)

if __name__ == ‘__main__’:

serv = TCPServer((”, 20000), EchoHandler)

serv.serve_forever()

定义了一个特殊的处理类,实现了一个 handle() 方法

request 属性是客户端socket,client_address 有客户端地址

这种情况下,我们创建的服务器是单线程的,如果希望处理多个客户端

可以利用ForkingTCPServer 或者是 ThreadingTCPServer

或者使用线程池

if __name__ == ‘__main__’:

from threading import Thread

NWORKERS = 16

serv = TCPServer((”, 20000), EchoHandler)

for n in range(NWORKERS):

t = Thread(target=serv.serve_forever)

t.daemon = True

t.start()

serv.serve_forever()

  1. IP_ADDRESS

将CIDR直接转换为IP 列表,可以为

>>> net = ipaddress.ip_network(‘123.45.67.64/27’)

>>> net

IPv4Network(‘123.45.67.64/27’)

>>> for a in net:

… print(a)

123.45.67.64

其本质是ipaddress.ip_address的数组

>>> a = ipaddress.ip_address(‘123.45.67.69’)

>>> a in net

True

而这个在实际使用中,经常需要配合str()使用

  1. 简单的REST接口

这里我们构建一个简单的REST接口通过网络远程控制或访问你的应用程序

利用WSGI标准(PEP 3333)这样的一个很小的库

# resty.py

import cgi

def notfound_404(environ, start_response):

start_response(‘404 Not Found’, [ (‘Content-type’, ‘text/plain’) ])

return [b’Not Found’]

class PathDispatcher:

def __init__(self):

self.pathmap = { }

def __call__(self, environ, start_response):

path = environ[‘PATH_INFO’]

params = cgi.FieldStorage(environ[‘wsgi.input’],

environ=environ)

method = environ[‘REQUEST_METHOD’].lower()

environ[‘params’] = { key: params.getvalue(key) for key in params }

handler = self.pathmap.get((method,path), notfound_404)

return handler(environ, start_response)

def register(self, method, path, function):

self.pathmap[method.lower(), path] = function

return function

这里我们编写一些处理器

import time

_hello_resp = ”’\

<html>

<head>

<title>Hello {name}</title>

</head>

<body>

<h1>Hello {name}!</h1>

</body>

</html>”’

def hello_world(environ, start_response):

start_response(‘200 OK’, [ (‘Content-type’,’text/html’)])

params = environ[‘params’]

resp = _hello_resp.format(name=params.get(‘name’))

yield resp.encode(‘utf-8′)

_localtime_resp = ”’\

<?xml version=”1.0″?>

<time>

<year>{t.tm_year}</year>

<month>{t.tm_mon}</month>

<day>{t.tm_mday}</day>

<hour>{t.tm_hour}</hour>

<minute>{t.tm_min}</minute>

<second>{t.tm_sec}</second>

</time>”’

def localtime(environ, start_response):

start_response(‘200 OK’, [ (‘Content-type’, ‘application/xml’) ])

resp = _localtime_resp.format(t=time.localtime())

yield resp.encode(‘utf-8’)

if __name__ == ‘__main__’:

from resty import PathDispatcher

from wsgiref.simple_server import make_server

# Create the dispatcher and register functions

dispatcher = PathDispatcher()

dispatcher.register(‘GET’, ‘/hello’, hello_world)

dispatcher.register(‘GET’, ‘/localtime’, localtime)

# Launch a basic server

httpd = make_server(”, 8080, dispatcher)

print(‘Serving on port 8080…’)

httpd.serve_forever()

整体接口如下

import cgi

def wsgi_app(environ, start_response):

pass

environ 属性是一个字典,包含了从web服务器如Apache[参考Internet RFC 3875]提供的CGI接口中获取的值,environ[‘REQUEST_METHOD’] 代表请求类型如GET、POST、HEAD等。 environ[‘PATH_INFO’] 表示被请求资源的路径。 调用 cgi.FieldStorage() 可以从请求中提取查询参数并将它们放入一个类字典对象中以便后面使用。

start_response 参数是一个为了初始化一个请求对象而必须被调用的函数。 第一个参数是返回的HTTP状态值,第二个参数是一个(名,值)元组列表,用来构建返回的HTTP头。

然后返回字符串序列

def wsgi_app(environ, start_response):

pass

start_response(‘200 OK’, [(‘Content-type’, ‘text/plain’)])

resp = []

resp.append(b’Hello World\n’)

resp.append(b’Goodbye!\n’)

return resp

或者,你还可以使用 yield :

def wsgi_app(environ, start_response):

pass

start_response(‘200 OK’, [(‘Content-type’, ‘text/plain’)])

yield b’Hello World\n’

yield b’Goodbye!\n’

  1. 远程调用

远程调用函数可以使用XML-RPC

from xmlrpc.server import SimpleXMLRPCServer

class KeyValueServer:

_rpc_methods_ = [‘get’, ‘set’, ‘delete’, ‘exists’, ‘keys’]

def __init__(self, address):

self._data = {}

self._serv = SimpleXMLRPCServer(address, allow_none=True)

for name in self._rpc_methods_:

self._serv.register_function(getattr(self, name))

def get(self, name):

return self._data[name]

def set(self, name, value):

self._data[name] = value

def delete(self, name):

del self._data[name]

def exists(self, name):

return name in self._data

def keys(self):

return list(self._data)

def serve_forever(self):

self._serv.serve_forever()

# Example

if __name__ == ‘__main__’:

kvserv = KeyValueServer((”, 15000))

kvserv.serve_forever()

生成一个SimpleXMLRPCServer,然后利用register_function注册函数

之后就serve_forever()函数不断监听

对应的使用就好比

>>> s = ServerProxy(‘http://localhost:15000’, allow_none=True)

>>> s.set(‘foo’, ‘bar’)

>>> s.set(‘spam’, [1, 2, 3])

>>> s.keys()

[‘spam’, ‘foo’]

>>> s.get(‘foo’)

‘bar’

>>> s.get(‘spam’)

[1, 2, 3]

>>> s.delete(‘spam’)

>>> s.exists(‘spam’)

False

>>>

发表评论

邮箱地址不会被公开。 必填项已用*标注