网络编程基础
计算机网络功能主要包括实现资源共享,实现数据信息的快速传递。
OSI七层模型
制定组织: ISO(国际标准化组织)
作用:使网络通信工作流程标准化
应用层 : 提供用户服务,具体功能有应用程序实现
表示层 : 数据的压缩优化加密
会话层 : 建立用户级的连接,选择适当的传输服务
传输层 : 提供传输服务
网络层 : 路由选择,网络互联
链路层 : 进行数据交换,控制具体数据的发送
物理层 : 提供数据传输的硬件保证,网卡接口,传输介质
优点
- 建立了统一的工作流程
- 分部清晰,各司其职,每个步骤分工明确
- 降低了各个模块之间的耦合度,便于开发
四层模型(TCP/IP模型)
背景 : 实际工作中工程师无法完全按照七层模型要求操作,逐渐演化为更符合实际情况的四层
数据传输过程
- 发送端由应用程序发送消息,逐层添加首部信息,最终在物理层发送消息包。
- 发送的消息经过多个节点(交换机,路由器)传输,最终到达目标主机。
- 目标主机由物理层逐层解析首部消息包,最终到应用程序呈现消息。
网络协议
在网络数据传输中,都遵循的规定,包括建立什么样的数据结构,什么样的特殊标志等。
网络地址
- IP地址
功能:确定一台主机的网络路由位置
结构
IPv4 点分十进制表示 172.40.91.185 每部分取值范围0–255
IPv6 128位 扩大了地址范围
查看本机网络地址命令: ifconfig
测试和某个主机是否联通:ping [ip] - 端口号(port)
作用:端口是网络地址的一部分,用于区分主机上不同的网络应用程序。
特点:一个系统中的应用监听端口不能重复
取值范围: 1 – 65535
1–1023 系统应用或者大众程序监听端口
1024–65535 自用端口
传输层服务
面向连接的传输服务(基于TCP协议的数据传输)
- 传输特征 : 提供了可靠的数据传输,可靠性指数据传输过程中无丢失,无失序,无差错,无重
复。 - 实现手段 : 在通信前需要建立数据连接,通信结束要正常断开连接。
三次握手(建立连接)
- 客户端向服务器发送消息报文请求连接
- 服务器收到请求后,回复报文确定可以连接
- 客户端收到回复,发送最终报文连接建立
四次挥手(断开连接)- 主动方发送报文请求断开连接
- 被动方收到请求后,立即回复,表示准备断开
- 被动方准备就绪,再次发送报文表示可以断开
- 主动方收到确定,发送最终报文完成断开
适用情况 : 对数据传输准确性有明确要求,传数文件较大,需要确保可靠性的情况。比如:网页获取,文件下载,邮件收发。
面向无连接的传输服务(基于UDP协议的数据传输)
- 传输特点 : 不保证传输的可靠性,传输过程没有连接和断开,数据收发自由随意。
- 适用情况 : 网络较差,对传输可靠性要求不高。比如:网络视频,群聊,广播
socket套接字编程
套接字介绍
- 套接字 : 实现网络编程进行数据传输的一种技术手段
- Python实现套接字编程:import socket
- 套接字分类
流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案。(面向连接–tcp协议–可靠的–流式套接字)
数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案。(无连接–udp协议–不可靠–数据报套接字)
tcp套接字编程
服务端流程
- 创建套接字
sockfd=socket.socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0)
功能:创建套接字
参数: socket_family 网络地址类型 AF_INET表示ipv4
socket_type 套接字类型 SOCK_STREAM(流式) SOCK_DGRAM(数据报)
proto 通常为0 选择子协议
返回值: 套接字对象 - 绑定地址
本地地址 : ‘localhost’ , ‘127.0.0.1’
网络地址 : ‘172.40.91.185’
自动获取地址: ‘0.0.0.0’
sockfd.bind(addr)
功能: 绑定本机网络地址
参数: 二元元组 (ip,port)
(‘0.0.0.0’,8888) - 设置监听
sockfd.listen(n)
功能 : 将套接字设置为监听套接字,确定监听队列大小
参数 : 监听队列大小 - 等待处理客户端连接请求
connfd,addr = sockfd.accept()
功能: 阻塞等待处理客户端请求
返回值: connfd 客户端连接套接字
addr 连接的客户端地址 - 消息收发
data = connfd.recv(buffersize)
功能 : 接受客户端消息
参数 :每次最多接收消息的大小
返回值: 接收到的内容
n = connfd.send(data)
功能 : 发送消息
参数 :要发送的内容 bytes格式
返回值: 发送的字节数 - 关闭套接字
sockfd.close()
功能:关闭套接字
"""
tcp服务端流程:重点代码
"""
import socket
# 创建tcp套接字对象
sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址
sockfd.bind(('127.0.0.1', 9988))
# 设置监听套接字
sockfd.listen(3)
# 处理客户端连接
connfd, addr = sockfd.accept()
# 收发消息(网络传输数据用字节串)
data = connfd.recv(1024)
n = connfd.send(b'Thanks')
# 关闭套接字
connfd.close()
sockfd.close()
客户端流程
- 创建套接字
注意:只有相同类型的套接字才能进行通信 - 请求连接
sockfd.connect(server_addr)
功能:连接服务器
参数:元组 服务器地址 - 收发消息
注意: 防止两端都阻塞,recv send要配合
(服务端先收后发,服务端先发后收) - 关闭套接字
"""
tcp客户端流程
"""
from socket import *
# 服务器地址
server_addr = ('127.0.0.1', 9988)
# 创建tcp套接字
sockfd = socket() # 默认值就是tcp套接字
# 连接服务器
sockfd.connect(server_addr)
# 发送接收消息
data = input(">>")
sockfd.send(data.encode())
data = sockfd.recv(1024)
print("From server:", data.decode())# 打印收到的消息转化为字符转格式
# 关闭套接字
sockfd.close()
tcp 套接字数据传输特点
- tcp连接中当一端退出,另一端如果阻塞在recv,此时recv会立即返回一个空字串。
- tcp连接中如果一端已经不存在,仍然试图通过send发送则会产生BrokenPipeError
- 一个监听套接字可以同时连接多个客户端,也能够重复被连接
网络收发缓冲区
- 网络缓冲区有效的协调了消息的收发速度
- send和recv实际是向缓冲区发送接收消息,当缓冲区不为空recv就不会阻塞。
tcp粘包
原因:tcp以字节流方式传输,没有消息边界。多次发送的消息被一次接收,此时就会形成粘包。
影响:如果每次发送内容是一个独立的含义,需要接收端独立解析此时粘包会有影响。
处理方法
- 人为的添加消息边界
- 控制发送速度
UDP套接字编程
服务端流程
- 创建数据报套接字
sockfd = socket(AF_INET,SOCK_DGRAM) - 绑定地址
sockfd.bind(addr) - 消息收发
data,addr = sockfd.recvfrom(buffersize)
功能: 接收UDP消息
参数: 每次最多接收多少字节
返回值: data 接收到的内容
addr 消息发送方地址
n = sockfd.sendto(data,addr)
功能: 发送UDP消息
参数: data 发送的内容 bytes格式
addr 目标地址
返回值:发送的字节数 - 关闭套接字
sockfd.close()
"""
udp服务端
"""
from socket import *
# 创建udp套接字
sockfd = socket(AF_INET, SOCK_DGRAM)
# 绑定地址
server_addr = (("127.0.0.1", 9988))
sockfd.bind(server_addr)
# 收发数据
data, addr = sockfd.recvfrom(1024)
sockfd.sendto(b'Thanks', addr)
# 关闭套接字
sockfd.close()
客户端流程
- 创建套接字
- 收发消息
- 关闭套接字
"""
udp客户端
"""
from socket import *
# 创建udp套接字
sockfd = socket(AF_INET, SOCK_DGRAM)
# 服务端地址
server_addr = ('127.0.0.1', 9988)
# 发收消息
deta = input(">>")
sockfd.sendto(deta.encode(), server_addr)
data, addr = sockfd.recvfrom(1024)
# 关闭套接字
sockfd.close()
总结 :tcp套接字和udp套接字编程区别
- 流式套接字是以字节流方式传输数据,数据报套接字以数据报形式传输
- tcp套接字会有粘包,udp套接字有消息边界不会粘包
- tcp套接字保证消息的完整性,udp套接字则不能
- tcp套接字依赖listen accept建立连接才能收发消息,udp套接字则不需要
- tcp套接字使用send,recv收发消息,udp套接字使用sendto,recvfrom
socket套接字常用属性
【1】 sockfd.type 套接字类型
【2】 sockfd.family 套接字地址类型
【3】 sockfd.getsockname() 获取套接字绑定地址
【4】 sockfd.fileno() 获取套接字的文件描述符
【5】 sockfd.getpeername() 获取连接套接字客户端地址
【6】 sockfd.setsockopt(level,option,value)
功能:设置套接字选项
参数: level 选项类别 SOL_SOCKET
option 具体选项内容
value 选项值
struct模块进行数据打包
- 原理: 将一组简单数据进行打包,转换为bytes格式发送。或者将一组bytes格式数据,进行解
析。 - 接口使用
Struct(fmt)
功能: 生成结构化对象
参数:fmt 定制的数据结构
st.pack(v1,v2,v3…)
功能: 将一组数据按照指定格式打包转换为bytes
参数:要打包的数据
返回值: bytes字节串
st.unpack(bytes_data)
功能: 将bytes字节串按照指定的格式解析
参数: 要解析的字节串
返回值: 解析后的内容
struct.pack(fmt,v1,v2,v3…)
struct.unpack(fmt,bytes_data)
说明: 可以使用struct模块直接调用pack unpack。此时这两函数第一个参数传入fmt。其他用法
功能相同
import struct
st = struct.Struct("i10sfi")
data = st.pack(1, b"s", 0.1, 1)
data = st.unpack(data)
HTTP传输
HTTP协议 (超文本传输协议)
- 用途 : 网页获取,数据的传输
- 特点
应用层协议,传输层使用tcp传输
简单,灵活,很多语言都有HTTP专门接口
无状态,协议不记录传输内容
http1.1 支持持久连接,丰富了请求类型 - 网页请求过程
- 客户端(浏览器)通过tcp传输,发送http请求给服务端
- 服务端接收到http请求后进行解析
- 服务端处理请求内容,组织响应内容
- 服务端将响应内容以http响应格式发送给浏览器
- 浏览器接收到响应内容,解析展示
HTTP请求(request)
- 请求行 : 具体的请求类别和请求内容
GET / HTTP/1.1
请求类别 请求内容 协议版本
请求类别:每个请求类别表示要做不同的事情
GET : 获取网络资源
POST :提交一定的信息,得到反馈
HEAD : 只获取网络资源的响应头
PUT : 更新服务器资源
DELETE : 删除服务器资源
CONNECT
TRACE : 测试
OPTIONS : 获取服务器性能信息 - 请求头:对请求的进一步解释和描述
Accept-Encoding: gzip - 空行
- 请求体: 请求参数或者提交内容
http响应(response)
响应格式:响应行,响应头,空行,响应体
- 响应行 : 反馈基本的响应情况
HTTP/1.1 200 OK
版本信息 响应码 附加信息
响应码 :
1xx 提示信息,表示请求被接收
2xx 响应成功
3xx 响应需要进一步操作,重定向
4xx 客户端错误
5xx 服务器错误 - 响应头:对响应内容的描述
Content-Type: text/html - 响应体:响应的主体内容信息
import socket
# 创建tcp套接字对象
sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址
sockfd.bind(('127.0.0.1', 9988))
# 设置监听套接字
sockfd.listen(3)
# 处理客户端连接
connfd, addr = sockfd.accept()
# 收发消息(网络传输数据用字节串)
data = connfd.recv(1024)
data = """HTTP/1.1 200 OK
Content-Type:text/html
Hello world
"""
n = connfd.send(data)
# 关闭套接字
connfd.close()
sockfd.close()