首先这本书还时非常好的,书中的示例代码都在更新,我上的代码都是他官方github上面下载的最新代码。
第一章 客户端/服务器网络编程简介
获取经纬度,首先是通过三方包实现,很方便。
from geopy.geocoders import Nominatim
if __name__ == '__main__':
address = '207 N. Defiance St, Archbold, OH'
user_agent = 'Foundations of Python Network Programming example search1.py'
# user_agent = 'sidian'
location = Nominatim(user_agent=user_agent).geocode(address)
print(location.latitude, location.longitude)
输出
41.5427511 -84.3067015
1.2应用层
书中通过调用requests的三方包实现。
import requests
def geocode(address):
base = 'https://nominatim.openstreetmap.org/search'
parameters = {'q': address, 'format': 'json'}
user_agent = 'Foundations of Python Network Programming example search2.py'
headers = {'User-Agent': user_agent}
# 携带了参数与头部信息
response = requests.get(base, params=parameters, headers=headers)
reply = response.json()
print(reply[0]['lat'], reply[0]['lon'])
if __name__ == '__main__':
geocode('207 N. Defiance St, Archbold, OH')
输出与上面一样
1.3协议的使用。
书中主要介绍了http协议在应用层的使用很宽广。
示例代码
import http.client
import json
from urllib.parse import quote_plus
base = '/search'
def geocode(address):
# 这里传入的headers与返回的response都是二进制文件。
path = '{}?q={}&format=json'.format(base, quote_plus(address))
user_agent = b'Foundations of Python Network Programming example search3.py'
headers = {b'User-Agent': user_agent}
# 这个定义了一个http协议的对象,参数只需要域名就可以了,因为协议已经写在方法里面了
connection = http.client.HTTPSConnection('nominatim.openstreetmap.org')
connection.request('GET', path, None, headers)
# 读取返回二进制数据
rawreply = connection.getresponse().read()
reply = json.loads(rawreply.decode('utf-8'))
print(reply[0]['lat'], reply[0]['lon'])
if __name__ == '__main__':
geocode('207 N. Defiance St, Archbold, OH')
可能是网络原因,没跑通。
1.4 一个原始的网络会话
书中已经用了一个简单的示例完成一个socket发送请求的情况。
#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter01/search4.py
# (The Google API originally used in this example now requires API keys,
# so here's an alternative that calls openstreetmap.org.)
import socket
import ssl
from urllib.parse import quote_plus
#请求头所有信息
request_text = """
GET /search?q={}&format=json HTTP/1.1
Host: nominatim.openstreetmap.org
User-Agent: Foundations of Python Network Programming example search4.py
Connection: close
"""
def geocode(address):
# 打开一个socket
unencrypted_sock = socket.socket()
# 连接socket,建立与服务器的三次握手
unencrypted_sock.connect(('nominatim.openstreetmap.org', 443))
# 建立ssl通道,进行通道加密
sock = ssl.wrap_socket(unencrypted_sock)
'''
也可以直接加密通道
sock = ssl.wrap_socket(unencrypted_sock)
加密通道与服务器进行连接,认证。
sock.connect(('nominatim.openstreetmap.org', 443))
'''
# 格式化请求头信息
request = request_text.format(quote_plus(address))
# 编码成字节码发送
sock.sendall(request.encode('ascii'))
# 定义空字节码
raw_reply = b''
while True:
# 接收返回的信息,设定块大小
more = sock.recv(4096)
if not more:
break
raw_reply += more
# 所有头信息,body都会返回
print(raw_reply.decode('utf-8'))
if __name__ == '__main__':
geocode('207 N. Defiance St, Archbold, OH')
输出
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 03 Dec 2020 08:27:37 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: OPTIONS,GET
1a7
[{"place_id":174011511,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright","osm_type":"way","osm_id":379245933,"boundingbox":["41.5427373","41.5433134","-84.3067095","-84.3067012"],"lat":"41.5427511","lon":"-84.3067015","display_name":"North Defiance Street, Burlington, Archbold, Fulton County, Ohio, 43502, United States of America","class":"highway","type":"secondary","importance":0.61}]
0
上面显式了一个标准的response的输出情况。
1.5层层深入
略
1.6编码与解码
if __name__ == '__main__':
# Translating from the outside world of bytes to Unicode characters.
input_bytes = b'xffxfe4x001x003x00 x00ix00sx00 x00ix00nx00.x00'
# 通过解码输出文字,这个作者真的用心良苦,用utf-16解码,让字节码与原丝字符串完全不一样
input_characters = input_bytes.decode('utf-16')
# 通过repr输出更加厉害
print(repr(input_characters))
# Translating characters back into bytes before sending them.
output_characters = 'We copy you down, Eagle.
'
# 编码成字节码
output_bytes = output_characters.encode('utf-8')
# 写入文件二进制格式,打开的时候就可以直接显示字符串,因为操作系统用软件打开文件,会自动帮你解码
with open('eagle.txt', 'wb') as f:
f.write(output_bytes)
上面是示例代码,跟书中的完全不一样,写的真心的不错。
实例代码中通过直接向二进制文件写入字节码,后续也能通过打开,更好的解释了文件内保存的都是二进制,我们看到的只不过是已经解码以后的字符串信息。
1.7 网际协议
网际协议(IP)是为全世界通过互联网连接的计算机赋予统一地址系统的一种机制,它使得数据包能够从互联网的一端发送至另一端。
也就使所谓的IP协议,但一般我们很少去接触IP协议。我们接触最多到传输层。
1.8 IP地址
示例代码
import socket
if __name__ == '__main__':
hostname = 'baidu.com'
# 获取域名的地址
addr = socket.gethostbyname(hostname)
print('The IP address of {} is {}'.format(hostname, addr))
通过socket.gethostbyname的方法来获得域名对应的IP地址。
现在的IP4用的4个字节,后面切换IP6的话,就是16字节。
一些特殊的IP地址段
127.*.*.*:以127开头的IP地址是特殊的预留地址段,这一地址段由机器上运行的本地应用程序使用
127.0.0.1 == localhost 对应就是本机。
10.*.*.*
172.16-31.*.*
193.168.*.*
上面这三个IP地址段为私有网络(Private subnet)预留的。构建内网的时候,可以拿来使用。
1.9路由
路由一般也不用我们操心,自己操作系统或者路由器会根据路由表对数据进行发送。
是否是同一个子网可以通过ip与掩码来提现
192.168.1.9/24,就是前面24位相同就可以了,后面8位有256种可能,一个给网关,一个.0表示子网名,255记得应该是广播地址。
1.10数据包分组
学的也模模糊糊,就知道了以太网只支持1500B的数据包,一般UDP不设置DF(Don't Fragment)标记,tcp设置。
很多概念性的东西,我学的还是比较累的。