1. 首先明白下协议栈和库的概念:
- 协议栈(Protocol Stack): 是指网络中各层协议的总和,其形象的反映了一个网络中文件传输的过程:由上层协议到底层协议,再由底层协议到上层协议。
- 库(Library):主要用来解析要使用的网络通信协议,包含Python内置标准库和第三方库。
网络编程实际上就是选择并使用一个已经支持所需网络操作的库的过程。
2. 下面就以一个例子来说明客户端和服务器端三种方式的通信过程:
2.1 题目内容
获取杭州市天气预报,分别用第三方库requests、基于http协议的标准库http.client、基于传输层上socket三种方法实现。
2.2 开发过程
-
系统结构:
C/S架构,即实现服务端软件与客户端软件基于网络通信
- 通信过程:
一次HTTP通信:
浏览器从URL中解析出服务器的主机名
浏览器将服务器的主机名转换成服务器的IP地址
浏览器将端口号从URL中解析出来
浏览器建立一条与Web服务器的TCP连接
浏览器向服务器发送一条HTTP请求报文
服务器向浏览器回送一条HTTP响应报文
关闭连接,浏览器显示文档
socket通信过程:
1) S: 创建socket(根据地址类型、socket类型)
2) S:为socket绑定对应的IP地址和端口号
3) S:监听端口号请求,接受用户发来的连接请求(此时socket还没打开)
4) C:创建socket
5) C: 打开socket,并通过IP地址+端口号试图connect服务器的socket
6) S: 接收到了用户发来的socket连接请求,被动打开socket,开始接收客户端请求,直到用户返回连接信息。这时候服务器的socket进入堵塞状态,即accept();一直到客户端返回连接信息后才返回,然后开始接收下一个用户端请求。
7) C:连接成功,开始向服务器输入状态信息
8) S:accept()返回,连接成功
9) C:写入信息
10) S:读取信息
11) C:关闭
12) S:关闭
-
网络协议:
(a) HTTP是个应用层协议
(b) socket是一个针对TCP/UDP编程的接口,socket是对TCP/IP协议的封装,但是socket本身并不是协议,而好似一个调用接口(API),通过socket我们才能使用TCP/IP协议。
2.3 系统测试
2.4 源码分析
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests #用于获取下载网站,即json网站页面
import sys
import json #用于将页面的json解析成Python能识别的字典表示
#输入地点
weatherAdress = input("请输入查询地:")
if weatherAdress == 'E' or weatherAdress == 'e':
sys.exit(0); #关闭程序
#下载天气JSON,
weatherJSon_url = 'http://wthrcdn.etouch.cn/weather_mini?city=%s'%(weatherAdress) #将链接定义为一个字符串
response = requests.get(weatherJSon_url) #获取并下载页面,内容会保存在respons.text成员变量里
try:
response.raise_for_status() #若请求失败则抛出异常,正常无操作
except:
print('error')
#将json文件格式导入成python的格式
weatherData = json.loads(response.text)
#漂亮打印出天气字典
#import pprint
#pprint.pprint(weatherData)
w = weatherData["data"]
print('现在是:%s 您在:%s'%(w['forecast'][0]['date'],w['city']))
print('温度:%s %s'%(w['wendu'],w['ganmao']))
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
import http.client #用于提供支持HTTP协议的客户端编程工具
import json
from urllib.parse import quote_plus #url编码工具
base = '/api/weather/city/101210101'
def weaforecast(address):
path = '{}?address={}&sensor=false'.format(base, quote_plus(address))
#print(path)
#HTTPConnection实例(是一个类)表示与HTTP服务器的连接
#实例化HTTPConnection,应传递一个主机和可选的端口号
connection = http.client.HTTPConnection('t.weather.sojson.com',80)
#使用指定的方法和url链接向服务器发送请求
connection.request('GET', path)
#调用服务器返回的内容
rawreply = connection.getresponse().read()
#将json字符串转为Python对象
w = json.loads(rawreply.decode('utf-8'))
print('现在是:%s 您在:%s'%(w['time'],w['cityInfo']['city']))
#import pprint
#pprint.pprint(rawreply)
print('湿度:%s 温度:%s 空气质量%s,%s'%(w['data']['shidu'],w['data']['wendu'],w['data']['quality'],w['data']['ganmao']))
if __name__=='__main__':
address = '杭州'
weaforecast(address)
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
import socket
from urllib.parse import quote_plus
#一个HTTP请求报文由请求行、请求头部、空行、请求数据4部分组成
#请求行由请求方法字段(GET\POST...)、url字段和HTTP协议版本3个字段组成,以\n分离
request_text = """\
GET /api/weather/city/101210101?address={}&sensor=false HTTP/1.1\r\n\
Host: t.weather.sojson.com:80\r\n\ #
User-Agent: wea4\r\n\
Connection: close\r\n\
\r\n\
"""
def weaforecast(address):
sock = socket.socket() #创建socket实例
sock.connect(('t.weather.sojson.com', 80)) #客户端建立连接
request = request_text.format(quote_plus(address)) #字符串Python格式化
sock.sendall(request.encode('ascii')) #客户端发送消息
raw_reply = b''
while True:
more = sock.recv(4096) #返回当前任意长度的可读数据,期望长度4096
if not more:
break
raw_reply += more
print(raw_reply.decode('utf-8'))
if __name__=='__main__':
#address = input("请输入地址:")
address = '杭州'
weaforecast(address)
2.5 理解
前面例子使用的协议栈包含4层:(第一层我没做)
①. 谷歌地理编码API: Pygeocoder——对如何用URL表示地理信息查询和获取包含坐标信息的JSON数据进行封装。
②. URL:Requests——标识了可以通过HTTP获取的文档
③. HTTP层:使用原始TCP/IP套接字,http.client——支持面向文档的命令(GET)
④. TCP/IP套接字:socket——只处理字节串的发送和接收
3. 编码与解码
4. IP协议
-
下面的程序用来向运行程序的操作系统请求解析主机名 www.python.org 。即将主机名转为IP地址。
-
特殊的IP地址段:
①. 127...:这一地址段由机器上运行的本地运用程序使用。其实是与同一机器上的一些其他服务或程序交互。127.0.0.1:这一地址被广泛使用,表示“运行该程序的机器本身”,通常通过主机名localhost访问。
②. 10...、172.16-31..、192.168..:这些IP地址段为私有子网内使用。没有意义,并不对应可连接的任一主机,这样可以把所有的网络流量隐藏。