python IO网络编程
---恢复内容开始---
一、IO操作
1.定义:在内存中存在数据交换的操作认为是IO操作,具体可分为三大类:
1>和终端交互:如input、output等
2>和磁盘交互:将内存中的数据永久的存储在磁盘(硬盘、U盘等)中
3>和网络交互:将内存中的数据发送到网络中
2.程序分类:根据IO操作量的大小分类
1>IO密集型程序:在程序执行中有大量IO操作,而cpu运算较少。消耗cpu较少,耗时长。
注:IO操作有一个共同的特点:耗时长,效率低(影响计算机的运行速度主要是硬盘----硬盘读写到内存的速度慢,即IO操作慢),此外,阻塞在IO操作中随处可见
固态硬盘(SSD)和机械硬盘(HDD):
固态硬盘的读取速度普遍可以达到400M/s,在开机和数据的载入中,速度得到了有效的提升,大幅度的提高了电脑的运行能力。写入速度也可以达到130M/s以上,
在写入大数据时,更加高效的储存能力大大缩短了办公时间。其读写速度是普通机械硬盘的3-5倍。
2>计算密集型程序:程序运行中计算较多,IO操作相对较少。cpu消耗多,执行速度快,几乎没有阻塞。
1.定义:在内存中存在数据交换的操作认为是IO操作,具体可分为三大类:
1>和终端交互:如input、output等
2>和磁盘交互:将内存中的数据永久的存储在磁盘(硬盘、U盘等)中
3>和网络交互:将内存中的数据发送到网络中
2.程序分类:根据IO操作量的大小分类
1>IO密集型程序:在程序执行中有大量IO操作,而cpu运算较少。消耗cpu较少,耗时长。
注:IO操作有一个共同的特点:耗时长,效率低(影响计算机的运行速度主要是硬盘----硬盘读写到内存的速度慢,即IO操作慢),此外,阻塞在IO操作中随处可见
固态硬盘(SSD)和机械硬盘(HDD):
固态硬盘的读取速度普遍可以达到400M/s,在开机和数据的载入中,速度得到了有效的提升,大幅度的提高了电脑的运行能力。写入速度也可以达到130M/s以上,
在写入大数据时,更加高效的储存能力大大缩短了办公时间。其读写速度是普通机械硬盘的3-5倍。
2>计算密集型程序:程序运行中计算较多,IO操作相对较少。cpu消耗多,执行速度快,几乎没有阻塞。
二、文件(内存与磁盘交互)
1.定义:文件是保存在持久化存储设备(硬盘、U盘、光盘..)上的一段数据。从功能角度分为文本文件(打开后会自动解码为字符)、二进制文件(视频、音频等)。
在Python里把文件视作一种类型的对象,类似之前学习过的其它类型。
2.字节串(bytes):在python3中引入了字节串的概念(用字节的方式表达字符串),与str不同,字节串以字节序列值表达数据,更方便用来处理二进程数据。因此在python3中字节串是常见的二进制数据展现方式。
1>普通的ascii编码字符串可以在前面加b转换为字节串,例如:b'hello' 程序代码为:print("bytes",b'hello')
2>字符串转换为字节串方法 :encode() 程序代码为: s="dahdjd大家好" print("bytes:",s.encode())
3>字节串转换为字符串方法 : decode(); 注只有utf-8编码形式的字节串可以转化为字符串 s="dahdjd大家好" b = s.encode() print("str:",b.decode())
注:
1>字节串是python表达二进制编码的一种形式
2>字符串转换为字节串用encode()或者ascii码字符串前加b或者通过bytes()函数将字符串强转为字节串(如bytes(“abc”,encoding="utf-8"))
3>字节串转换为字符串用decode()
4>通常情况下,只用utf-8的编码才可以用encode和decode相互转换
补充:
1>二进制(应用程序以字节为单位)
1G = 1024M 1M = 1024KB 1K = 1024bytes(应用层最小的存储单元为字节) 1byte = 8bit
2>bytes()内建函数
1>功能:将数据转换为字节串
2>参数: 整数n时,初始化一个长度为n的列表序列
字符串时, 将字符串转化为字节串
0-255整数列表时, 将列表整数转换为bytes字节串
1.定义:文件是保存在持久化存储设备(硬盘、U盘、光盘..)上的一段数据。从功能角度分为文本文件(打开后会自动解码为字符)、二进制文件(视频、音频等)。
在Python里把文件视作一种类型的对象,类似之前学习过的其它类型。
2.字节串(bytes):在python3中引入了字节串的概念(用字节的方式表达字符串),与str不同,字节串以字节序列值表达数据,更方便用来处理二进程数据。因此在python3中字节串是常见的二进制数据展现方式。
1>普通的ascii编码字符串可以在前面加b转换为字节串,例如:b'hello' 程序代码为:print("bytes",b'hello')
2>字符串转换为字节串方法 :encode() 程序代码为: s="dahdjd大家好" print("bytes:",s.encode())
3>字节串转换为字符串方法 : decode(); 注只有utf-8编码形式的字节串可以转化为字符串 s="dahdjd大家好" b = s.encode() print("str:",b.decode())
注:
1>字节串是python表达二进制编码的一种形式
2>字符串转换为字节串用encode()或者ascii码字符串前加b或者通过bytes()函数将字符串强转为字节串(如bytes(“abc”,encoding="utf-8"))
3>字节串转换为字符串用decode()
4>通常情况下,只用utf-8的编码才可以用encode和decode相互转换
补充:
1>二进制(应用程序以字节为单位)
1G = 1024M 1M = 1024KB 1K = 1024bytes(应用层最小的存储单元为字节) 1byte = 8bit
2>bytes()内建函数
1>功能:将数据转换为字节串
2>参数: 整数n时,初始化一个长度为n的列表序列
字符串时, 将字符串转化为字节串
0-255整数列表时, 将列表整数转换为bytes字节串
3.文件读写-----三步(打开文件,读写文件,关闭文件)内建函数实现三步
1>打开文件:
file_object = open(file_name, access_mode='r', buffering=-1)
功能:打开一个文件,返回一个文件对象。
参数:file_name————文件名(可包含路径);
access_mode————打开文件的方式,如果不写默认为‘r’
buffering————参数0表示无缓冲(即边读编写),1表示有行缓冲(遇到换行时与磁盘交互一次),如果是大于1表示直接指明缓冲区大小。如果不写或为负数则采用系统默认的缓冲行为, 返回值:成功返回文件流对象。失败得到IOError。
注:
1>缓冲:系统自动的在内存中为每一个正在使用的文件开辟一个缓冲区,从内存向磁盘输出数据必须先送到内存缓冲区,装满缓冲区在一起送到磁盘中去。从磁盘中读数据,
则一次从磁盘文件将一批数据读入到内存缓冲区中,然后再从缓冲区逐个的将数据送到程序的数据区。这样减少了内存与磁盘的交互次数,提高磁盘读写效率,
也保护了磁盘的使用寿命。(如:word工作时突然断电,重新开启时可以加载就是利用了缓冲)
2>buffering = 1时表示行缓存,在写入数据时遇到\n时,自动刷新缓冲区,即将缓冲区的内容写入磁盘
3>采用系统默认缓冲时,需要缓冲区满后才能自动写入磁盘
4>无论什么缓冲,当程序中断结束或者文件被关闭的时候都会讲缓冲区的内容写入磁盘
5>flush(),该函数调用后会进行一次磁盘交互,将缓冲区里的内容写入到磁盘中
文件模式 操作
r 以读方式打开 文件必须存在
w 以写方式打开文件不存在则创建,存在清空原有内容
a 以追加模式打开
r+ 以读写模式打开 文件必须存在
w+ 以读写模式打开文件不存在则创建,存在清空原有内容
a+ 以读写模式打开 追加模式
rb 以二进制读模式打开 同r
wb 以二进制写模式打开 同w
ab 以二进制追加模式打开 同a
rb+ 以二进制读写模式打开 同r+
wb+ 以二进制读写模式打开 同w+
ab+ 以二进制读写模式打开 同a+
补充:
文件偏移量(文件指针):
1.定义:打开一个文件进行操作时系统会自动生成一个记录,记录中描述了我们对文件的一系列操作。其中包括每次操作到的文件位置。文件的读写操作都是从这个位置开始进行的(简而言之:其代表文件的当前读写操作位置,随读写操作移动)。
1>以r或者w方式打开文件时,便宜量在开头
2>以a方式打开文件时,偏移量在末尾
2.人为控制偏移量,函数为seek(offset[,whence]):
功能:移动文件偏移量位置
参数:offset代表字节数,代表相对于某个位置的偏移量(以字节数表示),负数表示向前移动,正数表示向后移动
whence代表基准位置,默认值为0,代表从文件开头算起,1代表从当前位置算起,2代表从文件末尾算起
3.tell()
功能:获取文件偏移量
返回值:相对文件开头偏移量,值为字节数
文件描述符:
1.定义:系统中每一个IO操作都会被分配一个整数作为编号,该整数即这个IO操作的文件描述符(便于操作系统识别,不会重复)。
2.获取文件描述符:
fileno():通过IO对象获取对应的文件描述符,返回值为编号数
文件管理函数:
1.获取文件大小:os.path.getsize(file)
2.查看文件列表:os.listdir(dir)
3.查看文件是否存在:os.path.exists(file)
4.判断文件类型是否为普通文件:os.path.isfile(file) 普通文件第一位“-”是普通文件,为“d”的是目录
5.删除文件:os.remove(file)
2>读写文件:
1.读取文件(三种方式):
1.read([size]):用来直接读取字节到字符串中,最多读取给定数目个字节。如果没有给定size参数(默认值为-1)或者size值为负,文件将被读取直至末尾。文件过大时候建议在non-blocking模式下使用。
2.readline([size]):读取打开文件的一行(读取下个行结束符之前的所有字节)。然后整行,包括行结束符,作为字符串返回。和 read() 相同,它也有一个可选的 size 参数,默认为 -1,代表读至行结束符。如果提供了该参数,那么在超过size个字节后会返回不完整的行。
3.readlines([sizeint]):该方法并不像其它两个输入方法一样返回一个字符串。它会读取所有(剩余的)行然后把它们作为一个字符串列表返回。它的可选参数sizeint代表返回的最大字节大小。
注:
文件对象本身也是一个迭代器,在for循环中可以迭代文件的每一行:
for line in f:
print(line)
2.写入文件(两种方式):
1.write(string):功能与 read() 和 readline() 相反。它把含有文本数据或二进制数据块的字符串写入到文件中去。
2.writelines(str_list):和 readlines() 一样,writelines()方法是针对列表的操作,它接受一个字符串列表作为参数,将它们写入文件。行结束符并不会被自动加入,所以如果需要的话,你必须在调用writelines()前给每行结尾加上行结束符。
3>关闭文件:
file_object.close()
打开一个文件后我们就可以通过文件对象对文件进行操作了,当操作结束后使用close()关闭这个对象可以防止一些误操作,也可以节省资源。
4>with操作:
python中的with语句使用于对资源进行访问的场合,保证不管处理过程中是否发生错误或者异常都会执行规定的“清理”操作,释放被访问的资源,比如有文件读写后自动关闭、线程中锁的自动获取和释放等。
with语句的语法格式如下:
with context_expression [as target(s)]:
with-body
通过with方法可以不用close(),因为with生成的对象在语句块结束后会自动处理,所以也就不需要close了,但是这个文件对象只能在with语句块内使用。
with open('file','r+') as f:
f.read()
三、网络编程(内存与网络交互)
网络功能:实现资源共享,实现数据信息的快速传递。
七层模型/TCP/IP模型是指导思想,TCP协议和UDP协议是方案,套接字是实现方案的手段
注:应用层协议主要是TCP和UDP协议
1>OSI七层模型:
制定组织: ISO(国际标准化组织)
作用:使网络通信工作流程标准化
具体为:
应用层 : 提供用户服务,具体功能有应用程序实现
表示层 : 数据的压缩优化加密
会话层 : 建立用户级的连接,选择适当的传输服务
传输层 : 提供传输服务
网络层 : 路由选择,网络互联
链路层 : 进行数据交换,将数据转换为0和1形式的高低电流,控制具体数据的发送
物理层 : 提供数据传输的硬件保证,网卡接口,传输介质
优点:
1. 建立了统一的工作流程
2. 分部清晰,各司其职,每个步骤分工明确
3. 降低了各个模块之间的耦合度,便于开发(编程思想:高内聚(单个封装模块的功能单一),低耦合(各个封装模块之间的关联性小))
2>四层模型(TCP/IP模型):
背景 : 实际工作中工程师无法完全按照七层模型要求操作,逐渐演化为更符合实际情况的四层
数据传输过程:
1. 发送端由应用程序发送消息,逐层添加首部信息,最终在物理层发送消息包。
2. 发送的消息经过多个节点(交换机,路由器)传输,最终到达目标主机。
3. 目标主机由物理层逐层解析首部消息包,最终到应用程序呈现消息。
注:只要把应用层做好后,其他全部交给操作系统执行
补充:
网络协议:在网络数据传输中,都遵循的规定,包括建立什么样的数据结构,什么样的特殊标志等。
3>网络基础概念
1.网络主机(host):
功能:标识一台主机在网络中的位置(地址)
本地地址 : 'localhost' , '127.0.0.1',只能本机上的程序相互访问,其他主机无法访问
网络地址 : '172.40.91.185',其他主机可以访问
自动获取地址: '0.0.0.0',本机和其他主机都可以访问
查看本机网络地址命令: ifconfig
2.IP地址:
功能:确定一台主机的网络路由位置
结构:
IPv4 点分十进制表示 172.40.91.185 每部分取值范围0--255
IPv6 128位 扩大了地址范围
特殊IP:
127.0.0.1 本机测试IP
0.0.0.0 自动获取本机网卡地址
172.40.91.0 通常表示一个网段,如:局域网(交换机内的各主机相互传输),其传输数据速度快(中间节点少)
172.40.91.1 通常表示一个网关,如:交换机的地址,外界网络主机访问该交换机项下的主机时,需要先访问该交换机地址
172.40.91.255 用作广播地址
3.域名:
定义: 给网络服务器地址起的名字(网络主机IP地址 的别称)
作用: 方便记忆,表达一定的含义
ping [ip/域名] : 测试和某个主机是否联通
4.端口号(port):
作用:端口是网络地址的一部分,用于区分主机上不同的网络应用程序。
特点:一个系统中的应用监听端口不能重复
取值范围: 1 -- 65535
1--1023 系统应用或者大众程序监听端口
1024--65535 自用端口
4>传输层服务
1.面向连接的传输服务(基于TCP协议的数据传输)
1.传输特征:提供了可靠的数据传输,可靠性指数据传输过程中无丢失,无失序,无差错,无重复。
2.实现手段 : 在通信前需要建立数据连接,通信结束要正常断开连接。
三次握手(建立连接)
客户端向服务器发送消息报文请求连接
服务器收到请求后,回复报文确定可以连接
客户端收到回复,发送最终报文连接建立
四次回收(断开连接)
主动方发送报文请求断开连接
被动方收到请求后,立即回复,表示准备断开
被动方准备就绪,再次发送报文表示可以断开
主动方收到确定,发送最终报文完成断开
3.适用情况 : 对数据传输准确性有明确要求,传数文件较大,需要确保可靠性的情况。比如:网页获取,文件下载,邮件收发。
2.面向无连接的传输服务(基于UDP协议的数据传输)
1. 传输特点 : 不保证传输的可靠性,传输过程没有连接和断开,数据收发自由随意。
2. 适用情况 : 网络较差,对传输可靠性要求不高。比如:网络视频,群聊,广播
3.重点掌握:
OSI七层模型介绍一下,tcp/ip模型是什么?
tcp服务和udp服务有什么区别?
三次握手和四次挥手指什么,过程是怎样的?
4.TCP和UDP对比:
1.TCP传输需要建立连接和断开,而UDP传输不需要建立连接和断开
2.因为TCP传输必须先建立连接,事后必须断开,因而,其传输效率低于UDP传输
3.TCP数据传输可靠,UDP数据传输不稳定
5>socket套接字编程(实现基于TCP协议或者UDP协议数据网络传输的手段或者方法)--------网络编程和网络数据传输的手段方法
1.基本介绍:
1. 套接字 : 实现网络编程进行数据传输的一种技术手段
2. Python实现套接字编程:import socket
3. 套接字分类
1.流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案。(面向连接--tcp协议--可靠的--流式套接字)-------TCP套接字(俗称)
2.数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案。(无连接--udp协议--不可靠--数据报套接字)-------UDP套接字(俗称)
2.tcp套接字编程(流式套接字)
1. 服务端流程(见代码1)
1. 创建套接字
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 选择子协议,应用层用不到这个参数(因为应用层网络编程只有流式套接字和数据报套接字,而对应的TCP协议和UDP协议都没有子协议)
返回值: 套接字对象
2.绑定地址
sockfd.bind(addr)
功能: 绑定本机网络地址(服务器地址)
参数: 二元元组 (ip,port) ('0.0.0.0',8888)----端口号是网络地址的一部分,IP和端口号组成在一起构成完整的地址,在Python中网络地址的表现形式都是二元元组的形式
3.设置监听
sockfd.listen(n)
功能 :其一, 将套接字设置为监听套接字(使得普通流式套接字变成可以与客户端连接的监听套接字,可以同时连接多个客户端),其二,确定监听队列大小(既然可以连接多个客户端,就需要一个一个连接,这样客户端就需要排队等待连接)
---------该步骤确保所创建的套接字具备对别的客户端有 连接的功能,即该创建的套接字可以被客户端连接)
参数 : 监听队列大小(值的大小表明可以一次性容纳多少个客户端等待连接,如果同时需要连接的客户端数量大于监听队列值,则超过的客户端连接请求就会被服务端拒绝)在linux系统中n是无用的,因为linux系统会默认设置监听队列大小)
4.等待处理客户端连接请求
connfd,addr = sockfd.accept()
功能: 阻塞等待处理客户端请求
返回值: connfd 客户端连接套接字,专门用来与客户端通信的套接字,而sockfd套接字是专门用来建立连接的,connfd专门用来与客户端通信的(负责消息的收发工作),每个客户端与服务端建立连接时,服务端都会为每个客户端创建专属套接字与其通信
addr 连接的客户端地址
注:阻塞即程序运行至此无法继续运行,知道某种条件满足才继续向下执行,例如阻塞函数,在IO操作中很常见
5.消息收发
data = connfd.recv(buffersize)
功能 : 接受客户端消息
参数 :每次最多接收消息的大小,单位是字节数
返回值: 接收到的内容
n = connfd.send(data)
功能 : 发送消息
参数 :要发送的内容 (bytes格式)----在python3中要求,所有跟网络相关的数据消息传输要求必须是bytes格式(字节串格式)
返回值: 发送的字节数
6.关闭套接字
sockfd.close()
功能:关闭套接字
2.客户端流程(见代码1)(数据包套接字)
1. 创建套接字
同服务端,必须确保创建的套接字类型相同,这样才能建立通信
2.请求连接
sockfd.connect(server_addr)
功能:连接服务器
参数:元组 服务器地址
3.收发消息
与服务端相同,必须注意一端接收时,另一端必须为发送,否则会产生阻塞
4.关闭套接字
同服务端
注:
1.bind可以绑定客户端本机地址,也可以不绑定,不绑定的话,客户端的端口号由系统随机分配,连接的时候(connect会把客户端IP和随机分配的端口号给服务端)
2.一个客户端只会连接一个服务端,而服务端(监听套接字)会连接多个客户端,因此客户端就用socket创建的套接字对象进行消息收发即可,不像服务端还需要再创建新套接字对象专门收发消息
3.tcp 套接字数据传输特点(见代码2):
1.tcp连接中当一端退出,另一端如果阻塞在recv,此时recv会立即返回一个空字串,然后继续向下执行。
2.tcp连接中如果一端已经不存在,仍然试图通过send发送则会产生BrokenPipeError异常(第一次可以正常发出,但是没有客户端接收,待循环到第二次时,再次send,则会产生异常)
3.一个监听套接字可以同时连接多个客户端,也能够重复被连接
4.网络收发缓冲区
1. 网络缓冲区有效的协调了消息的收发速度(将服务器的recv()字节串参数的值减小到客户端发送的字节串值以下,会造成,服务端多次接收客户端消息,客户端却一次性接收多条服务端的消息(粘包)),如网速不好的情况下,可以很好地协调收发速度
2. send和recv实际是向缓冲区发送接收消息,当缓冲区不为空recv就不会阻塞。
3.缓冲区作用:协调网络消息收发速度或者减少交互次数
5.tcp粘包
原因:收发消息的速度不协调和tcp以字节流方式传输,没有消息边界。多次发送的消息被一次接收,此时就会形成粘包。
影响:如果每次发送内容是一个独立的含义(如发送账号密码时,必须将两者区分开,就会产生影响),需要接收端独立解析此时粘包会有影响。
处理方法:
1. 人为的添加消息边界:加特殊符号
2. 控制发送速度:加阻塞延迟
2.UDP套接字编程
1.服务端流程
1. 创建数据报套接字
sockfd = socket(AF_INET,SOCK_DGRAM)
2.绑定地址
sockfd.bind(addr)
3.消息收发
data,addr = sockfd.recvfrom(buffersize)
功能: 接收UDP消息
参数: 每次最多接收多少字节
返回值: data 接收到的内容
addr 消息发送方地址
n = sockfd.sendto(data,addr)
功能: 发送UDP消息
参数: data 发送的内容 bytes格式
addr 目标地址
返回值:发送的字节数
4. 关闭套接字
sockfd.close()
2.客户端流程
1. 创建套接字
2. 收发消息
3. 关闭套接字
注:如果一次性接收不完对方发来的信息,则,剩余的就会丢失(可以通过将服务端的recvfrom函数的字节值设置小于客户端的sendto 的字节值),即允许数据传输的丢失
总结 :tcp套接字和udp套接字编程区别
1. 流式套接字是以字节流方式传输数据,数据报套接字以数据报形式传输
2. tcp套接字会有粘包,udp套接字有消息边界不会粘包
3. tcp套接字保证消息的完整性,udp套接字则不能
4. tcp套接字依赖listen accept建立连接才能收发消息,udp套接字则不需要
5. tcp套接字使用send,recv收发消息,udp套接字使用sendto,recvfrom
3.socket模块方法和socket套接字属性
1. 部分socket模块方法
【1】 gethostname() 获取计算机名
【2】 gethostbyname('www.baidu.com') 获取主机ip地址
【3】 getservbyname('mysql') 获取服务端口号
【4】 getservbyport(3306) 获取端口对应服务
【5】 inet_aton('192.168.1.2') 将IP转换为bytes子串
【6】 inet_ntoa(b'\xc0\xa8\x01\x02') 将bytes子串转换为IP地址
2.套接字属性
【1】 sockfd.type 套接字类型,(是流式套接字还是数据报套接字)
【2】 sockfd.family 套接字地址类型(是IPV4还是IPV6)
【3】 sockfd.getsockname() 获取套接字绑定地址
【4】 sockfd.fileno() 获取套接字的文件描述符
【5】 sockfd.getpeername() 获取连接套接字客户端地址
【6】 sockfd.setsockopt(level,option,value)
功能:设置套接字选项
参数: level 选项类别 常用的为SOL_SOCKET,其项下具体子选项见下图
option 具体选项内容
value 选项值
例:设置端口号立即被重用:sockfd.setsockopt(SOL_SOCKETE,SO_REUSEADDR,1)
【7】 sockfd.getsockopt(level,option)
功能 : 获取套接字选项值
4.UDP套接字广播
广播定义 : 一端发送多点接收
广播地址 : 每个网络的最大地址为发送广播的地址,向该地址发送,则网段内所有主机都能接收。
5.TCP套接字之HTTP传输(作为应用层协议,其在选择传输层协议时使用了TCP传输)
1.HTTP协议 (超文本传输协议)
1. 用途 : 网页获取,数据的传输
2. 特点:
应用层协议,传输层使用tcp传输
简单,灵活,很多语言都有HTTP专门接口
无状态,协议不记录传输内容
http1.1 支持持久连接,丰富了请求类型
3. 网页请求过程
1.客户端(浏览器)通过tcp传输,发送http请求给服务端
-------即在浏览器地址栏输入域名,按回车即发送了http请求,这个请求必须要有固定的格式,格式由http协议规定,人为输入不规范时,浏览器会帮忙组织规范的请求,然后将规范的请求内容交由TCP发送
2.服务端接收到http请求后进行解析
3.服务端处理请求内容,组织响应内容
4.服务端将响应内容以http响应格式发送给浏览器
5.浏览器接收到响应内容,解析展示
注:
1.浏览器:用来呈现网页内容的软件,主要有谷歌浏览器,火狐浏览器,IE浏览器等
2.HTTP请求(request)
1.请求行 : 具体的请求类别和请求内容(发起请求的目的)
GET / HTTP/1.1
请求类别 请求内容 协议版本
请求类别:每个请求类别表示要做不同的事情
GET : 获取网络资源
POST :提交一定的信息,得到反馈
HEAD : 只获取网络资源的响应头
PUT : 更新服务器资源
DELETE : 删除服务器资源
CONNECT
TRACE : 测试
OPTIONS : 获取服务器性能信息
2.请求头:对请求的进一步解释和描述(键值对表示,一个键值对占一行,键的含义由http协议)
Accept-Encoding: gzip
3.空行
4.请求体: 请求参数或者提交内容
注:一个http请求中必须要有请求行和空行,客户端是浏览器,其内部也是用socket实现的,只不过其用软件封装了
3.http响应(response)
1.响应格式:响应行,响应头,空行,响应体
响应行 : 反馈基本的响应情况
HTTP/1.1 200 OK
版本信息 响应码 附加信息 响应吗和附加信息是成套匹配的
响应码 :
1xx 提示信息,表示请求被接收-----------后端可以看到,前段看不见
2xx 响应成功
3xx 响应需要进一步操作,重定向---------------请求接受到了,发送的请求网址解决不了,但是其可以将请求继续转发给可以解决问题的网址
4xx 客户端错误
5xx 服务器错误
响应头:对响应内容的描述(键值对表示,一个键值对占一行,键的含义由http协议)
Content-Type: text/html
响应体:响应的主体内容信息(反馈给客户端的内容)
注:谷歌浏览器要求必须按照http协议要求的响应格式书写才能反馈给客户端,火狐浏览器可以对格式不做要求,自动解析
6.struct模块的使用------主要用于与其他编程语言进行数据网络传输互动,每种编程语言都有套接字,但是对套接字的解析方法是不一样的,即struct模块用于不同编程语言的套接字信息传输转换
1. 原理: 将一组简单数据进行打包,转换为bytes格式发送。或者将一组bytes格式数据,进行解析。即主要两种功能:打包数据和解析数据,主要通过以下接口完成
2. 接口使用:
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。其他用法功能相同
小知识点:
1.Python异常分类(三类):Exception(父类)、KeyboardInterrupt(键盘信号异常)、系统异常
2.TCP套接字在客户端和服务端进行数据传输时,当服务端的recv()函数的接收字节值小于客户端的发送字节值时,会造成服务端多次接收消息,而客户端却一次性接收多条服务端的消息(原因:网络消息的收发缓冲区造成的)
3.网络消息的收发缓冲区作用:协调网络消息的收发速度,而文件读写缓冲区是减少内存与磁盘的交互次数
附:重点基础代码:
代码1:基于TCP协议(流式套接字)的客户端和服务端套接字连接编程:
注:该代码仅仅建立客户端和服务端的简单连接
""" TCP套接字服务端 """ import socket # print(dir(socket)) sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sockfd.bind(('0.0.0.0',8888)) sockfd.listen(5) print("Waiting for connect...") connfd,addr = sockfd.accept() data =connfd.recv(1024) print("服务端接收到的信息:",data.decode()) n = connfd.send("收到您的来信".encode()) print("服务端发送了 %d个字节的数据"%n) connfd.close() sockfd.close() """ tcp客户端程序 """ from socket import * sockfd = socket() sockfd.connect(('127.0.0.1',8888)) n = sockfd.send("小贱你好".encode()) print("客户端发送了 %d个字节消息"%n) data = sockfd.recv(1024) print("客户端收到的消息:",data.decode()) sockfd.close()
代码2:基于TCP协议的套接字连接,实现多客户端多次发送和接收消息:
注:这种多客户连接不是同时连接服务器的,只用上一个客户端断开才可以建立下一个客户端的连接
""" TCP套接字服务端 """ import socket # 创建流式套接字 sockfd = socket.socket(socket.AF_INET, \ socket.SOCK_STREAM) # 绑定地址 sockfd.bind(('127.0.0.1', 8880)) # 设置监听 sockfd.listen(5) # 等待处理客户端链接(多客户连接) while True: print("Waiting for connect....") try: connfd, addr = sockfd.accept() print("Connect from:", addr) except KeyboardInterrupt: print("退出服务") break # 收发消息 while True: data = connfd.recv(1024) # 得到空则退出循环(当客户端断开的时候,服务端的recv会立即结束阻塞,返回一个空子串) if not data: break print("接收到的消息:", data.decode()) n = connfd.send(b'Receive your message') print("发送了 %d 个字节数据" % n) connfd.close() # 关闭套接字 sockfd.close() """ tcp 客户端程序 """ from socket import * # 创建tcp套接字 sockfd = socket() # 发起连接 server_addr = ('172.40.91.188',8888) sockfd.connect(server_addr) # 收发消息 while True: data = input("消息:") if not data: break sockfd.send(data.encode()) data = sockfd.recv(1024) print("From server:",data.decode()) # 关闭 sockfd.close()
代码3:基于UDP协议的(数据报套接字)客户端和服务端套接字连接编程:
注:该代码仅仅建立客户端和服务端的简单连接
""" UDP套接字服务端 """ from socket import * # 创建数据报套接字 sockfd = socket(AF_INET, SOCK_DGRAM) # 绑定地址 server_addr = ('127.0.0.1',8880) sockfd.bind(server_addr) # 收发消息 while True: data,addr = sockfd.recvfrom(1024) print("收到的消息:",data.decode()) sockfd.sendto(b"Thanks",addr) # 关闭套接字 sockfd.close() """ udp套接字客户端 """ from socket import * # 服务器地址 HOST = '127.0.0.1' PORT = 8880 ADDR = (HOST,PORT) # 创建套接字 sockfd = socket(AF_INET,SOCK_DGRAM) # 收发消息 while True: data = input("Msg>>") if not data: break sockfd.sendto(data.encode(),ADDR) msg,addr = sockfd.recvfrom(1024) print("From server:",msg.decode()) sockfd.close()
练习:
""" 编写一个程序完成如下。 1. 每隔1秒向文件test.txt中写入一行数据,格式如下 1. 2019-7-30 12:12:12 2. 2019-7-30 12:12:13 3. 2019-7-30 12:12:19 ..... 2. 该程序无限循环,ctrl-c退出 3. 当重启程序时,内容会继续向下写,序号能够接上之前的 """ import time f = open('time.txt','a+') f.seek(0) # 文件偏移量移动到开头 n = 1 for line in f: n += 1 while True: time.sleep(1) s = "%d. %s\n"%(n,time.ctime()) f.write(s) f.flush() n += 1
代码4:广播----UDP套接字应用
# 广播接受 """ 1. 创建udp套接字 2. 设置套接字为可以接收广播 3. 选择接收端口 """ from socket import * s = socket(AF_INET,SOCK_DGRAM) # 让套接字可以接收广播 s.setsockopt(SOL_SOCKET,SO_BROADCAST,1) s.bind(('0.0.0.0',9999)) while True: try: msg,addr = s.recvfrom(1024) except KeyboardInterrupt: break else: print(msg.decode()) s.close() # 广播发送 from socket import * from time import sleep # 广播地址 dest = ('172.40.91.255',9999) s = socket(AF_INET,SOCK_DGRAM) s.setsockopt(SOL_SOCKET,SO_BROADCAST,1) data = """ ********************** 5.8 北京 初夏 喜欢夏天,但你比夏天更加明媚 ********************** """ while True: sleep(2) s.sendto(data.encode(),dest) s.close()
代码5:http传输---------TCP套接字应用
from socket import * # 创建tcp套接字 s = socket() s.bind(('0.0.0.0',8000)) s.listen(3) c,addr = s.accept() print("Connect from",addr) data = c.recv(4096) print(data) # http响应格式 data = """HTTP/1.1 200 OK Content-Type:text/html <h1>hello world</h1> """ c.send(data.encode()) c.close() s.close()
""" http 功能演示 将网页发送给浏览器展示 """ from socket import * # 处理浏览器的http请求 def handle(connfd): print("Request from", connfd.getpeername()) request = connfd.recv(4096) # 接收http请求 # 防止客户端断开 if not request: return # 将request按行分割 request_line = request.splitlines()[0].decode() # 获取请求内容 info = request_line.split(' ')[1] if info == '/': f = open('index.html') response = "HTTP/1.1 200 OK\r\n" response += "Content-Type: text/html\r\n" response += '\r\n' response += f.read() else: response = "HTTP/1.1 404 Not Found\r\n" response += "Content-Type: text/html\r\n" response += '\r\n' response += "<h1>Sorry....</h1>" # 向浏览器发送内容 connfd.send(response.encode()) # 搭建tcp网络 def main(): sockfd = socket() sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sockfd.bind(('0.0.0.0', 8000)) sockfd.listen(3) print("Listen the port 8000...") while True: connfd, addr = sockfd.accept() handle(connfd) # 处理浏览器请求 connfd.close() if __name__ == "__main__": main()
代码6:struct模块实现对约定数据格式的其他语言数据在Python中的转换
import struct # 方法1:利用Struct类创建对象 # 生成struct数据结构对象 st = struct.Struct("i8sif") # 打包的数据要与创建的数据结构对应,且所传的数据中字符串必须转化成字节串 data = st.pack(1, b'zhangsan', 16, 78.5) print(data) # 数据为字节串格式 # 将数据解析 d = st.unpack(data) print(d) # 数据为元组 # 方法2:直接利用模块调用方法 data = struct.pack("i4sf", 1, b"Lily", 80.5) print(data) d = struct.unpack("i4sf", data) print(d)
代码7:利用struct模块,对传输数据节结构化传输,接收端将发送端发送的结构化数据存储在文件中-----使用UDP传输
""" 接收端 """ from socket import * import struct s = socket(AF_INET, SOCK_DGRAM) s.bind(('127.0.0.1', 8888)) # 确定数据结构 st = struct.Struct("i32sif") # 打开文件 f = open('student.txt', 'a') while True: data, addr = s.recvfrom(1024) # 数据解析 data = st.unpack(data) # 整理写入内容 info = "%d %s %d %.2f\n"%(\ data[0],data[1].decode(), data[2], data[3]) f.write(info) f.close() s.close() """ 发送端 """ from socket import * import struct # 接收端地址 ADDR = ('127.0.0.1',8888) # 规定数据格式 st = struct.Struct('i32sif') s = socket(AF_INET,SOCK_DGRAM) while True: print("=================================") id = int(input("ID:")) name = input("NAME:").encode() age = int(input("AGE:")) score = float(input("SCORE:")) # 数据打包 data = st.pack(id,name,age,score) s.sendto(data,ADDR) s.close()
---恢复内容结束---
2.TCP数据传输可靠,UDP数据传输不稳定